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.";
"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";
"MessageUnsupported" = "This message is not supported on the web version of Telegram";
"ConversationDefaultRestrictedMedia" = "Sending media isn't allowed in this group.";
"AccDescrVoiceMessage" = "Record voice message";
"BotSettings" = "Settings";
@ -87,7 +88,7 @@
"ContextCopySelected" = "Copy Selected Text";
"ContextCopyText" = "Copy Text";
"ContextArchiveCollapse" = "Collapse";
"ContextArchiveExpand" = "Collapse";
"ContextArchiveExpand" = "Expand";
"ContextArchiveToMenu" = "Move to Main Menu";
"CallMessageVideoIncomingDeclined" = "Declined Video Call";
"CallMessageVideoOutgoingMissed" = "Canceled Video Call";
@ -129,6 +130,7 @@
"UserRestrictionsNoChangeInfo" = "can't change Info";
"UserRestrictionsInviteUsers" = "Add Users";
"UserRestrictionsPinMessages" = "Pin Messages";
"ChatPermissionNotAvailable" = "This permission is not available in public groups.";
"StatsMessageInteractionsTitle" = "INTERACTIONS";
"StatsGroupGrowthTitle" = "GROWTH";
"StatsGroupMembersTitle" = "GROUP MEMBERS";
@ -474,6 +476,7 @@
"AlwaysShareWith" = "Always Share With";
"NeverShareWith" = "Never Share With";
"SessionsTitle" = "Active Sessions";
"SessionTerminate" = "Terminate";
"OtherWebSessions" = "Connected Websites";
"BlockedUsers" = "Blocked Users";
"TwoStepVerification" = "Two-Step Verification";
@ -522,7 +525,7 @@
"WhoCanCallMe" = "Who can call me";
"PrivacyP2P" = "Peer-to-Peer Calls";
"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";
"ArchiveAndMute" = "Archive and Mute";
"ArchiveAndMuteInfo" = "Automatically archive and mute new chats, groups and channels from non-contacts.";
@ -689,6 +692,7 @@
"SettingsPerformanceStickerEffects" = "Sticker Effects";
"SettingsPerformanceAutoplayGif" = "Autoplay GIFs";
"SettingsPerformanceAutoplayVideo" = "Autoplay Videos";
"SettingsPerformanceGranularTitle" = "Resource-Intensive Processes";
"FavoriteStickers" = "Favorites";
"PremiumStickers" = "Premium Stickers";
"GroupStickers" = "Group Stickers";
@ -711,7 +715,6 @@
"ErrorUnspecified" = "Error";
"NoStickers" = "No stickers yet";
"ClearRecentEmoji" = "Clear recent emoji?";
"TextFormatAddLinkTitle" = "Add Link";
"Save" = "Save";
"ConversationEmptyPlaceholder" = "No messages here yet...";
"ConversationGreetingText" = "Send a message or tap on the greeting below.";
@ -957,8 +960,16 @@
"ChannelPermissionsHeader" = "What can members of this group do?";
"UserRestrictionsSend" = "Send Messages";
"UserRestrictionsSendMedia" = "Send Media";
"UserRestrictionsSendStickers" = "Send Stickers & GIFs";
"UserRestrictionsSendPolls" = "Send Polls";
"UserRestrictionsCreateTopics" = "Create Topics";
"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";
"UserRestrictionsChangeInfo" = "Change Chat Info";
"ChannelAddException" = "Add Exception";
@ -1089,12 +1100,14 @@
"ChannelPersmissionDeniedSendMessagesForever" = "The admins of this group have restricted your ability to send messages.";
"ChannelPersmissionDeniedSendMessagesDefaultRestrictedText" = "Sending messages is not allowed in this group.";
"Chats" = "Chats";
"ChatsPlural_one" = "{count} chat";
"ChatsPlural_other" = "{count} chats";
"NewDiscussionChatTitle" = "{name} Chat";
"FilterBots" = "Bots";
"FilterContacts" = "Contacts";
"FilterNonContacts" = "Non-Contacts";
"FromYou" = "You";
"InDlgAlbum" = "Album";
"Album" = "Album";
"AttachPhoto" = "Photo";
"AttachGif" = "GIF";
"AttachVideo" = "Video";
@ -1102,8 +1115,12 @@
"AttachMusic" = "Music";
"AttachContact" = "Contact";
"AttachStory" = "Story";
"MessageLocation" = "Location";
"MessageLiveLocation" = "Live Location";
"AttachInvoice" = "Invoice: {description}";
"AttachLocation" = "Location";
"AttachLiveLocation" = "Live Location";
"AttachGiveaway" = "Giveaway";
"AttachGiveawayResults" = "Giveaway Results";
"AttachTodo" = "Checklist";
"ServiceNotifications" = "service notifications";
"Bot" = "bot";
"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.";
"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";
"ChangeYourProfilePicture" = "Change your profile picture";
"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.";
"TooManyTabsReload" = "Reload app";
@ -1338,6 +1356,7 @@
"FormattingMonospaceAria" = "Monospace text";
"FormattingUnderlineAria" = "Underlined text";
"FormattingStrikethroughAria" = "Strikethrough text";
"FormattingAddLinkAria" = "Add Link";
"FormattingEnterUrl" = "Enter URL...";
"PreviewWebPageClose" = "Clear Webpage Preview";
"MediaLocaltionImageAlt" = "Location on a map";
@ -1395,6 +1414,12 @@
"ProfileOpenAppAbout" = "By launching this mini app, you agree to the {terms}.";
"ProfileOpenAppTerms" = "Terms of Service for 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?";
"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}";
@ -1544,6 +1569,7 @@
"StarsReactionTerms" = "By sending Stars you agree to the {link}";
"StarsReactionLinkText" = "Terms of Service";
"StarsReactionLink" = "https://telegram.org/tos/stars";
"AriaMiniApp" = "{bot} Mini App";
"MiniAppsMoreTabs_one" = "{botName} & {count} Other";
"MiniAppsMoreTabs_other" = "{botName} & {count} Others";
"PrizeCredits2_one" = "Your prize is {count} Star.";
@ -1645,6 +1671,9 @@
"SearchTabVoice" = "Voice";
"SearchTabMessages" = "Messages";
"SearchTabPublicPosts" = "Posts";
"SearchResultTopics" = "Topics";
"SearchResultMyChannels" = "Channels you joined";
"SearchResultRecommendedChannels" = "Similar Channels";
"StarsTransactionsAll" = "All Transactions";
"StarsTransactionsIncoming" = "Incoming";
"StarsTransactionsOutgoing" = "Outgoing";
@ -1701,6 +1730,8 @@
"CheckPasswordTitle" = "Enter Password";
"CheckPasswordPlaceholder" = "Password";
"CheckPasswordDescription" = "Please enter your password to continue.";
"PasswordFormPlaceholder" = "Password";
"PasswordFormSubmit" = "Next";
"ActionFallbackUser" = "User";
"ActionFallbackChat" = "Chat";
"ActionFallbackChannel" = "Channel";
@ -1921,8 +1952,8 @@
"GiftPremiumPayWithStarsAcc" = "Pay with stars";
"GetMoreStarsLinkText" = "Get More Stars >";
"GiftPremiumDescriptionYourBalance" = "Your balance is **{stars}**. {link}";
"StarsGiftCompleted"= "Gift sent!";
"GiftSent"= "Gift sent!";
"StarsGiftCompleted" = "Gift sent!";
"GiftSent" = "Gift sent!";
"PrivacyDescriptionMessagesContactsAndPremium" = "You can restrict messages from users who are not in your contacts and don't have Premium.";
"PrivacyChargeForMessages" = "Charge for Messages";
"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}";
"ButtonUndo" = "Undo";
"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.";
"ConfirmRemoveMessageFee" = "Yes";
"ConfirmDialogMessageRemoveFee" = "Are you sure you want to allow **{peer}** to message you for free?";
@ -1967,7 +1998,7 @@
"ComposerSubtitleFrozenAccount" = "Tap to view details";
"DescriptionRestrictedMedia" = "Posting media content is not allowed in this group.";
"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";
"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.";
@ -1996,7 +2027,7 @@
"GiftRibbonSale" = "sale";
"ButtonBuyGift" = "Buy for {stars}";
"GiftInfoBuyGift" = "{user} is selling this gift and you can buy it.";
"StarsGiftBought"= "You bought gift!";
"StarsGiftBought" = "You bought a gift!";
"ButtonSellGift" = "Sell for {stars}";
"GiftSellTitle" = "Sell Gift";
"Sell" = "Sell";
@ -2035,7 +2066,7 @@
"SendInStandardQuality" = "Send In Standard Quality";
"SendInHighQuality" = "Send In High Quality";
"MonoforumBadge" = "DIRECT";
"MonoforumStatus" = "Channel messages";
"MonoforumStatus" = "Direct messages";
"MonoforumComposerPlaceholder" = "Choose a message to reply";
"ChannelSendMessage" = "Direct Messages";
"AutomaticTranslation" = "Automatic Translation";
@ -2043,7 +2074,7 @@
"ComposerEmbeddedMessageSuggestedPostDescription" = "Tap to offer a price for publishing";
"TitleSuggestedPostAmountForAnyTime" = "{amount} for publishing anytime";
"ActionSuggestedPostOutgoing" = "**You** suggest to post this message.";
"ActionSuggestedPostIncoming" = "**{user}** suggest to post this message.";
"ActionSuggestedPostIncoming" = "**{user}** suggested to post this message.";
"ActionSuggestedChangesPrice" = "price";
"ActionSuggestedChangesText" = "text";
"ActionSuggestedChangesTime" = "time";
@ -2063,7 +2094,7 @@
"SuggestMessageTimeDescription" = "{hint} The post will remain available for at least {duration} from this date.";
"SuggestMessageAnytime" = "Anytime";
"ButtonOfferAmount" = "Offer {amount}";
"ButtonOfferFree" = "Offer Free";
"ButtonOfferFree" = "Offer for Free";
"ButtonUpdateTerms" = "Update Terms";
"InputPlaceholderPrice" = "Enter Price";
"SuggestedPostApprove" = "Approve";
@ -2079,10 +2110,10 @@
"SuggestedPostPublishedYou" = "📅 Your post was automatically published on {peer} **{date}**.";
"SuggestedPostCharged" = "💰 {user} has been charged {amount}.";
"SuggestedPostChargedYou" = "💰 You have been charged {amount}.";
"SuggestedPostReceiveAmount" = "⏳ {peer} will receive the {currency} once the post has been live for {duration}.";
"SuggestedPostReceiveAmountYou" = "⏳ {peer} will receive your {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 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.";
"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}.";
"SuggestedPostRefundedByUser" = "{channel} will not receive {amount} because {user} requested a refund.";
"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}**?";
"SuggestedPostRejected" = "**{peer}** rejected this message.";
"SuggestedPostRejectedYou" = "You rejected this message.";
"SuggestedPostRejectedWithReason" = "**{peer}** rejected this message with the comment.";
"SuggestedPostRejectedWithReasonYou" = "You rejected this message with the comment.";
"SuggestedPostRejectedWithReason" = "**{peer}** rejected this message with the comment:";
"SuggestedPostRejectedWithReasonYou" = "You rejected this message with the comment:";
"SuggestedPostRejectedComment" = "\"{comment}\"";
"ActionSuggestedPostSuccess" = "{channel} has received {amount} for publishing post";
"ComposerPlaceholderCaption" = "Caption";
@ -2101,15 +2132,15 @@
"SuggestedPostConfirmTitle" = "Accept Terms";
"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.";
"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.";
"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";
"ButtonPublishAtTime" = "Publish {time}";
"ButtonPublishAtTime" = "Post {time}";
"PublishNow" = "Publish Now";
"TitleNewToDoList" = "New Checklist";
"TitleEditToDoList" = "Edit Checklist";
"TitleAppendToDoList" = "Append Checklist";
"TitleAppendToDoList" = "Add Task";
"InputTitle" = "Title";
"TitleToDoList" = "Checklist";
"TitleTask" = "Task";
@ -2139,16 +2170,17 @@
"PremiumMore" = "More";
"SubscribeToTelegramPremiumForToggleTask" = "Subscribe to **Telegram Premium** to toggle tasks";
"SubscribeToTelegramPremiumForCreateToDo" = "Subscribe to **Telegram Premium** to create Checklists";
"SubscribeToTelegramPremiumForAppendToDo" = "Subscribe to **Telegram Premium** to append Checklists";
"HintTodoListTasksCount" = "You can add {count} more tasks";
"SubscribeToTelegramPremiumForAppendToDo" = "Subscribe to **Telegram Premium** to add tasks";
"HintTodoListTasksCount2_one" = "You can add {count} more task";
"HintTodoListTasksCount2_other" = "You can add {count} more tasks";
"ToDoListErrorChooseTitle" = "Please enter a title.";
"ToDoListErrorChooseTasks" = "Please enter at least one task.";
"GiftInfoCollectibleBy" = "Collectible #{number} by **{owner}**";
"PremiumPreviewTodo" = "Checklists";
"DescriptionAboutTon" = "Offer TON to submit post suggestions to channels on Telegram.";
"ButtonTopUpViaFragment" = "Top-Up Via Fragment";
"TonModalHint" = "You can top-up your TON using Fragment.";
"TonGiftReceived" = "TON Top-Up";
"ButtonTopUpViaFragment" = "Top Up Via Fragment";
"TonModalHint" = "You can top up your TON using Fragment.";
"TonGiftReceived" = "TON Top Up";
"MediaSpoilerSensitive" = "18+";
"TitleSensitiveModal" = "{years}+ content";
"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";
"ButtonAgeVerification" = "Verify My Age";
"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";
"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";
"PriceInTON" = "Price in TON";
"DescriptionComposerGiftMinimumCurrencyPrice" = "Minimum price is **{amount}**.";
@ -2199,7 +2233,7 @@
"PublicPostsPremiumFeatureSubtitle" = "Global search is a Premium feature.";
"PublicPostsSubscribeToPremium" = "Subscribe to Premium";
"NotificationPaidExtraSearch" = "{stars} spent on extra search.";
"PostsSearchTransaction" = "Posts Search";
"PostsSearchTransaction" = "Public Post Search";
"TitleRating" = "Rating";
"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:";
@ -2218,4 +2252,7 @@
"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}";
"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>
<p className="note">{lang('LoginEnterPasswordDescription')}</p>
<PasswordForm
clearError={clearAuthErrorKey}
onClearError={clearAuthErrorKey}
error={authErrorKey && lang.withRegular(authErrorKey)}
hint={authHint}
isLoading={authIsLoading}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,8 +19,8 @@ import renderText from '../helpers/renderText';
import useTimeout from '../../../hooks/schedulers/useTimeout';
import useFlag from '../../../hooks/useFlag';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import ListItem from '../../ui/ListItem';
import StickerView from '../StickerView';
@ -58,7 +58,7 @@ const UserBirthday = ({
const animationPlayedRef = useRef(false);
const [isPlayingAnimation, playAnimation, stopAnimation] = useFlag();
const lang = useOldLang();
const lang = useLang();
const {
formattedDate,
@ -139,7 +139,17 @@ const UserBirthday = ({
}
}, [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;
@ -175,7 +185,7 @@ const UserBirthday = ({
onSecondaryIconClick={handleOpenGiftModal}
>
<div className="title" dir={lang.isRtl ? 'rtl' : undefined}>
{renderText(lang(valueKey, [formattedDate, age], undefined, age))}
{renderText(value)}
</div>
<span className="subtitle">{lang(isToday ? 'ProfileBirthdayToday' : 'ProfileBirthday')}</span>
</ListItem>

View File

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

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@ import { formatDateTimeToString } from '../../../util/dates/dateFormat';
import getSessionIcon from './helpers/getSessionIcon';
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
import useOldLang from '../../../hooks/useOldLang';
import useLang from '../../../hooks/useLang';
import Icon from '../../common/icons/Icon';
import Button from '../../ui/Button';
@ -33,7 +33,7 @@ const SettingsActiveSession: FC<OwnProps & StateProps> = ({
isOpen, session, onClose,
}) => {
const { changeSessionSettings, terminateAuthorization } = getActions();
const lang = useOldLang();
const lang = useLang();
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}>
<Icon name="close" />
</Button>
<div className="modal-title">{lang('SessionPreview.Title')}</div>
<div className="modal-title">{lang('SessionPreviewTitle')}</div>
<Button
color="danger"
onClick={handleTerminateSessionClick}
className={buildClassName('modal-action-button', styles.headerButton)}
>
{lang('SessionPreview.TerminateSession')}
{lang('SessionPreviewTerminateSession')}
</Button>
</div>
);
@ -91,12 +91,12 @@ const SettingsActiveSession: FC<OwnProps & StateProps> = ({
)}
/>
<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)}
</div>
<dl className={styles.box}>
<dt>{lang('SessionPreview.App')}</dt>
<dt>{lang('SessionPreviewApp')}</dt>
<dd>
{renderingSession?.appName}
{' '}
@ -107,20 +107,23 @@ const SettingsActiveSession: FC<OwnProps & StateProps> = ({
{' '}
{renderingSession?.systemVersion}
</dd>
{renderingSession?.ip && (
<>
<dt>{lang('SessionPreviewIp')}</dt>
<dd>{renderingSession.ip}</dd>
</>
)}
<dt>{lang('SessionPreview.Ip')}</dt>
<dd>{renderingSession?.ip}</dd>
<dt>{lang('SessionPreview.Location')}</dt>
<dt>{lang('SessionPreviewLocation')}</dt>
<dd>{renderingSession && getLocation(renderingSession)}</dd>
</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}>
<span className={styles.actionName}>{lang('SessionPreview.Accept.Secret')}</span>
<span className={styles.actionName}>{lang('SessionPreviewAcceptSecret')}</span>
<Switcher
id="accept_secrets"
label="On"
@ -128,7 +131,7 @@ const SettingsActiveSession: FC<OwnProps & StateProps> = ({
/>
</ListItem>
<ListItem onClick={handleCallsStateChange}>
<span className={styles.actionName}>{lang('SessionPreview.Accept.Calls')}</span>
<span className={styles.actionName}>{lang('SessionPreviewAcceptCalls')}</span>
<Switcher
id="accept_calls"
label="On"

View File

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

View File

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

View File

@ -182,7 +182,7 @@ function SettingsPerformance({
</div>
<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) => {
return (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -284,8 +284,6 @@ export const CONTENT_TYPES_WITH_PREVIEW = new Set([
...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
export const SUPPORTED_TRANSLATION_LANGUAGES = [
// Official

View File

@ -17,8 +17,7 @@ import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
import { copyHtmlToClipboard } from '../../../util/clipboard';
import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { compact, findLast } from '../../../util/iteratees';
import * as langProvider from '../../../util/oldLangProvider';
import { oldTranslate } from '../../../util/oldLangProvider';
import { getTranslationFn } from '../../../util/localization';
import parseHtmlAsFormattedText from '../../../util/parseHtmlAsFormattedText';
import { getServerTime } from '../../../util/serverTime';
import versionNotification from '../../../versionNotification.txt';
@ -414,7 +413,7 @@ addActionHandler('focusMessage', (global, actions, payload): ActionReturnType =>
const chat = selectChat(global, chatId);
if (!chat) {
actions.showNotification({ message: oldTranslate('Conversation.ErrorInaccessibleMessage'), tabId });
actions.showNotification({ message: { key: 'ErrorFocusInaccessibleMessage' }, tabId });
return undefined;
}
@ -719,8 +718,9 @@ addActionHandler('toggleMessageSelection', (global, actions, payload): ActionRet
if (global.shouldShowContextMenuHint) {
actions.disableContextMenuHint();
actions.showNotification({
// eslint-disable-next-line @stylistic/max-len
message: `To **edit** or **reply**, close this menu. Then ${IS_TOUCH_ENV ? 'long tap' : 'right click'} on a message.`,
message: {
key: IS_TOUCH_ENV ? 'ContextMenuHintTouch' : 'ContextMenuHintMouse',
},
tabId,
});
}
@ -1050,7 +1050,7 @@ addActionHandler('closeSuggestedPostApprovalModal', (global, actions, payload):
function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) {
const { type: messageListType, threadId } = selectCurrentMessageList(global) || {};
const lang = langProvider.oldTranslate;
const lang = getTranslationFn();
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);
}
export function getFolderDescriptionText(lang: OldLangFn, folder: ApiChatFolder, chatsCount?: number) {
export function getFolderDescriptionText(lang: LangFn, folder: ApiChatFolder, chatsCount?: number) {
const {
excludedChatIds, includedChatIds,
bots, groups, contacts, nonContacts, channels,
@ -320,7 +320,7 @@ export function getFolderDescriptionText(lang: OldLangFn, folder: ApiChatFolder,
|| (excludedChatIds?.length)
|| (includedChatIds?.length)
)) {
return lang('Chats', chatsCount);
return lang('ChatsPlural', { count: chatsCount }, { pluralValue: chatsCount });
}
// 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 {
ApiMediaExtendedPreview, ApiMessage, MediaContent, StatefulMediaContent,
} from '../../api/types';
import type { OldLangFn } from '../../hooks/useOldLang';
import { ApiMessageEntityTypes } from '../../api/types';
import { CONTENT_NOT_SUPPORTED } from '../../config';
import { type LangFn } from '../../util/localization';
import trimText from '../../util/trimText';
import { renderTextWithEntities } from '../../components/common/helpers/renderTextWithEntities';
import {
getMessageText, getMessageTranscription,
getMessageTextWithFallback, getMessageTranscription,
} from './messages';
const SPOILER_CHARS = ['⠺', '⠵', '⠞', '⠟'];
export const TRUNCATED_SUMMARY_LENGTH = 80;
export function getMessageSummaryText(
lang: OldLangFn,
lang: LangFn,
message: ApiMessage,
statefulContent: StatefulMediaContent | undefined,
noEmoji = false,
@ -26,16 +25,20 @@ export function getMessageSummaryText(
) {
const emoji = !noEmoji && getMessageSummaryEmoji(message);
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;
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 textWithoutTranscription = getMessageText(statefulContent?.story || message)?.text;
const textWithoutTranscription = getMessageTextWithFallback(lang, statefulContent?.story || message)?.text;
if (!textWithoutTranscription) {
return transcription;
}
@ -56,7 +59,7 @@ export function getMessageTextWithSpoilers(message: ApiMessage, statefulContent:
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);
return transcription ? `${transcription}\n${text}` : text;
@ -111,12 +114,12 @@ export function getMessageSummaryEmoji(message: ApiMessage) {
}
export function getMediaContentTypeDescription(
lang: OldLangFn, content: MediaContent, statefulContent: StatefulMediaContent | undefined,
lang: LangFn, content: MediaContent, statefulContent: StatefulMediaContent | undefined,
) {
return getSummaryDescription(lang, content, statefulContent);
}
export function getMessageSummaryDescription(
lang: OldLangFn,
lang: LangFn,
message: ApiMessage,
statefulContent: StatefulMediaContent | undefined,
truncatedText?: string | TeactNode,
@ -125,7 +128,7 @@ export function getMessageSummaryDescription(
return getSummaryDescription(lang, message.content, statefulContent, message, truncatedText, isExtended);
}
function getSummaryDescription(
lang: OldLangFn,
lang: LangFn,
mediaContent: MediaContent,
statefulContent: StatefulMediaContent | undefined,
message?: ApiMessage,
@ -166,7 +169,7 @@ function getSummaryDescription(
if (message?.groupedId || isPaidMediaAlbum) {
hasUsedTruncatedText = true;
summary = truncatedText || lang('lng_in_dlg_album');
summary = truncatedText || lang('Album');
}
if (photo || isPaidMediaSinglePhoto) {
@ -210,7 +213,7 @@ function getSummaryDescription(
}
if (invoice) {
summary = invoice.extendedMedia ? invoice.title : `${lang('PaymentInvoice')}: ${invoice.description}`;
summary = invoice.extendedMedia ? invoice.title : lang('AttachInvoice', { description: invoice.description });
}
if (text) {
@ -222,11 +225,11 @@ function getSummaryDescription(
}
if (location?.mediaType === 'geo' || location?.mediaType === 'venue') {
summary = lang('Message.Location');
summary = lang('AttachLocation');
}
if (location?.mediaType === 'geoLive') {
summary = lang('Message.LiveLocation');
summary = lang('AttachLiveLocation');
}
if (game) {
@ -234,22 +237,22 @@ function getSummaryDescription(
}
if (giveaway) {
summary = lang('BoostingGiveawayChannelStarted');
summary = lang('AttachGiveaway');
}
if (giveawayResults) {
summary = lang('Message.GiveawayEndedWinners', giveawayResults.winnersCount);
summary = lang('AttachGiveawayResults');
}
if (storyData) {
summary = truncatedText || (message ? lang('ForwardedStory') : lang('Chat.ReplyStory'));
summary = truncatedText || lang('AttachStory');
}
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) {

View File

@ -17,7 +17,6 @@ import type { GlobalState } from '../types';
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../../api/types';
import {
CONTENT_NOT_SUPPORTED,
LOTTIE_STICKER_MIME_TYPE,
RE_LINK_TEMPLATE,
SERVICE_NOTIFICATIONS_USER_ID,
@ -95,7 +94,11 @@ export function groupStatefulContent({
}
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 {

View File

@ -1,13 +1,13 @@
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 { getGlobal } from '..';
import { getMessageStatefulContent } from './messages';
import { getMessageSummaryDescription, getMessageSummaryEmoji } from './messageSummary';
export function renderMessageSummaryHtml(
lang: OldLangFn,
lang: LangFn,
message: ApiMessage,
) {
const global = getGlobal();

View File

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