Localization: Various fixes (#6178)

This commit is contained in:
zubiden 2025-09-09 20:26:07 +02:00 committed by Alexander Zinchuk
parent 794e442d8b
commit 8b8c94b715
48 changed files with 390 additions and 272 deletions

View File

@ -76,6 +76,7 @@
"PremiumLimitAccountsNoPremium" = "You have reached your current limit of connected accounts. You can free one more place by subscribing to Telegram Premium."; "PremiumLimitAccountsNoPremium" = "You have reached your current limit of connected accounts. You can free one more place by subscribing to Telegram Premium.";
"PremiumLimitAccounts" = "You have reached your current limit of connected accounts. You can free one more place by subscribing to Telegram Premium with one of these connected accounts."; "PremiumLimitAccounts" = "You have reached your current limit of connected accounts. You can free one more place by subscribing to Telegram Premium with one of these connected accounts.";
"SendMessage" = "Send Message"; "SendMessage" = "Send Message";
"MessageUnsupported" = "This message is not supported on the web version of Telegram";
"ConversationDefaultRestrictedMedia" = "Sending media isn't allowed in this group."; "ConversationDefaultRestrictedMedia" = "Sending media isn't allowed in this group.";
"AccDescrVoiceMessage" = "Record voice message"; "AccDescrVoiceMessage" = "Record voice message";
"BotSettings" = "Settings"; "BotSettings" = "Settings";
@ -87,7 +88,7 @@
"ContextCopySelected" = "Copy Selected Text"; "ContextCopySelected" = "Copy Selected Text";
"ContextCopyText" = "Copy Text"; "ContextCopyText" = "Copy Text";
"ContextArchiveCollapse" = "Collapse"; "ContextArchiveCollapse" = "Collapse";
"ContextArchiveExpand" = "Collapse"; "ContextArchiveExpand" = "Expand";
"ContextArchiveToMenu" = "Move to Main Menu"; "ContextArchiveToMenu" = "Move to Main Menu";
"CallMessageVideoIncomingDeclined" = "Declined Video Call"; "CallMessageVideoIncomingDeclined" = "Declined Video Call";
"CallMessageVideoOutgoingMissed" = "Canceled Video Call"; "CallMessageVideoOutgoingMissed" = "Canceled Video Call";
@ -129,6 +130,7 @@
"UserRestrictionsNoChangeInfo" = "can't change Info"; "UserRestrictionsNoChangeInfo" = "can't change Info";
"UserRestrictionsInviteUsers" = "Add Users"; "UserRestrictionsInviteUsers" = "Add Users";
"UserRestrictionsPinMessages" = "Pin Messages"; "UserRestrictionsPinMessages" = "Pin Messages";
"ChatPermissionNotAvailable" = "This permission is not available in public groups.";
"StatsMessageInteractionsTitle" = "INTERACTIONS"; "StatsMessageInteractionsTitle" = "INTERACTIONS";
"StatsGroupGrowthTitle" = "GROWTH"; "StatsGroupGrowthTitle" = "GROWTH";
"StatsGroupMembersTitle" = "GROUP MEMBERS"; "StatsGroupMembersTitle" = "GROUP MEMBERS";
@ -474,6 +476,7 @@
"AlwaysShareWith" = "Always Share With"; "AlwaysShareWith" = "Always Share With";
"NeverShareWith" = "Never Share With"; "NeverShareWith" = "Never Share With";
"SessionsTitle" = "Active Sessions"; "SessionsTitle" = "Active Sessions";
"SessionTerminate" = "Terminate";
"OtherWebSessions" = "Connected Websites"; "OtherWebSessions" = "Connected Websites";
"BlockedUsers" = "Blocked Users"; "BlockedUsers" = "Blocked Users";
"TwoStepVerification" = "Two-Step Verification"; "TwoStepVerification" = "Two-Step Verification";
@ -522,7 +525,7 @@
"WhoCanCallMe" = "Who can call me"; "WhoCanCallMe" = "Who can call me";
"PrivacyP2P" = "Peer-to-Peer Calls"; "PrivacyP2P" = "Peer-to-Peer Calls";
"PrivacyForwardsTitle" = "Who can add a link to my account when forwarding my messages?"; "PrivacyForwardsTitle" = "Who can add a link to my account when forwarding my messages?";
"WhoCanAddMe" = "Who can add me to group chats?"; "WhoCanAddMe" = "Who can add me to groups and channels?";
"NewChatsFromNonContacts" = "New chats from unknown users"; "NewChatsFromNonContacts" = "New chats from unknown users";
"ArchiveAndMute" = "Archive and Mute"; "ArchiveAndMute" = "Archive and Mute";
"ArchiveAndMuteInfo" = "Automatically archive and mute new chats, groups and channels from non-contacts."; "ArchiveAndMuteInfo" = "Automatically archive and mute new chats, groups and channels from non-contacts.";
@ -689,6 +692,7 @@
"SettingsPerformanceStickerEffects" = "Sticker Effects"; "SettingsPerformanceStickerEffects" = "Sticker Effects";
"SettingsPerformanceAutoplayGif" = "Autoplay GIFs"; "SettingsPerformanceAutoplayGif" = "Autoplay GIFs";
"SettingsPerformanceAutoplayVideo" = "Autoplay Videos"; "SettingsPerformanceAutoplayVideo" = "Autoplay Videos";
"SettingsPerformanceGranularTitle" = "Resource-Intensive Processes";
"FavoriteStickers" = "Favorites"; "FavoriteStickers" = "Favorites";
"PremiumStickers" = "Premium Stickers"; "PremiumStickers" = "Premium Stickers";
"GroupStickers" = "Group Stickers"; "GroupStickers" = "Group Stickers";
@ -711,7 +715,6 @@
"ErrorUnspecified" = "Error"; "ErrorUnspecified" = "Error";
"NoStickers" = "No stickers yet"; "NoStickers" = "No stickers yet";
"ClearRecentEmoji" = "Clear recent emoji?"; "ClearRecentEmoji" = "Clear recent emoji?";
"TextFormatAddLinkTitle" = "Add Link";
"Save" = "Save"; "Save" = "Save";
"ConversationEmptyPlaceholder" = "No messages here yet..."; "ConversationEmptyPlaceholder" = "No messages here yet...";
"ConversationGreetingText" = "Send a message or tap on the greeting below."; "ConversationGreetingText" = "Send a message or tap on the greeting below.";
@ -957,8 +960,16 @@
"ChannelPermissionsHeader" = "What can members of this group do?"; "ChannelPermissionsHeader" = "What can members of this group do?";
"UserRestrictionsSend" = "Send Messages"; "UserRestrictionsSend" = "Send Messages";
"UserRestrictionsSendMedia" = "Send Media"; "UserRestrictionsSendMedia" = "Send Media";
"UserRestrictionsSendStickers" = "Send Stickers & GIFs"; "UserRestrictionsCreateTopics" = "Create Topics";
"UserRestrictionsSendPolls" = "Send Polls"; "SendMediaPermissionFiles" = "Files";
"SendMediaPermissionPhotos" = "Photos";
"SendMediaPermissionVideos" = "Videos";
"SendMediaPermissionStickersGifs" = "Stickers & GIFs";
"SendMediaPermissionAudios" = "Music";
"SendMediaPermissionVoices" = "Voice Messages";
"SendMediaPermissionRoundVideos" = "Video Messages";
"SendMediaPermissionWebPages" = "Embed Links";
"SendMediaPermissionPolls" = "Polls";
"UserRestrictionsEmbedLinks" = "Embed Links"; "UserRestrictionsEmbedLinks" = "Embed Links";
"UserRestrictionsChangeInfo" = "Change Chat Info"; "UserRestrictionsChangeInfo" = "Change Chat Info";
"ChannelAddException" = "Add Exception"; "ChannelAddException" = "Add Exception";
@ -1089,12 +1100,14 @@
"ChannelPersmissionDeniedSendMessagesForever" = "The admins of this group have restricted your ability to send messages."; "ChannelPersmissionDeniedSendMessagesForever" = "The admins of this group have restricted your ability to send messages.";
"ChannelPersmissionDeniedSendMessagesDefaultRestrictedText" = "Sending messages is not allowed in this group."; "ChannelPersmissionDeniedSendMessagesDefaultRestrictedText" = "Sending messages is not allowed in this group.";
"Chats" = "Chats"; "Chats" = "Chats";
"ChatsPlural_one" = "{count} chat";
"ChatsPlural_other" = "{count} chats";
"NewDiscussionChatTitle" = "{name} Chat"; "NewDiscussionChatTitle" = "{name} Chat";
"FilterBots" = "Bots"; "FilterBots" = "Bots";
"FilterContacts" = "Contacts"; "FilterContacts" = "Contacts";
"FilterNonContacts" = "Non-Contacts"; "FilterNonContacts" = "Non-Contacts";
"FromYou" = "You"; "FromYou" = "You";
"InDlgAlbum" = "Album"; "Album" = "Album";
"AttachPhoto" = "Photo"; "AttachPhoto" = "Photo";
"AttachGif" = "GIF"; "AttachGif" = "GIF";
"AttachVideo" = "Video"; "AttachVideo" = "Video";
@ -1102,8 +1115,12 @@
"AttachMusic" = "Music"; "AttachMusic" = "Music";
"AttachContact" = "Contact"; "AttachContact" = "Contact";
"AttachStory" = "Story"; "AttachStory" = "Story";
"MessageLocation" = "Location"; "AttachInvoice" = "Invoice: {description}";
"MessageLiveLocation" = "Live Location"; "AttachLocation" = "Location";
"AttachLiveLocation" = "Live Location";
"AttachGiveaway" = "Giveaway";
"AttachGiveawayResults" = "Giveaway Results";
"AttachTodo" = "Checklist";
"ServiceNotifications" = "service notifications"; "ServiceNotifications" = "service notifications";
"Bot" = "bot"; "Bot" = "bot";
"ALongTimeAgo" = "last seen a long time ago"; "ALongTimeAgo" = "last seen a long time ago";
@ -1277,6 +1294,7 @@
"SettingsPasscodeStart1" = "When you set up an additional passcode, a lock icon will appear on the chats page.\nTap it to lock and unlock your Telegram Web A."; "SettingsPasscodeStart1" = "When you set up an additional passcode, a lock icon will appear on the chats page.\nTap it to lock and unlock your Telegram Web A.";
"SettingsPasscodeStart2" = "Note: if you forget your local passcode, you'll need to log out of Telegram Web A and log in again."; "SettingsPasscodeStart2" = "Note: if you forget your local passcode, you'll need to log out of Telegram Web A and log in again.";
"CurrentPasswordPlaceholder" = "Current password"; "CurrentPasswordPlaceholder" = "Current password";
"ChangeYourProfilePicture" = "Change your profile picture";
"TooManyTabsTitle" = "Such error, many tabs"; "TooManyTabsTitle" = "Such error, many tabs";
"TooManyTabsDescription" = "Telegram supports only one active tab with the app.\nPlease reload this page to continue using this tab or close it."; "TooManyTabsDescription" = "Telegram supports only one active tab with the app.\nPlease reload this page to continue using this tab or close it.";
"TooManyTabsReload" = "Reload app"; "TooManyTabsReload" = "Reload app";
@ -1338,6 +1356,7 @@
"FormattingMonospaceAria" = "Monospace text"; "FormattingMonospaceAria" = "Monospace text";
"FormattingUnderlineAria" = "Underlined text"; "FormattingUnderlineAria" = "Underlined text";
"FormattingStrikethroughAria" = "Strikethrough text"; "FormattingStrikethroughAria" = "Strikethrough text";
"FormattingAddLinkAria" = "Add Link";
"FormattingEnterUrl" = "Enter URL..."; "FormattingEnterUrl" = "Enter URL...";
"PreviewWebPageClose" = "Clear Webpage Preview"; "PreviewWebPageClose" = "Clear Webpage Preview";
"MediaLocaltionImageAlt" = "Location on a map"; "MediaLocaltionImageAlt" = "Location on a map";
@ -1395,6 +1414,12 @@
"ProfileOpenAppAbout" = "By launching this mini app, you agree to the {terms}."; "ProfileOpenAppAbout" = "By launching this mini app, you agree to the {terms}.";
"ProfileOpenAppTerms" = "Terms of Service for Mini Apps"; "ProfileOpenAppTerms" = "Terms of Service for Mini Apps";
"ProfileBotOpenAppInfoLink" = "https://telegram.org/tos/mini-apps"; "ProfileBotOpenAppInfoLink" = "https://telegram.org/tos/mini-apps";
"ProfileBirthday" = "Date of birth";
"ProfileBirthdayToday" = "Birthday today";
"ProfileBirthdayValue" = "{date}";
"ProfileBirthdayValueYear" = "{date} ({age} years old)";
"ProfileBirthdayTodayValue" = "🎂 {date}";
"ProfileBirthdayTodayValueYear" = "🎂 {date} ({age} years old)";
"MonetizationInfoTONTitle" = "What is 💎 TON?"; "MonetizationInfoTONTitle" = "What is 💎 TON?";
"ChannelEarnLearnCoinAbout" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its high speed and low commissions on transactions. {link}"; "ChannelEarnLearnCoinAbout" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its high speed and low commissions on transactions. {link}";
"MonetizationBalanceZeroInfo" = "You will be able to collect rewards using Fragment, a third-party platform used by advertisers to pay for ads. {link}"; "MonetizationBalanceZeroInfo" = "You will be able to collect rewards using Fragment, a third-party platform used by advertisers to pay for ads. {link}";
@ -1544,6 +1569,7 @@
"StarsReactionTerms" = "By sending Stars you agree to the {link}"; "StarsReactionTerms" = "By sending Stars you agree to the {link}";
"StarsReactionLinkText" = "Terms of Service"; "StarsReactionLinkText" = "Terms of Service";
"StarsReactionLink" = "https://telegram.org/tos/stars"; "StarsReactionLink" = "https://telegram.org/tos/stars";
"AriaMiniApp" = "{bot} Mini App";
"MiniAppsMoreTabs_one" = "{botName} & {count} Other"; "MiniAppsMoreTabs_one" = "{botName} & {count} Other";
"MiniAppsMoreTabs_other" = "{botName} & {count} Others"; "MiniAppsMoreTabs_other" = "{botName} & {count} Others";
"PrizeCredits2_one" = "Your prize is {count} Star."; "PrizeCredits2_one" = "Your prize is {count} Star.";
@ -1645,6 +1671,9 @@
"SearchTabVoice" = "Voice"; "SearchTabVoice" = "Voice";
"SearchTabMessages" = "Messages"; "SearchTabMessages" = "Messages";
"SearchTabPublicPosts" = "Posts"; "SearchTabPublicPosts" = "Posts";
"SearchResultTopics" = "Topics";
"SearchResultMyChannels" = "Channels you joined";
"SearchResultRecommendedChannels" = "Similar Channels";
"StarsTransactionsAll" = "All Transactions"; "StarsTransactionsAll" = "All Transactions";
"StarsTransactionsIncoming" = "Incoming"; "StarsTransactionsIncoming" = "Incoming";
"StarsTransactionsOutgoing" = "Outgoing"; "StarsTransactionsOutgoing" = "Outgoing";
@ -1701,6 +1730,8 @@
"CheckPasswordTitle" = "Enter Password"; "CheckPasswordTitle" = "Enter Password";
"CheckPasswordPlaceholder" = "Password"; "CheckPasswordPlaceholder" = "Password";
"CheckPasswordDescription" = "Please enter your password to continue."; "CheckPasswordDescription" = "Please enter your password to continue.";
"PasswordFormPlaceholder" = "Password";
"PasswordFormSubmit" = "Next";
"ActionFallbackUser" = "User"; "ActionFallbackUser" = "User";
"ActionFallbackChat" = "Chat"; "ActionFallbackChat" = "Chat";
"ActionFallbackChannel" = "Channel"; "ActionFallbackChannel" = "Channel";
@ -1921,8 +1952,8 @@
"GiftPremiumPayWithStarsAcc" = "Pay with stars"; "GiftPremiumPayWithStarsAcc" = "Pay with stars";
"GetMoreStarsLinkText" = "Get More Stars >"; "GetMoreStarsLinkText" = "Get More Stars >";
"GiftPremiumDescriptionYourBalance" = "Your balance is **{stars}**. {link}"; "GiftPremiumDescriptionYourBalance" = "Your balance is **{stars}**. {link}";
"StarsGiftCompleted"= "Gift sent!"; "StarsGiftCompleted" = "Gift sent!";
"GiftSent"= "Gift sent!"; "GiftSent" = "Gift sent!";
"PrivacyDescriptionMessagesContactsAndPremium" = "You can restrict messages from users who are not in your contacts and don't have Premium."; "PrivacyDescriptionMessagesContactsAndPremium" = "You can restrict messages from users who are not in your contacts and don't have Premium.";
"PrivacyChargeForMessages" = "Charge for Messages"; "PrivacyChargeForMessages" = "Charge for Messages";
"PrivacyDescriptionChargeForMessages" = "Charge a fee for messages from people outside your contacts or those you haven't messaged first."; "PrivacyDescriptionChargeForMessages" = "Charge a fee for messages from people outside your contacts or those you haven't messaged first.";
@ -1948,7 +1979,7 @@
"MessageSentPaidToastText" = "You paid {amount}"; "MessageSentPaidToastText" = "You paid {amount}";
"ButtonUndo" = "Undo"; "ButtonUndo" = "Undo";
"ActionPaidOneMessageOutgoing" = "You paid {amount} to send a message"; "ActionPaidOneMessageOutgoing" = "You paid {amount} to send a message";
"ActionPaidOneMessageIncoming" = "You received {amount} from {user}"; "ActionPaidOneMessageIncoming" = "{user} paid {amount} to send a message";
"PaneMessagePaidMessageCharge" = "{peer} must pay {amount} for each message to you."; "PaneMessagePaidMessageCharge" = "{peer} must pay {amount} for each message to you.";
"ConfirmRemoveMessageFee" = "Yes"; "ConfirmRemoveMessageFee" = "Yes";
"ConfirmDialogMessageRemoveFee" = "Are you sure you want to allow **{peer}** to message you for free?"; "ConfirmDialogMessageRemoveFee" = "Are you sure you want to allow **{peer}** to message you for free?";
@ -1967,7 +1998,7 @@
"ComposerSubtitleFrozenAccount" = "Tap to view details"; "ComposerSubtitleFrozenAccount" = "Tap to view details";
"DescriptionRestrictedMedia" = "Posting media content is not allowed in this group."; "DescriptionRestrictedMedia" = "Posting media content is not allowed in this group.";
"DescriptionScheduledPaidMediaNotAllowed" = "Posting scheduled paid media content is not allowed"; "DescriptionScheduledPaidMediaNotAllowed" = "Posting scheduled paid media content is not allowed";
"DescriptionScheduledPaidMessagesNotAllowed" = "Scheduled paid messages is not allowed"; "DescriptionScheduledPaidMessagesNotAllowed" = "Paid messages can't be scheduled";
"GroupMessagesChargePrice" = "Charge Stars for Messages"; "GroupMessagesChargePrice" = "Charge Stars for Messages";
"RightsChargeStarsAbout" = "If you turn this on, regular members of the group will have to pay Stars to send messages."; "RightsChargeStarsAbout" = "If you turn this on, regular members of the group will have to pay Stars to send messages.";
"SetPriceGroupDescription" = "Your group will receive {percent} of the selected fee (~{amount}) for each incoming messages."; "SetPriceGroupDescription" = "Your group will receive {percent} of the selected fee (~{amount}) for each incoming messages.";
@ -1996,7 +2027,7 @@
"GiftRibbonSale" = "sale"; "GiftRibbonSale" = "sale";
"ButtonBuyGift" = "Buy for {stars}"; "ButtonBuyGift" = "Buy for {stars}";
"GiftInfoBuyGift" = "{user} is selling this gift and you can buy it."; "GiftInfoBuyGift" = "{user} is selling this gift and you can buy it.";
"StarsGiftBought"= "You bought gift!"; "StarsGiftBought" = "You bought a gift!";
"ButtonSellGift" = "Sell for {stars}"; "ButtonSellGift" = "Sell for {stars}";
"GiftSellTitle" = "Sell Gift"; "GiftSellTitle" = "Sell Gift";
"Sell" = "Sell"; "Sell" = "Sell";
@ -2035,7 +2066,7 @@
"SendInStandardQuality" = "Send In Standard Quality"; "SendInStandardQuality" = "Send In Standard Quality";
"SendInHighQuality" = "Send In High Quality"; "SendInHighQuality" = "Send In High Quality";
"MonoforumBadge" = "DIRECT"; "MonoforumBadge" = "DIRECT";
"MonoforumStatus" = "Channel messages"; "MonoforumStatus" = "Direct messages";
"MonoforumComposerPlaceholder" = "Choose a message to reply"; "MonoforumComposerPlaceholder" = "Choose a message to reply";
"ChannelSendMessage" = "Direct Messages"; "ChannelSendMessage" = "Direct Messages";
"AutomaticTranslation" = "Automatic Translation"; "AutomaticTranslation" = "Automatic Translation";
@ -2043,7 +2074,7 @@
"ComposerEmbeddedMessageSuggestedPostDescription" = "Tap to offer a price for publishing"; "ComposerEmbeddedMessageSuggestedPostDescription" = "Tap to offer a price for publishing";
"TitleSuggestedPostAmountForAnyTime" = "{amount} for publishing anytime"; "TitleSuggestedPostAmountForAnyTime" = "{amount} for publishing anytime";
"ActionSuggestedPostOutgoing" = "**You** suggest to post this message."; "ActionSuggestedPostOutgoing" = "**You** suggest to post this message.";
"ActionSuggestedPostIncoming" = "**{user}** suggest to post this message."; "ActionSuggestedPostIncoming" = "**{user}** suggested to post this message.";
"ActionSuggestedChangesPrice" = "price"; "ActionSuggestedChangesPrice" = "price";
"ActionSuggestedChangesText" = "text"; "ActionSuggestedChangesText" = "text";
"ActionSuggestedChangesTime" = "time"; "ActionSuggestedChangesTime" = "time";
@ -2063,7 +2094,7 @@
"SuggestMessageTimeDescription" = "{hint} The post will remain available for at least {duration} from this date."; "SuggestMessageTimeDescription" = "{hint} The post will remain available for at least {duration} from this date.";
"SuggestMessageAnytime" = "Anytime"; "SuggestMessageAnytime" = "Anytime";
"ButtonOfferAmount" = "Offer {amount}"; "ButtonOfferAmount" = "Offer {amount}";
"ButtonOfferFree" = "Offer Free"; "ButtonOfferFree" = "Offer for Free";
"ButtonUpdateTerms" = "Update Terms"; "ButtonUpdateTerms" = "Update Terms";
"InputPlaceholderPrice" = "Enter Price"; "InputPlaceholderPrice" = "Enter Price";
"SuggestedPostApprove" = "Approve"; "SuggestedPostApprove" = "Approve";
@ -2079,10 +2110,10 @@
"SuggestedPostPublishedYou" = "📅 Your post was automatically published on {peer} **{date}**."; "SuggestedPostPublishedYou" = "📅 Your post was automatically published on {peer} **{date}**.";
"SuggestedPostCharged" = "💰 {user} has been charged {amount}."; "SuggestedPostCharged" = "💰 {user} has been charged {amount}.";
"SuggestedPostChargedYou" = "💰 You have been charged {amount}."; "SuggestedPostChargedYou" = "💰 You have been charged {amount}.";
"SuggestedPostReceiveAmount" = "⏳ {peer} will receive the {currency} once the post has been live for {duration}."; "SuggestedPostReceiveAmount" = "⏳ {peer} will receive the payment once the post has been live for {duration}.";
"SuggestedPostReceiveAmountYou" = "⏳ {peer} will receive your {currency} once the post has been live for {duration}."; "SuggestedPostReceiveAmountYou" = "⏳ {peer} will receive your payment once the post has been live for {duration}.";
"SuggestedPostRefund" = "🔄 If {peer} removes the post before it has been live for {duration}, payment will be refunded."; "SuggestedPostRefund" = "🔄 If {peer} removes the post before it has been live for {duration}, payment will be refunded.";
"SuggestedPostRefundYou" = "🔄 If {peer} removes the post before it has been live for {duration}, your {currency} will be refunded."; "SuggestedPostRefundYou" = "🔄 If {peer} removes the post before it has been live for {duration}, your payment will be refunded.";
"SuggestedPostBalanceTooLow" = "⚠️ **Transaction failed** because {peer} didn't have enough {currency}."; "SuggestedPostBalanceTooLow" = "⚠️ **Transaction failed** because {peer} didn't have enough {currency}.";
"SuggestedPostRefundedByUser" = "{channel} will not receive {amount} because {user} requested a refund."; "SuggestedPostRefundedByUser" = "{channel} will not receive {amount} because {user} requested a refund.";
"SuggestedPostRefundedByChannel" = "{amount} was returned to {peer} because {channel} deleted the message."; "SuggestedPostRefundedByChannel" = "{amount} was returned to {peer} because {channel} deleted the message.";
@ -2092,8 +2123,8 @@
"DeclinePostDialogQuestion" = "Do you want to decline this post from **{sender}**?"; "DeclinePostDialogQuestion" = "Do you want to decline this post from **{sender}**?";
"SuggestedPostRejected" = "**{peer}** rejected this message."; "SuggestedPostRejected" = "**{peer}** rejected this message.";
"SuggestedPostRejectedYou" = "You rejected this message."; "SuggestedPostRejectedYou" = "You rejected this message.";
"SuggestedPostRejectedWithReason" = "**{peer}** rejected this message with the comment."; "SuggestedPostRejectedWithReason" = "**{peer}** rejected this message with the comment:";
"SuggestedPostRejectedWithReasonYou" = "You rejected this message with the comment."; "SuggestedPostRejectedWithReasonYou" = "You rejected this message with the comment:";
"SuggestedPostRejectedComment" = "\"{comment}\""; "SuggestedPostRejectedComment" = "\"{comment}\"";
"ActionSuggestedPostSuccess" = "{channel} has received {amount} for publishing post"; "ActionSuggestedPostSuccess" = "{channel} has received {amount} for publishing post";
"ComposerPlaceholderCaption" = "Caption"; "ComposerPlaceholderCaption" = "Caption";
@ -2101,15 +2132,15 @@
"SuggestedPostConfirmTitle" = "Accept Terms"; "SuggestedPostConfirmTitle" = "Accept Terms";
"SuggestedPostConfirmMessage" = "Do you want to publish this post from **{peer}**?"; "SuggestedPostConfirmMessage" = "Do you want to publish this post from **{peer}**?";
"SuggestedPostConfirmDetailsAdmin" = "You will receive **{amount}** ({commission}%) for publishing this post. It must remain visible for at least {duration} after publication."; "SuggestedPostConfirmDetailsAdmin" = "You will receive **{amount}** ({commission}%) for publishing this post. It must remain visible for at least {duration} after publication.";
"SuggestedPostConfirmDetailsUser" = "You will pay **{amount}** ({commission}%) for publishing this post. It must remain visible for at least {duration} after publication."; "SuggestedPostConfirmDetailsUser" = "You will pay **{amount}** for publishing this post. It must remain visible for at least {duration} after publication.";
"SuggestedPostConfirmDetailsWithTimeAdmin" = "You will receive **{amount}** ({commission}%) for publishing this post **{time}**. It must remain visible for at least {duration} after publication."; "SuggestedPostConfirmDetailsWithTimeAdmin" = "You will receive **{amount}** ({commission}%) for publishing this post **{time}**. It must remain visible for at least {duration} after publication.";
"SuggestedPostConfirmDetailsWithTimeUser" = "You will pay **{amount}** ({commission}%) for publishing this post **{time}**. It must remain visible for at least {duration} after publication."; "SuggestedPostConfirmDetailsWithTimeUser" = "You will pay **{amount}** for publishing this post **{time}**. It must remain visible for at least {duration} after publication.";
"ButtonPublish" = "Publish"; "ButtonPublish" = "Publish";
"ButtonPublishAtTime" = "Publish {time}"; "ButtonPublishAtTime" = "Post {time}";
"PublishNow" = "Publish Now"; "PublishNow" = "Publish Now";
"TitleNewToDoList" = "New Checklist"; "TitleNewToDoList" = "New Checklist";
"TitleEditToDoList" = "Edit Checklist"; "TitleEditToDoList" = "Edit Checklist";
"TitleAppendToDoList" = "Append Checklist"; "TitleAppendToDoList" = "Add Task";
"InputTitle" = "Title"; "InputTitle" = "Title";
"TitleToDoList" = "Checklist"; "TitleToDoList" = "Checklist";
"TitleTask" = "Task"; "TitleTask" = "Task";
@ -2139,16 +2170,17 @@
"PremiumMore" = "More"; "PremiumMore" = "More";
"SubscribeToTelegramPremiumForToggleTask" = "Subscribe to **Telegram Premium** to toggle tasks"; "SubscribeToTelegramPremiumForToggleTask" = "Subscribe to **Telegram Premium** to toggle tasks";
"SubscribeToTelegramPremiumForCreateToDo" = "Subscribe to **Telegram Premium** to create Checklists"; "SubscribeToTelegramPremiumForCreateToDo" = "Subscribe to **Telegram Premium** to create Checklists";
"SubscribeToTelegramPremiumForAppendToDo" = "Subscribe to **Telegram Premium** to append Checklists"; "SubscribeToTelegramPremiumForAppendToDo" = "Subscribe to **Telegram Premium** to add tasks";
"HintTodoListTasksCount" = "You can add {count} more tasks"; "HintTodoListTasksCount2_one" = "You can add {count} more task";
"HintTodoListTasksCount2_other" = "You can add {count} more tasks";
"ToDoListErrorChooseTitle" = "Please enter a title."; "ToDoListErrorChooseTitle" = "Please enter a title.";
"ToDoListErrorChooseTasks" = "Please enter at least one task."; "ToDoListErrorChooseTasks" = "Please enter at least one task.";
"GiftInfoCollectibleBy" = "Collectible #{number} by **{owner}**"; "GiftInfoCollectibleBy" = "Collectible #{number} by **{owner}**";
"PremiumPreviewTodo" = "Checklists"; "PremiumPreviewTodo" = "Checklists";
"DescriptionAboutTon" = "Offer TON to submit post suggestions to channels on Telegram."; "DescriptionAboutTon" = "Offer TON to submit post suggestions to channels on Telegram.";
"ButtonTopUpViaFragment" = "Top-Up Via Fragment"; "ButtonTopUpViaFragment" = "Top Up Via Fragment";
"TonModalHint" = "You can top-up your TON using Fragment."; "TonModalHint" = "You can top up your TON using Fragment.";
"TonGiftReceived" = "TON Top-Up"; "TonGiftReceived" = "TON Top Up";
"MediaSpoilerSensitive" = "18+"; "MediaSpoilerSensitive" = "18+";
"TitleSensitiveModal" = "{years}+ content"; "TitleSensitiveModal" = "{years}+ content";
"TextSensitiveModal" = "This media may contain sensitive content suitable only for adults. Do you still want to view it?"; "TextSensitiveModal" = "This media may contain sensitive content suitable only for adults. Do you still want to view it?";
@ -2162,9 +2194,11 @@
"TitleAgeCheckSuccess" = "Age Check Success"; "TitleAgeCheckSuccess" = "Age Check Success";
"ButtonAgeVerification" = "Verify My Age"; "ButtonAgeVerification" = "Verify My Age";
"GiftRibbonPremium" = "premium"; "GiftRibbonPremium" = "premium";
"NotificationGiftsLimit" = "You've already sent {count} of these gifts, and it's the limit."; "NotificationGiftsLimit2_one" = "You've already sent **one** of these gifts, and it's the limit.";
"NotificationGiftsLimit2_other" = "You've already sent {count} of these gifts, and it's the limit.";
"PremiumGiftHeader" = "Premium Gift"; "PremiumGiftHeader" = "Premium Gift";
"DescriptionGiftPremiumRequired" = "Subscribe to **Telegram Premium** to send up to **{count}** of these gifts and unlock access to multiple additional features."; "DescriptionGiftPremiumRequired2_one" = "Subscribe to **Telegram Premium** to send **one** of these gifts and unlock powerful features.";
"DescriptionGiftPremiumRequired2_other" = "Subscribe to **Telegram Premium** to send up to **{count}** of these gifts and unlock powerful features.";
"PriceInStars" = "Price in Stars"; "PriceInStars" = "Price in Stars";
"PriceInTON" = "Price in TON"; "PriceInTON" = "Price in TON";
"DescriptionComposerGiftMinimumCurrencyPrice" = "Minimum price is **{amount}**."; "DescriptionComposerGiftMinimumCurrencyPrice" = "Minimum price is **{amount}**.";
@ -2199,7 +2233,7 @@
"PublicPostsPremiumFeatureSubtitle" = "Global search is a Premium feature."; "PublicPostsPremiumFeatureSubtitle" = "Global search is a Premium feature.";
"PublicPostsSubscribeToPremium" = "Subscribe to Premium"; "PublicPostsSubscribeToPremium" = "Subscribe to Premium";
"NotificationPaidExtraSearch" = "{stars} spent on extra search."; "NotificationPaidExtraSearch" = "{stars} spent on extra search.";
"PostsSearchTransaction" = "Posts Search"; "PostsSearchTransaction" = "Public Post Search";
"TitleRating" = "Rating"; "TitleRating" = "Rating";
"RatingReflectsActivity" = "This rating reflects {name}'s activity on Telegram. It is based on:"; "RatingReflectsActivity" = "This rating reflects {name}'s activity on Telegram. It is based on:";
"RatingYourReflectsActivity" = "This rating reflects your activity on Telegram. It is based on:"; "RatingYourReflectsActivity" = "This rating reflects your activity on Telegram. It is based on:";
@ -2218,4 +2252,7 @@
"DescriptionFutureRating_one" = "This will be your rating in {time}, after {points} point is added. {link}"; "DescriptionFutureRating_one" = "This will be your rating in {time}, after {points} point is added. {link}";
"DescriptionFutureRating_other" = "This will be your rating in {time}, after {points} points are added. {link}"; "DescriptionFutureRating_other" = "This will be your rating in {time}, after {points} points are added. {link}";
"LinkDescriptionRatingBack" = "Back >"; "LinkDescriptionRatingBack" = "Back >";
"LinkDescriptionRatingPreview" = "Preview >"; "LinkDescriptionRatingPreview" = "Preview >";
"ErrorFocusInaccessibleMessage" = "Unfortunately, you can't access this message. You aren't a member of the chat where it was posted.";
"ContextMenuHintMouse" = "To edit or reply, close this menu. Then right click on a message.";
"ContextMenuHintTouch" = "To edit or reply, close this menu. Then long tap on a message.";

View File

@ -36,7 +36,7 @@ const AuthPassword: FC<StateProps> = ({
<h1>{lang('LoginHeaderPassword')}</h1> <h1>{lang('LoginHeaderPassword')}</h1>
<p className="note">{lang('LoginEnterPasswordDescription')}</p> <p className="note">{lang('LoginEnterPasswordDescription')}</p>
<PasswordForm <PasswordForm
clearError={clearAuthErrorKey} onClearError={clearAuthErrorKey}
error={authErrorKey && lang.withRegular(authErrorKey)} error={authErrorKey && lang.withRegular(authErrorKey)}
hint={authHint} hint={authHint}
isLoading={authIsLoading} isLoading={authIsLoading}

View File

@ -22,7 +22,7 @@ import { selectPeerStory, selectPollFromMessage } from '../../global/selectors';
import trimText from '../../util/trimText'; import trimText from '../../util/trimText';
import renderText from './helpers/renderText'; import renderText from './helpers/renderText';
import useOldLang from '../../hooks/useOldLang'; import useLang from '../../hooks/useLang';
import ActionMessageText from '../middle/message/ActionMessageText'; import ActionMessageText from '../middle/message/ActionMessageText';
import MessageText from './MessageText'; import MessageText from './MessageText';
@ -59,7 +59,7 @@ function MessageSummary({
observeIntersectionForLoading, observeIntersectionForLoading,
observeIntersectionForPlaying, observeIntersectionForPlaying,
}: OwnProps & StateProps) { }: OwnProps & StateProps) {
const lang = useOldLang(); const lang = useLang();
const extractedText = extractMessageText(message, inChatList); const extractedText = extractMessageText(message, inChatList);
const hasPoll = Boolean(getMessagePollId(message)); const hasPoll = Boolean(getMessagePollId(message));
const isAction = isActionMessage(message); const isAction = isActionMessage(message);

View File

@ -7,11 +7,11 @@ import type { ObserveFn } from '../../hooks/useIntersectionObserver';
import type { ThreadId } from '../../types'; import type { ThreadId } from '../../types';
import { ApiMessageEntityTypes } from '../../api/types'; import { ApiMessageEntityTypes } from '../../api/types';
import { CONTENT_NOT_SUPPORTED } from '../../config';
import { extractMessageText, stripCustomEmoji } from '../../global/helpers'; import { extractMessageText, stripCustomEmoji } from '../../global/helpers';
import trimText from '../../util/trimText'; import trimText from '../../util/trimText';
import { insertTextEntity, renderTextWithEntities } from './helpers/renderTextWithEntities'; import { insertTextEntity, renderTextWithEntities } from './helpers/renderTextWithEntities';
import useLang from '../../hooks/useLang';
import useSyncEffect from '../../hooks/useSyncEffect'; import useSyncEffect from '../../hooks/useSyncEffect';
import useUniqueId from '../../hooks/useUniqueId'; import useUniqueId from '../../hooks/useUniqueId';
@ -67,6 +67,8 @@ function MessageText({
const textCacheBusterRef = useRef(0); const textCacheBusterRef = useRef(0);
const lang = useLang();
const formattedText = translatedText || extractMessageText(messageOrStory, inChatList); const formattedText = translatedText || extractMessageText(messageOrStory, inChatList);
const adaptedFormattedText = isForAnimation && formattedText ? stripCustomEmoji(formattedText) : formattedText; const adaptedFormattedText = isForAnimation && formattedText ? stripCustomEmoji(formattedText) : formattedText;
const { text, entities } = adaptedFormattedText || {}; const { text, entities } = adaptedFormattedText || {};
@ -106,7 +108,7 @@ function MessageText({
}, [entitiesWithFocusedQuote]) || 0; }, [entitiesWithFocusedQuote]) || 0;
if (!text && !canBeEmpty) { if (!text && !canBeEmpty) {
return <span className="content-unsupported">{CONTENT_NOT_SUPPORTED}</span>; return <span className="content-unsupported">{lang('MessageUnsupported')}</span>;
} }
return ( return (

View File

@ -13,7 +13,7 @@ import stopEvent from '../../util/stopEvent';
import useTimeout from '../../hooks/schedulers/useTimeout'; import useTimeout from '../../hooks/schedulers/useTimeout';
import useAppLayout from '../../hooks/useAppLayout'; import useAppLayout from '../../hooks/useAppLayout';
import useOldLang from '../../hooks/useOldLang'; import useLang from '../../hooks/useLang';
import Button from '../ui/Button'; import Button from '../ui/Button';
import Icon from './icons/Icon'; import Icon from './icons/Icon';
@ -29,8 +29,8 @@ type OwnProps = {
shouldShowSubmit?: boolean; shouldShowSubmit?: boolean;
shouldResetValue?: boolean; shouldResetValue?: boolean;
isPasswordVisible?: boolean; isPasswordVisible?: boolean;
clearError: NoneToVoidFunction;
noRipple?: boolean; noRipple?: boolean;
onClearError: NoneToVoidFunction;
onChangePasswordVisibility: (state: boolean) => void; onChangePasswordVisibility: (state: boolean) => void;
onInputChange?: (password: string) => void; onInputChange?: (password: string) => void;
onSubmit?: (password: string) => void; onSubmit?: (password: string) => void;
@ -41,20 +41,21 @@ const PasswordForm: FC<OwnProps> = ({
isPasswordVisible, isPasswordVisible,
error, error,
hint, hint,
placeholder = 'Password', placeholder,
submitLabel = 'Next', submitLabel,
description, description,
shouldShowSubmit, shouldShowSubmit,
shouldResetValue, shouldResetValue,
shouldDisablePasswordManager = false, shouldDisablePasswordManager = false,
noRipple = false, noRipple = false,
clearError, onClearError,
onChangePasswordVisibility, onChangePasswordVisibility,
onInputChange, onInputChange,
onSubmit, onSubmit,
}) => { }) => {
const inputRef = useRef<HTMLInputElement>(); const inputRef = useRef<HTMLInputElement>();
const lang = useOldLang();
const lang = useLang();
const { isMobile } = useAppLayout(); const { isMobile } = useAppLayout();
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
@ -84,7 +85,7 @@ const PasswordForm: FC<OwnProps> = ({
function onPasswordChange(e: ChangeEvent<HTMLInputElement>) { function onPasswordChange(e: ChangeEvent<HTMLInputElement>) {
if (error) { if (error) {
clearError(); onClearError();
} }
const { target } = e; const { target } = e;
@ -141,14 +142,14 @@ const PasswordForm: FC<OwnProps> = ({
maxLength={256} maxLength={256}
dir="auto" dir="auto"
/> />
<label>{error || hint || placeholder}</label> <label>{error || hint || placeholder || lang('PasswordFormPlaceholder')}</label>
<div <div
className="div-button toggle-password" className="div-button toggle-password"
onClick={togglePasswordVisibility} onClick={togglePasswordVisibility}
role="button" role="button"
tabIndex={0} tabIndex={0}
title="Toggle password visibility" title={lang('AriaPasswordToggle')}
aria-label="Toggle password visibility" aria-label={lang('AriaPasswordToggle')}
> >
<Icon name={isPasswordVisible ? 'eye' : 'eye-crossed'} /> <Icon name={isPasswordVisible ? 'eye' : 'eye-crossed'} />
</div> </div>
@ -156,7 +157,7 @@ const PasswordForm: FC<OwnProps> = ({
{description && <p className="description">{description}</p>} {description && <p className="description">{description}</p>}
{onSubmit && (canSubmit || shouldShowSubmit) && ( {onSubmit && (canSubmit || shouldShowSubmit) && (
<Button type="submit" ripple={!noRipple} isLoading={isLoading} disabled={!canSubmit}> <Button type="submit" ripple={!noRipple} isLoading={isLoading} disabled={!canSubmit}>
{submitLabel} {submitLabel || lang('PasswordFormSubmit')}
</Button> </Button>
)} )}
</form> </form>

View File

@ -73,7 +73,7 @@ const VerificationMonetizationModal = ({
placeholder={lang('CheckPasswordPlaceholder')} placeholder={lang('CheckPasswordPlaceholder')}
error={renderingModal?.errorKey && lang.withRegular(renderingModal.errorKey)} error={renderingModal?.errorKey && lang.withRegular(renderingModal.errorKey)}
description={lang('CheckPasswordDescription')} description={lang('CheckPasswordDescription')}
clearError={handleClearError} onClearError={handleClearError}
isLoading={renderingModal?.isLoading} isLoading={renderingModal?.isLoading}
hint={passwordHint} hint={passwordHint}
isPasswordVisible={shouldShowPassword} isPasswordVisible={shouldShowPassword}

View File

@ -6,7 +6,8 @@ import type { ObserveFn } from '../../hooks/useIntersectionObserver';
import type { TextPart } from '../../types'; import type { TextPart } from '../../types';
import { import {
getFirstLinkInMessage, getMessageText, getFirstLinkInMessage,
getMessageTextWithFallback,
} from '../../global/helpers'; } from '../../global/helpers';
import { selectWebPageFromMessage } from '../../global/selectors'; import { selectWebPageFromMessage } from '../../global/selectors';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
@ -15,6 +16,7 @@ import trimText from '../../util/trimText';
import { renderMessageSummary } from './helpers/renderMessageText'; import { renderMessageSummary } from './helpers/renderMessageText';
import renderText from './helpers/renderText'; import renderText from './helpers/renderText';
import useLang from '../../hooks/useLang';
import useLastCallback from '../../hooks/useLastCallback'; import useLastCallback from '../../hooks/useLastCallback';
import useOldLang from '../../hooks/useOldLang'; import useOldLang from '../../hooks/useOldLang';
@ -45,7 +47,8 @@ type StateProps = {
const WebLink = ({ const WebLink = ({
message, webPage, senderTitle, isProtected, observeIntersection, onMessageClick, message, webPage, senderTitle, isProtected, observeIntersection, onMessageClick,
}: OwnProps & StateProps) => { }: OwnProps & StateProps) => {
const lang = useOldLang(); const lang = useLang();
const oldLang = useOldLang();
let linkData: ApiWebPageWithFormatted | undefined = webPage; let linkData: ApiWebPageWithFormatted | undefined = webPage;
@ -57,7 +60,7 @@ const WebLink = ({
linkData = { linkData = {
siteName: domain.replace(/^www./, ''), siteName: domain.replace(/^www./, ''),
url: url.includes('://') ? url : url.includes('@') ? `mailto:${url}` : `http://${url}`, url: url.includes('://') ? url : url.includes('@') ? `mailto:${url}` : `http://${url}`,
formattedDescription: getMessageText(message)?.text !== url formattedDescription: getMessageTextWithFallback(lang, message)?.text !== url
? renderMessageSummary(lang, message, undefined, undefined, MAX_TEXT_LENGTH) ? renderMessageSummary(lang, message, undefined, undefined, MAX_TEXT_LENGTH)
: undefined, : undefined,
} as ApiWebPageWithFormatted; } as ApiWebPageWithFormatted;
@ -125,7 +128,7 @@ const WebLink = ({
onClick={handleMessageClick} onClick={handleMessageClick}
isRtl={lang.isRtl} isRtl={lang.isRtl}
> >
{formatPastTimeShort(lang, message.date * 1000)} {formatPastTimeShort(oldLang, message.date * 1000)}
</Link> </Link>
</div> </div>
)} )}

View File

@ -11,7 +11,7 @@ import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
import type { ChatTranslatedMessages } from '../../../types'; import type { ChatTranslatedMessages } from '../../../types';
import type { IconName } from '../../../types/icons'; import type { IconName } from '../../../types/icons';
import { CONTENT_NOT_SUPPORTED, TON_CURRENCY_CODE } from '../../../config'; import { TON_CURRENCY_CODE } from '../../../config';
import { import {
getMessageIsSpoiler, getMessageIsSpoiler,
getMessageRoundVideo, getMessageRoundVideo,
@ -213,8 +213,8 @@ const EmbeddedMessage: FC<OwnProps> = ({
function renderMediaContentType(media?: MediaContainer) { function renderMediaContentType(media?: MediaContainer) {
if (!media || media.content.text) return NBSP; if (!media || media.content.text) return NBSP;
const description = getMediaContentTypeDescription(oldLang, media.content, {}); const description = getMediaContentTypeDescription(lang, media.content, {});
if (!description || description === CONTENT_NOT_SUPPORTED) return NBSP; if (!description) return NBSP;
return ( return (
<span> <span>
{renderText(description)} {renderText(description)}

View File

@ -1,13 +1,12 @@
import { getGlobal } from '../../../global'; import { getGlobal } from '../../../global';
import type { ApiMessage, ApiSponsoredMessage } from '../../../api/types'; import type { ApiMessage, ApiSponsoredMessage } from '../../../api/types';
import type { OldLangFn } from '../../../hooks/useOldLang';
import type { TextPart, ThreadId } from '../../../types'; import type { TextPart, ThreadId } from '../../../types';
import { ApiMessageEntityTypes } from '../../../api/types'; import { ApiMessageEntityTypes } from '../../../api/types';
import { import {
getMessageStatefulContent, getMessageStatefulContent,
getMessageText, getMessageTextWithFallback,
} from '../../../global/helpers'; } from '../../../global/helpers';
import { import {
getMessageSummaryDescription, getMessageSummaryDescription,
@ -16,6 +15,7 @@ import {
TRUNCATED_SUMMARY_LENGTH, TRUNCATED_SUMMARY_LENGTH,
} from '../../../global/helpers/messageSummary'; } from '../../../global/helpers/messageSummary';
import { getMessageKey } from '../../../util/keys/messageKey'; import { getMessageKey } from '../../../util/keys/messageKey';
import { getTranslationFn, type LangFn } from '../../../util/localization';
import trimText from '../../../util/trimText'; import trimText from '../../../util/trimText';
import renderText from './renderText'; import renderText from './renderText';
import { renderTextWithEntities } from './renderTextWithEntities'; import { renderTextWithEntities } from './renderTextWithEntities';
@ -48,7 +48,7 @@ export function renderMessageText({
const { text, entities } = message.content.text || {}; const { text, entities } = message.content.text || {};
if (!text) { if (!text) {
const contentNotSupportedText = getMessageText(message)?.text; const contentNotSupportedText = getMessageTextWithFallback(getTranslationFn(), message)?.text;
return contentNotSupportedText ? [trimText(contentNotSupportedText, truncateLength)] : undefined; return contentNotSupportedText ? [trimText(contentNotSupportedText, truncateLength)] : undefined;
} }
@ -73,7 +73,7 @@ export function renderMessageText({
// TODO Use Message Summary component instead // TODO Use Message Summary component instead
export function renderMessageSummary( export function renderMessageSummary(
lang: OldLangFn, lang: LangFn,
message: ApiMessage, message: ApiMessage,
noEmoji = false, noEmoji = false,
highlight?: string, highlight?: string,

View File

@ -19,8 +19,8 @@ import renderText from '../helpers/renderText';
import useTimeout from '../../../hooks/schedulers/useTimeout'; import useTimeout from '../../../hooks/schedulers/useTimeout';
import useFlag from '../../../hooks/useFlag'; import useFlag from '../../../hooks/useFlag';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback'; import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import ListItem from '../../ui/ListItem'; import ListItem from '../../ui/ListItem';
import StickerView from '../StickerView'; import StickerView from '../StickerView';
@ -58,7 +58,7 @@ const UserBirthday = ({
const animationPlayedRef = useRef(false); const animationPlayedRef = useRef(false);
const [isPlayingAnimation, playAnimation, stopAnimation] = useFlag(); const [isPlayingAnimation, playAnimation, stopAnimation] = useFlag();
const lang = useOldLang(); const lang = useLang();
const { const {
formattedDate, formattedDate,
@ -139,7 +139,17 @@ const UserBirthday = ({
} }
}, [isInSettings, isPlayingAnimation]); }, [isInSettings, isPlayingAnimation]);
const valueKey = `ProfileBirthday${isToday ? 'Today' : ''}Value${age ? 'Year' : ''}`; const value = useMemo(() => {
if (age) {
return lang(
`ProfileBirthday${isToday ? 'Today' : ''}ValueYear`,
{ date: formattedDate, age },
{ pluralValue: age },
);
}
return lang(`ProfileBirthday${isToday ? 'Today' : ''}Value`, { date: formattedDate });
}, [age, formattedDate, isToday, lang]);
const canGiftPremium = isToday && !user.isPremium && !user.isSelf && !isPremiumPurchaseBlocked; const canGiftPremium = isToday && !user.isPremium && !user.isSelf && !isPremiumPurchaseBlocked;
@ -175,7 +185,7 @@ const UserBirthday = ({
onSecondaryIconClick={handleOpenGiftModal} onSecondaryIconClick={handleOpenGiftModal}
> >
<div className="title" dir={lang.isRtl ? 'rtl' : undefined}> <div className="title" dir={lang.isRtl ? 'rtl' : undefined}>
{renderText(lang(valueKey, [formattedDate, age], undefined, age))} {renderText(value)}
</div> </div>
<span className="subtitle">{lang(isToday ? 'ProfileBirthdayToday' : 'ProfileBirthday')}</span> <span className="subtitle">{lang(isToday ? 'ProfileBirthdayToday' : 'ProfileBirthday')}</span>
</ListItem> </ListItem>

View File

@ -6,7 +6,6 @@ import type {
ApiChat, ApiMessage, ApiMessageOutgoingStatus, ApiChat, ApiMessage, ApiMessageOutgoingStatus,
ApiUser, ApiUser,
} from '../../../api/types'; } from '../../../api/types';
import type { OldLangFn } from '../../../hooks/useOldLang';
import { import {
getMessageIsSpoiler, getMessageIsSpoiler,
@ -18,11 +17,13 @@ import {
import { selectChat, selectUser } from '../../../global/selectors'; import { selectChat, selectUser } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import { formatPastTimeShort } from '../../../util/dates/dateFormat'; import { formatPastTimeShort } from '../../../util/dates/dateFormat';
import { type LangFn } from '../../../util/localization';
import { renderMessageSummary } from '../../common/helpers/renderMessageText'; import { renderMessageSummary } from '../../common/helpers/renderMessageText';
import useMessageMediaHash from '../../../hooks/media/useMessageMediaHash'; import useMessageMediaHash from '../../../hooks/media/useMessageMediaHash';
import useThumbnail from '../../../hooks/media/useThumbnail'; import useThumbnail from '../../../hooks/media/useThumbnail';
import useAppLayout from '../../../hooks/useAppLayout'; import useAppLayout from '../../../hooks/useAppLayout';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback'; import useLastCallback from '../../../hooks/useLastCallback';
import useMedia from '../../../hooks/useMedia'; import useMedia from '../../../hooks/useMedia';
import useOldLang from '../../../hooks/useOldLang'; import useOldLang from '../../../hooks/useOldLang';
@ -68,7 +69,8 @@ const ChatMessage: FC<OwnProps & StateProps> = ({
focusMessage({ chatId, messageId: message.id, shouldReplaceHistory: true }); focusMessage({ chatId, messageId: message.id, shouldReplaceHistory: true });
}); });
const lang = useOldLang(); const lang = useLang();
const oldLang = useOldLang();
const buttonRef = useSelectWithEnter(handleClick); const buttonRef = useSelectWithEnter(handleClick);
@ -98,7 +100,7 @@ const ChatMessage: FC<OwnProps & StateProps> = ({
/> />
<div className="message-date"> <div className="message-date">
<Link className="date"> <Link className="date">
{formatPastTimeShort(lang, message.date * 1000)} {formatPastTimeShort(oldLang, message.date * 1000)}
</Link> </Link>
</div> </div>
@ -114,7 +116,7 @@ const ChatMessage: FC<OwnProps & StateProps> = ({
}; };
function renderSummary( function renderSummary(
lang: OldLangFn, message: ApiMessage, blobUrl?: string, searchQuery?: string, isRoundVideo?: boolean, lang: LangFn, message: ApiMessage, blobUrl?: string, searchQuery?: string, isRoundVideo?: boolean,
) { ) {
if (!blobUrl) { if (!blobUrl) {
return renderMessageSummary(lang, message, undefined, searchQuery); return renderMessageSummary(lang, message, undefined, searchQuery);

View File

@ -12,7 +12,7 @@ import { throttle } from '../../../util/schedulers';
import { renderMessageSummary } from '../../common/helpers/renderMessageText'; import { renderMessageSummary } from '../../common/helpers/renderMessageText';
import useAppLayout from '../../../hooks/useAppLayout'; import useAppLayout from '../../../hooks/useAppLayout';
import useOldLang from '../../../hooks/useOldLang'; import useLang from '../../../hooks/useLang';
import NothingFound from '../../common/NothingFound'; import NothingFound from '../../common/NothingFound';
import InfiniteScroll from '../../ui/InfiniteScroll'; import InfiniteScroll from '../../ui/InfiniteScroll';
@ -53,7 +53,7 @@ const ChatMessageResults: FC<OwnProps & StateProps> = ({
}) => { }) => {
const { searchMessagesGlobal, openThread } = getActions(); const { searchMessagesGlobal, openThread } = getActions();
const lang = useOldLang(); const lang = useLang();
const { isMobile } = useAppLayout(); const { isMobile } = useAppLayout();
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => { const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
@ -133,14 +133,14 @@ const ChatMessageResults: FC<OwnProps & StateProps> = ({
{nothingFound && ( {nothingFound && (
<NothingFound <NothingFound
withSticker withSticker
text={lang('ChatList.Search.NoResults')} text={lang('ChatListSearchNoResults')}
description={lang('ChatList.Search.NoResultsDescription')} description={lang('ChatListSearchNoResultsDescription')}
/> />
)} )}
{Boolean(foundTopicIds?.length) && ( {Boolean(foundTopicIds?.length) && (
<div className="pb-2"> <div className="pb-2">
<h3 className="section-heading topic-search-heading" dir={lang.isRtl ? 'auto' : undefined}> <h3 className="section-heading topic-search-heading" dir={lang.isRtl ? 'auto' : undefined}>
{lang('Topics')} {lang('SearchResultTopics')}
</h3> </h3>
{foundTopicIds.map((id) => { {foundTopicIds.map((id) => {
return ( return (

View File

@ -31,7 +31,6 @@ import useHorizontalScroll from '../../../hooks/useHorizontalScroll';
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver'; import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
import useLang from '../../../hooks/useLang'; import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback'; import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import Icon from '../../common/icons/Icon'; import Icon from '../../common/icons/Icon';
import NothingFound from '../../common/NothingFound'; import NothingFound from '../../common/NothingFound';
@ -102,7 +101,6 @@ const ChatResults: FC<OwnProps & StateProps> = ({
const containerRef = useRef<HTMLDivElement>(); const containerRef = useRef<HTMLDivElement>();
const chatSelectionRef = useRef<HTMLDivElement>(); const chatSelectionRef = useRef<HTMLDivElement>();
const oldLang = useOldLang();
const lang = useLang(); const lang = useLang();
const { isMobile } = useAppLayout(); const { isMobile } = useAppLayout();
@ -318,7 +316,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
function renderFoundMessage(message: ApiMessage) { function renderFoundMessage(message: ApiMessage) {
const chatsById = getGlobal().chats.byId; const chatsById = getGlobal().chats.byId;
const text = renderMessageSummary(oldLang, message); const text = renderMessageSummary(lang, message);
const chat = chatsById[message.chatId]; const chat = chatsById[message.chatId];
if (!text || !chat) { if (!text || !chat) {
@ -375,14 +373,14 @@ const ChatResults: FC<OwnProps & StateProps> = ({
{nothingFound && ( {nothingFound && (
<NothingFound <NothingFound
withSticker withSticker
text={oldLang('ChatList.Search.NoResults')} text={lang('ChatListSearchNoResults')}
description={oldLang('ChatList.Search.NoResultsDescription')} description={lang('ChatListSearchNoResultsDescription')}
/> />
)} )}
{Boolean(localResults.length) && !isChannelList && ( {Boolean(localResults.length) && !isChannelList && (
<div <div
className="chat-selection no-scrollbar" className="chat-selection no-scrollbar"
dir={oldLang.isRtl ? 'rtl' : undefined} dir={lang.isRtl ? 'rtl' : undefined}
ref={chatSelectionRef} ref={chatSelectionRef}
> >
{localResults.map((id) => ( {localResults.map((id) => (
@ -397,13 +395,13 @@ const ChatResults: FC<OwnProps & StateProps> = ({
)} )}
{Boolean(localResults.length) && ( {Boolean(localResults.length) && (
<div className="search-section"> <div className="search-section">
<h3 className="section-heading" dir={oldLang.isRtl ? 'auto' : undefined}> <h3 className="section-heading" dir={lang.isRtl ? 'auto' : undefined}>
{localResults.length > LESS_LIST_ITEMS_AMOUNT && ( {localResults.length > LESS_LIST_ITEMS_AMOUNT && (
<Link className="Link" onClick={handleClickShowMoreLocal}> <Link className="Link" onClick={handleClickShowMoreLocal}>
{oldLang(shouldShowMoreLocal ? 'ChatList.Search.ShowLess' : 'ChatList.Search.ShowMore')} {lang(shouldShowMoreLocal ? 'ChatListSearchShowLess' : 'ChatListSearchShowMore')}
</Link> </Link>
)} )}
{oldLang(isChannelList ? 'SearchMyChannels' : 'DialogList.SearchSectionDialogs')} {lang(isChannelList ? 'SearchResultMyChannels' : 'DialogListSearchSectionDialogs')}
</h3> </h3>
{localResults.map((id, index) => { {localResults.map((id, index) => {
if (!shouldShowMoreLocal && index >= LESS_LIST_ITEMS_AMOUNT) { if (!shouldShowMoreLocal && index >= LESS_LIST_ITEMS_AMOUNT) {
@ -422,13 +420,13 @@ const ChatResults: FC<OwnProps & StateProps> = ({
)} )}
{Boolean(globalResults.length) && ( {Boolean(globalResults.length) && (
<div className="search-section"> <div className="search-section">
<h3 className="section-heading" dir={oldLang.isRtl ? 'auto' : undefined}> <h3 className="section-heading" dir={lang.isRtl ? 'auto' : undefined}>
{globalResults.length > LESS_LIST_ITEMS_AMOUNT && ( {globalResults.length > LESS_LIST_ITEMS_AMOUNT && (
<Link className="Link" onClick={handleClickShowMoreGlobal}> <Link className="Link" onClick={handleClickShowMoreGlobal}>
{oldLang(shouldShowMoreGlobal ? 'ChatList.Search.ShowLess' : 'ChatList.Search.ShowMore')} {lang(shouldShowMoreGlobal ? 'ChatListSearchShowLess' : 'ChatListSearchShowMore')}
</Link> </Link>
)} )}
{oldLang('DialogList.SearchSectionGlobal')} {lang('DialogListSearchSectionGlobal')}
</h3> </h3>
{sponsoredPeer && ( {sponsoredPeer && (
<LeftSearchResultSponsored sponsoredPeer={sponsoredPeer} observeIntersection={observe} /> <LeftSearchResultSponsored sponsoredPeer={sponsoredPeer} observeIntersection={observe} />
@ -450,8 +448,8 @@ const ChatResults: FC<OwnProps & StateProps> = ({
)} )}
{Boolean(suggestedChannelIds?.length) && !searchQuery && ( {Boolean(suggestedChannelIds?.length) && !searchQuery && (
<div className="search-section"> <div className="search-section">
<h3 className="section-heading" dir={oldLang.isRtl ? 'auto' : undefined}> <h3 className="section-heading" dir={lang.isRtl ? 'auto' : undefined}>
{oldLang('SearchRecommendedChannels')} {lang('SearchResultRecommendedChannels')}
</h3> </h3>
{suggestedChannelIds.map((id) => { {suggestedChannelIds.map((id) => {
return ( return (
@ -468,7 +466,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
{renderContextMenu()} {renderContextMenu()}
{shouldRenderMessagesSection && ( {shouldRenderMessagesSection && (
<div className="search-section"> <div className="search-section">
<h3 className="section-heading" dir={oldLang.isRtl ? 'auto' : undefined}> <h3 className="section-heading" dir={lang.isRtl ? 'auto' : undefined}>
{!isChannelList && ( {!isChannelList && (
<Link className="Link menuTrigger dropDownLink" onClick={handleClickContext}> <Link className="Link menuTrigger dropDownLink" onClick={handleClickContext}>
{lang('SearchContextCaption', { {lang('SearchContextCaption', {
@ -489,7 +487,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
</Transition> </Transition>
</Link> </Link>
)} )}
{oldLang('SearchMessages')} {lang('SearchMessages')}
</h3> </h3>
{actualFoundIds.map(renderFoundMessage)} {actualFoundIds.map(renderFoundMessage)}
</div> </div>

View File

@ -16,7 +16,6 @@ import { renderMessageSummary } from '../../common/helpers/renderMessageText';
import useLang from '../../../hooks/useLang'; import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback'; import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import NothingFound from '../../common/NothingFound'; import NothingFound from '../../common/NothingFound';
import InfiniteScroll from '../../ui/InfiniteScroll'; import InfiniteScroll from '../../ui/InfiniteScroll';
@ -53,7 +52,6 @@ const PublicPostsResults = ({
const { searchMessagesGlobal } = getActions(); const { searchMessagesGlobal } = getActions();
const lang = useLang(); const lang = useLang();
const oldLang = useOldLang();
const handleSearch = useLastCallback(() => { const handleSearch = useLastCallback(() => {
if (!searchQuery) return; if (!searchQuery) return;
@ -90,7 +88,7 @@ const PublicPostsResults = ({
function renderFoundMessage(message: ApiMessage) { function renderFoundMessage(message: ApiMessage) {
const chatsById = getGlobal().chats.byId; const chatsById = getGlobal().chats.byId;
const text = renderMessageSummary(oldLang, message); const text = renderMessageSummary(lang, message);
const chat = chatsById[message.chatId]; const chat = chatsById[message.chatId];
if (!text || !chat) { if (!text || !chat) {
@ -130,8 +128,8 @@ const PublicPostsResults = ({
> >
{isNothingFound && ( {isNothingFound && (
<NothingFound <NothingFound
text={oldLang('ChatList.Search.NoResults')} text={lang('ChatListSearchNoResults')}
description={oldLang('ChatList.Search.NoResultsDescription')} description={lang('ChatListSearchNoResultsDescription')}
withSticker withSticker
/> />
)} )}

View File

@ -9,7 +9,7 @@ import { formatDateTimeToString } from '../../../util/dates/dateFormat';
import getSessionIcon from './helpers/getSessionIcon'; import getSessionIcon from './helpers/getSessionIcon';
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev'; import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
import useOldLang from '../../../hooks/useOldLang'; import useLang from '../../../hooks/useLang';
import Icon from '../../common/icons/Icon'; import Icon from '../../common/icons/Icon';
import Button from '../../ui/Button'; import Button from '../../ui/Button';
@ -33,7 +33,7 @@ const SettingsActiveSession: FC<OwnProps & StateProps> = ({
isOpen, session, onClose, isOpen, session, onClose,
}) => { }) => {
const { changeSessionSettings, terminateAuthorization } = getActions(); const { changeSessionSettings, terminateAuthorization } = getActions();
const lang = useOldLang(); const lang = useLang();
const renderingSession = useCurrentOrPrev(session, true); const renderingSession = useCurrentOrPrev(session, true);
@ -66,13 +66,13 @@ const SettingsActiveSession: FC<OwnProps & StateProps> = ({
<Button round color="translucent" size="smaller" ariaLabel={lang('Close')} onClick={onClose}> <Button round color="translucent" size="smaller" ariaLabel={lang('Close')} onClick={onClose}>
<Icon name="close" /> <Icon name="close" />
</Button> </Button>
<div className="modal-title">{lang('SessionPreview.Title')}</div> <div className="modal-title">{lang('SessionPreviewTitle')}</div>
<Button <Button
color="danger" color="danger"
onClick={handleTerminateSessionClick} onClick={handleTerminateSessionClick}
className={buildClassName('modal-action-button', styles.headerButton)} className={buildClassName('modal-action-button', styles.headerButton)}
> >
{lang('SessionPreview.TerminateSession')} {lang('SessionPreviewTerminateSession')}
</Button> </Button>
</div> </div>
); );
@ -91,12 +91,12 @@ const SettingsActiveSession: FC<OwnProps & StateProps> = ({
)} )}
/> />
<h3 className={styles.title} dir="auto">{renderingSession?.deviceModel}</h3> <h3 className={styles.title} dir="auto">{renderingSession?.deviceModel}</h3>
<div className={styles.date} aria-label={lang('PrivacySettings.LastSeen')}> <div className={styles.date} aria-label={lang('PrivacySettingsLastSeen')}>
{formatDateTimeToString(renderingSession.dateActive * 1000, lang.code)} {formatDateTimeToString(renderingSession.dateActive * 1000, lang.code)}
</div> </div>
<dl className={styles.box}> <dl className={styles.box}>
<dt>{lang('SessionPreview.App')}</dt> <dt>{lang('SessionPreviewApp')}</dt>
<dd> <dd>
{renderingSession?.appName} {renderingSession?.appName}
{' '} {' '}
@ -107,20 +107,23 @@ const SettingsActiveSession: FC<OwnProps & StateProps> = ({
{' '} {' '}
{renderingSession?.systemVersion} {renderingSession?.systemVersion}
</dd> </dd>
{renderingSession?.ip && (
<>
<dt>{lang('SessionPreviewIp')}</dt>
<dd>{renderingSession.ip}</dd>
</>
)}
<dt>{lang('SessionPreview.Ip')}</dt> <dt>{lang('SessionPreviewLocation')}</dt>
<dd>{renderingSession?.ip}</dd>
<dt>{lang('SessionPreview.Location')}</dt>
<dd>{renderingSession && getLocation(renderingSession)}</dd> <dd>{renderingSession && getLocation(renderingSession)}</dd>
</dl> </dl>
<p className={styles.note}>{lang('SessionPreview.IpDesc')}</p> <p className={styles.note}>{lang('SessionPreviewIpDesc')}</p>
<h4 className={styles.actionHeader}>{lang('AuthSessions.View.AcceptTitle')}</h4> <h4 className={styles.actionHeader}>{lang('AuthSessionsViewAcceptTitle')}</h4>
<ListItem onClick={handleSecretChatsStateChange}> <ListItem onClick={handleSecretChatsStateChange}>
<span className={styles.actionName}>{lang('SessionPreview.Accept.Secret')}</span> <span className={styles.actionName}>{lang('SessionPreviewAcceptSecret')}</span>
<Switcher <Switcher
id="accept_secrets" id="accept_secrets"
label="On" label="On"
@ -128,7 +131,7 @@ const SettingsActiveSession: FC<OwnProps & StateProps> = ({
/> />
</ListItem> </ListItem>
<ListItem onClick={handleCallsStateChange}> <ListItem onClick={handleCallsStateChange}>
<span className={styles.actionName}>{lang('SessionPreview.Accept.Calls')}</span> <span className={styles.actionName}>{lang('SessionPreviewAcceptCalls')}</span>
<Switcher <Switcher
id="accept_calls" id="accept_calls"
label="On" label="On"

View File

@ -11,6 +11,7 @@ import getSessionIcon from './helpers/getSessionIcon';
import useFlag from '../../../hooks/useFlag'; import useFlag from '../../../hooks/useFlag';
import useHistoryBack from '../../../hooks/useHistoryBack'; import useHistoryBack from '../../../hooks/useHistoryBack';
import useLang from '../../../hooks/useLang';
import useOldLang from '../../../hooks/useOldLang'; import useOldLang from '../../../hooks/useOldLang';
import ConfirmDialog from '../../ui/ConfirmDialog'; import ConfirmDialog from '../../ui/ConfirmDialog';
@ -44,7 +45,8 @@ const SettingsActiveSessions: FC<OwnProps & StateProps> = ({
changeSessionTtl, changeSessionTtl,
} = getActions(); } = getActions();
const lang = useOldLang(); const oldLang = useOldLang();
const lang = useLang();
const [isConfirmTerminateAllDialogOpen, openConfirmTerminateAllDialog, closeConfirmTerminateAllDialog] = useFlag(); const [isConfirmTerminateAllDialogOpen, openConfirmTerminateAllDialog, closeConfirmTerminateAllDialog] = useFlag();
const [openedSessionHash, setOpenedSessionHash] = useState<string | undefined>(); const [openedSessionHash, setOpenedSessionHash] = useState<string | undefined>();
const [isModalOpen, openModal, closeModal] = useFlag(); const [isModalOpen, openModal, closeModal] = useFlag();
@ -80,21 +82,21 @@ const SettingsActiveSessions: FC<OwnProps & StateProps> = ({
const AUTO_TERMINATE_OPTIONS = useMemo(() => { const AUTO_TERMINATE_OPTIONS = useMemo(() => {
const options = [{ const options = [{
label: lang('Weeks', 1, 'i'), label: lang('Weeks', { count: 1 }, { pluralValue: 1 }),
value: '7', value: '7',
}, { }, {
label: lang('Months', 1, 'i'), label: lang('Months', { count: 1 }, { pluralValue: 1 }),
value: '30', value: '30',
}, { }, {
label: lang('Months', 3, 'i'), label: lang('Months', { count: 3 }, { pluralValue: 3 }),
value: '90', value: '90',
}, { }, {
label: lang('Months', 6, 'i'), label: lang('Months', { count: 6 }, { pluralValue: 6 }),
value: '183', value: '183',
}]; }];
if (ttlDays && ttlDays >= 365) { if (ttlDays && ttlDays >= 365) {
options.push({ options.push({
label: lang('Years', 1, 'i'), label: lang('Years', { count: 1 }, { pluralValue: 1 }),
value: '365', value: '365',
}); });
} }
@ -144,7 +146,7 @@ const SettingsActiveSessions: FC<OwnProps & StateProps> = ({
return ( return (
<div className="settings-item"> <div className="settings-item">
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}> <h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
{lang('AuthSessions.CurrentSession')} {lang('AuthSessionsCurrentSession')}
</h4> </h4>
<ListItem narrow inactive icon={`device-${getSessionIcon(session)}`} iconClassName="icon-device"> <ListItem narrow inactive icon={`device-${getSessionIcon(session)}`} iconClassName="icon-device">
@ -224,7 +226,7 @@ const SettingsActiveSessions: FC<OwnProps & StateProps> = ({
ripple ripple
narrow narrow
contextActions={[{ contextActions={[{
title: 'Terminate', title: lang('SessionTerminate'),
icon: 'stop', icon: 'stop',
destructive: true, destructive: true,
handler: () => { handler: () => {
@ -236,7 +238,7 @@ const SettingsActiveSessions: FC<OwnProps & StateProps> = ({
onClick={() => { handleOpenSessionModal(session.hash); }} onClick={() => { handleOpenSessionModal(session.hash); }}
> >
<div className="multiline-item full-size" dir="auto"> <div className="multiline-item full-size" dir="auto">
<span className="date">{formatPastTimeShort(lang, session.dateActive * 1000)}</span> <span className="date">{formatPastTimeShort(oldLang, session.dateActive * 1000)}</span>
<span className="title">{session.deviceModel}</span> <span className="title">{session.deviceModel}</span>
<span className="subtitle black tight"> <span className="subtitle black tight">
{session.appName} {session.appName}

View File

@ -2,7 +2,7 @@ import type { FC } from '../../../lib/teact/teact';
import { memo, useCallback, useState } from '../../../lib/teact/teact'; import { memo, useCallback, useState } from '../../../lib/teact/teact';
import useHistoryBack from '../../../hooks/useHistoryBack'; import useHistoryBack from '../../../hooks/useHistoryBack';
import useOldLang from '../../../hooks/useOldLang'; import useLang from '../../../hooks/useLang';
import PasswordForm from '../../common/PasswordForm'; import PasswordForm from '../../common/PasswordForm';
import PasswordMonkey from '../../common/PasswordMonkey'; import PasswordMonkey from '../../common/PasswordMonkey';
@ -15,46 +15,44 @@ type OwnProps = {
placeholder?: string; placeholder?: string;
hint?: string; hint?: string;
submitLabel?: string; submitLabel?: string;
clearError?: NoneToVoidFunction;
onSubmit: (password: string) => void;
isActive?: boolean; isActive?: boolean;
onSubmit: (password: string) => void;
onClearError?: NoneToVoidFunction;
onReset: () => void; onReset: () => void;
}; };
const EQUAL_PASSWORD_ERROR = 'Passwords Should Be Equal';
const SettingsPasswordForm: FC<OwnProps> = ({ const SettingsPasswordForm: FC<OwnProps> = ({
isActive, isActive,
onReset,
error, error,
isLoading, isLoading,
shouldDisablePasswordManager, shouldDisablePasswordManager,
expectedPassword, expectedPassword,
placeholder = 'Current Password', placeholder,
hint, hint,
submitLabel, submitLabel,
clearError,
onSubmit, onSubmit,
onClearError,
onReset,
}) => { }) => {
const [validationError, setValidationError] = useState<string>(''); const [validationError, setValidationError] = useState<string>('');
const [shouldShowPassword, setShouldShowPassword] = useState(false); const [shouldShowPassword, setShouldShowPassword] = useState(false);
const lang = useLang();
const handleSubmit = useCallback((newPassword) => { const handleSubmit = useCallback((newPassword) => {
if (expectedPassword && newPassword !== expectedPassword) { if (expectedPassword && newPassword !== expectedPassword) {
setValidationError(EQUAL_PASSWORD_ERROR); setValidationError(lang('SettingsPasswordEqual'));
} else { } else {
onSubmit(newPassword); onSubmit(newPassword);
} }
}, [onSubmit, expectedPassword]); }, [onSubmit, expectedPassword, lang]);
const handleClearError = useCallback(() => { const handleClearError = useCallback(() => {
if (clearError) { if (onClearError) {
clearError(); onClearError();
} }
setValidationError(''); setValidationError('');
}, [clearError]); }, [onClearError]);
const lang = useOldLang();
useHistoryBack({ useHistoryBack({
isActive, isActive,
@ -71,10 +69,10 @@ const SettingsPasswordForm: FC<OwnProps> = ({
<PasswordForm <PasswordForm
error={validationError || error} error={validationError || error}
hint={hint} hint={hint}
placeholder={placeholder} placeholder={placeholder || lang('CurrentPasswordPlaceholder')}
shouldDisablePasswordManager={shouldDisablePasswordManager} shouldDisablePasswordManager={shouldDisablePasswordManager}
submitLabel={submitLabel || lang('Next')} submitLabel={submitLabel}
clearError={handleClearError} onClearError={handleClearError}
isLoading={isLoading} isLoading={isLoading}
isPasswordVisible={shouldShowPassword} isPasswordVisible={shouldShowPassword}
shouldResetValue={isActive} shouldResetValue={isActive}

View File

@ -182,7 +182,7 @@ function SettingsPerformance({
</div> </div>
<div className="settings-item-simple settings-item__with-shifted-dropdown"> <div className="settings-item-simple settings-item__with-shifted-dropdown">
<h3 className="settings-item-header" dir="auto">Resource-Intensive Processes</h3> <h3 className="settings-item-header" dir="auto">{lang('SettingsPerformanceGranularTitle')}</h3>
{PERFORMANCE_OPTIONS.map(([sectionName, options], index) => { {PERFORMANCE_OPTIONS.map(([sectionName, options], index) => {
return ( return (

View File

@ -20,7 +20,7 @@ import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEn
import { useFolderManagerForChatsCount } from '../../../../hooks/useFolderManager'; import { useFolderManagerForChatsCount } from '../../../../hooks/useFolderManager';
import useHistoryBack from '../../../../hooks/useHistoryBack'; import useHistoryBack from '../../../../hooks/useHistoryBack';
import useOldLang from '../../../../hooks/useOldLang'; import useLang from '../../../../hooks/useLang';
import usePreviousDeprecated from '../../../../hooks/usePreviousDeprecated'; import usePreviousDeprecated from '../../../../hooks/usePreviousDeprecated';
import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview'; import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview';
@ -117,7 +117,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
onCreateFolder(); onCreateFolder();
}, [foldersById, maxFolders, onCreateFolder, openLimitReachedModal]); }, [foldersById, maxFolders, onCreateFolder, openLimitReachedModal]);
const lang = useOldLang(); const lang = useLang();
useHistoryBack({ useHistoryBack({
isActive, isActive,
@ -226,7 +226,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
{canCreateNewFolder && ( {canCreateNewFolder && (
<Button <Button
// TODO: Refactor button component to handle icon placemenet with props // TODO: Move icon into button prop
className="settings-button with-icon" className="settings-button with-icon"
color="primary" color="primary"
pill pill
@ -349,7 +349,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
); );
}) : userFolders && !userFolders.length ? ( }) : userFolders && !userFolders.length ? (
<p className="settings-item-description my-4" dir="auto"> <p className="settings-item-description my-4" dir="auto">
You have no folders yet. {lang('SettingsFoldersEmpty')}
</p> </p>
) : <Loading />} ) : <Loading />}
</div> </div>

View File

@ -160,7 +160,7 @@ const SettingsPasscode: FC<OwnProps & StateProps> = ({
<SettingsPasscodeForm <SettingsPasscodeForm
shouldDisablePasswordManager shouldDisablePasswordManager
error={error} error={error}
clearError={clearPasscodeError} onClearError={clearPasscodeError}
placeholder={lang('PasscodeController.Current.Placeholder')} placeholder={lang('PasscodeController.Current.Placeholder')}
onSubmit={handleChangePasswordCurrent} onSubmit={handleChangePasswordCurrent}
isActive={isActive || [ isActive={isActive || [
@ -206,7 +206,7 @@ const SettingsPasscode: FC<OwnProps & StateProps> = ({
<SettingsPasscodeForm <SettingsPasscodeForm
shouldDisablePasswordManager shouldDisablePasswordManager
error={error ? lang(error) : undefined} error={error ? lang(error) : undefined}
clearError={clearPasscodeError} onClearError={clearPasscodeError}
placeholder={lang('PasscodeController.Current.Placeholder')} placeholder={lang('PasscodeController.Current.Placeholder')}
onSubmit={handleTurnOff} onSubmit={handleTurnOff}
isActive={isActive} isActive={isActive}

View File

@ -5,7 +5,7 @@ import { STICKER_SIZE_PASSCODE } from '../../../../config';
import { LOCAL_TGS_URLS } from '../../../common/helpers/animatedAssets'; import { LOCAL_TGS_URLS } from '../../../common/helpers/animatedAssets';
import useHistoryBack from '../../../../hooks/useHistoryBack'; import useHistoryBack from '../../../../hooks/useHistoryBack';
import useOldLang from '../../../../hooks/useOldLang'; import useLang from '../../../../hooks/useLang';
import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview'; import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview';
import Button from '../../../ui/Button'; import Button from '../../../ui/Button';
@ -13,15 +13,15 @@ import Button from '../../../ui/Button';
import lockPreviewUrl from '../../../../assets/lock.png'; import lockPreviewUrl from '../../../../assets/lock.png';
type OwnProps = { type OwnProps = {
onStart: NoneToVoidFunction;
isActive?: boolean; isActive?: boolean;
onStart: NoneToVoidFunction;
onReset: () => void; onReset: () => void;
}; };
const SettingsPasscodeStart: FC<OwnProps> = ({ const SettingsPasscodeStart: FC<OwnProps> = ({
isActive, onReset, onStart, isActive, onReset, onStart,
}) => { }) => {
const lang = useOldLang(); const lang = useLang();
useHistoryBack({ isActive, onBack: onReset }); useHistoryBack({ isActive, onBack: onReset });
@ -36,11 +36,10 @@ const SettingsPasscodeStart: FC<OwnProps> = ({
/> />
<p className="settings-item-description" dir="auto"> <p className="settings-item-description" dir="auto">
When you set up an additional passcode, a lock icon will appear on the chats page. {lang('SettingsPasscodeStart1', undefined, { withNodes: true, renderTextFilters: ['br'] })}
Tap it to lock and unlock your Telegram Web A.
</p> </p>
<p className="settings-item-description mb-3" dir="auto"> <p className="settings-item-description mb-3" dir="auto">
Note: if you forget your local passcode, you&apos;ll need to log out of Telegram Web A and log in again. {lang('SettingsPasscodeStart2', undefined, { withNodes: true, renderTextFilters: ['br'] })}
</p> </p>
</div> </div>

View File

@ -284,7 +284,7 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
<SettingsTwoFaPassword <SettingsTwoFaPassword
isLoading={isLoading} isLoading={isLoading}
error={errorKey && lang.withRegular(errorKey)} error={errorKey && lang.withRegular(errorKey)}
clearError={clearTwoFaError} onClearError={clearTwoFaError}
hint={hint} hint={hint}
onSubmit={handleChangePasswordCurrent} onSubmit={handleChangePasswordCurrent}
isActive={isActive || [ isActive={isActive || [
@ -344,7 +344,7 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
<SettingsTwoFaPassword <SettingsTwoFaPassword
isLoading={isLoading} isLoading={isLoading}
error={errorKey && lang.withRegular(errorKey)} error={errorKey && lang.withRegular(errorKey)}
clearError={clearTwoFaError} onClearError={clearTwoFaError}
hint={hint} hint={hint}
onSubmit={handleTurnOff} onSubmit={handleTurnOff}
isActive={isActive} isActive={isActive}
@ -357,7 +357,7 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
<SettingsTwoFaPassword <SettingsTwoFaPassword
isLoading={isLoading} isLoading={isLoading}
error={errorKey && lang.withRegular(errorKey)} error={errorKey && lang.withRegular(errorKey)}
clearError={clearTwoFaError} onClearError={clearTwoFaError}
hint={hint} hint={hint}
onSubmit={handleRecoveryEmailCurrentPassword} onSubmit={handleRecoveryEmailCurrentPassword}
isActive={isActive || [ isActive={isActive || [

View File

@ -132,7 +132,7 @@ const LockScreen: FC<OwnProps & StateProps> = ({
error={validationError} error={validationError}
placeholder={lang('Passcode.EnterPasscodePlaceholder')} placeholder={lang('Passcode.EnterPasscodePlaceholder')}
submitLabel={lang('Next')} submitLabel={lang('Next')}
clearError={handleClearError} onClearError={handleClearError}
isPasswordVisible={shouldShowPasscode} isPasswordVisible={shouldShowPasscode}
noRipple noRipple
onChangePasswordVisibility={setShouldShowPasscode} onChangePasswordVisibility={setShouldShowPasscode}

View File

@ -12,8 +12,8 @@ import { selectChat, selectChatFullInfo } from '../../global/selectors';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
import stopEvent from '../../util/stopEvent'; import stopEvent from '../../util/stopEvent';
import useLang from '../../hooks/useLang';
import useLastCallback from '../../hooks/useLastCallback'; import useLastCallback from '../../hooks/useLastCallback';
import useOldLang from '../../hooks/useOldLang';
import Checkbox from '../ui/Checkbox'; import Checkbox from '../ui/Checkbox';
@ -61,7 +61,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
const { isForum } = chat || {}; const { isForum } = chat || {};
const lang = useOldLang(); const lang = useLang();
const isPublic = useMemo(() => chat && isChatPublic(chat), [chat]); const isPublic = useMemo(() => chat && isChatPublic(chat), [chat]);
const shouldDisablePermissionForPublicGroup = hasLinkedChat || isPublic; const shouldDisablePermissionForPublicGroup = hasLinkedChat || isPublic;
@ -81,7 +81,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
}); });
const handleDisabledClick = useLastCallback(() => { const handleDisabledClick = useLastCallback(() => {
showNotification({ message: lang('lng_rights_permission_unavailable') }); showNotification({ message: lang('ChatPermissionNotAvailable') });
}); });
return ( return (
@ -119,7 +119,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
<Checkbox <Checkbox
name="sendPhotos" name="sendPhotos"
checked={!permissions.sendPhotos} checked={!permissions.sendPhotos}
label={lang('UserRestrictionsSendPhotos')} label={lang('SendMediaPermissionPhotos')}
blocking blocking
permissionGroup={permissionGroup} permissionGroup={permissionGroup}
onChange={handlePermissionChange} onChange={handlePermissionChange}
@ -131,7 +131,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
<Checkbox <Checkbox
name="sendVideos" name="sendVideos"
checked={!permissions.sendVideos} checked={!permissions.sendVideos}
label={lang('UserRestrictionsSendVideos')} label={lang('SendMediaPermissionVideos')}
blocking blocking
permissionGroup={permissionGroup} permissionGroup={permissionGroup}
onChange={handlePermissionChange} onChange={handlePermissionChange}
@ -143,7 +143,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
<Checkbox <Checkbox
name="sendStickers" name="sendStickers"
checked={!permissions.sendStickers && !permissions.sendGifs} checked={!permissions.sendStickers && !permissions.sendGifs}
label={lang('UserRestrictionsSendStickers')} label={lang('SendMediaPermissionStickersGifs')}
blocking blocking
permissionGroup={permissionGroup} permissionGroup={permissionGroup}
onChange={handlePermissionChange} onChange={handlePermissionChange}
@ -155,7 +155,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
<Checkbox <Checkbox
name="sendAudios" name="sendAudios"
checked={!permissions.sendAudios} checked={!permissions.sendAudios}
label={lang('UserRestrictionsSendMusic')} label={lang('SendMediaPermissionAudios')}
blocking blocking
permissionGroup={permissionGroup} permissionGroup={permissionGroup}
onChange={handlePermissionChange} onChange={handlePermissionChange}
@ -167,7 +167,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
<Checkbox <Checkbox
name="sendDocs" name="sendDocs"
checked={!permissions.sendDocs} checked={!permissions.sendDocs}
label={lang('UserRestrictionsSendFiles')} label={lang('SendMediaPermissionFiles')}
blocking blocking
permissionGroup={permissionGroup} permissionGroup={permissionGroup}
onChange={handlePermissionChange} onChange={handlePermissionChange}
@ -179,7 +179,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
<Checkbox <Checkbox
name="sendVoices" name="sendVoices"
checked={!permissions.sendVoices} checked={!permissions.sendVoices}
label={lang('UserRestrictionsSendVoices')} label={lang('SendMediaPermissionVoices')}
blocking blocking
permissionGroup={permissionGroup} permissionGroup={permissionGroup}
onChange={handlePermissionChange} onChange={handlePermissionChange}
@ -191,7 +191,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
<Checkbox <Checkbox
name="sendRoundvideos" name="sendRoundvideos"
checked={!permissions.sendRoundvideos} checked={!permissions.sendRoundvideos}
label={lang('UserRestrictionsSendRound')} label={lang('SendMediaPermissionRoundVideos')}
blocking blocking
permissionGroup={permissionGroup} permissionGroup={permissionGroup}
onChange={handlePermissionChange} onChange={handlePermissionChange}
@ -203,7 +203,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
<Checkbox <Checkbox
name="embedLinks" name="embedLinks"
checked={!permissions.embedLinks} checked={!permissions.embedLinks}
label={lang('UserRestrictionsEmbedLinks')} label={lang('SendMediaPermissionWebPages')}
blocking blocking
permissionGroup={permissionGroup} permissionGroup={permissionGroup}
onChange={handlePermissionChange} onChange={handlePermissionChange}
@ -215,7 +215,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
<Checkbox <Checkbox
name="sendPolls" name="sendPolls"
checked={!permissions.sendPolls} checked={!permissions.sendPolls}
label={lang('UserRestrictionsSendPolls')} label={lang('SendMediaPermissionPolls')}
blocking blocking
permissionGroup={permissionGroup} permissionGroup={permissionGroup}
onChange={handlePermissionChange} onChange={handlePermissionChange}
@ -271,7 +271,7 @@ const PermissionCheckboxList: FC<OwnProps & StateProps> = ({
<Checkbox <Checkbox
name="manageTopics" name="manageTopics"
checked={!permissions.manageTopics} checked={!permissions.manageTopics}
label={lang('CreateTopicsPermission')} label={lang('UserRestrictionsCreateTopics')}
blocking blocking
permissionGroup={permissionGroup} permissionGroup={permissionGroup}
onChange={handlePermissionChange} onChange={handlePermissionChange}

View File

@ -321,9 +321,10 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
} }
function getHeaderDescription() { function getHeaderDescription() {
if (gift) { if (gift && gift.type !== 'starGiftUnique' && gift.perUserTotal) {
const perUserTotal = gift.type !== 'starGiftUnique' ? gift.perUserTotal : 0; return lang('DescriptionGiftPremiumRequired2', { count: gift.perUserTotal }, {
return lang('DescriptionGiftPremiumRequired', { count: perUserTotal }); pluralValue: gift.perUserTotal,
});
} }
if (isGift) { if (isGift) {

View File

@ -6,6 +6,7 @@ import buildClassName from '../../../util/buildClassName';
import useEffectOnce from '../../../hooks/useEffectOnce'; import useEffectOnce from '../../../hooks/useEffectOnce';
import useFlag from '../../../hooks/useFlag'; import useFlag from '../../../hooks/useFlag';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback'; import useLastCallback from '../../../hooks/useLastCallback';
import useResizeObserver from '../../../hooks/useResizeObserver'; import useResizeObserver from '../../../hooks/useResizeObserver';
@ -23,6 +24,8 @@ const DropTarget: FC<OwnProps> = ({ isQuick, isGeneric, onFileSelect }) => {
const ref = useRef<HTMLDivElement>(); const ref = useRef<HTMLDivElement>();
const svgRef = useRef<SVGSVGElement>(); const svgRef = useRef<SVGSVGElement>();
const lang = useLang();
const [isHovered, markHovered, unmarkHovered] = useFlag(); const [isHovered, markHovered, unmarkHovered] = useFlag();
const handleDragLeave = useLastCallback((e: React.DragEvent<HTMLDivElement>) => { const handleDragLeave = useLastCallback((e: React.DragEvent<HTMLDivElement>) => {
@ -65,13 +68,17 @@ const DropTarget: FC<OwnProps> = ({ isQuick, isGeneric, onFileSelect }) => {
onDragLeave={handleDragLeave} onDragLeave={handleDragLeave}
data-dropzone data-dropzone
> >
<svg className="target-outline-container"> <svg className="target-outline-container" ref={svgRef}>
<rect className="target-outline" x="0" y="0" width="100%" height="100%" rx="8" /> <rect className="target-outline" x="0" y="0" width="100%" height="100%" rx="8" />
</svg> </svg>
<div className="target-content"> <div className="target-content">
<Icon name={isQuick ? 'photo' : 'document'} /> <Icon name={isQuick ? 'photo' : 'document'} />
<div className="title">Drop files here to send them</div> <div className="title">{lang('FileDropZoneTitle')}</div>
{!isGeneric && <div className="description">{isQuick ? 'in a quick way' : 'without compression'}</div>} {!isGeneric && (
<div className="description">
{isQuick ? lang('FileDropZoneQuick') : lang('FileDropZoneNoCompression')}
</div>
)}
</div> </div>
</div> </div>
); );

View File

@ -16,8 +16,8 @@ import stopEvent from '../../../util/stopEvent';
import { INPUT_CUSTOM_EMOJI_SELECTOR } from './helpers/customEmoji'; import { INPUT_CUSTOM_EMOJI_SELECTOR } from './helpers/customEmoji';
import useFlag from '../../../hooks/useFlag'; import useFlag from '../../../hooks/useFlag';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback'; import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import useShowTransitionDeprecated from '../../../hooks/useShowTransitionDeprecated'; import useShowTransitionDeprecated from '../../../hooks/useShowTransitionDeprecated';
import useVirtualBackdrop from '../../../hooks/useVirtualBackdrop'; import useVirtualBackdrop from '../../../hooks/useVirtualBackdrop';
@ -71,6 +71,8 @@ const TextFormatter: FC<OwnProps> = ({
const [inputClassName, setInputClassName] = useState<string | undefined>(); const [inputClassName, setInputClassName] = useState<string | undefined>();
const [selectedTextFormats, setSelectedTextFormats] = useState<ISelectedTextFormats>({}); const [selectedTextFormats, setSelectedTextFormats] = useState<ISelectedTextFormats>({});
const lang = useLang();
useEffect(() => (isOpen ? captureEscKeyListener(onClose) : undefined), [isOpen, onClose]); useEffect(() => (isOpen ? captureEscKeyListener(onClose) : undefined), [isOpen, onClose]);
useVirtualBackdrop( useVirtualBackdrop(
isOpen, isOpen,
@ -338,7 +340,7 @@ const TextFormatter: FC<OwnProps> = ({
document.execCommand( document.execCommand(
'insertHTML', 'insertHTML',
false, false,
`<a href=${formattedLinkUrl} class="text-entity-link" dir="auto">${text}</a>`, `<a href="${formattedLinkUrl}" class="text-entity-link" dir="auto">${text}</a>`,
); );
onClose(); onClose();
}); });
@ -377,10 +379,9 @@ const TextFormatter: FC<OwnProps> = ({
return () => document.removeEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown);
}, [isOpen, handleKeyDown]); }, [isOpen, handleKeyDown]);
const lang = useOldLang();
function handleContainerKeyDown(e: React.KeyboardEvent<HTMLDivElement>) { function handleContainerKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {
if (e.key === 'Enter' && isLinkControlOpen) { if (e.key === 'Enter' && isLinkControlOpen) {
if (!linkUrl.trim()) return;
handleLinkUrlConfirm(); handleLinkUrlConfirm();
e.preventDefault(); e.preventDefault();
} }
@ -417,7 +418,7 @@ const TextFormatter: FC<OwnProps> = ({
<div className="TextFormatter-buttons"> <div className="TextFormatter-buttons">
<Button <Button
color="translucent" color="translucent"
ariaLabel="Spoiler text" ariaLabel={lang('FormattingSpoilerAria')}
className={getFormatButtonClassName('spoiler')} className={getFormatButtonClassName('spoiler')}
onClick={handleSpoilerText} onClick={handleSpoilerText}
> >
@ -426,7 +427,7 @@ const TextFormatter: FC<OwnProps> = ({
<div className="TextFormatter-divider" /> <div className="TextFormatter-divider" />
<Button <Button
color="translucent" color="translucent"
ariaLabel="Bold text" ariaLabel={lang('FormattingBoldAria')}
className={getFormatButtonClassName('bold')} className={getFormatButtonClassName('bold')}
onClick={handleBoldText} onClick={handleBoldText}
> >
@ -434,7 +435,7 @@ const TextFormatter: FC<OwnProps> = ({
</Button> </Button>
<Button <Button
color="translucent" color="translucent"
ariaLabel="Italic text" ariaLabel={lang('FormattingItalicAria')}
className={getFormatButtonClassName('italic')} className={getFormatButtonClassName('italic')}
onClick={handleItalicText} onClick={handleItalicText}
> >
@ -442,7 +443,7 @@ const TextFormatter: FC<OwnProps> = ({
</Button> </Button>
<Button <Button
color="translucent" color="translucent"
ariaLabel="Underlined text" ariaLabel={lang('FormattingUnderlineAria')}
className={getFormatButtonClassName('underline')} className={getFormatButtonClassName('underline')}
onClick={handleUnderlineText} onClick={handleUnderlineText}
> >
@ -450,7 +451,7 @@ const TextFormatter: FC<OwnProps> = ({
</Button> </Button>
<Button <Button
color="translucent" color="translucent"
ariaLabel="Strikethrough text" ariaLabel={lang('FormattingStrikethroughAria')}
className={getFormatButtonClassName('strikethrough')} className={getFormatButtonClassName('strikethrough')}
onClick={handleStrikethroughText} onClick={handleStrikethroughText}
> >
@ -458,14 +459,14 @@ const TextFormatter: FC<OwnProps> = ({
</Button> </Button>
<Button <Button
color="translucent" color="translucent"
ariaLabel="Monospace text" ariaLabel={lang('FormattingMonospaceAria')}
className={getFormatButtonClassName('monospace')} className={getFormatButtonClassName('monospace')}
onClick={handleMonospaceText} onClick={handleMonospaceText}
> >
<Icon name="monospace" /> <Icon name="monospace" />
</Button> </Button>
<div className="TextFormatter-divider" /> <div className="TextFormatter-divider" />
<Button color="translucent" ariaLabel={lang('TextFormat.AddLinkTitle')} onClick={openLinkControl}> <Button color="translucent" ariaLabel={lang('FormattingAddLinkAria')} onClick={openLinkControl}>
<Icon name="link" /> <Icon name="link" />
</Button> </Button>
</div> </div>
@ -485,7 +486,7 @@ const TextFormatter: FC<OwnProps> = ({
className="TextFormatter-link-url-input" className="TextFormatter-link-url-input"
type="text" type="text"
value={linkUrl} value={linkUrl}
placeholder="Enter URL..." placeholder={lang('FormattingEnterUrl')}
autoComplete="off" autoComplete="off"
inputMode="url" inputMode="url"
dir="auto" dir="auto"

View File

@ -340,6 +340,8 @@ const ToDoListModal = ({
}); });
} }
const moreTasksCount = maxItemsCount - items.length - (isAddTaskMode && editingTodo ? editingTodo.items.length : 0);
return ( return (
<Modal isOpen={isOpen} onClose={onClear} header={renderHeader()} className="ToDoListModal"> <Modal isOpen={isOpen} onClose={onClear} header={renderHeader()} className="ToDoListModal">
{!isAddTaskMode && ( {!isAddTaskMode && (
@ -369,8 +371,10 @@ const ToDoListModal = ({
</div> </div>
<div className="items-count-hint"> <div className="items-count-hint">
{lang('HintTodoListTasksCount', { {lang('HintTodoListTasksCount2', {
count: maxItemsCount - items.length - (isAddTaskMode && editingTodo ? editingTodo.items.length : 0), count: moreTasksCount,
}, {
pluralValue: moreTasksCount,
})} })}
</div> </div>

View File

@ -12,7 +12,7 @@ import {
} from '../../../config'; } from '../../../config';
import { import {
getMainUsername, getMainUsername,
getMessageInvoice, getMessageText, isChatChannel, getMessageInvoice, getMessageTextWithFallback, isChatChannel,
} from '../../../global/helpers'; } from '../../../global/helpers';
import { getMessageContent } from '../../../global/helpers'; import { getMessageContent } from '../../../global/helpers';
import { getPeerTitle } from '../../../global/helpers/peers'; import { getPeerTitle } from '../../../global/helpers/peers';
@ -116,7 +116,7 @@ const ActionMessageText = ({
switch (action.type) { switch (action.type) {
case 'pinMessage': { case 'pinMessage': {
if (replyMessage) { if (replyMessage) {
const formattedText = getMessageText(replyMessage); const formattedText = getMessageTextWithFallback(lang, replyMessage);
if (formattedText) { if (formattedText) {
const textLink = renderMessageLink( const textLink = renderMessageLink(
replyMessage, replyMessage,

View File

@ -1763,7 +1763,7 @@ const Message: FC<OwnProps & StateProps> = ({
color="translucent-white" color="translucent-white"
round round
size="tiny" size="tiny"
ariaLabel="Focus message" ariaLabel={lang('FocusMessage')}
onClick={isPinnedList ? handleFocus : handleFocusForwarded} onClick={isPinnedList ? handleFocus : handleFocusForwarded}
> >
<Icon name="arrow-right" /> <Icon name="arrow-right" />

View File

@ -113,7 +113,6 @@ const SuggestedPostApproval = ({
{translateWithYou(lang, 'SuggestedPostReceiveAmount', !isAdmin, { {translateWithYou(lang, 'SuggestedPostReceiveAmount', !isAdmin, {
peer: renderChatLink(), peer: renderChatLink(),
duration, duration,
currency: currency === TON_CURRENCY_CODE ? lang('CurrencyTon') : lang('CurrencyStars'),
}, { withMarkdown: true })} }, { withMarkdown: true })}
</div> </div>
@ -121,7 +120,6 @@ const SuggestedPostApproval = ({
{translateWithYou(lang, 'SuggestedPostRefund', !isAdmin, { {translateWithYou(lang, 'SuggestedPostRefund', !isAdmin, {
peer: renderChatLink(), peer: renderChatLink(),
duration, duration,
currency: currency === TON_CURRENCY_CODE ? lang('CurrencyTon') : lang('CurrencyStars'),
}, { withMarkdown: true })} }, { withMarkdown: true })}
</div> </div>
</> </>

View File

@ -21,6 +21,7 @@ import {
copyTextToClipboard, copyTextToClipboard,
} from '../../../../util/clipboard'; } from '../../../../util/clipboard';
import getMessageIdsForSelectedText from '../../../../util/getMessageIdsForSelectedText'; import getMessageIdsForSelectedText from '../../../../util/getMessageIdsForSelectedText';
import { getTranslationFn } from '../../../../util/localization';
import * as mediaLoader from '../../../../util/mediaLoader'; import * as mediaLoader from '../../../../util/mediaLoader';
import { renderMessageText } from '../../../common/helpers/renderMessageText'; import { renderMessageText } from '../../../common/helpers/renderMessageText';
@ -99,7 +100,7 @@ export function getMessageCopyOptions(
if (clipboardText) { if (clipboardText) {
copyHtmlToClipboard( copyHtmlToClipboard(
clipboardText.join(''), clipboardText.join(''),
getMessageTextWithSpoilers(message, statefulContent)!, getMessageTextWithSpoilers(getTranslationFn(), message, statefulContent)!,
); );
} }
} }

View File

@ -86,9 +86,10 @@ function GiftItemStar({
if (isUserLimitReached) { if (isUserLimitReached) {
showNotification({ showNotification({
message: lang('NotificationGiftsLimit', { message: lang('NotificationGiftsLimit2', {
count: perUserTotal, count: perUserTotal,
}, { }, {
pluralValue: perUserTotal!,
withMarkdown: true, withMarkdown: true,
withNodes: true, withNodes: true,
}), }),

View File

@ -142,7 +142,7 @@ const GiftWithdrawModal = ({ modal, hasPassword, passwordHint }: OwnProps & Stat
placeholder={lang('CheckPasswordPlaceholder')} placeholder={lang('CheckPasswordPlaceholder')}
error={renderingModal?.errorKey && lang.withRegular(renderingModal?.errorKey)} error={renderingModal?.errorKey && lang.withRegular(renderingModal?.errorKey)}
description={lang('CheckPasswordDescription')} description={lang('CheckPasswordDescription')}
clearError={clearGiftWithdrawError} onClearError={clearGiftWithdrawError}
isLoading={renderingModal?.isLoading} isLoading={renderingModal?.isLoading}
hint={passwordHint} hint={passwordHint}
isPasswordVisible={shouldShowPassword} isPasswordVisible={shouldShowPassword}

View File

@ -1056,7 +1056,7 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
)} )}
style={frameStyle} style={frameStyle}
src={url} src={url}
title={`${bot?.firstName} Web App`} title={lang('AriaMiniApp', { bot: bot?.firstName })}
sandbox={SANDBOX_ATTRIBUTES} sandbox={SANDBOX_ATTRIBUTES}
allow="camera; microphone; geolocation; clipboard-write;" allow="camera; microphone; geolocation; clipboard-write;"
allowFullScreen allowFullScreen

View File

@ -56,7 +56,7 @@ const PasswordConfirm: FC<OwnProps & StateProps> = ({
hint={passwordHint} hint={passwordHint}
description={oldLang('PaymentConfirmationMessage', cardName)} description={oldLang('PaymentConfirmationMessage', cardName)}
placeholder={oldLang('Password')} placeholder={oldLang('Password')}
clearError={clearPaymentError} onClearError={clearPaymentError}
shouldShowSubmit={false} shouldShowSubmit={false}
shouldResetValue={isActive} shouldResetValue={isActive}
isPasswordVisible={shouldShowPassword} isPasswordVisible={shouldShowPassword}

View File

@ -256,7 +256,7 @@ const PaymentModal: FC<OwnProps & StateProps> = ({
isText isText
onClick={handleClearPaymentError} onClick={handleClearPaymentError}
> >
{oldLang('OK')} {lang('OK')}
</Button> </Button>
</div> </div>
</Modal> </Modal>
@ -540,10 +540,7 @@ const PaymentModal: FC<OwnProps & StateProps> = ({
onCloseAnimationEnd={handleModalClose} onCloseAnimationEnd={handleModalClose}
> >
<p> <p>
Sorry, Telegram Web A doesn&apos;t support payments with this provider yet. {lang('PaymentsProvidesNotSupported', undefined, { withNodes: true, renderTextFilters: ['br'] })}
{' '}
<br />
Please use one of our mobile apps to do this.
</p> </p>
<div className="dialog-buttons mt-2"> <div className="dialog-buttons mt-2">
<Button <Button
@ -573,7 +570,7 @@ const PaymentModal: FC<OwnProps & StateProps> = ({
round round
size="smaller" size="smaller"
onClick={step === PaymentStep.Checkout ? closeModal : handleBackClick} onClick={step === PaymentStep.Checkout ? closeModal : handleBackClick}
ariaLabel="Close" ariaLabel={lang('Close')}
> >
<Icon name={step === PaymentStep.Checkout ? 'close' : 'arrow-left'} /> <Icon name={step === PaymentStep.Checkout ? 'close' : 'arrow-left'} />
</Button> </Button>

View File

@ -601,7 +601,7 @@ const Profile: FC<OwnProps & StateProps> = ({
className="content empty-list" className="content empty-list"
> >
{!noSpinner && !forceRenderHiddenMembers && <Spinner />} {!noSpinner && !forceRenderHiddenMembers && <Spinner />}
{forceRenderHiddenMembers && <NothingFound text="You have no access to group members list." />} {forceRenderHiddenMembers && <NothingFound text={lang('ChatMemberListNoAccess')} />}
</div> </div>
); );
} }
@ -617,7 +617,7 @@ const Profile: FC<OwnProps & StateProps> = ({
switch (resultType) { switch (resultType) {
case 'members': case 'members':
text = areMembersHidden ? 'You have no access to group members list.' : 'No members found'; text = areMembersHidden ? lang('ChatMemberListNoAccess') : lang('NoMembersFound');
break; break;
case 'commonChats': case 'commonChats':
text = oldLang('NoGroupsInCommon'); text = oldLang('NoGroupsInCommon');

View File

@ -3,7 +3,6 @@ import { memo, useCallback } from '../../../lib/teact/teact';
import { getActions } from '../../../global'; import { getActions } from '../../../global';
import type { ApiMessage, StatisticsMessageInteractionCounter } from '../../../api/types'; import type { ApiMessage, StatisticsMessageInteractionCounter } from '../../../api/types';
import type { OldLangFn } from '../../../hooks/useOldLang';
import { import {
getMessageRoundVideo, getMessageRoundVideo,
@ -11,12 +10,13 @@ import {
} from '../../../global/helpers'; } from '../../../global/helpers';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import { formatDateTimeToString } from '../../../util/dates/dateFormat'; import { formatDateTimeToString } from '../../../util/dates/dateFormat';
import { type LangFn } from '../../../util/localization';
import { renderMessageSummary } from '../../common/helpers/renderMessageText'; import { renderMessageSummary } from '../../common/helpers/renderMessageText';
import useMessageMediaHash from '../../../hooks/media/useMessageMediaHash'; import useMessageMediaHash from '../../../hooks/media/useMessageMediaHash';
import useThumbnail from '../../../hooks/media/useThumbnail'; import useThumbnail from '../../../hooks/media/useThumbnail';
import useLang from '../../../hooks/useLang';
import useMedia from '../../../hooks/useMedia'; import useMedia from '../../../hooks/useMedia';
import useOldLang from '../../../hooks/useOldLang';
import Icon from '../../common/icons/Icon'; import Icon from '../../common/icons/Icon';
import StatisticsRecentPostMeta from './StatisticsRecentPostMeta'; import StatisticsRecentPostMeta from './StatisticsRecentPostMeta';
@ -29,7 +29,7 @@ export type OwnProps = {
}; };
const StatisticsRecentMessage: FC<OwnProps> = ({ postStatistic, message }) => { const StatisticsRecentMessage: FC<OwnProps> = ({ postStatistic, message }) => {
const lang = useOldLang(); const lang = useLang();
const { toggleMessageStatistics } = getActions(); const { toggleMessageStatistics } = getActions();
const thumbDataUri = useThumbnail(message); const thumbDataUri = useThumbnail(message);
@ -54,7 +54,11 @@ const StatisticsRecentMessage: FC<OwnProps> = ({ postStatistic, message }) => {
{renderSummary(lang, message, mediaBlobUrl || thumbDataUri, isRoundVideo)} {renderSummary(lang, message, mediaBlobUrl || thumbDataUri, isRoundVideo)}
</div> </div>
<div className={styles.meta}> <div className={styles.meta}>
{lang('ChannelStats.ViewsCount', postStatistic.viewsCount, 'i')} {lang(
'ChannelStatsViewsCount',
{ count: postStatistic.viewsCount },
{ pluralValue: postStatistic.viewsCount },
)}
</div> </div>
</div> </div>
@ -68,7 +72,7 @@ const StatisticsRecentMessage: FC<OwnProps> = ({ postStatistic, message }) => {
); );
}; };
function renderSummary(lang: OldLangFn, message: ApiMessage, blobUrl?: string, isRoundVideo?: boolean) { function renderSummary(lang: LangFn, message: ApiMessage, blobUrl?: string, isRoundVideo?: boolean) {
if (!blobUrl) { if (!blobUrl) {
return renderMessageSummary(lang, message); return renderMessageSummary(lang, message);
} }

View File

@ -6,6 +6,8 @@ import {
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
import useLang from '../../hooks/useLang';
import Icon from '../common/icons/Icon'; import Icon from '../common/icons/Icon';
import CropModal from './CropModal'; import CropModal from './CropModal';
@ -20,7 +22,7 @@ interface OwnProps {
} }
const AvatarEditable: FC<OwnProps> = ({ const AvatarEditable: FC<OwnProps> = ({
title = 'Change your profile picture', title,
disabled, disabled,
isForForum, isForForum,
currentAvatarBlobUrl, currentAvatarBlobUrl,
@ -29,6 +31,8 @@ const AvatarEditable: FC<OwnProps> = ({
const [selectedFile, setSelectedFile] = useState<File | undefined>(); const [selectedFile, setSelectedFile] = useState<File | undefined>();
const [croppedBlobUrl, setCroppedBlobUrl] = useState<string | undefined>(currentAvatarBlobUrl); const [croppedBlobUrl, setCroppedBlobUrl] = useState<string | undefined>(currentAvatarBlobUrl);
const lang = useLang();
useEffect(() => { useEffect(() => {
setCroppedBlobUrl(currentAvatarBlobUrl); setCroppedBlobUrl(currentAvatarBlobUrl);
}, [currentAvatarBlobUrl]); }, [currentAvatarBlobUrl]);
@ -70,7 +74,7 @@ const AvatarEditable: FC<OwnProps> = ({
className={labelClassName} className={labelClassName}
role="button" role="button"
tabIndex={0} tabIndex={0}
title={title} title={title || lang('ChangeYourProfilePicture')}
> >
<input <input
type="file" type="file"
@ -78,7 +82,7 @@ const AvatarEditable: FC<OwnProps> = ({
accept="image/png, image/jpeg" accept="image/png, image/jpeg"
/> />
<Icon name="camera-add" /> <Icon name="camera-add" />
{croppedBlobUrl && <img src={croppedBlobUrl} draggable={false} alt="Avatar" />} {croppedBlobUrl && <img src={croppedBlobUrl} draggable={false} alt="" />}
</label> </label>
<CropModal file={selectedFile} onClose={handleModalClose} onChange={handleAvatarCrop} /> <CropModal file={selectedFile} onClose={handleModalClose} onChange={handleAvatarCrop} />
</div> </div>

View File

@ -7,7 +7,7 @@ import type { TextPart } from '../../types';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
import useKeyboardListNavigation from '../../hooks/useKeyboardListNavigation'; import useKeyboardListNavigation from '../../hooks/useKeyboardListNavigation';
import useOldLang from '../../hooks/useOldLang'; import useLang from '../../hooks/useLang';
import Button from './Button'; import Button from './Button';
import Modal from './Modal'; import Modal from './Modal';
@ -38,7 +38,7 @@ const ConfirmDialog: FC<OwnProps> = ({
header, header,
text, text,
textParts, textParts,
confirmLabel = 'Confirm', confirmLabel,
confirmIsDestructive, confirmIsDestructive,
isConfirmDisabled, isConfirmDisabled,
isOnlyConfirm, isOnlyConfirm,
@ -49,7 +49,7 @@ const ConfirmDialog: FC<OwnProps> = ({
onClose, onClose,
onCloseAnimationEnd, onCloseAnimationEnd,
}) => { }) => {
const lang = useOldLang(); const lang = useLang();
const containerRef = useRef<HTMLDivElement>(); const containerRef = useRef<HTMLDivElement>();
@ -84,7 +84,7 @@ const ConfirmDialog: FC<OwnProps> = ({
color={confirmIsDestructive ? 'danger' : 'primary'} color={confirmIsDestructive ? 'danger' : 'primary'}
disabled={isConfirmDisabled} disabled={isConfirmDisabled}
> >
{confirmLabel} {confirmLabel || lang('GeneralConfirm')}
</Button> </Button>
{!isOnlyConfirm && <Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>} {!isOnlyConfirm && <Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>}
</div> </div>

View File

@ -284,8 +284,6 @@ export const CONTENT_TYPES_WITH_PREVIEW = new Set([
...SUPPORTED_VIDEO_CONTENT_TYPES, ...SUPPORTED_VIDEO_CONTENT_TYPES,
]); ]);
export const CONTENT_NOT_SUPPORTED = 'The message is not supported on this version of Telegram.';
// Taken from https://github.com/telegramdesktop/tdesktop/blob/41d9a9fcbd0c809c60ddbd9350791b1436aff7d9/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp#L28 // Taken from https://github.com/telegramdesktop/tdesktop/blob/41d9a9fcbd0c809c60ddbd9350791b1436aff7d9/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp#L28
export const SUPPORTED_TRANSLATION_LANGUAGES = [ export const SUPPORTED_TRANSLATION_LANGUAGES = [
// Official // Official

View File

@ -17,8 +17,7 @@ import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
import { copyHtmlToClipboard } from '../../../util/clipboard'; import { copyHtmlToClipboard } from '../../../util/clipboard';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { compact, findLast } from '../../../util/iteratees'; import { compact, findLast } from '../../../util/iteratees';
import * as langProvider from '../../../util/oldLangProvider'; import { getTranslationFn } from '../../../util/localization';
import { oldTranslate } from '../../../util/oldLangProvider';
import parseHtmlAsFormattedText from '../../../util/parseHtmlAsFormattedText'; import parseHtmlAsFormattedText from '../../../util/parseHtmlAsFormattedText';
import { getServerTime } from '../../../util/serverTime'; import { getServerTime } from '../../../util/serverTime';
import versionNotification from '../../../versionNotification.txt'; import versionNotification from '../../../versionNotification.txt';
@ -414,7 +413,7 @@ addActionHandler('focusMessage', (global, actions, payload): ActionReturnType =>
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat) { if (!chat) {
actions.showNotification({ message: oldTranslate('Conversation.ErrorInaccessibleMessage'), tabId }); actions.showNotification({ message: { key: 'ErrorFocusInaccessibleMessage' }, tabId });
return undefined; return undefined;
} }
@ -719,8 +718,9 @@ addActionHandler('toggleMessageSelection', (global, actions, payload): ActionRet
if (global.shouldShowContextMenuHint) { if (global.shouldShowContextMenuHint) {
actions.disableContextMenuHint(); actions.disableContextMenuHint();
actions.showNotification({ actions.showNotification({
// eslint-disable-next-line @stylistic/max-len message: {
message: `To **edit** or **reply**, close this menu. Then ${IS_TOUCH_ENV ? 'long tap' : 'right click'} on a message.`, key: IS_TOUCH_ENV ? 'ContextMenuHintTouch' : 'ContextMenuHintMouse',
},
tabId, tabId,
}); });
} }
@ -1050,7 +1050,7 @@ addActionHandler('closeSuggestedPostApprovalModal', (global, actions, payload):
function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) { function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) {
const { type: messageListType, threadId } = selectCurrentMessageList(global) || {}; const { type: messageListType, threadId } = selectCurrentMessageList(global) || {};
const lang = langProvider.oldTranslate; const lang = getTranslationFn();
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);

View File

@ -304,7 +304,7 @@ export function getCanDeleteChat(chat: ApiChat) {
return isChatBasicGroup(chat) || ((isChatSuperGroup(chat) || isChatChannel(chat)) && chat.isCreator); return isChatBasicGroup(chat) || ((isChatSuperGroup(chat) || isChatChannel(chat)) && chat.isCreator);
} }
export function getFolderDescriptionText(lang: OldLangFn, folder: ApiChatFolder, chatsCount?: number) { export function getFolderDescriptionText(lang: LangFn, folder: ApiChatFolder, chatsCount?: number) {
const { const {
excludedChatIds, includedChatIds, excludedChatIds, includedChatIds,
bots, groups, contacts, nonContacts, channels, bots, groups, contacts, nonContacts, channels,
@ -320,7 +320,7 @@ export function getFolderDescriptionText(lang: OldLangFn, folder: ApiChatFolder,
|| (excludedChatIds?.length) || (excludedChatIds?.length)
|| (includedChatIds?.length) || (includedChatIds?.length)
)) { )) {
return lang('Chats', chatsCount); return lang('ChatsPlural', { count: chatsCount }, { pluralValue: chatsCount });
} }
// Otherwise, we return a short description of a single filter // Otherwise, we return a short description of a single filter

View File

@ -3,21 +3,20 @@ import type { TeactNode } from '../../lib/teact/teact';
import type { import type {
ApiMediaExtendedPreview, ApiMessage, MediaContent, StatefulMediaContent, ApiMediaExtendedPreview, ApiMessage, MediaContent, StatefulMediaContent,
} from '../../api/types'; } from '../../api/types';
import type { OldLangFn } from '../../hooks/useOldLang';
import { ApiMessageEntityTypes } from '../../api/types'; import { ApiMessageEntityTypes } from '../../api/types';
import { CONTENT_NOT_SUPPORTED } from '../../config'; import { type LangFn } from '../../util/localization';
import trimText from '../../util/trimText'; import trimText from '../../util/trimText';
import { renderTextWithEntities } from '../../components/common/helpers/renderTextWithEntities'; import { renderTextWithEntities } from '../../components/common/helpers/renderTextWithEntities';
import { import {
getMessageText, getMessageTranscription, getMessageTextWithFallback, getMessageTranscription,
} from './messages'; } from './messages';
const SPOILER_CHARS = ['⠺', '⠵', '⠞', '⠟']; const SPOILER_CHARS = ['⠺', '⠵', '⠞', '⠟'];
export const TRUNCATED_SUMMARY_LENGTH = 80; export const TRUNCATED_SUMMARY_LENGTH = 80;
export function getMessageSummaryText( export function getMessageSummaryText(
lang: OldLangFn, lang: LangFn,
message: ApiMessage, message: ApiMessage,
statefulContent: StatefulMediaContent | undefined, statefulContent: StatefulMediaContent | undefined,
noEmoji = false, noEmoji = false,
@ -26,16 +25,20 @@ export function getMessageSummaryText(
) { ) {
const emoji = !noEmoji && getMessageSummaryEmoji(message); const emoji = !noEmoji && getMessageSummaryEmoji(message);
const emojiWithSpace = emoji ? `${emoji} ` : ''; const emojiWithSpace = emoji ? `${emoji} ` : '';
const text = trimText(getMessageTextWithSpoilers(message, statefulContent), truncateLength); const text = trimText(getMessageTextWithSpoilers(lang, message, statefulContent), truncateLength);
const description = getMessageSummaryDescription(lang, message, statefulContent, text, isExtended) as string; const description = getMessageSummaryDescription(lang, message, statefulContent, text, isExtended) as string;
return `${emojiWithSpace}${description}`; return `${emojiWithSpace}${description}`;
} }
export function getMessageTextWithSpoilers(message: ApiMessage, statefulContent: StatefulMediaContent | undefined) { export function getMessageTextWithSpoilers(
lang: LangFn,
message: ApiMessage,
statefulContent: StatefulMediaContent | undefined,
) {
const transcription = getMessageTranscription(message); const transcription = getMessageTranscription(message);
const textWithoutTranscription = getMessageText(statefulContent?.story || message)?.text; const textWithoutTranscription = getMessageTextWithFallback(lang, statefulContent?.story || message)?.text;
if (!textWithoutTranscription) { if (!textWithoutTranscription) {
return transcription; return transcription;
} }
@ -56,7 +59,7 @@ export function getMessageTextWithSpoilers(message: ApiMessage, statefulContent:
const spoiler = generateBrailleSpoiler(length); const spoiler = generateBrailleSpoiler(length);
return `${accText.substr(0, offset)}${spoiler}${accText.substr(offset + length, accText.length)}`; return `${accText.slice(0, offset)}${spoiler}${accText.slice(offset + length)}`;
}, textWithoutTranscription); }, textWithoutTranscription);
return transcription ? `${transcription}\n${text}` : text; return transcription ? `${transcription}\n${text}` : text;
@ -111,12 +114,12 @@ export function getMessageSummaryEmoji(message: ApiMessage) {
} }
export function getMediaContentTypeDescription( export function getMediaContentTypeDescription(
lang: OldLangFn, content: MediaContent, statefulContent: StatefulMediaContent | undefined, lang: LangFn, content: MediaContent, statefulContent: StatefulMediaContent | undefined,
) { ) {
return getSummaryDescription(lang, content, statefulContent); return getSummaryDescription(lang, content, statefulContent);
} }
export function getMessageSummaryDescription( export function getMessageSummaryDescription(
lang: OldLangFn, lang: LangFn,
message: ApiMessage, message: ApiMessage,
statefulContent: StatefulMediaContent | undefined, statefulContent: StatefulMediaContent | undefined,
truncatedText?: string | TeactNode, truncatedText?: string | TeactNode,
@ -125,7 +128,7 @@ export function getMessageSummaryDescription(
return getSummaryDescription(lang, message.content, statefulContent, message, truncatedText, isExtended); return getSummaryDescription(lang, message.content, statefulContent, message, truncatedText, isExtended);
} }
function getSummaryDescription( function getSummaryDescription(
lang: OldLangFn, lang: LangFn,
mediaContent: MediaContent, mediaContent: MediaContent,
statefulContent: StatefulMediaContent | undefined, statefulContent: StatefulMediaContent | undefined,
message?: ApiMessage, message?: ApiMessage,
@ -166,7 +169,7 @@ function getSummaryDescription(
if (message?.groupedId || isPaidMediaAlbum) { if (message?.groupedId || isPaidMediaAlbum) {
hasUsedTruncatedText = true; hasUsedTruncatedText = true;
summary = truncatedText || lang('lng_in_dlg_album'); summary = truncatedText || lang('Album');
} }
if (photo || isPaidMediaSinglePhoto) { if (photo || isPaidMediaSinglePhoto) {
@ -210,7 +213,7 @@ function getSummaryDescription(
} }
if (invoice) { if (invoice) {
summary = invoice.extendedMedia ? invoice.title : `${lang('PaymentInvoice')}: ${invoice.description}`; summary = invoice.extendedMedia ? invoice.title : lang('AttachInvoice', { description: invoice.description });
} }
if (text) { if (text) {
@ -222,11 +225,11 @@ function getSummaryDescription(
} }
if (location?.mediaType === 'geo' || location?.mediaType === 'venue') { if (location?.mediaType === 'geo' || location?.mediaType === 'venue') {
summary = lang('Message.Location'); summary = lang('AttachLocation');
} }
if (location?.mediaType === 'geoLive') { if (location?.mediaType === 'geoLive') {
summary = lang('Message.LiveLocation'); summary = lang('AttachLiveLocation');
} }
if (game) { if (game) {
@ -234,22 +237,22 @@ function getSummaryDescription(
} }
if (giveaway) { if (giveaway) {
summary = lang('BoostingGiveawayChannelStarted'); summary = lang('AttachGiveaway');
} }
if (giveawayResults) { if (giveawayResults) {
summary = lang('Message.GiveawayEndedWinners', giveawayResults.winnersCount); summary = lang('AttachGiveawayResults');
} }
if (storyData) { if (storyData) {
summary = truncatedText || (message ? lang('ForwardedStory') : lang('Chat.ReplyStory')); summary = truncatedText || lang('AttachStory');
} }
if (todo) { if (todo) {
summary = lang('Chat.Todo.Message.Title'); summary = lang('AttachTodo');
} }
return summary || CONTENT_NOT_SUPPORTED; return summary || lang('MessageUnsupported');
} }
export function generateBrailleSpoiler(length: number) { export function generateBrailleSpoiler(length: number) {

View File

@ -17,7 +17,6 @@ import type { GlobalState } from '../types';
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../../api/types'; import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../../api/types';
import { import {
CONTENT_NOT_SUPPORTED,
LOTTIE_STICKER_MIME_TYPE, LOTTIE_STICKER_MIME_TYPE,
RE_LINK_TEMPLATE, RE_LINK_TEMPLATE,
SERVICE_NOTIFICATIONS_USER_ID, SERVICE_NOTIFICATIONS_USER_ID,
@ -95,7 +94,11 @@ export function groupStatefulContent({
} }
export function getMessageText(message: MediaContainer) { export function getMessageText(message: MediaContainer) {
return hasMessageText(message) ? message.content.text || { text: CONTENT_NOT_SUPPORTED } : undefined; return hasMessageText(message) ? message.content.text : undefined;
}
export function getMessageTextWithFallback(lang: LangFn, message: MediaContainer) {
return hasMessageText(message) ? message.content.text || { text: lang('MessageUnsupported') } : undefined;
} }
export function getMessageCustomShape(message: ApiMessage): boolean { export function getMessageCustomShape(message: ApiMessage): boolean {

View File

@ -1,13 +1,13 @@
import type { ApiMessage } from '../../api/types'; import type { ApiMessage } from '../../api/types';
import type { OldLangFn } from '../../hooks/useOldLang';
import { type LangFn } from '../../util/localization';
import { renderMessageText } from '../../components/common/helpers/renderMessageText'; import { renderMessageText } from '../../components/common/helpers/renderMessageText';
import { getGlobal } from '..'; import { getGlobal } from '..';
import { getMessageStatefulContent } from './messages'; import { getMessageStatefulContent } from './messages';
import { getMessageSummaryDescription, getMessageSummaryEmoji } from './messageSummary'; import { getMessageSummaryDescription, getMessageSummaryEmoji } from './messageSummary';
export function renderMessageSummaryHtml( export function renderMessageSummaryHtml(
lang: OldLangFn, lang: LangFn,
message: ApiMessage, message: ApiMessage,
) { ) {
const global = getGlobal(); const global = getGlobal();

View File

@ -58,6 +58,7 @@ export interface LangPair {
'PremiumLimitAccountsNoPremium': undefined; 'PremiumLimitAccountsNoPremium': undefined;
'PremiumLimitAccounts': undefined; 'PremiumLimitAccounts': undefined;
'SendMessage': undefined; 'SendMessage': undefined;
'MessageUnsupported': undefined;
'ConversationDefaultRestrictedMedia': undefined; 'ConversationDefaultRestrictedMedia': undefined;
'AccDescrVoiceMessage': undefined; 'AccDescrVoiceMessage': undefined;
'BotSettings': undefined; 'BotSettings': undefined;
@ -111,6 +112,7 @@ export interface LangPair {
'UserRestrictionsNoChangeInfo': undefined; 'UserRestrictionsNoChangeInfo': undefined;
'UserRestrictionsInviteUsers': undefined; 'UserRestrictionsInviteUsers': undefined;
'UserRestrictionsPinMessages': undefined; 'UserRestrictionsPinMessages': undefined;
'ChatPermissionNotAvailable': undefined;
'StatsMessageInteractionsTitle': undefined; 'StatsMessageInteractionsTitle': undefined;
'StatsGroupGrowthTitle': undefined; 'StatsGroupGrowthTitle': undefined;
'StatsGroupMembersTitle': undefined; 'StatsGroupMembersTitle': undefined;
@ -424,6 +426,7 @@ export interface LangPair {
'AlwaysShareWith': undefined; 'AlwaysShareWith': undefined;
'NeverShareWith': undefined; 'NeverShareWith': undefined;
'SessionsTitle': undefined; 'SessionsTitle': undefined;
'SessionTerminate': undefined;
'OtherWebSessions': undefined; 'OtherWebSessions': undefined;
'BlockedUsers': undefined; 'BlockedUsers': undefined;
'TwoStepVerification': undefined; 'TwoStepVerification': undefined;
@ -608,6 +611,7 @@ export interface LangPair {
'SettingsPerformanceStickerEffects': undefined; 'SettingsPerformanceStickerEffects': undefined;
'SettingsPerformanceAutoplayGif': undefined; 'SettingsPerformanceAutoplayGif': undefined;
'SettingsPerformanceAutoplayVideo': undefined; 'SettingsPerformanceAutoplayVideo': undefined;
'SettingsPerformanceGranularTitle': undefined;
'FavoriteStickers': undefined; 'FavoriteStickers': undefined;
'PremiumStickers': undefined; 'PremiumStickers': undefined;
'GroupStickers': undefined; 'GroupStickers': undefined;
@ -627,7 +631,6 @@ export interface LangPair {
'ErrorUnspecified': undefined; 'ErrorUnspecified': undefined;
'NoStickers': undefined; 'NoStickers': undefined;
'ClearRecentEmoji': undefined; 'ClearRecentEmoji': undefined;
'TextFormatAddLinkTitle': undefined;
'Save': undefined; 'Save': undefined;
'ConversationEmptyPlaceholder': undefined; 'ConversationEmptyPlaceholder': undefined;
'ConversationGreetingText': undefined; 'ConversationGreetingText': undefined;
@ -832,8 +835,16 @@ export interface LangPair {
'ChannelPermissionsHeader': undefined; 'ChannelPermissionsHeader': undefined;
'UserRestrictionsSend': undefined; 'UserRestrictionsSend': undefined;
'UserRestrictionsSendMedia': undefined; 'UserRestrictionsSendMedia': undefined;
'UserRestrictionsSendStickers': undefined; 'UserRestrictionsCreateTopics': undefined;
'UserRestrictionsSendPolls': undefined; 'SendMediaPermissionFiles': undefined;
'SendMediaPermissionPhotos': undefined;
'SendMediaPermissionVideos': undefined;
'SendMediaPermissionStickersGifs': undefined;
'SendMediaPermissionAudios': undefined;
'SendMediaPermissionVoices': undefined;
'SendMediaPermissionRoundVideos': undefined;
'SendMediaPermissionWebPages': undefined;
'SendMediaPermissionPolls': undefined;
'UserRestrictionsEmbedLinks': undefined; 'UserRestrictionsEmbedLinks': undefined;
'UserRestrictionsChangeInfo': undefined; 'UserRestrictionsChangeInfo': undefined;
'ChannelAddException': undefined; 'ChannelAddException': undefined;
@ -944,7 +955,7 @@ export interface LangPair {
'FilterContacts': undefined; 'FilterContacts': undefined;
'FilterNonContacts': undefined; 'FilterNonContacts': undefined;
'FromYou': undefined; 'FromYou': undefined;
'InDlgAlbum': undefined; 'Album': undefined;
'AttachPhoto': undefined; 'AttachPhoto': undefined;
'AttachGif': undefined; 'AttachGif': undefined;
'AttachVideo': undefined; 'AttachVideo': undefined;
@ -952,8 +963,10 @@ export interface LangPair {
'AttachMusic': undefined; 'AttachMusic': undefined;
'AttachContact': undefined; 'AttachContact': undefined;
'AttachStory': undefined; 'AttachStory': undefined;
'MessageLocation': undefined; 'AttachLocation': undefined;
'MessageLiveLocation': undefined; 'AttachGiveaway': undefined;
'AttachGiveawayResults': undefined;
'AttachTodo': undefined;
'ServiceNotifications': undefined; 'ServiceNotifications': undefined;
'Bot': undefined; 'Bot': undefined;
'ALongTimeAgo': undefined; 'ALongTimeAgo': undefined;
@ -1086,6 +1099,7 @@ export interface LangPair {
'SettingsPasscodeStart1': undefined; 'SettingsPasscodeStart1': undefined;
'SettingsPasscodeStart2': undefined; 'SettingsPasscodeStart2': undefined;
'CurrentPasswordPlaceholder': undefined; 'CurrentPasswordPlaceholder': undefined;
'ChangeYourProfilePicture': undefined;
'TooManyTabsTitle': undefined; 'TooManyTabsTitle': undefined;
'TooManyTabsDescription': undefined; 'TooManyTabsDescription': undefined;
'TooManyTabsReload': undefined; 'TooManyTabsReload': undefined;
@ -1141,6 +1155,7 @@ export interface LangPair {
'FormattingMonospaceAria': undefined; 'FormattingMonospaceAria': undefined;
'FormattingUnderlineAria': undefined; 'FormattingUnderlineAria': undefined;
'FormattingStrikethroughAria': undefined; 'FormattingStrikethroughAria': undefined;
'FormattingAddLinkAria': undefined;
'FormattingEnterUrl': undefined; 'FormattingEnterUrl': undefined;
'PreviewWebPageClose': undefined; 'PreviewWebPageClose': undefined;
'MediaLocaltionImageAlt': undefined; 'MediaLocaltionImageAlt': undefined;
@ -1190,6 +1205,8 @@ export interface LangPair {
'ReplyInPrivateMessage': undefined; 'ReplyInPrivateMessage': undefined;
'ProfileOpenAppTerms': undefined; 'ProfileOpenAppTerms': undefined;
'ProfileBotOpenAppInfoLink': undefined; 'ProfileBotOpenAppInfoLink': undefined;
'ProfileBirthday': undefined;
'ProfileBirthdayToday': undefined;
'MonetizationInfoTONTitle': undefined; 'MonetizationInfoTONTitle': undefined;
'AriaSearchOlderResult': undefined; 'AriaSearchOlderResult': undefined;
'AriaSearchNewerResult': undefined; 'AriaSearchNewerResult': undefined;
@ -1339,6 +1356,9 @@ export interface LangPair {
'SearchTabVoice': undefined; 'SearchTabVoice': undefined;
'SearchTabMessages': undefined; 'SearchTabMessages': undefined;
'SearchTabPublicPosts': undefined; 'SearchTabPublicPosts': undefined;
'SearchResultTopics': undefined;
'SearchResultMyChannels': undefined;
'SearchResultRecommendedChannels': undefined;
'StarsTransactionsAll': undefined; 'StarsTransactionsAll': undefined;
'StarsTransactionsIncoming': undefined; 'StarsTransactionsIncoming': undefined;
'StarsTransactionsOutgoing': undefined; 'StarsTransactionsOutgoing': undefined;
@ -1392,6 +1412,8 @@ export interface LangPair {
'CheckPasswordTitle': undefined; 'CheckPasswordTitle': undefined;
'CheckPasswordPlaceholder': undefined; 'CheckPasswordPlaceholder': undefined;
'CheckPasswordDescription': undefined; 'CheckPasswordDescription': undefined;
'PasswordFormPlaceholder': undefined;
'PasswordFormSubmit': undefined;
'ActionFallbackUser': undefined; 'ActionFallbackUser': undefined;
'ActionFallbackChat': undefined; 'ActionFallbackChat': undefined;
'ActionFallbackChannel': undefined; 'ActionFallbackChannel': undefined;
@ -1655,6 +1677,9 @@ export interface LangPair {
'RatingNegativeLevel': undefined; 'RatingNegativeLevel': undefined;
'LinkDescriptionRatingBack': undefined; 'LinkDescriptionRatingBack': undefined;
'LinkDescriptionRatingPreview': undefined; 'LinkDescriptionRatingPreview': undefined;
'ErrorFocusInaccessibleMessage': undefined;
'ContextMenuHintMouse': undefined;
'ContextMenuHintTouch': undefined;
} }
export interface LangPairWithVariables<V = LangVariable> { export interface LangPairWithVariables<V = LangVariable> {
@ -1901,6 +1926,9 @@ export interface LangPairWithVariables<V = LangVariable> {
'NewDiscussionChatTitle': { 'NewDiscussionChatTitle': {
'name': V; 'name': V;
}; };
'AttachInvoice': {
'description': V;
};
'LastSeenTodayAt': { 'LastSeenTodayAt': {
'time': V; 'time': V;
}; };
@ -1973,6 +2001,20 @@ export interface LangPairWithVariables<V = LangVariable> {
'ProfileOpenAppAbout': { 'ProfileOpenAppAbout': {
'terms': V; 'terms': V;
}; };
'ProfileBirthdayValue': {
'date': V;
};
'ProfileBirthdayValueYear': {
'date': V;
'age': V;
};
'ProfileBirthdayTodayValue': {
'date': V;
};
'ProfileBirthdayTodayValueYear': {
'date': V;
'age': V;
};
'ChannelEarnLearnCoinAbout': { 'ChannelEarnLearnCoinAbout': {
'link': V; 'link': V;
}; };
@ -2135,6 +2177,9 @@ export interface LangPairWithVariables<V = LangVariable> {
'StarsReactionTerms': { 'StarsReactionTerms': {
'link': V; 'link': V;
}; };
'AriaMiniApp': {
'bot': V;
};
'StarsSubscribeInfo': { 'StarsSubscribeInfo': {
'link': V; 'link': V;
}; };
@ -2545,8 +2590,8 @@ export interface LangPairWithVariables<V = LangVariable> {
'amount': V; 'amount': V;
}; };
'ActionPaidOneMessageIncoming': { 'ActionPaidOneMessageIncoming': {
'amount': V;
'user': V; 'user': V;
'amount': V;
}; };
'PaneMessagePaidMessageCharge': { 'PaneMessagePaidMessageCharge': {
'peer': V; 'peer': V;
@ -2688,12 +2733,10 @@ export interface LangPairWithVariables<V = LangVariable> {
}; };
'SuggestedPostReceiveAmount': { 'SuggestedPostReceiveAmount': {
'peer': V; 'peer': V;
'currency': V;
'duration': V; 'duration': V;
}; };
'SuggestedPostReceiveAmountYou': { 'SuggestedPostReceiveAmountYou': {
'peer': V; 'peer': V;
'currency': V;
'duration': V; 'duration': V;
}; };
'SuggestedPostRefund': { 'SuggestedPostRefund': {
@ -2703,7 +2746,6 @@ export interface LangPairWithVariables<V = LangVariable> {
'SuggestedPostRefundYou': { 'SuggestedPostRefundYou': {
'peer': V; 'peer': V;
'duration': V; 'duration': V;
'currency': V;
}; };
'SuggestedPostBalanceTooLow': { 'SuggestedPostBalanceTooLow': {
'peer': V; 'peer': V;
@ -2748,7 +2790,6 @@ export interface LangPairWithVariables<V = LangVariable> {
}; };
'SuggestedPostConfirmDetailsUser': { 'SuggestedPostConfirmDetailsUser': {
'amount': V; 'amount': V;
'commission': V;
'duration': V; 'duration': V;
}; };
'SuggestedPostConfirmDetailsWithTimeAdmin': { 'SuggestedPostConfirmDetailsWithTimeAdmin': {
@ -2759,7 +2800,6 @@ export interface LangPairWithVariables<V = LangVariable> {
}; };
'SuggestedPostConfirmDetailsWithTimeUser': { 'SuggestedPostConfirmDetailsWithTimeUser': {
'amount': V; 'amount': V;
'commission': V;
'time': V; 'time': V;
'duration': V; 'duration': V;
}; };
@ -2819,9 +2859,6 @@ export interface LangPairWithVariables<V = LangVariable> {
'tasks': V; 'tasks': V;
'list': V; 'list': V;
}; };
'HintTodoListTasksCount': {
'count': V;
};
'GiftInfoCollectibleBy': { 'GiftInfoCollectibleBy': {
'number': V; 'number': V;
'owner': V; 'owner': V;
@ -2832,12 +2869,6 @@ export interface LangPairWithVariables<V = LangVariable> {
'ButtonSensitiveAlways': { 'ButtonSensitiveAlways': {
'years': V; 'years': V;
}; };
'NotificationGiftsLimit': {
'count': V;
};
'DescriptionGiftPremiumRequired': {
'count': V;
};
'DescriptionComposerGiftMinimumCurrencyPrice': { 'DescriptionComposerGiftMinimumCurrencyPrice': {
'amount': V; 'amount': V;
}; };
@ -2984,6 +3015,9 @@ export interface LangPairPluralWithVariables<V = LangVariable> {
'ChannelStatsSharesCount': { 'ChannelStatsSharesCount': {
'count': V; 'count': V;
}; };
'ChatsPlural': {
'count': V;
};
'LastSeenMinutesAgo': { 'LastSeenMinutesAgo': {
'count': V; 'count': V;
}; };
@ -3187,9 +3221,18 @@ export interface LangPairPluralWithVariables<V = LangVariable> {
'MessageActionTodoTaskCount': { 'MessageActionTodoTaskCount': {
'count': V; 'count': V;
}; };
'HintTodoListTasksCount2': {
'count': V;
};
'TextAgeVerificationModal': { 'TextAgeVerificationModal': {
'count': V; 'count': V;
}; };
'NotificationGiftsLimit2': {
'count': V;
};
'DescriptionGiftPremiumRequired2': {
'count': V;
};
'RemainingPublicPostsSearch': { 'RemainingPublicPostsSearch': {
'count': V; 'count': V;
}; };