diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarTextContextMenu.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarTextContextMenu.kt
new file mode 100644
index 0000000000000000000000000000000000000000..db17bba0ae49e6b36d4a91c8169f167d51231c3e
--- /dev/null
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarTextContextMenu.kt
@@ -0,0 +1,55 @@
+/*
+ * Briar Desktop
+ * Copyright (C) 2023 The Briar Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package org.briarproject.briar.desktop.ui
+
+import androidx.compose.foundation.ContextMenuArea
+import androidx.compose.foundation.ContextMenuItem
+import androidx.compose.foundation.ContextMenuState
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.text.TextContextMenu
+import androidx.compose.foundation.text.TextContextMenu.TextManager
+import androidx.compose.runtime.Composable
+import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
+
+@OptIn(ExperimentalFoundationApi::class)
+object BriarTextContextMenu : TextContextMenu {
+    @Composable
+    override fun Area(textManager: TextManager, state: ContextMenuState, content: @Composable () -> Unit) {
+        val items = {
+            listOfNotNull(
+                textManager.cut?.let {
+                    ContextMenuItem(i18n("cut"), it)
+                },
+                textManager.copy?.let {
+                    // don't show copy option if no text is selected
+                    if (textManager.selectedText.isEmpty()) null
+                    else ContextMenuItem(i18n("copy"), it)
+                },
+                textManager.paste?.let {
+                    ContextMenuItem(i18n("paste"), it)
+                },
+                textManager.selectAll?.let {
+                    ContextMenuItem(i18n("select_all"), it)
+                },
+            )
+        }
+
+        ContextMenuArea(items, state, content = content)
+    }
+}
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
index c7b63caf06925e6d4ca6ba6f6f66ab0b7dce005b..c9fe9daf2a696da2a7f9469f427a3cc414658813 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
@@ -1,6 +1,6 @@
 /*
  * Briar Desktop
- * Copyright (C) 2021-2022 The Briar Project
+ * Copyright (C) 2021-2023 The Briar Project
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -18,9 +18,11 @@
 
 package org.briarproject.briar.desktop.ui
 
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.text.LocalTextContextMenu
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.DisposableEffect
@@ -34,8 +36,6 @@ import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.toAwtImage
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.platform.LocalLocalization
-import androidx.compose.ui.platform.PlatformLocalization
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.window.FrameWindowScope
 import androidx.compose.ui.window.Window
@@ -117,14 +117,9 @@ constructor(
         }
     }
 
+    @OptIn(ExperimentalFoundationApi::class)
     @Composable
     override fun start(onClose: () -> Unit) {
-        val platformLocalization = object : PlatformLocalization {
-            override val copy = i18n("copy")
-            override val cut = i18n("cut")
-            override val paste = i18n("paste")
-            override val selectAll = i18n("select_all")
-        }
         val focusState = remember { WindowFocusState() }
 
         Window(
@@ -167,11 +162,19 @@ constructor(
                 val messageCounterListener: MessageCounterListener = { (type, total, groups, inc) ->
                     if (inc && total > 0 && !focusState.focused) {
                         val callback: NotificationProvider.() -> Unit = when (type) {
-                            PrivateMessage -> { { notifyPrivateMessages(total, groups) } }
-                            Forum -> { { notifyForumPosts(total, groups) } }
+                            PrivateMessage -> {
+                                { notifyPrivateMessages(total, groups) }
+                            }
+
+                            Forum -> {
+                                { notifyForumPosts(total, groups) }
+                            }
                         }
                         val (lastNotification, setLastNotification) = when (type) {
-                            PrivateMessage -> lastNotificationPrivateMessage to { v: Long -> lastNotificationPrivateMessage = v }
+                            PrivateMessage -> lastNotificationPrivateMessage to { v: Long ->
+                                lastNotificationPrivateMessage = v
+                            }
+
                             Forum -> lastNotificationForum to { v: Long -> lastNotificationForum = v }
                         }
 
@@ -209,7 +212,7 @@ constructor(
                 LocalViewModelProvider provides viewModelProvider,
                 LocalAvatarManager provides avatarManager,
                 LocalConfiguration provides configuration,
-                LocalLocalization provides platformLocalization,
+                LocalTextContextMenu provides BriarTextContextMenu,
             ) {
                 // invalidate whole application window in case the theme or language setting is changed
                 configuration.invalidateScreen.react {