mirror of
https://github.com/discourse/discourse.git
synced 2024-11-30 12:43:54 -06:00
REFACTOR: Fix pluralized strings in chat plugin (#20357)
* FIX: Use pluralized string * REFACTOR: Fix misuse of pluralized string * REFACTOR: Fix misuse of pluralized string * DEV: Remove linting of `one` key in MessageFormat string, it doesn't work * REFACTOR: Fix misuse of pluralized string This also ensures that the URL works on subfolder and shows the site setting link only for admins instead of staff. The string is quite complicated, so the best option was to switch to MessageFormat. * REFACTOR: Fix misuse of pluralized string * FIX: Use pluralized string This also ensures that the URL works on subfolder and shows the site setting link only for admins instead of staff. * REFACTOR: Correctly pluralize reaction tooltips in chat This also ensures that maximum 5 usernames are shown and fixes the number of "others" which was off by 1 if the current user reacted on a message. * REFACTOR: Use translatable string as comma separator * DEV: Add comment to translation to clarify the meaning of `%{identifier}` * REFACTOR: Use translatable comma separator and use explicit interpolation keys * REFACTOR: Don't interpolate lowercase channel status * REFACTOR: Fix misuse of pluralized string * REFACTOR: Don't interpolate channel status * REFACTOR: Use %{count} interpolation key * REFACTOR: Fix misuse of pluralized string * REFACTOR: Correctly pluralize DM chat channel titles
This commit is contained in:
parent
d71a82786a
commit
7ef482a292
@ -138,7 +138,8 @@ class TranslationOverride < ActiveRecord::Base
|
||||
:base,
|
||||
I18n.t(
|
||||
"activerecord.errors.models.translation_overrides.attributes.value.invalid_interpolation_keys",
|
||||
keys: invalid_keys.join(", "),
|
||||
keys: invalid_keys.join(I18n.t("word_connector.comma")),
|
||||
count: invalid_keys.size,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -161,6 +161,9 @@ en:
|
||||
email: "Send via email"
|
||||
url: "Copy and share URL"
|
||||
|
||||
word_connector:
|
||||
comma: ", "
|
||||
|
||||
action_codes:
|
||||
public_topic: "Made this topic public %{when}"
|
||||
open_topic: "Converted this to a topic %{when}"
|
||||
|
@ -641,7 +641,9 @@ en:
|
||||
translation_overrides:
|
||||
attributes:
|
||||
value:
|
||||
invalid_interpolation_keys: 'The following interpolation key(s) are invalid: "%{keys}"'
|
||||
invalid_interpolation_keys:
|
||||
one: 'The following interpolation key is invalid: %{keys}'
|
||||
other: 'The following interpolation keys are invalid: %{keys}'
|
||||
watched_word:
|
||||
attributes:
|
||||
word:
|
||||
|
@ -75,10 +75,6 @@ class ChatChannel < ActiveRecord::Base
|
||||
Chat::ChatChannelMembershipManager.new(self).unfollow(user)
|
||||
end
|
||||
|
||||
def status_name
|
||||
I18n.t("chat.channel.statuses.#{self.status}")
|
||||
end
|
||||
|
||||
def url
|
||||
"#{Discourse.base_url}/chat/c/#{self.slug || "-"}/#{self.id}"
|
||||
end
|
||||
|
@ -52,7 +52,7 @@ class ChatMessage < ActiveRecord::Base
|
||||
:base,
|
||||
I18n.t(
|
||||
"chat.errors.minimum_length_not_met",
|
||||
minimum: SiteSetting.chat_minimum_message_length,
|
||||
count: SiteSetting.chat_minimum_message_length,
|
||||
),
|
||||
)
|
||||
end
|
||||
@ -60,7 +60,7 @@ class ChatMessage < ActiveRecord::Base
|
||||
if message_too_long?
|
||||
self.errors.add(
|
||||
:base,
|
||||
I18n.t("chat.errors.message_too_long", maximum: SiteSetting.chat_maximum_message_length),
|
||||
I18n.t("chat.errors.message_too_long", count: SiteSetting.chat_maximum_message_length),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -25,7 +25,7 @@ class DirectMessage < ActiveRecord::Base
|
||||
|
||||
# direct message to self
|
||||
if users.empty?
|
||||
return I18n.t("chat.channel.dm_title.single_user", user: "@#{acting_user.username}")
|
||||
return I18n.t("chat.channel.dm_title.single_user", username: "@#{acting_user.username}")
|
||||
end
|
||||
|
||||
# all users deleted
|
||||
@ -36,13 +36,16 @@ class DirectMessage < ActiveRecord::Base
|
||||
return(
|
||||
I18n.t(
|
||||
"chat.channel.dm_title.multi_user_truncated",
|
||||
users: usernames_formatted[0..4].join(", "),
|
||||
leftover: usernames_formatted.length - 5,
|
||||
comma_separated_usernames: usernames_formatted[0..4].join(I18n.t("word_connector.comma")),
|
||||
count: usernames_formatted.length - 5,
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
I18n.t("chat.channel.dm_title.multi_user", users: usernames_formatted.join(", "))
|
||||
I18n.t(
|
||||
"chat.channel.dm_title.multi_user",
|
||||
comma_separated_usernames: usernames_formatted.join(I18n.t("word_connector.comma")),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -40,7 +40,7 @@ class ChatMessageSerializer < ApplicationSerializer
|
||||
.reactions
|
||||
.group_by(&:emoji)
|
||||
.each do |emoji, reactions|
|
||||
users = reactions[0..6].map(&:user).filter { |user| user.id != scope&.user&.id }[0..5]
|
||||
users = reactions[0..5].map(&:user).filter { |user| user.id != scope&.user&.id }[0..4]
|
||||
|
||||
next unless Emoji.exists?(emoji)
|
||||
|
||||
|
@ -4,7 +4,6 @@ import Component from "@ember/component";
|
||||
import {
|
||||
CHANNEL_STATUSES,
|
||||
channelStatusIcon,
|
||||
channelStatusName,
|
||||
} from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||
|
||||
export default Component.extend({
|
||||
@ -38,20 +37,28 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
_shortStatusMessage(channelStatus) {
|
||||
return channelStatusName(channelStatus);
|
||||
switch (channelStatus) {
|
||||
case CHANNEL_STATUSES.archived:
|
||||
return I18n.t("chat.channel_status.archived");
|
||||
case CHANNEL_STATUSES.closed:
|
||||
return I18n.t("chat.channel_status.closed");
|
||||
case CHANNEL_STATUSES.open:
|
||||
return I18n.t("chat.channel_status.open");
|
||||
case CHANNEL_STATUSES.readOnly:
|
||||
return I18n.t("chat.channel_status.read_only");
|
||||
}
|
||||
},
|
||||
|
||||
_longStatusMessage(channelStatus) {
|
||||
switch (channelStatus) {
|
||||
case CHANNEL_STATUSES.closed:
|
||||
return I18n.t("chat.channel_status.closed_header");
|
||||
break;
|
||||
case CHANNEL_STATUSES.readOnly:
|
||||
return I18n.t("chat.channel_status.read_only_header");
|
||||
break;
|
||||
case CHANNEL_STATUSES.archived:
|
||||
return I18n.t("chat.channel_status.archived_header");
|
||||
break;
|
||||
case CHANNEL_STATUSES.closed:
|
||||
return I18n.t("chat.channel_status.closed_header");
|
||||
case CHANNEL_STATUSES.open:
|
||||
return I18n.t("chat.channel_status.open_header");
|
||||
case CHANNEL_STATUSES.readOnly:
|
||||
return I18n.t("chat.channel_status.read_only_header");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -19,7 +19,6 @@ import { readOnly, reads } from "@ember/object/computed";
|
||||
import { SKIP } from "discourse/lib/autocomplete";
|
||||
import { Promise } from "rsvp";
|
||||
import { translations } from "pretty-text/emoji/data";
|
||||
import { channelStatusName } from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||
import { setupHashtagAutocomplete } from "discourse/lib/hashtag-autocomplete";
|
||||
import {
|
||||
chatComposerButtons,
|
||||
@ -566,17 +565,21 @@ export default Component.extend(TextareaTextManipulation, {
|
||||
@discourseComputed("userSilenced", "chatChannel.{chatable.users.[],id}")
|
||||
placeholder(userSilenced, chatChannel) {
|
||||
if (!chatChannel.canModifyMessages(this.currentUser)) {
|
||||
return I18n.t("chat.placeholder_new_message_disallowed", {
|
||||
status: channelStatusName(chatChannel.status).toLowerCase(),
|
||||
});
|
||||
return I18n.t(
|
||||
`chat.placeholder_new_message_disallowed.${chatChannel.status}`
|
||||
);
|
||||
}
|
||||
|
||||
if (chatChannel.isDraft) {
|
||||
return I18n.t("chat.placeholder_start_conversation", {
|
||||
usernames: chatChannel?.chatable?.users?.length
|
||||
? chatChannel.chatable.users.mapBy("username").join(", ")
|
||||
: "...",
|
||||
});
|
||||
if (chatChannel?.chatable?.users?.length) {
|
||||
return I18n.t("chat.placeholder_start_conversation_users", {
|
||||
usernames: chatChannel.chatable.users
|
||||
.mapBy("username")
|
||||
.join(I18n.t("word_connector.comma")),
|
||||
});
|
||||
} else {
|
||||
return I18n.t("chat.placeholder_start_conversation");
|
||||
}
|
||||
}
|
||||
|
||||
if (userSilenced) {
|
||||
@ -596,14 +599,14 @@ export default Component.extend(TextareaTextManipulation, {
|
||||
return I18n.t("chat.placeholder_self");
|
||||
}
|
||||
|
||||
return I18n.t("chat.placeholder_others", {
|
||||
messageRecipient: directMessageRecipients
|
||||
return I18n.t("chat.placeholder_users", {
|
||||
commaSeparatedNames: directMessageRecipients
|
||||
.map((u) => u.name || `@${u.username}`)
|
||||
.join(", "),
|
||||
.join(I18n.t("word_connector.comma")),
|
||||
});
|
||||
} else {
|
||||
return I18n.t("chat.placeholder_others", {
|
||||
messageRecipient: `#${chatChannel.title}`,
|
||||
return I18n.t("chat.placeholder_channel", {
|
||||
channelName: `#${chatChannel.title}`,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -2,6 +2,7 @@ import Component from "@glimmer/component";
|
||||
import I18n from "I18n";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { inject as service } from "@ember/service";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
|
||||
export default class ChatMentionWarnings extends Component {
|
||||
@service siteSettings;
|
||||
@ -79,31 +80,22 @@ export default class ChatMentionWarnings extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
let notificationLimit = I18n.t(
|
||||
"chat.mention_warning.groups.notification_limit"
|
||||
);
|
||||
|
||||
if (this.currentUser.staff) {
|
||||
notificationLimit = htmlSafe(
|
||||
`<a
|
||||
target="_blank"
|
||||
href="/admin/site_settings/category/plugins?filter=max_mentions_per_chat_message"
|
||||
>
|
||||
${notificationLimit}
|
||||
</a>`
|
||||
if (this.currentUser.admin) {
|
||||
return htmlSafe(
|
||||
I18n.t("chat.mention_warning.too_many_mentions_admin", {
|
||||
count: this.siteSettings.max_mentions_per_chat_message,
|
||||
siteSettingUrl: getURL(
|
||||
"/admin/site_settings/category/plugins?filter=max_mentions_per_chat_message"
|
||||
),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return htmlSafe(
|
||||
I18n.t("chat.mention_warning.too_many_mentions", {
|
||||
count: this.siteSettings.max_mentions_per_chat_message,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const settingLimit = I18n.t("chat.mention_warning.mentions_limit", {
|
||||
count: this.siteSettings.max_mentions_per_chat_message,
|
||||
});
|
||||
|
||||
return htmlSafe(
|
||||
I18n.t("chat.mention_warning.too_many_mentions", {
|
||||
notification_limit: notificationLimit,
|
||||
limit: settingLimit,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get unreachableBody() {
|
||||
@ -111,17 +103,21 @@ export default class ChatMentionWarnings extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.unreachableGroupMentionsCount <= 2) {
|
||||
return I18n.t("chat.mention_warning.groups.unreachable", {
|
||||
group: this.unreachableGroupMentions[0],
|
||||
group_2: this.unreachableGroupMentions[1],
|
||||
count: this.unreachableGroupMentionsCount,
|
||||
});
|
||||
} else {
|
||||
return I18n.t("chat.mention_warning.groups.unreachable_multiple", {
|
||||
group: this.unreachableGroupMentions[0],
|
||||
count: this.unreachableGroupMentionsCount - 1, //N others
|
||||
});
|
||||
switch (this.unreachableGroupMentionsCount) {
|
||||
case 1:
|
||||
return I18n.t("chat.mention_warning.groups.unreachable_1", {
|
||||
group: this.unreachableGroupMentions[0],
|
||||
});
|
||||
case 2:
|
||||
return I18n.t("chat.mention_warning.groups.unreachable_2", {
|
||||
group1: this.unreachableGroupMentions[0],
|
||||
group2: this.unreachableGroupMentions[1],
|
||||
});
|
||||
default:
|
||||
return I18n.t("chat.mention_warning.groups.unreachable_multiple", {
|
||||
group: this.unreachableGroupMentions[0],
|
||||
count: this.unreachableGroupMentionsCount - 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,44 +126,18 @@ export default class ChatMentionWarnings extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
let notificationLimit = I18n.t(
|
||||
"chat.mention_warning.groups.notification_limit"
|
||||
return htmlSafe(
|
||||
I18n.messageFormat("chat.mention_warning.groups.too_many_members_MF", {
|
||||
groupCount: this.overMembersLimitMentionsCount,
|
||||
isAdmin: this.currentUser.admin,
|
||||
siteSettingUrl: getURL(
|
||||
"/admin/site_settings/category/plugins?filter=max_users_notified_per_group_mention"
|
||||
),
|
||||
notificationLimit:
|
||||
this.siteSettings.max_users_notified_per_group_mention,
|
||||
group1: this.overMembersLimitGroupMentions[0],
|
||||
group2: this.overMembersLimitGroupMentions[1],
|
||||
})
|
||||
);
|
||||
|
||||
if (this.currentUser.staff) {
|
||||
notificationLimit = htmlSafe(
|
||||
`<a
|
||||
target="_blank"
|
||||
href="/admin/site_settings/category/plugins?filter=max_users_notified_per_group_mention"
|
||||
>
|
||||
${notificationLimit}
|
||||
</a>`
|
||||
);
|
||||
}
|
||||
|
||||
const settingLimit = I18n.t("chat.mention_warning.groups.users_limit", {
|
||||
count: this.siteSettings.max_users_notified_per_group_mention,
|
||||
});
|
||||
|
||||
if (this.hasOverMembersLimitGroupMentions <= 2) {
|
||||
return htmlSafe(
|
||||
I18n.t("chat.mention_warning.groups.too_many_members", {
|
||||
group: this.overMembersLimitGroupMentions[0],
|
||||
group_2: this.overMembersLimitGroupMentions[1],
|
||||
count: this.overMembersLimitMentionsCount,
|
||||
notification_limit: notificationLimit,
|
||||
limit: settingLimit,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return htmlSafe(
|
||||
I18n.t("chat.mention_warning.groups.too_many_members_multiple", {
|
||||
group: this.overMembersLimitGroupMentions[0],
|
||||
count: this.overMembersLimitMentionsCount - 1, //N others
|
||||
notification_limit: notificationLimit,
|
||||
limit: settingLimit,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,37 +83,91 @@ export default class ChatMessageReaction extends Component {
|
||||
|
||||
@computed("reaction")
|
||||
get popoverContent() {
|
||||
let usernames = this.reaction.users.mapBy("username").join(", ");
|
||||
if (this.reaction.reacted) {
|
||||
if (this.reaction.count === 1) {
|
||||
return I18n.t("chat.reactions.only_you", {
|
||||
emoji: this.reaction.emoji,
|
||||
});
|
||||
} else if (this.reaction.count > 1 && this.reaction.count < 6) {
|
||||
return I18n.t("chat.reactions.and_others", {
|
||||
usernames,
|
||||
emoji: this.reaction.emoji,
|
||||
});
|
||||
} else if (this.reaction.count >= 6) {
|
||||
return I18n.t("chat.reactions.you_others_and_more", {
|
||||
usernames,
|
||||
emoji: this.reaction.emoji,
|
||||
more: this.reaction.count - 5,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (this.reaction.count > 0 && this.reaction.count < 6) {
|
||||
return I18n.t("chat.reactions.only_others", {
|
||||
usernames,
|
||||
emoji: this.reaction.emoji,
|
||||
});
|
||||
} else if (this.reaction.count >= 6) {
|
||||
return I18n.t("chat.reactions.others_and_more", {
|
||||
usernames,
|
||||
emoji: this.reaction.emoji,
|
||||
more: this.reaction.count - 5,
|
||||
});
|
||||
}
|
||||
return this.reaction.reacted
|
||||
? this._reactionTextWithSelf()
|
||||
: this._reactionText();
|
||||
}
|
||||
|
||||
_reactionTextWithSelf() {
|
||||
const reactionCount = this.reaction.count;
|
||||
|
||||
if (reactionCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reactionCount === 1) {
|
||||
return I18n.t("chat.reactions.only_you", {
|
||||
emoji: this.reaction.emoji,
|
||||
});
|
||||
}
|
||||
|
||||
const maxUsernames = 4;
|
||||
const usernames = this.reaction.users
|
||||
.slice(0, maxUsernames)
|
||||
.mapBy("username");
|
||||
|
||||
if (reactionCount === 2) {
|
||||
return I18n.t("chat.reactions.you_and_single_user", {
|
||||
emoji: this.reaction.emoji,
|
||||
username: usernames.pop(),
|
||||
});
|
||||
}
|
||||
|
||||
// `-1` because the current user ("you") isn't included in `usernames`
|
||||
const unnamedUserCount = reactionCount - usernames.length - 1;
|
||||
|
||||
if (unnamedUserCount > 0) {
|
||||
return I18n.t("chat.reactions.you_multiple_users_and_more", {
|
||||
emoji: this.reaction.emoji,
|
||||
commaSeparatedUsernames: this._joinUsernames(usernames),
|
||||
count: unnamedUserCount,
|
||||
});
|
||||
}
|
||||
|
||||
return I18n.t("chat.reactions.you_and_multiple_users", {
|
||||
emoji: this.reaction.emoji,
|
||||
username: usernames.pop(),
|
||||
commaSeparatedUsernames: this._joinUsernames(usernames),
|
||||
});
|
||||
}
|
||||
|
||||
_reactionText() {
|
||||
const reactionCount = this.reaction.count;
|
||||
|
||||
if (reactionCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxUsernames = 5;
|
||||
const usernames = this.reaction.users
|
||||
.slice(0, maxUsernames)
|
||||
.mapBy("username");
|
||||
|
||||
if (reactionCount === 1) {
|
||||
return I18n.t("chat.reactions.single_user", {
|
||||
emoji: this.reaction.emoji,
|
||||
username: usernames.pop(),
|
||||
});
|
||||
}
|
||||
|
||||
const unnamedUserCount = reactionCount - usernames.length;
|
||||
|
||||
if (unnamedUserCount > 0) {
|
||||
return I18n.t("chat.reactions.multiple_users_and_more", {
|
||||
emoji: this.reaction.emoji,
|
||||
commaSeparatedUsernames: this._joinUsernames(usernames),
|
||||
count: unnamedUserCount,
|
||||
});
|
||||
}
|
||||
|
||||
return I18n.t("chat.reactions.multiple_users", {
|
||||
emoji: this.reaction.emoji,
|
||||
username: usernames.pop(),
|
||||
commaSeparatedUsernames: this._joinUsernames(usernames),
|
||||
});
|
||||
}
|
||||
|
||||
_joinUsernames(usernames) {
|
||||
return usernames.join(I18n.t("word_connector.comma"));
|
||||
}
|
||||
}
|
||||
|
@ -411,49 +411,53 @@ export default class ChatMessage extends Component {
|
||||
}
|
||||
|
||||
get mentionedCannotSeeText() {
|
||||
return I18n.t("chat.mention_warning.cannot_see", {
|
||||
username: this.mentionWarning?.cannot_see?.[0]?.username,
|
||||
count: this.mentionWarning?.cannot_see?.length,
|
||||
others: this._othersTranslation(
|
||||
this.mentionWarning?.cannot_see?.length - 1
|
||||
),
|
||||
});
|
||||
return this._findTranslatedWarning(
|
||||
"chat.mention_warning.cannot_see",
|
||||
"chat.mention_warning.cannot_see_multiple",
|
||||
{
|
||||
username: this.mentionWarning?.cannot_see?.[0]?.username,
|
||||
count: this.mentionWarning?.cannot_see?.length,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get mentionedWithoutMembershipText() {
|
||||
return I18n.t("chat.mention_warning.without_membership", {
|
||||
username: this.mentionWarning?.without_membership?.[0]?.username,
|
||||
count: this.mentionWarning?.without_membership?.length,
|
||||
others: this._othersTranslation(
|
||||
this.mentionWarning?.without_membership?.length - 1
|
||||
),
|
||||
});
|
||||
return this._findTranslatedWarning(
|
||||
"chat.mention_warning.without_membership",
|
||||
"chat.mention_warning.without_membership_multiple",
|
||||
{
|
||||
username: this.mentionWarning?.without_membership?.[0]?.username,
|
||||
count: this.mentionWarning?.without_membership?.length,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get groupsWithDisabledMentions() {
|
||||
return I18n.t("chat.mention_warning.group_mentions_disabled", {
|
||||
group_name: this.mentionWarning?.group_mentions_disabled?.[0],
|
||||
count: this.mentionWarning?.group_mentions_disabled?.length,
|
||||
others: this._othersTranslation(
|
||||
this.mentionWarning?.group_mentions_disabled?.length - 1
|
||||
),
|
||||
});
|
||||
return this._findTranslatedWarning(
|
||||
"chat.mention_warning.group_mentions_disabled",
|
||||
"chat.mention_warning.group_mentions_disabled_multiple",
|
||||
{
|
||||
group_name: this.mentionWarning?.group_mentions_disabled?.[0],
|
||||
count: this.mentionWarning?.group_mentions_disabled?.length,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get groupsWithTooManyMembers() {
|
||||
return I18n.t("chat.mention_warning.too_many_members", {
|
||||
group_name: this.mentionWarning.groups_with_too_many_members?.[0],
|
||||
count: this.mentionWarning.groups_with_too_many_members?.length,
|
||||
others: this._othersTranslation(
|
||||
this.mentionWarning.groups_with_too_many_members?.length - 1
|
||||
),
|
||||
});
|
||||
return this._findTranslatedWarning(
|
||||
"chat.mention_warning.too_many_members",
|
||||
"chat.mention_warning.too_many_members_multiple",
|
||||
{
|
||||
group_name: this.mentionWarning.groups_with_too_many_members?.[0],
|
||||
count: this.mentionWarning.groups_with_too_many_members?.length,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_othersTranslation(othersCount) {
|
||||
return I18n.t("chat.mention_warning.warning_multiple", {
|
||||
count: othersCount,
|
||||
});
|
||||
_findTranslatedWarning(oneKey, multipleKey, args) {
|
||||
const translationKey = args.count === 1 ? oneKey : multipleKey;
|
||||
args.count--;
|
||||
return I18n.t(translationKey, args);
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -33,14 +33,18 @@ export default Component.extend({
|
||||
|
||||
if (usernames.length < 4) {
|
||||
const lastUsername = usernames.pop();
|
||||
const commaSeparatedUsernames = usernames.join(", ");
|
||||
const commaSeparatedUsernames = usernames.join(
|
||||
I18n.t("word_connector.comma")
|
||||
);
|
||||
return I18n.t("chat.replying_indicator.multiple_users", {
|
||||
commaSeparatedUsernames,
|
||||
lastUsername,
|
||||
});
|
||||
}
|
||||
|
||||
const commaSeparatedUsernames = usernames.slice(0, 2).join(", ");
|
||||
const commaSeparatedUsernames = usernames
|
||||
.slice(0, 2)
|
||||
.join(I18n.t("word_connector.comma"));
|
||||
return I18n.t("chat.replying_indicator.many_users", {
|
||||
commaSeparatedUsernames,
|
||||
count: usernames.length - 2,
|
||||
|
@ -103,20 +103,40 @@ export default class CreateChannelController extends Controller.extend(
|
||||
|
||||
_updateAutoJoinConfirmWarning(category, catPermissions) {
|
||||
const allowedGroups = catPermissions.allowed_groups;
|
||||
let warning;
|
||||
|
||||
if (catPermissions.private) {
|
||||
const warningTranslationKey =
|
||||
allowedGroups.length < 3 ? "warning_groups" : "warning_multiple_groups";
|
||||
|
||||
this.set(
|
||||
"autoJoinWarning",
|
||||
I18n.t(`chat.create_channel.auto_join_users.${warningTranslationKey}`, {
|
||||
members_count: catPermissions.members_count,
|
||||
group: escapeExpression(allowedGroups[0]),
|
||||
group_2: escapeExpression(allowedGroups[1]),
|
||||
count: allowedGroups.length,
|
||||
})
|
||||
);
|
||||
switch (allowedGroups.length) {
|
||||
case 1:
|
||||
warning = I18n.t(
|
||||
"chat.create_channel.auto_join_users.warning_1_group",
|
||||
{
|
||||
count: catPermissions.members_count,
|
||||
group: escapeExpression(allowedGroups[0]),
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
warning = I18n.t(
|
||||
"chat.create_channel.auto_join_users.warning_2_groups",
|
||||
{
|
||||
count: catPermissions.members_count,
|
||||
group1: escapeExpression(allowedGroups[0]),
|
||||
group2: escapeExpression(allowedGroups[1]),
|
||||
}
|
||||
);
|
||||
break;
|
||||
default:
|
||||
warning = I18n.messageFormat(
|
||||
"chat.create_channel.auto_join_users.warning_multiple_groups_MF",
|
||||
{
|
||||
groupCount: allowedGroups.length - 1,
|
||||
userCount: catPermissions.members_count,
|
||||
groupName: escapeExpression(allowedGroups[0]),
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.set(
|
||||
"autoJoinWarning",
|
||||
@ -125,6 +145,8 @@ export default class CreateChannelController extends Controller.extend(
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.set("autoJoinWarning", warning);
|
||||
}
|
||||
|
||||
_updatePermissionsHint(category) {
|
||||
@ -136,20 +158,42 @@ export default class CreateChannelController extends Controller.extend(
|
||||
.then((catPermissions) => {
|
||||
this._updateAutoJoinConfirmWarning(category, catPermissions);
|
||||
const allowedGroups = catPermissions.allowed_groups;
|
||||
const translationKey =
|
||||
allowedGroups.length < 3 ? "hint_groups" : "hint_multiple_groups";
|
||||
const settingLink = `/c/${escapeExpression(fullSlug)}/edit/security`;
|
||||
let hint;
|
||||
|
||||
this.set(
|
||||
"categoryPermissionsHint",
|
||||
htmlSafe(
|
||||
I18n.t(`chat.create_channel.choose_category.${translationKey}`, {
|
||||
link: `/c/${escapeExpression(fullSlug)}/edit/security`,
|
||||
hint: escapeExpression(allowedGroups[0]),
|
||||
hint_2: escapeExpression(allowedGroups[1]),
|
||||
count: allowedGroups.length,
|
||||
})
|
||||
)
|
||||
);
|
||||
switch (allowedGroups.length) {
|
||||
case 1:
|
||||
hint = I18n.t(
|
||||
"chat.create_channel.choose_category.hint_1_group",
|
||||
{
|
||||
settingLink,
|
||||
group: escapeExpression(allowedGroups[0]),
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
hint = I18n.t(
|
||||
"chat.create_channel.choose_category.hint_2_groups",
|
||||
{
|
||||
settingLink,
|
||||
group1: escapeExpression(allowedGroups[0]),
|
||||
group2: escapeExpression(allowedGroups[1]),
|
||||
}
|
||||
);
|
||||
break;
|
||||
default:
|
||||
hint = I18n.t(
|
||||
"chat.create_channel.choose_category.hint_multiple_groups",
|
||||
{
|
||||
settingLink,
|
||||
group: escapeExpression(allowedGroups[0]),
|
||||
count: allowedGroups.length - 1,
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
this.set("categoryPermissionsHint", htmlSafe(hint));
|
||||
});
|
||||
} else {
|
||||
this.set("categoryPermissionsHint", DEFAULT_HINT);
|
||||
|
@ -223,8 +223,8 @@ export default {
|
||||
}
|
||||
|
||||
get title() {
|
||||
return I18n.t("chat.placeholder_others", {
|
||||
messageRecipient: this.channel.escapedTitle,
|
||||
return I18n.t("chat.placeholder_channel", {
|
||||
channelName: this.channel.escapedTitle,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import RestModel from "discourse/models/rest";
|
||||
import I18n from "I18n";
|
||||
import User from "discourse/models/user";
|
||||
import UserChatChannelMembership from "discourse/plugins/chat/discourse/models/user-chat-channel-membership";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
@ -21,19 +20,6 @@ export const CHANNEL_STATUSES = {
|
||||
archived: "archived",
|
||||
};
|
||||
|
||||
export function channelStatusName(channelStatus) {
|
||||
switch (channelStatus) {
|
||||
case CHANNEL_STATUSES.open:
|
||||
return I18n.t("chat.channel_status.open");
|
||||
case CHANNEL_STATUSES.readOnly:
|
||||
return I18n.t("chat.channel_status.read_only");
|
||||
case CHANNEL_STATUSES.closed:
|
||||
return I18n.t("chat.channel_status.closed");
|
||||
case CHANNEL_STATUSES.archived:
|
||||
return I18n.t("chat.channel_status.archived");
|
||||
}
|
||||
}
|
||||
|
||||
export function channelStatusIcon(channelStatus) {
|
||||
if (channelStatus === CHANNEL_STATUSES.open) {
|
||||
return null;
|
||||
|
@ -106,46 +106,96 @@ en:
|
||||
new_messages: "new messages"
|
||||
mention_warning:
|
||||
dismiss: "dismiss"
|
||||
cannot_see:
|
||||
one: "%{username} cannot access this channel and was not notified."
|
||||
other: "%{username} and %{others} cannot access this channel and were not notified."
|
||||
cannot_see: "%{username} can't access this channel and was not notified."
|
||||
cannot_see_multiple:
|
||||
one: "%{username} and %{count} other user cannot access this channel and were not notified."
|
||||
other: "%{username} and %{count} other users cannot access this channel and were not notified."
|
||||
invitations_sent:
|
||||
one: "Invitation sent"
|
||||
other: "Invitations sent"
|
||||
invite: "Invite to channel"
|
||||
without_membership:
|
||||
one: "%{username} has not joined this channel."
|
||||
other: "%{username} and %{others} have not joined this channel."
|
||||
group_mentions_disabled:
|
||||
one: "%{group_name} doesn't allow mentions"
|
||||
other: "%{group_name} and %{others} doesn't allow mentions"
|
||||
too_many_members:
|
||||
one: "%{group_name} has too many members. No one was notified"
|
||||
other: "%{group_name} and %{others} have too many members. No one was notified"
|
||||
warning_multiple:
|
||||
one: "%{count} other"
|
||||
other: "%{count} others"
|
||||
|
||||
without_membership: "%{username} has not joined this channel."
|
||||
without_membership_multiple:
|
||||
one: "%{username} and %{count} other user have not joined this channel."
|
||||
other: "%{username} and %{count} other users have not joined this channel."
|
||||
group_mentions_disabled: "%{group_name} doesn't allow mentions."
|
||||
group_mentions_disabled_multiple:
|
||||
one: "%{group_name} and %{count} other group don't allow mentions."
|
||||
other: "%{group_name} and %{count} other groups don't allow mentions."
|
||||
too_many_members: "%{group_name} has too many members. No one was notified."
|
||||
too_many_members_multiple:
|
||||
one: "%{group_name} and %{count} other group have too many members. No one was notified."
|
||||
other: "%{group_name} and %{count} other groups have too many members. No one was notified."
|
||||
groups:
|
||||
header:
|
||||
some: "Some users won't be notified"
|
||||
all: "Nobody will be notified"
|
||||
unreachable:
|
||||
one: "@%{group} doesn't allow mentions"
|
||||
other: "@%{group} and @%{group_2} doesn't allow mentions"
|
||||
unreachable_multiple: "@%{group} and %{count} others doesn't allow mentions"
|
||||
too_many_members:
|
||||
one: "Mentioning @%{group} exceeds the %{notification_limit} of %{limit}"
|
||||
other: "Mentioning both @%{group} or @%{group_2} exceeds the %{notification_limit} of %{limit}"
|
||||
too_many_members_multiple: "These %{count} groups exceed the %{notification_limit} of %{limit}"
|
||||
users_limit:
|
||||
one: "%{count} user"
|
||||
other: "%{count} users"
|
||||
notification_limit: "notification limit"
|
||||
too_many_mentions: "This message exceeds the %{notification_limit} of %{limit}"
|
||||
mentions_limit:
|
||||
one: "%{count} mention"
|
||||
other: "%{count} mentions"
|
||||
unreachable_1: "@%{group} doesn't allow mentions."
|
||||
unreachable_2: "@%{group1} and @%{group2} don't allow mentions."
|
||||
unreachable_multiple:
|
||||
one: "@%{group} and %{count} other group don't allow mentions."
|
||||
other: "@%{group} and %{count} other groups don't allow mentions."
|
||||
too_many_members_MF: |
|
||||
{ groupCount, plural,
|
||||
=1 {
|
||||
{ isAdmin, select,
|
||||
true {
|
||||
{ notificationLimit, plural,
|
||||
one {Mentioning @{group1} exceeds the <a href="{siteSettingUrl}" target="_blank">notification limit</a> of # user.}
|
||||
other {Mentioning @{group1} exceeds the <a href="{siteSettingUrl}" target="_blank">notification limit</a> of # users.}
|
||||
}
|
||||
}
|
||||
false {
|
||||
{ notificationLimit, plural,
|
||||
one {Mentioning @{group1} exceeds the notification limit of # user.}
|
||||
other {Mentioning @{group1} exceeds the notification limit of # users.}
|
||||
}
|
||||
}
|
||||
other {}
|
||||
}
|
||||
}
|
||||
=2 {
|
||||
{ isAdmin, select,
|
||||
true {
|
||||
{ notificationLimit, plural,
|
||||
one {Mentioning @{group1} and @{group2} exceeds the <a href="{siteSettingUrl}" target="_blank">notification limit</a> of # user.}
|
||||
other {Mentioning @{group1} and @{group2} exceeds the <a href="{siteSettingUrl}" target="_blank">notification limit</a> of # users.}
|
||||
}
|
||||
}
|
||||
false {
|
||||
{ notificationLimit, plural,
|
||||
one {Mentioning @{group1} and @{group2} exceeds the notification limit of # user.}
|
||||
other {Mentioning @{group1} and @{group2} exceeds the notification limit of # users.}
|
||||
}
|
||||
}
|
||||
other {}
|
||||
}
|
||||
}
|
||||
other {
|
||||
{ isAdmin, select,
|
||||
true {
|
||||
{ notificationLimit, plural,
|
||||
one {Mentioning these {groupCount} groups exceeds the <a href="{siteSettingUrl}" target="_blank">notification limit</a> of # user.}
|
||||
other {Mentioning these {groupCount} groups exceeds the <a href="{siteSettingUrl}" target="_blank">notification limit</a> of # users.}
|
||||
}
|
||||
}
|
||||
false {
|
||||
{ notificationLimit, plural,
|
||||
one {Mentioning these {groupCount} groups exceeds the notification limit of # user.}
|
||||
other {Mentioning these {groupCount} groups exceeds the notification limit of # users.}
|
||||
}
|
||||
}
|
||||
other {}
|
||||
}
|
||||
}
|
||||
}
|
||||
too_many_mentions:
|
||||
one: "This message exceeds the notification limit of %{count} mention."
|
||||
other: "This message exceeds the notification limit of %{count} mentions."
|
||||
too_many_mentions_admin:
|
||||
one: 'This message exceeds the <a href="%{siteSettingUrl}" target="_blank">notification limit</a> of %{count} mention.'
|
||||
other: 'This message exceeds the <a href="%{siteSettingUrl}" target="_blank">notification limit</a> of %{count} mentions.'
|
||||
|
||||
aria_roles:
|
||||
header: "Chat header"
|
||||
composer: "Chat composer"
|
||||
@ -163,10 +213,15 @@ en:
|
||||
close_full_page: "Close full-screen chat"
|
||||
open_message: "Open message in chat"
|
||||
placeholder_self: "Jot something down"
|
||||
placeholder_others: "Chat with %{messageRecipient}"
|
||||
placeholder_new_message_disallowed: "Channel is %{status}, you cannot send new messages right now."
|
||||
placeholder_channel: "Chat with %{channelName}"
|
||||
placeholder_users: "Chat with %{commaSeparatedNames}"
|
||||
placeholder_new_message_disallowed:
|
||||
archived: "Channel is archived, you cannot send new messages right now."
|
||||
closed: "Channel is closed, you cannot send new messages right now."
|
||||
read_only: "Channel is read only, you cannot send new messages right now."
|
||||
placeholder_silenced: "You cannot send messages at this time."
|
||||
placeholder_start_conversation: Start a conversation with %{usernames}
|
||||
placeholder_start_conversation: "Start a conversation with ..."
|
||||
placeholder_start_conversation_users: "Start a conversation with %{commaSeparatedUsernames}"
|
||||
remove_upload: "Remove file"
|
||||
react: "React with emoji"
|
||||
reply: "Reply"
|
||||
@ -276,18 +331,36 @@ en:
|
||||
create_channel:
|
||||
auto_join_users:
|
||||
public_category_warning: "%{category} is a public category. Automatically add all recently active users to this channel?"
|
||||
warning_groups:
|
||||
one: Automatically add %{members_count} users from %{group}?
|
||||
other: Automatically add %{members_count} users from %{group} and %{group_2}?
|
||||
warning_multiple_groups: Automatically add %{members_count} users from %{group_1} and %{count} others?
|
||||
warning_1_group:
|
||||
one: "Automatically add %{count} user from %{group}?"
|
||||
other: "Automatically add %{count} users from %{group}?"
|
||||
warning_2_groups:
|
||||
one: "Automatically add %{count} user from %{group1} and %{group2}?"
|
||||
other: "Automatically add %{count} users from %{group1} and %{group2}?"
|
||||
warning_multiple_groups_MF: |
|
||||
{ groupCount, plural,
|
||||
one {
|
||||
{ userCount, plural,
|
||||
one {Automatically add {userCount} user from {groupName} and {groupCount} other group?}
|
||||
other {Automatically add {userCount} users from {groupName} and {groupCount} other group?}
|
||||
}
|
||||
}
|
||||
other {
|
||||
{ userCount, plural,
|
||||
one {Automatically add {userCount} user from {groupName} and {groupCount} other groups?}
|
||||
other {Automatically add {userCount} users from {groupName} and {groupCount} other groups?}
|
||||
}
|
||||
}
|
||||
}
|
||||
choose_category:
|
||||
label: "Choose a category"
|
||||
none: "select one..."
|
||||
default_hint: Manage access by visiting <a href=%{link} target="_blank">%{category} security settings</a>
|
||||
hint_groups:
|
||||
one: Users in %{hint} will have access to this channel per the <a href=%{link} target="_blank">security settings</a>
|
||||
other: Users in %{hint} and %{hint_2} will have access to this channel per the <a href=%{link} target="_blank">security settings</a>
|
||||
hint_multiple_groups: Users in %{hint_1} and %{count} other groups will have access to this channel per the <a href=%{link} target="_blank">security settings</a>
|
||||
hint_1_group: 'Users in %{group} will have access to this channel per the <a href="%{settingLink}" target="_blank">security settings</a>'
|
||||
hint_2_groups: 'Users in %{group1} and %{group2} will have access to this channel per the <a href="%{settingLink}" target="_blank">security settings</a>'
|
||||
hint_multiple_groups:
|
||||
one: 'Users in %{group} and %{count} other group will have access to this channel per the <a href="%{settingLink}" target="_blank">security settings</a>'
|
||||
other: 'Users in %{group} and %{count} other groups will have access to this channel per the <a href="%{settingLink}" target="_blank">security settings</a>'
|
||||
create: "Create channel"
|
||||
description: "Description (optional)"
|
||||
name: "Channel name"
|
||||
@ -303,10 +376,16 @@ en:
|
||||
|
||||
reactions:
|
||||
only_you: "You reacted with :%{emoji}:"
|
||||
and_others: "You, %{usernames} reacted with :%{emoji}:"
|
||||
only_others: "%{usernames} reacted with :%{emoji}:"
|
||||
others_and_more: "%{usernames} and %{more} others reacted with :%{emoji}:"
|
||||
you_others_and_more: "You, %{usernames} and %{more} others reacted with :%{emoji}:"
|
||||
you_and_single_user: "You and %{username} reacted with :%{emoji}:"
|
||||
you_and_multiple_users: "You, %{commaSeparatedUsernames} and %{username} reacted with :%{emoji}:"
|
||||
you_multiple_users_and_more:
|
||||
one: "You, %{commaSeparatedUsernames} and %{count} other reacted with :%{emoji}:"
|
||||
other: "You, %{commaSeparatedUsernames} and %{count} others reacted with :%{emoji}:"
|
||||
single_user: "%{username} reacted with :%{emoji}:"
|
||||
multiple_users: "%{commaSeparatedUsernames} and %{username} reacted with :%{emoji}:"
|
||||
multiple_users_and_more:
|
||||
one: "%{commaSeparatedUsernames} and %{count} other reacted with :%{emoji}:"
|
||||
other: "%{commaSeparatedUsernames} and %{count} others reacted with :%{emoji}:"
|
||||
|
||||
composer:
|
||||
toggle_toolbar: "Toggle toolbar"
|
||||
@ -465,11 +544,13 @@ en:
|
||||
direct: 'mentioned you in "%{channel}"'
|
||||
direct_html: '<span>%{username}</span> <span>mentioned you in "%{channel}"</span>'
|
||||
other_plain: 'mentioned %{identifier} in "%{channel}"'
|
||||
# %{identifier} is either @here or @all
|
||||
other_html: '<span>%{username}</span> <span>mentioned %{identifier} in "%{channel}"</span>'
|
||||
direct_message_chat_mention:
|
||||
direct: "mentioned you in personal chat"
|
||||
direct_html: "<span>%{username}</span> <span>mentioned you in personal chat</span>"
|
||||
other_plain: "mentioned %{identifier} in personal chat"
|
||||
# %{identifier} is either @here or @all
|
||||
other_html: "<span>%{username}</span> <span>mentioned %{identifier} in personal chat</span>"
|
||||
chat_message: "New chat message"
|
||||
chat_quoted: "%{username} quoted your chat message"
|
||||
|
@ -49,16 +49,26 @@ en:
|
||||
deleted_chat_username: deleted
|
||||
errors:
|
||||
channel_exists_for_category: "A channel already exists for this category and name"
|
||||
channel_new_message_disallowed: "The channel is %{status}, no new messages can be sent"
|
||||
channel_modify_message_disallowed: "The channel is %{status}, no messages can be edited or deleted"
|
||||
channel_new_message_disallowed:
|
||||
archived: "The channel is archived, no new messages can be sent"
|
||||
closed: "The channel is closed, no new messages can be sent"
|
||||
read_only: "The channel is read only, no new messages can be sent"
|
||||
channel_modify_message_disallowed:
|
||||
archived: "The channel is archived, no messages can be edited or deleted"
|
||||
closed: "The channel is closed, no messages can be edited or deleted"
|
||||
read_only: "The channel is read only, no messages can be edited or deleted"
|
||||
user_cannot_send_message: "You cannot send messages at this time."
|
||||
rate_limit_exceeded: "Exceeded the limit of chat messages that can be sent within 30 seconds"
|
||||
auto_silence_from_flags: "Chat message flagged with score high enough to silence user."
|
||||
channel_cannot_be_archived: "The channel cannot be archived at this time, it must be either closed or open to archive."
|
||||
duplicate_message: "You posted an identical message too recently."
|
||||
delete_channel_failed: "Delete channel failed, please try again."
|
||||
minimum_length_not_met: "Message is too short, must have a minimum of %{minimum} characters."
|
||||
message_too_long: "Message is too long, messages must be a maximum of %{maximum} characters."
|
||||
minimum_length_not_met:
|
||||
one: "Message is too short, must have a minimum of %{count} character."
|
||||
other: "Message is too short, must have a minimum of %{count} characters."
|
||||
message_too_long:
|
||||
one: "Message is too long, messages must be a maximum of %{count} characters."
|
||||
other: "Message is too long, messages must be a maximum of %{count} characters."
|
||||
draft_too_long: "Draft is too long."
|
||||
max_reactions_limit_reached: "New reactions are not allowed on this message."
|
||||
message_move_invalid_channel: "The source and destination channel must be public channels."
|
||||
@ -70,8 +80,9 @@ en:
|
||||
actor_disallowed_dms: "You have chosen to prevent users from sending you private and direct messages, so you cannot create new direct messages."
|
||||
actor_preventing_target_user_from_dm: "You have chosen to prevent %{username} from sending you private and direct messages, so you cannot create new direct messages to them."
|
||||
user_cannot_send_direct_messages: "Sorry, you cannot send direct messages."
|
||||
over_chat_max_direct_message_users_allow_self: "You can only create a direct message with yourself."
|
||||
over_chat_max_direct_message_users:
|
||||
one: "You can only create a direct message with yourself."
|
||||
one: "You can't create a direct message with more than %{count} other user."
|
||||
other: "You can't create a direct message with more than %{count} other users."
|
||||
original_message_not_found: "The ancestor of the message you are replying cannot be found or has been deleted."
|
||||
reviewables:
|
||||
@ -110,20 +121,17 @@ en:
|
||||
transcript_title: "Transcript of previous messages in %{channel_name}"
|
||||
transcript_body: "To give you more context, we included a transcript of the previous messages in this conversation (up to ten):\n\n%{transcript}"
|
||||
channel:
|
||||
statuses:
|
||||
read_only: "Read Only"
|
||||
archived: "Archived"
|
||||
closed: "Closed"
|
||||
open: "Open"
|
||||
archive:
|
||||
first_post_raw: "This topic is an archive of the [%{channel_name}](%{channel_url}) chat channel."
|
||||
messages_moved:
|
||||
one: "@%{acting_username} moved a message to the [%{channel_name}](%{first_moved_message_url}) channel."
|
||||
other: "@%{acting_username} moved %{count} messages to the [%{channel_name}](%{first_moved_message_url}) channel."
|
||||
dm_title:
|
||||
single_user: "%{user}"
|
||||
multi_user: "%{users}"
|
||||
multi_user_truncated: "%{users} and %{leftover} others"
|
||||
single_user: "%{username}"
|
||||
multi_user: "%{comma_separated_usernames}"
|
||||
multi_user_truncated:
|
||||
one: "%{comma_separated_usernames} and %{count} other"
|
||||
other: "%{comma_separated_usernames} and %{count} others"
|
||||
|
||||
category_channel:
|
||||
errors:
|
||||
|
@ -82,10 +82,7 @@ class Chat::ChatMessageCreator
|
||||
raise StandardError.new(I18n.t("chat.errors.user_cannot_send_direct_messages"))
|
||||
else
|
||||
raise StandardError.new(
|
||||
I18n.t(
|
||||
"chat.errors.channel_new_message_disallowed",
|
||||
status: @chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_new_message_disallowed.#{@chat_channel.status}"),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -53,10 +53,7 @@ class Chat::ChatMessageReactor
|
||||
raise Discourse::InvalidAccess.new(
|
||||
nil,
|
||||
nil,
|
||||
custom_message: "chat.errors.channel_modify_message_disallowed",
|
||||
custom_message_params: {
|
||||
status: @chat_channel.status_name,
|
||||
},
|
||||
custom_message: "chat.errors.channel_modify_message_disallowed.#{@chat_channel.status}",
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -51,10 +51,7 @@ class Chat::ChatMessageUpdater
|
||||
def validate_channel_status!
|
||||
return if @guardian.can_modify_channel_message?(@chat_channel)
|
||||
raise StandardError.new(
|
||||
I18n.t(
|
||||
"chat.errors.channel_modify_message_disallowed",
|
||||
status: @chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_modify_message_disallowed.#{@chat_channel.status}"),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -30,12 +30,16 @@ module Chat::DirectMessageChannelCreator
|
||||
target_users = target_users.reject { |user| user.id == acting_user.id }
|
||||
|
||||
if !acting_user.staff? && target_users.size > SiteSetting.chat_max_direct_message_users
|
||||
raise NotAllowed.new(
|
||||
I18n.t(
|
||||
"chat.errors.over_chat_max_direct_message_users",
|
||||
count: SiteSetting.chat_max_direct_message_users + 1, # +1 for the acting_user
|
||||
),
|
||||
)
|
||||
if SiteSetting.chat_max_direct_message_users == 0
|
||||
raise NotAllowed.new(I18n.t("chat.errors.over_chat_max_direct_message_users_allow_self"))
|
||||
else
|
||||
raise NotAllowed.new(
|
||||
I18n.t(
|
||||
"chat.errors.over_chat_max_direct_message_users",
|
||||
count: SiteSetting.chat_max_direct_message_users + 1, # +1 for the acting_user
|
||||
),
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -64,7 +64,7 @@ describe Chat::ChatMessageCreator do
|
||||
expect(creator.error.message).to match(
|
||||
I18n.t(
|
||||
"chat.errors.minimum_length_not_met",
|
||||
{ minimum: SiteSetting.chat_minimum_message_length },
|
||||
{ count: SiteSetting.chat_minimum_message_length },
|
||||
),
|
||||
)
|
||||
end
|
||||
@ -79,10 +79,7 @@ describe Chat::ChatMessageCreator do
|
||||
)
|
||||
expect(creator.failed?).to eq(true)
|
||||
expect(creator.error.message).to match(
|
||||
I18n.t(
|
||||
"chat.errors.message_too_long",
|
||||
{ maximum: SiteSetting.chat_maximum_message_length },
|
||||
),
|
||||
I18n.t("chat.errors.message_too_long", { count: SiteSetting.chat_maximum_message_length }),
|
||||
)
|
||||
end
|
||||
|
||||
@ -866,10 +863,7 @@ describe Chat::ChatMessageCreator do
|
||||
creator = create_message(user1)
|
||||
expect(creator.failed?).to eq(true)
|
||||
expect(creator.error.message).to eq(
|
||||
I18n.t(
|
||||
"chat.errors.channel_new_message_disallowed",
|
||||
status: public_chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_new_message_disallowed.closed"),
|
||||
)
|
||||
end
|
||||
|
||||
@ -885,18 +879,12 @@ describe Chat::ChatMessageCreator do
|
||||
creator = create_message(user1)
|
||||
expect(creator.failed?).to eq(true)
|
||||
expect(creator.error.message).to eq(
|
||||
I18n.t(
|
||||
"chat.errors.channel_new_message_disallowed",
|
||||
status: public_chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_new_message_disallowed.read_only"),
|
||||
)
|
||||
creator = create_message(admin1)
|
||||
expect(creator.failed?).to eq(true)
|
||||
expect(creator.error.message).to eq(
|
||||
I18n.t(
|
||||
"chat.errors.channel_new_message_disallowed",
|
||||
status: public_chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_new_message_disallowed.read_only"),
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -908,18 +896,12 @@ describe Chat::ChatMessageCreator do
|
||||
creator = create_message(user1)
|
||||
expect(creator.failed?).to eq(true)
|
||||
expect(creator.error.message).to eq(
|
||||
I18n.t(
|
||||
"chat.errors.channel_new_message_disallowed",
|
||||
status: public_chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_new_message_disallowed.archived"),
|
||||
)
|
||||
creator = create_message(admin1)
|
||||
expect(creator.failed?).to eq(true)
|
||||
expect(creator.error.message).to eq(
|
||||
I18n.t(
|
||||
"chat.errors.channel_new_message_disallowed",
|
||||
status: public_chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_new_message_disallowed.archived"),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -62,7 +62,7 @@ describe Chat::ChatMessageUpdater do
|
||||
expect(updater.error.message).to match(
|
||||
I18n.t(
|
||||
"chat.errors.minimum_length_not_met",
|
||||
{ minimum: SiteSetting.chat_minimum_message_length },
|
||||
{ count: SiteSetting.chat_minimum_message_length },
|
||||
),
|
||||
)
|
||||
expect(chat_message.reload.message).to eq(og_message)
|
||||
@ -82,7 +82,7 @@ describe Chat::ChatMessageUpdater do
|
||||
)
|
||||
expect(updater.failed?).to eq(true)
|
||||
expect(updater.error.message).to match(
|
||||
I18n.t("chat.errors.message_too_long", { maximum: SiteSetting.chat_maximum_message_length }),
|
||||
I18n.t("chat.errors.message_too_long", { count: SiteSetting.chat_maximum_message_length }),
|
||||
)
|
||||
expect(chat_message.reload.message).to eq(og_message)
|
||||
end
|
||||
@ -528,10 +528,7 @@ describe Chat::ChatMessageUpdater do
|
||||
updater = update_message(user1)
|
||||
expect(updater.failed?).to eq(true)
|
||||
expect(updater.error.message).to eq(
|
||||
I18n.t(
|
||||
"chat.errors.channel_modify_message_disallowed",
|
||||
status: public_chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_modify_message_disallowed.closed"),
|
||||
)
|
||||
end
|
||||
|
||||
@ -548,18 +545,12 @@ describe Chat::ChatMessageUpdater do
|
||||
updater = update_message(user1)
|
||||
expect(updater.failed?).to eq(true)
|
||||
expect(updater.error.message).to eq(
|
||||
I18n.t(
|
||||
"chat.errors.channel_modify_message_disallowed",
|
||||
status: public_chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_modify_message_disallowed.read_only"),
|
||||
)
|
||||
updater = update_message(admin1)
|
||||
expect(updater.failed?).to eq(true)
|
||||
expect(updater.error.message).to eq(
|
||||
I18n.t(
|
||||
"chat.errors.channel_modify_message_disallowed",
|
||||
status: public_chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_modify_message_disallowed.read_only"),
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -571,18 +562,12 @@ describe Chat::ChatMessageUpdater do
|
||||
updater = update_message(user1)
|
||||
expect(updater.failed?).to eq(true)
|
||||
expect(updater.error.message).to eq(
|
||||
I18n.t(
|
||||
"chat.errors.channel_modify_message_disallowed",
|
||||
status: public_chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_modify_message_disallowed.archived"),
|
||||
)
|
||||
updater = update_message(admin1)
|
||||
expect(updater.failed?).to eq(true)
|
||||
expect(updater.error.message).to eq(
|
||||
I18n.t(
|
||||
"chat.errors.channel_modify_message_disallowed",
|
||||
status: public_chat_channel.status_name,
|
||||
),
|
||||
I18n.t("chat.errors.channel_modify_message_disallowed.archived"),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -212,7 +212,7 @@ describe Chat::DirectMessageChannelCreator do
|
||||
subject.create!(acting_user: user_1, target_users: [user_1, user_2])
|
||||
}.to raise_error(
|
||||
Chat::DirectMessageChannelCreator::NotAllowed,
|
||||
I18n.t("chat.errors.over_chat_max_direct_message_users", count: 1),
|
||||
I18n.t("chat.errors.over_chat_max_direct_message_users_allow_self"),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -20,7 +20,8 @@ describe DirectMessage do
|
||||
expect(direct_message.chat_channel_title_for_user(chat_channel, user1)).to eq(
|
||||
I18n.t(
|
||||
"chat.channel.dm_title.multi_user",
|
||||
users: [user3, user2].map { |u| "@#{u.username}" }.join(", "),
|
||||
comma_separated_usernames:
|
||||
[user3, user2].map { |u| "@#{u.username}" }.join(I18n.t("word_connector.comma")),
|
||||
),
|
||||
)
|
||||
end
|
||||
@ -36,8 +37,12 @@ describe DirectMessage do
|
||||
expect(direct_message.chat_channel_title_for_user(chat_channel, user1)).to eq(
|
||||
I18n.t(
|
||||
"chat.channel.dm_title.multi_user_truncated",
|
||||
users: users[1..5].sort_by(&:username).map { |u| "@#{u.username}" }.join(", "),
|
||||
leftover: 2,
|
||||
comma_separated_usernames:
|
||||
users[1..5]
|
||||
.sort_by(&:username)
|
||||
.map { |u| "@#{u.username}" }
|
||||
.join(I18n.t("word_connector.comma")),
|
||||
count: 2,
|
||||
),
|
||||
)
|
||||
end
|
||||
@ -46,7 +51,7 @@ describe DirectMessage do
|
||||
direct_message = Fabricate(:direct_message, users: [user1, user2])
|
||||
|
||||
expect(direct_message.chat_channel_title_for_user(chat_channel, user1)).to eq(
|
||||
I18n.t("chat.channel.dm_title.single_user", user: "@#{user2.username}"),
|
||||
I18n.t("chat.channel.dm_title.single_user", username: "@#{user2.username}"),
|
||||
)
|
||||
end
|
||||
|
||||
@ -54,7 +59,7 @@ describe DirectMessage do
|
||||
direct_message = Fabricate(:direct_message, users: [user1])
|
||||
|
||||
expect(direct_message.chat_channel_title_for_user(chat_channel, user1)).to eq(
|
||||
I18n.t("chat.channel.dm_title.single_user", user: "@#{user1.username}"),
|
||||
I18n.t("chat.channel.dm_title.single_user", username: "@#{user1.username}"),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -320,7 +320,7 @@ RSpec.describe Chat::ChatController do
|
||||
post "/chat/#{chat_channel.id}.json", params: { message: message }
|
||||
expect(response.status).to eq(422)
|
||||
expect(response.parsed_body["errors"]).to include(
|
||||
I18n.t("chat.errors.channel_new_message_disallowed", status: chat_channel.status_name),
|
||||
I18n.t("chat.errors.channel_new_message_disallowed.closed"),
|
||||
)
|
||||
end
|
||||
|
||||
@ -336,7 +336,7 @@ RSpec.describe Chat::ChatController do
|
||||
post "/chat/#{chat_channel.id}.json", params: { message: message }
|
||||
expect(response.status).to eq(422)
|
||||
expect(response.parsed_body["errors"]).to include(
|
||||
I18n.t("chat.errors.channel_new_message_disallowed", status: chat_channel.status_name),
|
||||
I18n.t("chat.errors.channel_new_message_disallowed.read_only"),
|
||||
)
|
||||
end
|
||||
|
||||
@ -903,7 +903,7 @@ RSpec.describe Chat::ChatController do
|
||||
}.not_to change { chat_message.reactions.where(user: user, emoji: emoji).count }
|
||||
expect(response.status).to eq(403)
|
||||
expect(response.parsed_body["errors"]).to include(
|
||||
I18n.t("chat.errors.channel_modify_message_disallowed", status: chat_channel.status_name),
|
||||
I18n.t("chat.errors.channel_modify_message_disallowed.#{chat_channel.status}"),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -55,7 +55,7 @@ RSpec.describe Chat::IncomingChatWebhooksController do
|
||||
}.not_to change { ChatMessage.where(chat_channel: chat_channel).count }
|
||||
expect(response.status).to eq(422)
|
||||
expect(response.parsed_body["errors"]).to include(
|
||||
I18n.t("chat.errors.channel_new_message_disallowed", status: chat_channel.status_name),
|
||||
I18n.t("chat.errors.channel_new_message_disallowed.read_only"),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -30,11 +30,7 @@ RSpec.describe "Closed channel", type: :system, js: true do
|
||||
chat.visit_channel(channel_1)
|
||||
|
||||
expect(page).to have_field(
|
||||
placeholder:
|
||||
I18n.t(
|
||||
"js.chat.placeholder_new_message_disallowed",
|
||||
status: I18n.t("js.chat.channel_status.closed").downcase,
|
||||
),
|
||||
placeholder: I18n.t("js.chat.placeholder_new_message_disallowed.closed"),
|
||||
disabled: true,
|
||||
)
|
||||
end
|
||||
@ -54,7 +50,7 @@ RSpec.describe "Closed channel", type: :system, js: true do
|
||||
chat.visit_channel(channel_1)
|
||||
|
||||
expect(page).to have_no_field(
|
||||
placeholder: I18n.t("js.chat.placeholder_new_message_disallowed"),
|
||||
placeholder: I18n.t("js.chat.placeholder_new_message_disallowed.closed"),
|
||||
disabled: true,
|
||||
)
|
||||
end
|
||||
|
@ -69,7 +69,7 @@ RSpec.describe "Create channel", type: :system, js: true do
|
||||
end
|
||||
end
|
||||
|
||||
context "when category has a malicous group name" do
|
||||
context "when category has a malicious group name" do
|
||||
fab!(:group_1) do
|
||||
group = Group.new(name: "<script>e</script>")
|
||||
group.save(validate: false)
|
||||
|
@ -22,7 +22,7 @@ RSpec.describe "JIT messages", type: :system, js: true do
|
||||
channel.send_message("hi @#{other_user.username}")
|
||||
|
||||
expect(page).to have_content(
|
||||
I18n.t("js.chat.mention_warning.without_membership.one", username: other_user.username),
|
||||
I18n.t("js.chat.mention_warning.without_membership", username: other_user.username),
|
||||
wait: 5,
|
||||
)
|
||||
end
|
||||
@ -44,7 +44,7 @@ RSpec.describe "JIT messages", type: :system, js: true do
|
||||
channel.send_message("hi @#{other_user.username}")
|
||||
|
||||
expect(page).to have_content(
|
||||
I18n.t("js.chat.mention_warning.cannot_see.one", username: other_user.username),
|
||||
I18n.t("js.chat.mention_warning.cannot_see", username: other_user.username),
|
||||
wait: 5,
|
||||
)
|
||||
end
|
||||
@ -61,7 +61,7 @@ RSpec.describe "JIT messages", type: :system, js: true do
|
||||
channel.send_message("hi @#{group_1.name}")
|
||||
|
||||
expect(page).to have_content(
|
||||
I18n.t("js.chat.mention_warning.group_mentions_disabled.one", group_name: group_1.name),
|
||||
I18n.t("js.chat.mention_warning.group_mentions_disabled", group_name: group_1.name),
|
||||
wait: 5,
|
||||
)
|
||||
end
|
||||
|
@ -30,7 +30,7 @@ RSpec.describe "Read only", type: :system, js: true do
|
||||
chat.visit_channel(channel_1)
|
||||
|
||||
expect(page).to have_field(
|
||||
placeholder: I18n.t("js.chat.placeholder_new_message_disallowed", status: "read only"),
|
||||
placeholder: I18n.t("js.chat.placeholder_new_message_disallowed.read_only"),
|
||||
disabled: true,
|
||||
)
|
||||
end
|
||||
@ -50,7 +50,7 @@ RSpec.describe "Read only", type: :system, js: true do
|
||||
chat.visit_channel(channel_1)
|
||||
|
||||
expect(page).to have_field(
|
||||
placeholder: I18n.t("js.chat.placeholder_new_message_disallowed", status: "read only"),
|
||||
placeholder: I18n.t("js.chat.placeholder_new_message_disallowed.read_only"),
|
||||
disabled: true,
|
||||
)
|
||||
end
|
||||
|
@ -37,8 +37,6 @@ class LocaleFileValidator
|
||||
"Pluralized strings must have only the sub-keys 'one' and 'other'.\nThe following keys have missing or additional keys:",
|
||||
invalid_one_keys:
|
||||
"The following keys contain the number 1 instead of the interpolation key %{count}:",
|
||||
invalid_message_format_one_key:
|
||||
"The following keys use 'one {1 foo}' instead of the generic 'one {# foo}':",
|
||||
}
|
||||
|
||||
PLURALIZATION_KEYS = %w[zero one two few many other]
|
||||
@ -88,7 +86,6 @@ class LocaleFileValidator
|
||||
@errors[:invalid_relative_links] = []
|
||||
@errors[:invalid_relative_image_sources] = []
|
||||
@errors[:invalid_interpolation_key_format] = []
|
||||
@errors[:invalid_message_format_one_key] = []
|
||||
|
||||
each_translation(yaml) do |key, value|
|
||||
@errors[:invalid_relative_links] << key if value.match?(%r{href\s*=\s*["']/[^/]|\]\(/[^/]}i)
|
||||
@ -98,10 +95,6 @@ class LocaleFileValidator
|
||||
if value.match?(/{{.+?}}/) && !key.end_with?("_MF")
|
||||
@errors[:invalid_interpolation_key_format] << key
|
||||
end
|
||||
|
||||
if key.end_with?("_MF") && value.match?(/one {.*?1.*?}/)
|
||||
@errors[:invalid_message_format_one_key] << key
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -27,6 +27,7 @@ RSpec.describe TranslationOverride do
|
||||
I18n.t(
|
||||
"activerecord.errors.models.translation_overrides.attributes.value.invalid_interpolation_keys",
|
||||
keys: "key, omg",
|
||||
count: 2,
|
||||
),
|
||||
)
|
||||
end
|
||||
@ -61,6 +62,7 @@ RSpec.describe TranslationOverride do
|
||||
I18n.t(
|
||||
"activerecord.errors.models.translation_overrides.attributes.value.invalid_interpolation_keys",
|
||||
keys: "something",
|
||||
count: 1,
|
||||
),
|
||||
)
|
||||
end
|
||||
@ -78,6 +80,7 @@ RSpec.describe TranslationOverride do
|
||||
I18n.t(
|
||||
"activerecord.errors.models.translation_overrides.attributes.value.invalid_interpolation_keys",
|
||||
keys: "topic_title_url_encoded",
|
||||
count: 1,
|
||||
),
|
||||
)
|
||||
end
|
||||
@ -132,6 +135,7 @@ RSpec.describe TranslationOverride do
|
||||
I18n.t(
|
||||
"activerecord.errors.models.translation_overrides.attributes.value.invalid_interpolation_keys",
|
||||
keys: "key3, key4",
|
||||
count: 2,
|
||||
),
|
||||
)
|
||||
end
|
||||
|
@ -165,6 +165,7 @@ RSpec.describe Admin::EmailTemplatesController do
|
||||
I18n.t(
|
||||
"activerecord.errors.models.translation_overrides.attributes.value.invalid_interpolation_keys",
|
||||
keys: "email_wrongfix",
|
||||
count: 1,
|
||||
)
|
||||
}",
|
||||
]
|
||||
@ -183,6 +184,7 @@ RSpec.describe Admin::EmailTemplatesController do
|
||||
I18n.t(
|
||||
"activerecord.errors.models.translation_overrides.attributes.value.invalid_interpolation_keys",
|
||||
keys: "invalid",
|
||||
count: 1,
|
||||
)
|
||||
}",
|
||||
]
|
||||
@ -201,12 +203,14 @@ RSpec.describe Admin::EmailTemplatesController do
|
||||
I18n.t(
|
||||
"activerecord.errors.models.translation_overrides.attributes.value.invalid_interpolation_keys",
|
||||
keys: "invalid",
|
||||
count: 1,
|
||||
)
|
||||
}",
|
||||
"<b>Body</b>: #{
|
||||
I18n.t(
|
||||
"activerecord.errors.models.translation_overrides.attributes.value.invalid_interpolation_keys",
|
||||
keys: "invalid",
|
||||
count: 1,
|
||||
)
|
||||
}",
|
||||
]
|
||||
|
@ -580,6 +580,7 @@ RSpec.describe Admin::SiteTextsController do
|
||||
I18n.t(
|
||||
"activerecord.errors.models.translation_overrides.attributes.value.invalid_interpolation_keys",
|
||||
keys: "key, omg",
|
||||
count: 2,
|
||||
),
|
||||
)
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user