diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 5abef260cd..3e4dc7382d 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8175,3 +8175,56 @@ Sorry for the inconvenience."; "CreateTopic.EnterTopicTitle" = "ENTER TOPIC TITLE"; "CreateTopic.EnterTopicTitlePlaceholder" = "What do you want to discuss?"; "CreateTopic.SelectTopicIcon" = "SELECT TOPIC ICON"; + +"ChatList.Context.CloseTopic" = "Close"; +"ChatList.Context.ReopenTopic" = "Reopen"; +"Chat.ContextViewAsTopics" = "View as Topics"; +"Chat.ContextViewAsMessages" = "View as Messages"; +"Chat.CreateTopic" = "New Topic"; + +"ChatList.DeleteTopicConfirmationText" = "This will delete the topic with all its messages"; +"ChatList.DeleteTopicConfirmationAction" = "Delete Topic"; + +"Notification.TopicCreated" = "Topic created"; +"Notification.TopicClosed" = "Topic closed"; +"Notification.TopicReopened" = "Topic reopened"; +"Notification.TopicRenamed" = "Topic renamed to \"%1$@\""; +"Notification.TopicIconChanged" = "Topic icon changed to %1$@"; +"Notification.TopicIconRemoved" = "Topic icon changed"; + +"Chat.EmptyTopicPlaceholder.Title" = "Almost done!"; +"Chat.EmptyTopicPlaceholder.Text" = "Send the first message to\nstart this topic."; + +"Chat.Message.TopicAuthorBadge" = "Topic Author"; + +"Chat.PanelRestartTopic" = "Restart Topic"; +"Chat.PanelTopicClosedText" = "The topic is closed by admin"; +"Chat.PanelForumModeReplyText" = "Swipe left on a message to reply"; + +"PeerInfo.TopicHeaderLocation" = "in %1$@"; +"PeerInfo.OptionTopics" = "Topics"; +"PeerInfo.OptionTopicsText" = "The group chat will be divided into topics created by admins or users."; +"PeerInfo.TopicsLimitedParticipantCountText_1" = "Only groups with more than **%d member** can have topics enabled."; +"PeerInfo.TopicsLimitedParticipantCountText_any" = "Only groups with more than **%d members** can have topics enabled."; +"PeerInfo.TopicsLimitedDiscussionGroups" = "Topics are currently unavailable in groups connected to channels."; + +"PeerInfo.TopicNotificationExceptions_1" = "There is [1 topic]() that is listed as exception."; +"PeerInfo.TopicNotificationExceptions_any" = "There are [%d topics]() that are listed as exceptions."; + +"PeerInfo.NotificationMemberAdded" = "**%1$@** added to the group."; +"PeerInfo.NotificationMultipleMembersAdded_1" = "**%d** member added to the group."; +"PeerInfo.NotificationMultipleMembersAdded_any" = "**%d** members added to the group."; + +"Channel.AdminLog.TopicCreated" = "%1$@ created topic %2$@"; +"Channel.AdminLog.TopicDeleted" = "%1$@ deleted topic %2$@"; +"Channel.AdminLog.TopicPinned" = "%1$@ pinned topic %2$@"; +"Channel.AdminLog.TopicUnpinned" = "%1$@ pinned topic %2$@"; +"Channel.AdminLog.TopicsEnabled" = "%1$@ enabled topics"; +"Channel.AdminLog.TopicsDisabled" = "%1$@ disabled topics"; +"Channel.AdminLog.TopicReopened" = "%1$@ reopened topic %2$@"; +"Channel.AdminLog.TopicClosed" = "%1$@ closed topic %2$@"; +"Channel.AdminLog.TopicRenamed" = "%1$@ renamed topic %2$@ to %3$@"; +"Channel.AdminLog.TopicRenamedWithIcon" = "%1$@ renamed topic %2$@ to %3$@ and changed icon to %4$@"; +"Channel.AdminLog.TopicRenamedWithRemovedIcon" = "%1$@ renamed topic %2$@ to %3$@ and removed icon"; +"Channel.AdminLog.TopicChangedIcon" = "%1$@ changed topic %2$@ icon to %3$@"; +"Channel.AdminLog.TopicRemovedIcon" = "%1$@ removed topic %2$@ icon"; diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 6855db3d88..6626825e5a 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -516,8 +516,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: var items: [ContextMenuItem] = [] if channel.hasPermission(.manageTopics) { - //TODO:localize - items.append(.action(ContextMenuActionItem(text: isPinned ? "Unpin" : "Pin", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin": "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in + items.append(.action(ContextMenuActionItem(text: isPinned ? presentationData.strings.ChatList_Context_Unpin : presentationData.strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin": "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.default) let _ = context.engine.peers.setForumChannelTopicPinned(id: peerId, threadId: threadId, isPinned: !isPinned).start() @@ -724,8 +723,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: canOpenClose = true } if canOpenClose { - //TODO:localize - items.append(.action(ContextMenuActionItem(text: threadData.isClosed ? "Restart" : "Close", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: threadData.isClosed ? "Chat/Context Menu/Play": "Chat/Context Menu/Pause"), color: theme.contextMenu.primaryColor) }, action: { _, f in + items.append(.action(ContextMenuActionItem(text: threadData.isClosed ? presentationData.strings.ChatList_Context_ReopenTopic : presentationData.strings.ChatList_Context_CloseTopic, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: threadData.isClosed ? "Chat/Context Menu/Play": "Chat/Context Menu/Pause"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.default) let _ = context.engine.peers.setForumChannelTopicClosed(id: peerId, threadId: threadId, isClosed: !threadData.isClosed).start() diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index cb2605ed78..24e09f2c32 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -406,9 +406,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController title = self.presentationData.strings.ChatList_ArchivedChatsTitle } case let .forum(peerId): - //TODO:localize - title = "Forum" - + title = "" self.forumChannelTracker = ForumChannelTopics(account: self.context.account, peerId: peerId) self.moreBarButton.contextAction = { [weak self] sourceNode, gesture in @@ -518,8 +516,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController case .member: strongSelf.setToolbar(nil, transition: .animated(duration: 0.4, curve: .spring)) default: - //TODO:localize - strongSelf.setToolbar(Toolbar(leftAction: nil, rightAction: nil, middleAction: ToolbarAction(title: "Join", isEnabled: true)), transition: .animated(duration: 0.4, curve: .spring)) + strongSelf.setToolbar(Toolbar(leftAction: nil, rightAction: nil, middleAction: ToolbarAction(title: strongSelf.presentationData.strings.Channel_JoinChannel, isEnabled: true)), transition: .animated(duration: 0.4, curve: .spring)) } } }) @@ -678,8 +675,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController defaultTitle = strongSelf.presentationData.strings.ChatList_ArchivedChatsTitle } case .forum: - //TODO:localize - defaultTitle = "Forum" + defaultTitle = "" } let previousEditingAndNetworkState = previousEditingAndNetworkStateValue.swap((stateAndFilterId.state.editing, networkState)) if stateAndFilterId.state.editing { @@ -2575,7 +2571,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController var items: [ContextMenuItem] = [] - items.append(.action(ContextMenuActionItem(text: "View as Topics", icon: { theme in + items.append(.action(ContextMenuActionItem(text: strings.Chat_ContextViewAsTopics, icon: { theme in if !isViewingAsTopics { return nil } @@ -2590,7 +2586,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let chatController = context.sharedContext.makeChatListController(context: context, location: .forum(peerId: peerId), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false) navigationController.replaceController(sourceController, with: chatController, animated: false) }))) - items.append(.action(ContextMenuActionItem(text: "View as Messages", icon: { theme in + items.append(.action(ContextMenuActionItem(text: strings.Chat_ContextViewAsMessages, icon: { theme in if isViewingAsTopics { return nil } @@ -2607,8 +2603,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }))) items.append(.separator) - //TODO:localize - items.append(.action(ContextMenuActionItem(text: "Group Info", icon: { theme in + items.append(.action(ContextMenuActionItem(text: strings.GroupInfo_Title, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Contact List/CreateGroupActionIcon"), color: theme.contextMenu.primaryColor) }, action: { [weak sourceController] _, f in f(.default) @@ -2625,8 +2620,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }))) if channel.hasPermission(.inviteMembers) { - //TODO:localize - items.append(.action(ContextMenuActionItem(text: "Add Member", icon: { theme in + items.append(.action(ContextMenuActionItem(text: strings.GroupInfo_AddParticipant, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { [weak sourceController] _, f in f(.default) @@ -2655,8 +2649,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } else if channel.hasPermission(.createTopics) { items.append(.separator) - //TODO:localize - items.append(.action(ContextMenuActionItem(text: "New Topic", icon: { theme in + items.append(.action(ContextMenuActionItem(text: strings.Chat_CreateTopic, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { action in action.dismissWithResult(.default) @@ -3745,9 +3738,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let actionSheet = ActionSheetController(presentationData: self.presentationData) var items: [ActionSheetItem] = [] - //TODO:localize - items.append(ActionSheetTextItem(title: "This will delete the topic with all its messages", parseMarkdown: true)) - items.append(ActionSheetButtonItem(title: "Delete", color: .destructive, action: { [weak self, weak actionSheet] in + items.append(ActionSheetTextItem(title: self.presentationData.strings.ChatList_DeleteTopicConfirmationText, parseMarkdown: true)) + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteTopicConfirmationAction, color: .destructive, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() self?.commitDeletePeerThread(peerId: peerId, threadId: threadId) })) diff --git a/submodules/PaymentMethodUI/Sources/AddPaymentMethodSheetScreen.swift b/submodules/PaymentMethodUI/Sources/AddPaymentMethodSheetScreen.swift index 6b4004236a..fa1a4287be 100644 --- a/submodules/PaymentMethodUI/Sources/AddPaymentMethodSheetScreen.swift +++ b/submodules/PaymentMethodUI/Sources/AddPaymentMethodSheetScreen.swift @@ -59,7 +59,6 @@ private final class AddPaymentMethodSheetContent: CombinedComponent { transition: context.transition ) - //TODO:localize let title = title.update( component: MultilineTextComponent( text: .plain(NSAttributedString(string: "Payment Method", font: UIFont.boldSystemFont(ofSize: 17.0), textColor: .black)), @@ -71,7 +70,6 @@ private final class AddPaymentMethodSheetContent: CombinedComponent { transition: context.transition ) - //TODO:localize let text = text.update( component: MultilineTextComponent( text: .plain(NSAttributedString(string: "Add your debit or credit card to buy goods and services on Telegram.", font: UIFont.systemFont(ofSize: 15.0), textColor: .gray)), @@ -83,7 +81,6 @@ private final class AddPaymentMethodSheetContent: CombinedComponent { transition: context.transition ) - //TODO:localize let actionButton = actionButton.update( component: SolidRoundedButtonComponent( title: "Add Payment Method", @@ -102,7 +99,6 @@ private final class AddPaymentMethodSheetContent: CombinedComponent { transition: context.transition ) - //TODO:localize let cancelButton = cancelButton.update( component: Button( content: AnyComponent( diff --git a/submodules/PaymentMethodUI/Sources/PaymentCardEntryScreen.swift b/submodules/PaymentMethodUI/Sources/PaymentCardEntryScreen.swift index fc21792866..79f2aa5dd8 100644 --- a/submodules/PaymentMethodUI/Sources/PaymentCardEntryScreen.swift +++ b/submodules/PaymentMethodUI/Sources/PaymentCardEntryScreen.swift @@ -406,10 +406,8 @@ public final class PaymentCardEntryScreen: ViewControllerComponentContainer { updateModelKeyImpl?(key, value) }), navigationBarAppearance: .transparent) - //TODO:localize self.title = "Add Payment Method" - //TODO:localize self.doneItem = UIBarButtonItem(title: "Add", style: .done, target: self, action: #selector(self.donePressed)) self.navigationItem.setRightBarButton(self.doneItem, animated: false) self.doneItem?.isEnabled = false diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift index 9a37540554..ef535ec308 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift @@ -32,6 +32,16 @@ public struct AdminLogEventsResult { } public enum AdminLogEventAction { + public struct ForumTopicInfo { + public var info: EngineMessageHistoryThread.Info + public var isClosed: Bool + + public init(info: EngineMessageHistoryThread.Info, isClosed: Bool) { + self.info = info + self.isClosed = isClosed + } + } + case changeTitle(prev: String, new: String) case changeAbout(prev: String, new: String) case changeUsername(prev: String, new: String) @@ -71,7 +81,7 @@ public enum AdminLogEventAction { case changeUsernames(prev: [String], new: [String]) case createTopic(info: EngineMessageHistoryThread.Info) case deleteTopic(info: EngineMessageHistoryThread.Info) - case editTopic(prevInfo: EngineMessageHistoryThread.Info, newInfo: EngineMessageHistoryThread.Info) + case editTopic(prevInfo: ForumTopicInfo, newInfo: ForumTopicInfo) case pinTopic(prevInfo: EngineMessageHistoryThread.Info?, newInfo: EngineMessageHistoryThread.Info?) case toggleForum(isForum: Bool) } @@ -289,20 +299,20 @@ func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: PeerId, m action = .deleteTopic(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0)) } case let .channelAdminLogEventActionEditTopic(prevTopic, newTopic): - let prevInfo: EngineMessageHistoryThread.Info + let prevInfo: AdminLogEventAction.ForumTopicInfo switch prevTopic { - case let .forumTopic(_, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): - prevInfo = EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor) + case let .forumTopic(flags, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): + prevInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor), isClosed: (flags & (1 << 2)) != 0) case .forumTopicDeleted: - prevInfo = EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0) + prevInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), isClosed: false) } - let newInfo: EngineMessageHistoryThread.Info + let newInfo: AdminLogEventAction.ForumTopicInfo switch newTopic { - case let .forumTopic(_, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): - newInfo = EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor) + case let .forumTopic(flags, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): + newInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor), isClosed: (flags & (1 << 2)) != 0) case .forumTopicDeleted: - newInfo = EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0) + newInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), isClosed: false) } action = .editTopic(prevInfo: prevInfo, newInfo: newInfo) diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 638da07cc9..4de986dcc4 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -680,10 +680,8 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, attributedString = addAttributesToStringWithRanges(strings.Notification_PremiumGift_Sent(authorName, price)._tuple, body: bodyAttributes, argumentAttributes: attributes) } case .topicCreated: - //TODO:localize - attributedString = NSAttributedString(string: "Topic created", font: titleFont, textColor: primaryTextColor) + attributedString = NSAttributedString(string: strings.Notification_TopicCreated, font: titleFont, textColor: primaryTextColor) case let .topicEdited(components): - //TODO:localize if let isClosed = components.compactMap({ item -> Bool? in switch item { case let .isClosed(isClosed): @@ -693,9 +691,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } }).first { if isClosed { - attributedString = NSAttributedString(string: "Topic closed", font: titleFont, textColor: primaryTextColor) + attributedString = NSAttributedString(string: strings.Notification_TopicClosed, font: titleFont, textColor: primaryTextColor) } else { - attributedString = NSAttributedString(string: "Topic restarted", font: titleFont, textColor: primaryTextColor) + attributedString = NSAttributedString(string: strings.Notification_TopicReopened, font: titleFont, textColor: primaryTextColor) } } else if let title = components.compactMap({ item -> String? in switch item { @@ -705,7 +703,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, return nil } }).first { - attributedString = NSAttributedString(string: "Topic renamed to \"\(title)\"", font: titleFont, textColor: primaryTextColor) + attributedString = NSAttributedString(string: strings.Notification_TopicRenamed(title).string, font: titleFont, textColor: primaryTextColor) } else if let maybeFileId = components.compactMap({ item -> Int64? in switch item { case let .iconFileId(id): @@ -714,9 +712,12 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, return nil } }).first { - let _ = maybeFileId - //TODO:localize - attributedString = NSAttributedString(string: "Topic icon changed", font: titleFont, textColor: primaryTextColor) + if maybeFileId != 0 { + let bodyAttributes = MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [:]) + attributedString = addAttributesToStringWithRanges(strings.Notification_TopicIconChanged(".")._tuple, body: bodyAttributes, argumentAttributes: [0: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil)])]) + } else { + attributedString = NSAttributedString(string: strings.Notification_TopicIconRemoved, font: titleFont, textColor: primaryTextColor) + } } case .unknown: attributedString = nil diff --git a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift index 5d86ea21cf..327dec4e13 100644 --- a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift +++ b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift @@ -807,7 +807,6 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { let presentationData = context.sharedContext.currentPresentationData.with { $0 } self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: presentationData.theme.rootController.navigationBar.accentTextColor)) } else { - //TODO:localize self.navigationItem.rightBarButtonItem = self.doneBarItem } } diff --git a/submodules/TelegramUI/Sources/ChatEmptyNode.swift b/submodules/TelegramUI/Sources/ChatEmptyNode.swift index 8976820818..69203ec817 100644 --- a/submodules/TelegramUI/Sources/ChatEmptyNode.swift +++ b/submodules/TelegramUI/Sources/ChatEmptyNode.swift @@ -819,10 +819,8 @@ final class ChatEmptyNodeTopicChatContent: ASDisplayNode, ChatEmptyNodeContent, self.currentTheme = interfaceState.theme self.currentStrings = interfaceState.strings - //TODO:localize - self.titleNode.attributedText = NSAttributedString(string: "Almost done!", font: titleFont, textColor: serviceColor.primaryText) - - self.textNode.attributedText = NSAttributedString(string: "Send the first message to\nstart this topic.", font: messageFont, textColor: serviceColor.primaryText) + self.titleNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_EmptyTopicPlaceholder_Title, font: titleFont, textColor: serviceColor.primaryText) + self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_EmptyTopicPlaceholder_Text, font: messageFont, textColor: serviceColor.primaryText) } let inset: CGFloat diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index a6ce37ab36..14c8f4c944 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -1399,8 +1399,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode if authorRank == nil { if let topicAuthorId = item.associatedData.topicAuthorId, topicAuthorId == message.author?.id { - //TODO:localize - authorRank = .custom("Topic Author") + authorRank = .custom(item.presentationData.strings.Chat_Message_TopicAuthorBadge) } } case .group: diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift index cbf871fd33..25e009fcfa 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift @@ -1655,7 +1655,6 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } case let .createTopic(info): - //TODO:localize var peers = SimpleDictionary() var author: Peer? if let peer = self.entry.peers[self.entry.event.peerId] { @@ -1665,9 +1664,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { var text: String = "" var entities: [MessageTextEntity] = [] - let tempString = PresentationStrings.FormattedString(string: "Topic \"\(info.title)\" created", ranges: []) - - appendAttributedText(text: tempString, generateEntities: { index in + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicCreated(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", info.title), generateEntities: { index in if index == 0, let author = author { return [.TextMention(peerId: author.id)] } @@ -1677,7 +1674,6 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:]) return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .deleteTopic(info): - //TODO:localize var peers = SimpleDictionary() var author: Peer? if let peer = self.entry.peers[self.entry.event.peerId] { @@ -1686,20 +1682,19 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } var text: String = "" var entities: [MessageTextEntity] = [] - - let tempString = PresentationStrings.FormattedString(string: "Topic \"\(info.title)\" deleted", ranges: []) - - appendAttributedText(text: tempString, generateEntities: { index in + + let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicDeleted(authorTitle, info.title), generateEntities: { index in if index == 0, let author = author { return [.TextMention(peerId: author.id)] } return [] }, to: &text, entities: &entities) + let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:]) return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .editTopic(_, newInfo): - //TODO:localize + case let .editTopic(prevInfo, newInfo): var peers = SimpleDictionary() var author: Peer? if let peer = self.entry.peers[self.entry.event.peerId] { @@ -1708,20 +1703,70 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } var text: String = "" var entities: [MessageTextEntity] = [] - - let tempString = PresentationStrings.FormattedString(string: "Topic \"\(newInfo.title)\" edited", ranges: []) - - appendAttributedText(text: tempString, generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] + /* + "Channel.AdminLog.TopicRenamed" = "%1$@ renamed topic %2$@ to %3$@"; + "Channel.AdminLog.TopicRenamedWithIcon" = "%1$@ renamed topic %2$@ to %3$@ and changed icon to %4$@"; + "Channel.AdminLog.TopicRenamedWithRemovedIcon" = "%1$@ renamed topic %2$@ to %3$@ and removed icon"; + "Channel.AdminLog.TopicChangedIcon" = "%1$@ changed topic %2$@ icon to %3$@"; + "Channel.AdminLog.TopicRemovedIcon" = "%1$@ removed topic %2$@ icon";*/ + + let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + if prevInfo.isClosed != newInfo.isClosed { + appendAttributedText(text: newInfo.isClosed ? self.presentationData.strings.Channel_AdminLog_TopicClosed(authorTitle, newInfo.info.title) : self.presentationData.strings.Channel_AdminLog_TopicReopened(authorTitle, newInfo.info.title), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) + } else if prevInfo.info.title != newInfo.info.title && prevInfo.info.icon != newInfo.info.icon { + if let fileId = newInfo.info.icon { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRenamedWithIcon(authorTitle, prevInfo.info.title, newInfo.info.title, "."), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 3 { + return [.CustomEmoji(stickerPack: nil, fileId: fileId)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRenamedWithRemovedIcon(authorTitle, prevInfo.info.title, newInfo.info.title), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) } - return [] - }, to: &text, entities: &entities) + } else if prevInfo.info.icon != newInfo.info.icon { + if let fileId = newInfo.info.icon { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicChangedIcon(authorTitle, newInfo.info.title, "."), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 2 { + return [.CustomEmoji(stickerPack: nil, fileId: fileId)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRemovedIcon(authorTitle, newInfo.info.title), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) + } + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRenamed(authorTitle, prevInfo.info.title, newInfo.info.title), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) + } + let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:]) return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .pinTopic(prevInfo, newInfo): - //TODO:localize var peers = SimpleDictionary() var author: Peer? if let peer = self.entry.peers[self.entry.event.peerId] { @@ -1730,27 +1775,36 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } var text: String = "" var entities: [MessageTextEntity] = [] - - let tempString: PresentationStrings.FormattedString + + let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + if let newInfo = newInfo { - tempString = PresentationStrings.FormattedString(string: "Topic \"\(newInfo.title)\" pinned", ranges: []) + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicPinned(authorTitle, newInfo.title), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) } else if let prevInfo = prevInfo { - tempString = PresentationStrings.FormattedString(string: "Topic \"\(prevInfo.title)\" unpinned", ranges: []) + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicUnpinned(authorTitle, prevInfo.title), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) } else { - tempString = PresentationStrings.FormattedString(string: "Topic unpinned", ranges: []) + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicUnpinned(authorTitle, ""), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) } - appendAttributedText(text: tempString, generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } - return [] - }, to: &text, entities: &entities) let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:]) return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleForum(isForum): - //TODO:localize var peers = SimpleDictionary() var author: Peer? if let peer = self.entry.peers[self.entry.event.peerId] { @@ -1760,14 +1814,14 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { var text: String = "" var entities: [MessageTextEntity] = [] - let tempString = PresentationStrings.FormattedString(string: "Forum \(isForum ? "enabled" : "disabled")", ranges: []) - - appendAttributedText(text: tempString, generateEntities: { index in + let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + appendAttributedText(text: isForum ? self.presentationData.strings.Channel_AdminLog_TopicsEnabled(authorTitle) : self.presentationData.strings.Channel_AdminLog_TopicsDisabled(authorTitle), generateEntities: { index in if index == 0, let author = author { return [.TextMention(peerId: author.id)] } return [] }, to: &text, entities: &entities) + let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:]) return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) diff --git a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift index 593eff609c..2f34774565 100644 --- a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift @@ -49,8 +49,7 @@ private enum ChatReportPeerTitleButton: Equatable { case .addMembers: return strings.Conversation_AddMembers case .restartTopic: - //TODO:localize - return "Restart Topic" + return strings.Chat_PanelRestartTopic } } } diff --git a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift index 0709a95b20..247a175925 100644 --- a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift @@ -46,11 +46,10 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode { var iconImage: UIImage? if let threadData = interfaceState.threadData, threadData.isClosed { - //TODO:localize iconImage = PresentationResourcesChat.chatPanelLockIcon(interfaceState.theme) - self.textNode.attributedText = NSAttributedString(string: "The topic is closed by admin", font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) + self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelTopicClosedText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum), case .peer = interfaceState.chatLocation { - self.textNode.attributedText = NSAttributedString(string: "Swipe left on a message to reply", font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) + self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelForumModeReplyText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else if let (untilDate, personal) = bannedPermission { if personal && untilDate != 0 && untilDate != Int32.max { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_RestrictedTextTimed(stringForFullDate(timestamp: untilDate, strings: interfaceState.strings, dateTimeFormat: interfaceState.dateTimeFormat)).string, font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 77c35beee9..3ee90b13a9 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -2646,12 +2646,11 @@ final class PeerInfoHeaderNode: ASDisplayNode { } else if let _ = threadData { let subtitleColor: UIColor = presentationData.theme.list.itemSecondaryTextColor - //TODO:localize - var statusText = "in " + let statusText: String if let addressName = peer.addressName { - statusText += "@\(addressName)" + statusText = presentationData.strings.PeerInfo_TopicHeaderLocation("@\(addressName)").string } else { - statusText += peer.debugDisplayTitle + statusText = presentationData.strings.PeerInfo_TopicHeaderLocation(peer.debugDisplayTitle).string } smallSubtitleString = NSAttributedString(string: statusText, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7)) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 1ea61ec6ee..e9c9ee2d29 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -1627,8 +1627,8 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr if isCreator, let appConfiguration = data.appConfiguration { var minParticipants = 200 - if let data = appConfiguration.data, let value = data["forum_upgrade_participants_min"] as? Int { - minParticipants = value + if let data = appConfiguration.data, let value = data["forum_upgrade_participants_min"] as? Double { + minParticipants = Int(value) } var canSetupTopics = false @@ -1646,15 +1646,15 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr } if canSetupTopics { - //TODO:localize - items[.peerDataSettings]!.append(PeerInfoScreenSwitchItem(id: ItemTopics, text: "Topics", value: channel.flags.contains(.isForum), icon: UIImage(bundleImageName: "Settings/Menu/Topics"), isLocked: topicsLimitedReason != nil, toggled: { value in + items[.peerDataSettings]!.append(PeerInfoScreenSwitchItem(id: ItemTopics, text: presentationData.strings.PeerInfo_OptionTopics, value: channel.flags.contains(.isForum), icon: UIImage(bundleImageName: "Settings/Menu/Topics"), isLocked: topicsLimitedReason != nil, toggled: { value in if let topicsLimitedReason = topicsLimitedReason { interaction.displayTopicsLimited(topicsLimitedReason) } else { interaction.toggleForumTopics(value) } })) - items[.peerDataSettings]!.append(PeerInfoScreenCommentItem(id: ItemTopicsText, text: "The group chat will be divided into topics created by admins or users.")) + + items[.peerDataSettings]!.append(PeerInfoScreenCommentItem(id: ItemTopicsText, text: presentationData.strings.PeerInfo_OptionTopicsText)) } } @@ -2095,14 +2095,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate return } - //TODO:localize let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } let text: String switch reason { case let .participants(minCount): - text = "Only groups with more than **\(minCount) members** can have topics enabled." + text = self.presentationData.strings.PeerInfo_TopicsLimitedParticipantCountText(Int32(minCount)) case .discussion: - text = "Topics are currently unavailable in groups connected to channels." + text = self.presentationData.strings.PeerInfo_TopicsLimitedDiscussionGroups } self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_topics", scale: 0.066, colors: [:], title: nil, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) } @@ -4257,13 +4256,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate if !self.forumTopicNotificationExceptions.isEmpty { items.append(.separator) - //TODO:localize - let text: String - if self.forumTopicNotificationExceptions.count == 1 { - text = "There is [1 topic]() that is listed as exception." - } else { - text = "There are [\(self.forumTopicNotificationExceptions.count) topics]() that are listed as exceptions." - } + let text: String = self.presentationData.strings.PeerInfo_TopicNotificationExceptions(Int32(self.forumTopicNotificationExceptions.count)) items.append(.action(ContextMenuActionItem( text: text, @@ -9707,12 +9700,11 @@ func presentAddMembersImpl(context: AccountContext, updatedPresentationData: (in let presentationData = context.sharedContext.currentPresentationData.with { $0 } let peers = maybePeers.compactMap { $0.value } - //TODO:localize let text: String if peers.count == 1 { - text = "**\(peers[0].displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder))** added to the group." + text = presentationData.strings.PeerInfo_NotificationMemberAdded(peers[0].displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string } else { - text = "**\(peers.count)** members added to the group." + text = presentationData.strings.PeerInfo_NotificationMultipleMembersAdded(Int32(peers.count)) } parentController?.present(UndoOverlayController(presentationData: presentationData, content: .peers(context: context, peers: peers, title: nil, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) })