From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: alina sireneva <alina@tei.su>
Date: Fri, 8 May 2026 13:52:58 +0200
Subject: translator-related features

---
 .../org/telegram/messenger/MessageObject.java |  13 +-
 .../messenger/TranslateController.java        |   7 +-
 .../java/org/telegram/ui/ArticleViewer.java   |  17 +-
 .../telegram/ui/Cells/ChatMessageCell.java    |  41 +++-
 .../ui/Cells/TextSelectionHelper.java         |   1 +
 .../java/org/telegram/ui/ChatActivity.java    | 190 ++----------------
 .../org/telegram/ui/web/WebActionBar.java     |   3 +
 7 files changed, 82 insertions(+), 190 deletions(-)

diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java
@@ -3669,7 +3669,7 @@ public class MessageObject {
     public boolean updateTranslation(boolean force) {
         boolean replyUpdated = replyMessageObject != null && replyMessageObject != this && replyMessageObject.updateTranslation(force);
         TranslateController translateController = MessagesController.getInstance(currentAccount).getTranslateController();
-        final TLRPC.TL_textWithEntities translatedText = messageOwner != null ? (messageOwner.voiceTranscriptionOpen ? messageOwner.translatedVoiceTranscription : messageOwner.translatedText) : null;
+        final TLRPC.TL_textWithEntities translatedText = messageOwner != null ? (messageOwner.voiceTranscriptionOpen ? messageOwner.translatedVoiceTranscription : desu.inugram.helpers.translate.TranslateHelper.viewTranslatedText(this, messageOwner.translatedText)) : null;
         final TLRPC.TL_textWithEntities summarizedText = messageOwner != null && messageOwner.summarizedOpen ? messageOwner.summaryText : null;
         final TLRPC.TL_textWithEntities summarizeTranslatedText = messageOwner != null && messageOwner.summarizedOpen ? messageOwner.translatedSummaryText : null;
         if (
@@ -3705,6 +3705,7 @@ public class MessageObject {
             generateCaption();
             return replyUpdated || true;
         } else if (
+            desu.inugram.helpers.translate.TranslateHelper.isManualTranslated(this) && messageOwner != null && (translatedText != null || messageOwner.translatedPoll != null) ||
             messageOwner != null &&
             TranslateController.isTranslatable(this) &&
             translateController.isTranslatingDialog(getDialogId()) &&
@@ -7438,7 +7439,7 @@ public class MessageObject {
         }
         if (linkDescription == null) {
             if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaWebPage && getMedia(messageOwner).webpage instanceof TLRPC.TL_webPage && getMedia(messageOwner).webpage.description != null) {
-                linkDescription = Spannable.Factory.getInstance().newSpannable(getMedia(messageOwner).webpage.description);
+                linkDescription = Spannable.Factory.getInstance().newSpannable(desu.inugram.helpers.translate.TranslateHelper.viewWebPage(this, (TLRPC.TL_webPage) getMedia(messageOwner).webpage).description);
                 String siteName = getMedia(messageOwner).webpage.site_name;
                 if (siteName != null) {
                     siteName = siteName.toLowerCase();
@@ -7590,8 +7591,9 @@ public class MessageObject {
         } else if (messageOwner.translatedText != null && translated) {
             captionSummarized = false;
             captionTranslated = true;
-            text = messageOwner.translatedText.text;
-            entities = messageOwner.translatedText.entities;
+            TLRPC.TL_textWithEntities inu_tt = desu.inugram.helpers.translate.TranslateHelper.viewTranslatedText(this, messageOwner.translatedText);
+            text = inu_tt.text;
+            entities = inu_tt.entities;
         } else {
             captionSummarized = false;
             captionTranslated = false;
@@ -7939,7 +7941,8 @@ public class MessageObject {
             if (messageOwner.voiceTranscriptionOpen) {
                 return messageOwner.translatedVoiceTranscription != null ? messageOwner.translatedVoiceTranscription.entities : null;
             } else {
-                return messageOwner.translatedText != null ? messageOwner.translatedText.entities : null;
+                TLRPC.TL_textWithEntities inu_tt = desu.inugram.helpers.translate.TranslateHelper.viewTranslatedText(this, messageOwner.translatedText);
+                return inu_tt != null ? inu_tt.entities : null;
             }
         }
         return messageOwner.entities;
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java b/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java
@@ -38,6 +38,7 @@ import org.telegram.ui.Components.TranslateAlert2;
 import org.telegram.ui.LaunchActivity;
 import org.telegram.ui.PremiumPreviewFragment;
 import org.telegram.ui.RestrictedLanguagesSelectActivity;
+import desu.inugram.helpers.translate.TranslateHelper;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -1021,7 +1022,7 @@ public class TranslateController extends BaseController {
         int reqId = -1;
     }
 
-    private void pushToTranslate(
+    public void pushToTranslate(
         MessageObject message,
         String language,
         Utilities.Callback4<Boolean, Integer, TLRPC.TL_textWithEntities, String> callback
@@ -1606,7 +1607,7 @@ public class TranslateController extends BaseController {
                 (messageObject.messageOwner.voiceTranscriptionOpen && messageObject.messageOwner.voiceTranscriptionFinal ?
                     loadingTranscriptionTranslations : loadingTranslations
                 ).contains(messageObject.getId()) &&
-                isTranslatingDialog(messageObject.getDialogId())
+                (isTranslatingDialog(messageObject.getDialogId()) || TranslateHelper.isManualTranslated(messageObject))
             );
         }
     }
@@ -1615,7 +1616,7 @@ public class TranslateController extends BaseController {
         if (messageObject == null) {
             return false;
         }
-        if (!isTranslatingDialog(messageObject.getDialogId())) {
+        if (!isTranslatingDialog(messageObject.getDialogId()) && !TranslateHelper.isManualTranslated(messageObject, group)) {
             return false;
         }
         final boolean isTranscription = messageObject.messageOwner != null && messageObject.messageOwner.voiceTranscriptionOpen && messageObject.messageOwner.voiceTranscriptionFinal;
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java
@@ -302,7 +302,7 @@ public class ArticleViewer extends IArticleViewer implements NotificationCenter.
     private WindowManager.LayoutParams windowLayoutParams;
     private WindowView windowView;
     private FrameLayout containerView;
-    private WebActionBar actionBar;
+    public WebActionBar actionBar;
     private AddressBarList addressBarList;
 //    private FrameLayout headerView;
 //    private SimpleTextView titleTextView;
@@ -1975,6 +1975,7 @@ public class ArticleViewer extends IArticleViewer implements NotificationCenter.
             pages[index].setType(PageLayout.TYPE_ARTICLE);
             adapter.isRtl = webPage.cached_page.rtl;
             adapter.currentPage = webPage;
+            desu.inugram.helpers.translate.InstantViewHelper.onPageOpened(this, webPage);
 
             int numBlocks = 0;
             int count = webPage.cached_page.blocks.size();
@@ -2890,6 +2891,10 @@ public class ArticleViewer extends IArticleViewer implements NotificationCenter.
         if (richText == null) {
             return null;
         }
+        if (richText == parentRichText) {
+            CharSequence inu_cached = desu.inugram.helpers.translate.InstantViewHelper.lookupRich(parent instanceof ArticleViewer ? (ArticleViewer) parent : null, richText);
+            if (inu_cached != null) return inu_cached;
+        }
         if (richText instanceof TL_iv.textFixed) {
             return getText(parent, page, parentView, parentRichText, ((TL_iv.textFixed) richText).text, parentBlock, maxWidth);
         } else if (richText instanceof TL_iv.textItalic) {
@@ -3455,7 +3460,7 @@ public class ArticleViewer extends IArticleViewer implements NotificationCenter.
 
         CharSequence text;
         if (plainText != null) {
-            text = plainText;
+            text = desu.inugram.helpers.translate.InstantViewHelper.translate(parent instanceof ArticleViewer ? (ArticleViewer) parent : null, plainText);
         } else {
             text = getText(parent, parentAdapter, parentView, richText, richText, parentBlock, width);
         }
@@ -4340,6 +4345,10 @@ public class ArticleViewer extends IArticleViewer implements NotificationCenter.
         headerProgressPaint.setColor(0xff242426);
         navigationBarPaint.setColor(Color.BLACK);
         actionBar = new WebActionBar(activity, getResourcesProvider()) {
+            @Override
+            protected ArticleViewer inu_getOwnerArticleViewer() {
+                return ArticleViewer.this;
+            }
             @Override
             protected void onOpenedMenu() {
                 pages[0].listView.stopScroll();
@@ -4786,6 +4795,8 @@ public class ArticleViewer extends IArticleViewer implements NotificationCenter.
             if (id == WebActionBar.search_item) {
                 actionBar.setHeight(currentHeaderHeight = dp(56));
                 actionBar.showSearch(true, true);
+            } else if (id == desu.inugram.helpers.translate.InstantViewHelper.MENU_ITEM_ID) {
+                desu.inugram.helpers.translate.InstantViewHelper.onClick(this);
             } else if (id == WebActionBar.share_item) {
                 String url;
                 if (pages[0].isWeb()) {
@@ -6418,7 +6429,7 @@ public class ArticleViewer extends IArticleViewer implements NotificationCenter.
         private ArrayList<Object> textBlocks = new ArrayList<>();
         private HashMap<String, Integer> searchTextOffset = new HashMap<>();
 
-        private TLRPC.WebPage currentPage;
+        public TLRPC.WebPage currentPage;
         private TL_iv.pageBlockChannel channelBlock;
         private boolean isRtl;
         private final boolean padding;
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java
@@ -1050,6 +1050,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
     public int textX;
     private int unmovedTextX;
     private int linkPreviewY;
+    private float inu_capturedPhotoY = Float.NaN;
+    private org.telegram.ui.Components.AnimatedFloat inu_photoYReflowAF;
     private int factCheckY;
     public int textY;
     public int totalHeight;
@@ -1803,6 +1805,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
     private int lastDeleteDate;
     private int lastViewsCount;
     private int lastRepliesCount;
+    private int lastTimeAdditionsHash;
     private float selectedBackgroundProgress;
     private boolean lastTranslated;
 
@@ -6760,7 +6763,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
                 lastPostAuthor != messageObject.messageOwner.post_author ||
                 wasPinned != isPinned ||
                 newReply != lastReplyMessage ||
-                messageObject.translated != lastTranslated;
+                messageObject.translated != lastTranslated ||
+                desu.inugram.helpers.chat.ChatHelper.timeAdditionsHash(messageObject) != lastTimeAdditionsHash;
         boolean groupChanged = groupedMessages != currentMessagesGroup;
         boolean pollChanged = false;
 
@@ -6902,6 +6906,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
             linkPreviewY = 0;
             factCheckY = 0;
             lastTranslated = messageObject.translated;
+            lastTimeAdditionsHash = desu.inugram.helpers.chat.ChatHelper.timeAdditionsHash(messageObject);
             lastSendState = messageObject.messageOwner.send_state;
             lastDeleteDate = messageObject.messageOwner.destroyTime;
             lastViewsCount = messageObject.messageOwner.views;
@@ -7868,7 +7873,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
                         isSmallImage = false;
                         linkPreviewAbove = currentMessageObject.messageOwner != null && currentMessageObject.messageOwner.invert_media;
                     } else if (hasLinkPreview) {
-                        TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) webpage;
+                        TLRPC.TL_webPage webPage = desu.inugram.helpers.translate.TranslateHelper.viewWebPage(messageObject, (TLRPC.TL_webPage) webpage);
                         site_name = webPage.site_name;
                         title = drawInstantViewType != 6 && drawInstantViewType != 7 ? webPage.title : null;
                         if (instantViewTypeIsGiftAuction != null) {
@@ -12421,6 +12426,27 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
         }
     }
 
+    public void inu_capturePhotoYBeforeReflow() {
+        if (drawPhotoImage && (hasLinkPreview || hasGamePreview || hasInvoicePreview) && photoImage != null) {
+            inu_capturedPhotoY = photoImage.getImageY();
+        }
+    }
+
+    private float inu_applyPhotoYReflow(float newY) {
+        if (!Float.isNaN(inu_capturedPhotoY)) {
+            if (inu_photoYReflowAF == null) {
+                inu_photoYReflowAF = new org.telegram.ui.Components.AnimatedFloat(0, this, 0, androidx.recyclerview.widget.ChatListItemAnimator.DEFAULT_DURATION, androidx.recyclerview.widget.ChatListItemAnimator.DEFAULT_INTERPOLATOR);
+            }
+            float delta = inu_capturedPhotoY - newY;
+            inu_capturedPhotoY = Float.NaN;
+            if (Math.abs(delta) > 0.5f) {
+                inu_photoYReflowAF.force(delta);
+            }
+        }
+        if (inu_photoYReflowAF == null) return newY;
+        return newY + inu_photoYReflowAF.set(0);
+    }
+
     @Override
     public void invalidate() {
         if (currentMessageObject == null) {
@@ -15503,7 +15529,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
             restore = true;
 
             AndroidUtilities.rectTmp.set(linkX, linkPreviewY - dp(6), linkX + width, linkPreviewY + linkPreviewHeight + (drawInstantButtonInside && drawInstantView ? dp(42) : 0));
-            linkLine.setLoading(loading);
+            linkLine.setLoading(loading || desu.inugram.helpers.translate.TranslateHelper.isWebPageTranslating(currentMessageObject));
             float rad = (float) Math.floor(SharedConfig.bubbleRadius / (currentMessageObject.isSponsored() ? 2f : 3f));
             linkLine
                 .offsetEmoji(0, drawPhotoImageBefore ? (1f - isSmallImage()) * (dp(18) + photoImage.getImageHeight() + (siteNameLayout != null ? siteNameLayout.getLineBottom(siteNameLayout.getLineCount() - 1) : 0)) : 0)
@@ -15878,7 +15904,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
                 photoImage.setImageY(smallImageStartY);
             } else {
                 photoImage.setImageX(linkX + (hasInvoicePreview ? -dp(6.3f) : dp(10)));
-                photoImage.setImageY(linkPreviewY);
+                photoImage.setImageY(inu_applyPhotoYReflow(linkPreviewY));
             }
             if (transitionParams != null && transitionParams.animateSmallImage) {
                 if (!isSmallImage && documentAttachType != DOCUMENT_ATTACH_TYPE_DOCUMENT) {
@@ -16750,7 +16776,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
 
         final TranslateController translateController = MessagesController.getInstance(currentAccount).getTranslateController();
         final boolean translating = translateController.isTranslating(currentMessageObject);
-        final boolean shouldTranslate = currentMessageObject != null && TranslateController.isTranslatable(currentMessageObject) && translateController.isTranslatingDialog(currentMessageObject.getDialogId());
+        final boolean shouldTranslate = currentMessageObject != null && (TranslateController.isTranslatable(currentMessageObject) && translateController.isTranslatingDialog(currentMessageObject.getDialogId()) || desu.inugram.helpers.translate.TranslateHelper.isManualTranslated(currentMessageObject, currentMessagesGroup));
         final boolean shouldSummarize = currentMessageObject != null && currentMessageObject.messageOwner.summarizedOpen;
         final boolean isFinal = shouldTranslate == (currentMessageObject != null && currentMessageObject.translated) && shouldSummarize == (currentMessageObject != null && currentMessageObject.summarized);
         if (origin == !isFinal) {
@@ -18429,7 +18455,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
         } else if (edited) {
             timeString = AppGlobalConfig.getInstance(currentAccount).messagePrimaryEditedDate.get() ?
                 LocaleController.formatPmEditedDate(currentMessagesGroup != null ? currentMessagesGroup.getMaxEditDate() : messageObject.messageOwner.edit_date) :
-                (getString(R.string.EditedMessage) + " " + LocaleController.getInstance().getFormatterDay().format((long) (messageObject.messageOwner.date) * 1000));
+                ((desu.inugram.InuConfig.COMPACT_EDITED.getValue() ? "" : getString(R.string.EditedMessage) + " ") + LocaleController.getInstance().getFormatterDay().format((long) (messageObject.messageOwner.date) * 1000));
         } else if (currentMessageObject.isSaved && currentMessageObject.messageOwner.fwd_from != null && (currentMessageObject.messageOwner.fwd_from.date != 0 || currentMessageObject.messageOwner.fwd_from.saved_date != 0)) {
             int date = currentMessageObject.messageOwner.fwd_from.saved_date;
             if (date == 0) {
@@ -18482,7 +18508,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
                 currentTimeString = TextUtils.concat(formatString(R.string.MessageScheduledRepeatSeconds, period), ", ", currentTimeString);
             }
         }
-        timeTextWidth = timeWidth = (int) Math.ceil(Theme.chat_timePaint.measureText(currentTimeString, 0, currentTimeString == null ? 0 : currentTimeString.length()));
+        currentTimeString = desu.inugram.helpers.chat.ChatHelper.timePrefix(currentMessageObject, currentTimeString, edited);
+        timeTextWidth = timeWidth = (int) Math.ceil(Theme.chat_timePaint.measureText(currentTimeString, 0, currentTimeString == null ? 0 : currentTimeString.length())) + desu.inugram.helpers.chat.ChatHelper.extraTimeWidth(currentMessageObject, edited);
         if (currentMessageObject.scheduled && currentMessageObject.messageOwner.date == 0x7FFFFFFE || currentMessageObject.notime) {
             timeWidth -= dp(8);
         }
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java
@@ -315,6 +315,7 @@ public abstract class TextSelectionHelper<Cell extends TextSelectionHelper.Selec
         touchSlop = ViewConfiguration.get(ApplicationLoader.applicationContext).getScaledTouchSlop();
         selectionPaint.setPathEffect(new CornerPathEffect(cornerRadius = dp(6)));
         selectionPath.setRectsUnionDiffDelta(1f);
+        desu.inugram.helpers.translate.TranslateHelper.installSelectionTranslate(this);
     }
 
     public void setInvalidateParent() {
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java
@@ -11228,7 +11228,7 @@ public class ChatActivity extends BaseFragment implements
         if (translateItem == null) {
             return;
         }
-        translateItem.setVisibility(getMessagesController().getTranslateController().isTranslateDialogHidden(getDialogId()) && getMessagesController().getTranslateController().isDialogTranslatable(getDialogId()) ? View.VISIBLE : View.GONE);
+        translateItem.setVisibility(getMessagesController().getTranslateController().isTranslateDialogHidden(getDialogId()) /* && getMessagesController().getTranslateController().isDialogTranslatable(getDialogId()) */ ? View.VISIBLE : View.GONE);
     }
 
     private Animator infoTopViewAnimator;
@@ -24718,6 +24718,10 @@ public class ChatActivity extends BaseFragment implements
     }
 
     private void saveGeneralScroll() {
+        if (chatListView != null && chatLayoutManager != null && forwardEndReached[0] && !chatListView.canScrollVertically(1)) {
+            chatLayoutManager.scrollToPositionWithOffset(0, 0);
+            return;
+        }
         int scrollByCellHeight = 0;
         ChatMessageCell scrollByCell = null;
         for (int i = 0; i < chatListView.getChildCount(); ++i) {
@@ -24772,16 +24776,19 @@ public class ChatActivity extends BaseFragment implements
                 boolean update = false;
                 if (cellMessageObject.getId() == messageObject.getId()) {
                     cellMessageObject.messageOwner.translatedText = messageObject.messageOwner.translatedText;
+                    cellMessageObject.messageOwner.translatedVoiceTranscription = messageObject.messageOwner.translatedVoiceTranscription;
+                    cellMessageObject.messageOwner.translatedPoll = messageObject.messageOwner.translatedPoll;
                     cellMessageObject.messageOwner.translatedToLanguage = messageObject.messageOwner.translatedToLanguage;
+                    cellMessageObject.messageOwner.originalLanguage = messageObject.messageOwner.originalLanguage;
                     cellMessageObject.messageOwner.summaryText = messageObject.messageOwner.summaryText;
                     cellMessageObject.messageOwner.translatedSummaryText = messageObject.messageOwner.translatedSummaryText;
                     cellMessageObject.messageOwner.translatedSummaryLanguage = messageObject.messageOwner.translatedSummaryLanguage;
-                    if (cellMessageObject.updateTranslation(false)) {
-                        update = true;
-                        ArrayList<Integer> dependentMessages = replyMessageOwners.get(cellMessageObject.getId());
-                        if (dependentMessages != null) {
-                            updateMessagesReplyTranslation(dependentMessages, messageObject);
-                        }
+                    cellMessageObject.linkDescription = null;
+                    cellMessageObject.updateTranslation(false);
+                    update = true;
+                    ArrayList<Integer> dependentMessages = replyMessageOwners.get(cellMessageObject.getId());
+                    if (dependentMessages != null) {
+                        updateMessagesReplyTranslation(dependentMessages, messageObject);
                     }
                 }
                 MessageObject.GroupedMessages group = groupedMessagesMap.get(messageObject.getGroupId());
@@ -24810,6 +24817,7 @@ public class ChatActivity extends BaseFragment implements
                         saveGeneralScroll();
                     }
                     cellMessageObject.forceUpdate = true;
+                    cell.inu_capturePhotoYBeforeReflow();
                     cell.setMessageObject(cellMessageObject, cell.getCurrentMessagesGroup(), cell.isPinnedBottom(), cell.isPinnedTop(), cell.isFirstInChat(), cell.isLastInChatList());
                     if (group != null) {
                         if (chatListItemAnimator != null) {
@@ -31888,169 +31896,7 @@ public class ChatActivity extends BaseFragment implements
                     });
                     cell.setOnLongClickListener(v1 -> ChatHelper.onMenuOptionLongClick(this, popupLayout, cell, options, i, selectedObject, selectedObjectGroup));
                     if (option == OPTION_TRANSLATE) {
-                        final boolean translateEnabled = getMessagesController().getTranslateController().isContextTranslateEnabled();
-                        String toLangDefault = LocaleController.getInstance().getCurrentLocale().getLanguage();
-                        String toLang = TranslateAlert2.getToLanguage();
-                        int[] messageIdToTranslate = new int[] { message.getId() };
-                        final CharSequence finalMessageText = message.getMessageTextToTranslate(groupedMessages, messageIdToTranslate);
-                        Utilities.CallbackReturn<URLSpan, Boolean> onLinkPress = (link) -> {
-                            didPressMessageUrl(link, false, selectedObject, v instanceof ChatMessageCell ? (ChatMessageCell) v : null);
-                            return true;
-                        };
-                        TLRPC.InputPeer inputPeer = selectedObject != null && (selectedObject.isPoll() || selectedObject.isVoiceTranscriptionOpen() || selectedObject.isSponsored() || selectedObject.scheduled || chatMode == MODE_QUICK_REPLIES) ? null : getMessagesController().getInputPeer(dialog_id);
-//                        final boolean shouldTranslateByText = selectedObject != null && (selectedObject.isPoll() || selectedObject.isVoiceTranscriptionOpen() || selectedObject.isSponsored() || selectedObject.scheduled || chatMode == MODE_QUICK_REPLIES);
-                        final TL_iv.RichMessage richMessageToTranslate = selectedObject != null && selectedObject.type == MessageObject.TYPE_ARTICLE && selectedObject.messageOwner != null ? selectedObject.messageOwner.rich_message : null;
-                        if (richMessageToTranslate != null) {
-                            final String fromLang = selectedObject.messageOwner.originalLanguage;
-                            cell.setVisibility(View.VISIBLE);
-                            cell.setOnClickListener(e -> {
-                                if (selectedObject == null || i >= options.size() || getParentActivity() == null) {
-                                    return;
-                                }
-                                String toLangValue = fromLang != null && fromLang.equals(toLang) ? toLangDefault : toLang;
-                                TranslateAlert2 alert = TranslateAlert2.showAlert(getParentActivity(), this, currentAccount, inputPeer, messageIdToTranslate[0], fromLang, toLangValue, richMessageToTranslate, noforwardsOrPaidMedia, onLinkPress, () -> dimBehindView(false));
-                                alert.setDimBehind(false);
-                                closeMenu(false);
-
-                                int hintCount = MessagesController.getNotificationsSettings(currentAccount).getInt("dialog_show_translate_count" + getDialogId(), 5);
-                                if (hintCount > 0) {
-                                    hintCount--;
-                                    MessagesController.getNotificationsSettings(currentAccount).edit().putInt("dialog_show_translate_count" + getDialogId(), hintCount).apply();
-                                    updateTopPanel(true);
-                                }
-                            });
-                        } else if (selectedObject != null && selectedObject.messageOwner != null && selectedObject.messageOwner.originalLanguage != null) {
-                            waitForLangDetection.set(false);
-                            String fromLang = selectedObject.messageOwner.originalLanguage;
-                            cell.setVisibility(
-                                fromLang != null && (!fromLang.equals(toLang) || !fromLang.equals(toLangDefault) || fromLang.equals(TranslateController.UNKNOWN_LANGUAGE)) && (
-                                    translateEnabled && !RestrictedLanguagesSelectActivity.getRestrictedLanguages().contains(fromLang) ||
-                                    (currentChat != null && (currentChat.has_link || ChatObject.isPublic(currentChat)) || selectedObject.messageOwner.fwd_from != null) && ("uk".equals(fromLang) || "ru".equals(fromLang))
-                                ) ? View.VISIBLE : View.GONE
-                            );
-                            cell.setOnClickListener(e -> {
-                                if (selectedObject == null || i >= options.size() || getParentActivity() == null) {
-                                    return;
-                                }
-
-                                String toLangValue = fromLang != null && fromLang.equals(toLang) ? toLangDefault : toLang;
-                                ArrayList<TLRPC.MessageEntity> entities = selectedObject != null && selectedObject.messageOwner != null ? selectedObject.messageOwner.entities : null;
-                                TranslateAlert2 alert = TranslateAlert2.showAlert(getParentActivity(), this, currentAccount, inputPeer, messageIdToTranslate[0], selectedObject.summarized, fromLang, toLangValue, finalMessageText, entities, noforwardsOrPaidMedia, onLinkPress, () -> dimBehindView(false));
-                                alert.setDimBehind(false);
-                                closeMenu(false);
-                                
-//                                final TranslateAlert3 alert =
-//                                    new TranslateAlert3(getContext(), resourceProvider)
-//                                        .setText(fromLang, finalMessageText)
-//                                        .setMessage(dialog_id, shouldTranslateByText ? 0 : messageIdToTranslate[0], selectedObject.summarized)
-//                                        .setToLanguage(fromLang != null && fromLang.equals(toLang) ? toLangDefault : toLang)
-//                                        .setNoforwards(noforwardsOrPaidMedia)
-//                                        .setOnLinkPress(onLinkPress);
-//                                alert.setOnDismissListener(() -> dimBehindView(false));
-//                                alert.setDimBehind(false);
-//                                alert.show();
-//                                closeMenu(false);
-
-                                int hintCount = MessagesController.getNotificationsSettings(currentAccount).getInt("dialog_show_translate_count" + getDialogId(), 5);
-                                if (hintCount > 0) {
-                                    hintCount--;
-                                    MessagesController.getNotificationsSettings(currentAccount).edit().putInt("dialog_show_translate_count" + getDialogId(), hintCount).apply();
-                                    updateTopPanel(true);
-                                }
-                            });
-                        } else if (LanguageDetector.hasSupport()) {
-                            final String[] fromLang = {null};
-                            cell.setVisibility(View.GONE);
-                            waitForLangDetection.set(true);
-                            LanguageDetector.detectLanguage(
-                                finalMessageText.toString(),
-                                (String lang) -> {
-                                    fromLang[0] = lang;
-                                    if (fromLang[0] != null && (!fromLang[0].equals(toLang) || !fromLang[0].equals(toLangDefault) || fromLang[0].equals(TranslateController.UNKNOWN_LANGUAGE)) && (
-                                        translateEnabled && !RestrictedLanguagesSelectActivity.getRestrictedLanguages().contains(fromLang[0]) ||
-                                        (currentChat != null && (currentChat.has_link || ChatObject.isPublic(currentChat)) || selectedObject.messageOwner.fwd_from != null) && ("uk".equals(fromLang[0]) || "ru".equals(fromLang[0]))
-                                    )) {
-                                        cell.setVisibility(View.VISIBLE);
-                                    }
-                                    waitForLangDetection.set(false);
-                                    if (onLangDetectionDone.get() != null) {
-                                        onLangDetectionDone.get().run();
-                                        onLangDetectionDone.set(null);
-                                    }
-                                },
-                                (Exception e) -> {
-                                    FileLog.e("mlkit: failed to detect language in message");
-                                    waitForLangDetection.set(false);
-                                    if (onLangDetectionDone.get() != null) {
-                                        onLangDetectionDone.get().run();
-                                        onLangDetectionDone.set(null);
-                                    }
-                                }
-                            );
-                            cell.setOnClickListener(e -> {
-                                if (selectedObject == null || i >= options.size() || getParentActivity() == null) {
-                                    return;
-                                }
-                                String toLangValue = fromLang[0] != null && fromLang[0].equals(toLang) ? toLangDefault : toLang;
-                                ArrayList<TLRPC.MessageEntity> entities = selectedObject != null && selectedObject.messageOwner != null ? selectedObject.messageOwner.entities : null;
-                                TranslateAlert2 alert = TranslateAlert2.showAlert(getParentActivity(), this, currentAccount, inputPeer, messageIdToTranslate[0], selectedObject.summarized, fromLang[0], toLangValue, finalMessageText, entities, noforwardsOrPaidMedia, onLinkPress, () -> dimBehindView(false));
-                                alert.setDimBehind(false);
-                                closeMenu(false);
-
-//                                final TranslateAlert3 alert =
-//                                    new TranslateAlert3(getContext(), resourceProvider)
-//                                        .setText(fromLang[0], finalMessageText)
-//                                        .setMessage(dialog_id, shouldTranslateByText ? 0 : messageIdToTranslate[0], selectedObject.summarized)
-//                                        .setToLanguage(fromLang[0] != null && fromLang[0].equals(toLang) ? toLangDefault : toLang)
-//                                        .setNoforwards(noforwardsOrPaidMedia)
-//                                        .setOnLinkPress(onLinkPress);
-//                                alert.setOnDismissListener(() -> dimBehindView(false));
-//                                alert.setDimBehind(false);
-//                                alert.show();
-//                                closeMenu(false);
-
-                                int hintCount = MessagesController.getNotificationsSettings(currentAccount).getInt("dialog_show_translate_count" + getDialogId(), 5);
-                                if (hintCount > 0) {
-                                    hintCount--;
-                                    MessagesController.getNotificationsSettings(currentAccount).edit().putInt("dialog_show_translate_count" + getDialogId(), hintCount).apply();
-                                    updateTopPanel(true);
-                                }
-                            });
-                            cell.postDelayed(() -> {
-                                if (onLangDetectionDone.get() != null) {
-                                    onLangDetectionDone.getAndSet(null).run();
-                                }
-                            }, 250);
-                        } else if (translateEnabled) {
-                            cell.setOnClickListener(e -> {
-                                if (selectedObject == null || i >= options.size() || getParentActivity() == null) {
-                                    return;
-                                }
-
-                                TranslateAlert2 alert = TranslateAlert2.showAlert(getParentActivity(), this, currentAccount, inputPeer, messageIdToTranslate[0], selectedObject.summarized, "und", toLang, finalMessageText, null, noforwardsOrPaidMedia, onLinkPress, () -> dimBehindView(false));
-                                alert.setDimBehind(false);
-                                closeMenu(false);
-
-//                                final TranslateAlert3 alert = new TranslateAlert3(getContext(), getResourceProvider())
-//                                    .setMessage(dialog_id, shouldTranslateByText ? 0 : messageIdToTranslate[0], selectedObject.summarized)
-//                                    .setText(finalMessageText)
-//                                    .setNoforwards(noforwardsOrPaidMedia)
-//                                    .setOnLinkPress(onLinkPress);
-//                                alert.setOnDismissListener(() ->  dimBehindView(false));
-//                                alert.setDimBehind(false);
-//                                alert.show();
-//                                closeMenu(false);
-
-                                int hintCount = MessagesController.getNotificationsSettings(currentAccount).getInt("dialog_show_translate_count" + getDialogId(), 5);
-                                if (hintCount > 0) {
-                                    hintCount--;
-                                    MessagesController.getNotificationsSettings(currentAccount).edit().putInt("dialog_show_translate_count" + getDialogId(), hintCount).apply();
-                                    updateTopPanel(true);
-                                }
-                            });
-                        } else {
-                            cell.setVisibility(View.GONE);
-                        }
+                        desu.inugram.helpers.translate.TranslateHelper.setupTranslateMenuCell(this, cell, selectedObject, selectedObjectGroup, waitForLangDetection, onLangDetectionDone);
                     }
                 }
                 if (selectedObject != null && selectedObject.messageOwner != null && selectedObject.messageOwner.video_processing_pending) {
@@ -45769,7 +45615,7 @@ public class ChatActivity extends BaseFragment implements
                     options.add(OPTION_PIN);
                     icons.add(R.drawable.msg_pin);
                 }
-                if (selectedObject != null && selectedObject.contentType == 0 && ((!TextUtils.isEmpty(selectedObject.getMessageTextToTranslate(groupedMessages, null)) && !selectedObject.isAnimatedEmoji() && !selectedObject.isDice()) || (selectedObject.type == MessageObject.TYPE_ARTICLE && selectedObject.messageOwner != null && selectedObject.messageOwner.rich_message != null && !selectedObject.translated))) {
+                if (getMessagesController().getTranslateController().isContextTranslateEnabled() && selectedObject != null && selectedObject.contentType == 0 && ((!TextUtils.isEmpty(selectedObject.getMessageTextToTranslate(groupedMessages, null)) && !selectedObject.isAnimatedEmoji() && !selectedObject.isDice()) || (selectedObject.type == MessageObject.TYPE_ARTICLE && selectedObject.messageOwner != null && selectedObject.messageOwner.rich_message != null && !selectedObject.translated))) {
                     items.add(LocaleController.getString(R.string.TranslateMessage));
                     options.add(OPTION_TRANSLATE);
                     icons.add(R.drawable.msg_translate);
@@ -46093,7 +45939,7 @@ public class ChatActivity extends BaseFragment implements
                     options.add(OPTION_PIN);
                     icons.add(R.drawable.msg_pin);
                 }
-                if (selectedObject != null && selectedObject.contentType == 0 && ((!TextUtils.isEmpty(selectedObject.getMessageTextToTranslate(selectedObjectGroup, null)) && !selectedObject.isAnimatedEmoji() && !selectedObject.isDice()) || (selectedObject.type == MessageObject.TYPE_ARTICLE && selectedObject.messageOwner != null && selectedObject.messageOwner.rich_message != null && !selectedObject.translated))) {
+                if (getMessagesController().getTranslateController().isContextTranslateEnabled() && selectedObject != null && selectedObject.contentType == 0 && ((!TextUtils.isEmpty(selectedObject.getMessageTextToTranslate(selectedObjectGroup, null)) && !selectedObject.isAnimatedEmoji() && !selectedObject.isDice()) || (selectedObject.type == MessageObject.TYPE_ARTICLE && selectedObject.messageOwner != null && selectedObject.messageOwner.rich_message != null && !selectedObject.translated))) {
                     items.add(LocaleController.getString(R.string.TranslateMessage));
                     options.add(OPTION_TRANSLATE);
                     icons.add(R.drawable.msg_translate);
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/web/WebActionBar.java b/TMessagesProj/src/main/java/org/telegram/ui/web/WebActionBar.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/web/WebActionBar.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/web/WebActionBar.java
@@ -127,6 +127,8 @@ public class WebActionBar extends FrameLayout {
     public static final int forward_item = 9;
     public static final int instant_item = 10;
 
+    protected ArticleViewer inu_getOwnerArticleViewer() { return null; }
+
     public WebActionBar(Context context, Theme.ResourcesProvider resourcesProvider) {
         super(context);
         this.resourcesProvider = resourcesProvider;
@@ -215,6 +217,7 @@ public class WebActionBar extends FrameLayout {
                 o.add(R.drawable.msg_openin, getString(R.string.OpenInExternalApp), click.run(open_item));
                 o.add(R.drawable.msg_search, getString(R.string.Search), click.run(search_item));
                 o.addIf(!isLocal, R.drawable.msg_share, getString(R.string.ShareFile), click.run(share_item));
+                desu.inugram.helpers.translate.InstantViewHelper.addMenuItem(o, click.run(desu.inugram.helpers.translate.InstantViewHelper.MENU_ITEM_ID), inu_getOwnerArticleViewer());
                 o.add(R.drawable.msg_settings_old, getString(R.string.Settings), click.run(settings_item));
             } else if (menuType == ArticleViewer.PageLayout.TYPE_WEB) {
                 if (!isTonsite) {
