diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsActivity.kt
index 5c8b8c54de..3bc8b8ef43 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsActivity.kt
@@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase
 import org.thoughtcrime.securesms.database.ThreadDatabase
 import org.thoughtcrime.securesms.preferences.ExpirationRadioOption
 import org.thoughtcrime.securesms.preferences.RadioOptionAdapter
+import org.thoughtcrime.securesms.preferences.radioOption
 import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
 import javax.inject.Inject
 import kotlin.math.max
@@ -43,30 +44,27 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() {
     }
 
     private val viewModel: ExpirationSettingsViewModel by viewModels {
-        val afterReadOptions = resources.getIntArray(R.array.read_expiration_time_values).map(Int::toString)
+        val afterReadOptions = resources.getIntArray(R.array.read_expiration_time_values)
             .zip(resources.getStringArray(R.array.read_expiration_time_names)) { value, name ->
-                ExpirationRadioOption(ExpiryMode.AfterRead(value.toLong()), name, getString(R.string.AccessibilityId_time_option))
+                radioOption(ExpiryMode.AfterRead(value.toLong()), name) { contentDescription(R.string.AccessibilityId_time_option) }
             }
-        val afterSendOptions = resources.getIntArray(R.array.send_expiration_time_values).map(Int::toString)
+        val afterSendOptions = resources.getIntArray(R.array.send_expiration_time_values)
             .zip(resources.getStringArray(R.array.send_expiration_time_names)) { value, name ->
-                ExpirationRadioOption(ExpiryMode.AfterSend(value.toLong()), name, getString(R.string.AccessibilityId_time_option))
+                radioOption(ExpiryMode.AfterSend(value.toLong()), name) { contentDescription(R.string.AccessibilityId_time_option) }
             }
         viewModelFactory.create(threadId, mayAddTestExpiryOption(afterReadOptions), mayAddTestExpiryOption(afterSendOptions))
     }
 
-    private fun mayAddTestExpiryOption(expiryOptions: List<ExpirationRadioOption>): List<ExpirationRadioOption> {
-        return if (BuildConfig.DEBUG) {
-            val options = expiryOptions.toMutableList()
-            val added = when (options.first().value) {
+    private fun mayAddTestExpiryOption(expiryOptions: List<ExpirationRadioOption>): List<ExpirationRadioOption> =
+        if (BuildConfig.DEBUG) {
+            when (expiryOptions.first().value) {
                 is ExpiryMode.AfterRead -> ExpiryMode.AfterRead(60)
                 is ExpiryMode.AfterSend -> ExpiryMode.AfterSend(60)
                 is ExpiryMode.Legacy -> ExpiryMode.Legacy(60)
                 ExpiryMode.NONE -> ExpiryMode.NONE // shouldn't happen
-            }
-            options.add(1, ExpirationRadioOption(added, "1 Minute (for testing purposes)"))
-            options
+            }.let { radioOption(it, "1 Minute (for testing purposes)") }
+                .let { expiryOptions.toMutableList().apply { add(1, it) } }
         } else expiryOptions
-    }
 
     override fun onSaveInstanceState(outState: Bundle) {
         super.onSaveInstanceState(outState)
@@ -184,55 +182,41 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() {
             if (viewModel.recipient.value?.isContactRecipient == true && viewModel.recipient.value?.isLocalNumber == false) {
                 deleteTypeOptions.addAll(
                     listOf(
-                        ExpirationRadioOption(
-                            value = ExpiryMode.NONE,
-                            title = getString(R.string.expiration_off),
-                            contentDescription = getString(R.string.AccessibilityId_disable_disappearing_messages)
-                        ),
-                        ExpirationRadioOption(
-                            value = ExpiryMode.AfterRead(0),
-                            title = getString(R.string.expiration_type_disappear_after_read),
-                            subtitle = getString(R.string.expiration_type_disappear_after_read_description),
-                            contentDescription = getString(R.string.AccessibilityId_disappear_after_read_option)
-                        ),
-                        ExpirationRadioOption(
-                            value = ExpiryMode.AfterSend(0),
-                            title = getString(R.string.expiration_type_disappear_after_send),
-                            subtitle = getString(R.string.expiration_type_disappear_after_send_description),
-                            contentDescription = getString(R.string.AccessibilityId_disappear_after_send_option)
-                        )
+                        radioOption(ExpiryMode.NONE, R.string.expiration_off) {
+                            contentDescription(R.string.AccessibilityId_disable_disappearing_messages)
+                        },
+                        radioOption(ExpiryMode.AfterRead(0), R.string.expiration_type_disappear_after_read) {
+                            subtitle(R.string.expiration_type_disappear_after_read_description)
+                            contentDescription(R.string.AccessibilityId_disappear_after_read_option)
+                        },
+                        radioOption(ExpiryMode.AfterSend(0), R.string.expiration_type_disappear_after_send) {
+                            subtitle(R.string.expiration_type_disappear_after_send_description)
+                            contentDescription(R.string.AccessibilityId_disappear_after_send_option)
+                        }
                     )
                 )
             } else if (viewModel.recipient.value?.isLocalNumber == true) {
                 deleteTypeOptions.addAll(
                     listOf(
-                        ExpirationRadioOption(
-                            value = ExpiryMode.NONE,
-                            title = getString(R.string.expiration_off),
-                            contentDescription = getString(R.string.AccessibilityId_disable_disappearing_messages)
-                        ),
-                        ExpirationRadioOption(
-                            value = ExpiryMode.AfterSend(0),
-                            title = getString(R.string.expiration_type_disappear_after_send),
-                            subtitle = getString(R.string.expiration_type_disappear_after_send_description),
-                            contentDescription = getString(R.string.AccessibilityId_disappear_after_send_option)
-                        )
+                        radioOption(ExpiryMode.NONE, R.string.expiration_off) {
+                            contentDescription(R.string.AccessibilityId_disable_disappearing_messages)
+                        },
+                        radioOption(ExpiryMode.AfterSend(0), R.string.expiration_type_disappear_after_send) {
+                            subtitle(R.string.expiration_type_disappear_after_send_description)
+                            contentDescription(R.string.AccessibilityId_disappear_after_send_option)
+                        }
                     )
                 )
             } else if (viewModel.recipient.value?.isClosedGroupRecipient == true) {
                 deleteTypeOptions.addAll(
                     listOf(
-                        ExpirationRadioOption(
-                            value = ExpiryMode.NONE,
-                            title = getString(R.string.expiration_off),
-                            contentDescription = getString(R.string.AccessibilityId_disable_disappearing_messages)
-                        ),
-                        ExpirationRadioOption(
-                            value = ExpiryMode.AfterSend(0),
-                            title = getString(R.string.expiration_type_disappear_after_send),
-                            subtitle = getString(R.string.expiration_type_disappear_after_send_description),
-                            contentDescription = getString(R.string.AccessibilityId_disappear_after_send_option)
-                        )
+                        radioOption(ExpiryMode.NONE, R.string.expiration_off) {
+                            contentDescription(R.string.AccessibilityId_disable_disappearing_messages)
+                        },
+                        radioOption(ExpiryMode.AfterSend(0), R.string.expiration_type_disappear_after_send) {
+                            subtitle(R.string.expiration_type_disappear_after_send_description)
+                            contentDescription(R.string.AccessibilityId_disappear_after_send_option)
+                        }
                     )
                 )
             }
@@ -240,48 +224,36 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() {
             if (viewModel.recipient.value?.isContactRecipient == true && viewModel.recipient.value?.isLocalNumber == false) {
                 deleteTypeOptions.addAll(
                     listOf(
-                        ExpirationRadioOption(
-                            value = ExpiryMode.NONE,
-                            title = getString(R.string.expiration_off),
-                            contentDescription = getString(R.string.AccessibilityId_disable_disappearing_messages)
-                        ),
-                        ExpirationRadioOption(
-                            value = ExpiryMode.Legacy(0),
-                            title = getString(R.string.expiration_type_disappear_legacy),
-                            subtitle = getString(R.string.expiration_type_disappear_legacy_description)
-                        ),
-                        ExpirationRadioOption(
-                            value = ExpiryMode.AfterRead(0),
-                            title = getString(R.string.expiration_type_disappear_after_read),
-                            subtitle = getString(R.string.expiration_type_disappear_after_read_description),
-                            enabled = false,
-                            contentDescription = getString(R.string.AccessibilityId_disappear_after_read_option)
-                        ),
-                        ExpirationRadioOption(
-                            value = ExpiryMode.AfterSend(0),
-                            title = getString(R.string.expiration_type_disappear_after_send),
-                            subtitle = getString(R.string.expiration_type_disappear_after_send_description),
-                            enabled = false,
-                            contentDescription = getString(R.string.AccessibilityId_disappear_after_send_option)
-                        )
+                        radioOption(ExpiryMode.NONE, R.string.expiration_off) {
+                            contentDescription(R.string.AccessibilityId_disable_disappearing_messages)
+                        },
+                        radioOption(ExpiryMode.Legacy(0), R.string.expiration_type_disappear_legacy) {
+                            subtitle(R.string.expiration_type_disappear_legacy_description)
+                        },
+                        radioOption(ExpiryMode.AfterRead(0), R.string.expiration_type_disappear_after_read) {
+                            subtitle(R.string.expiration_type_disappear_after_read_description)
+                            enabled = false
+                            contentDescription(R.string.AccessibilityId_disappear_after_read_option)
+                        },
+                        radioOption(ExpiryMode.AfterSend(0), R.string.expiration_type_disappear_after_send) {
+                            subtitle(R.string.expiration_type_disappear_after_send_description)
+                            enabled = false
+                            contentDescription(R.string.AccessibilityId_disappear_after_send_option)
+                        }
                     )
                 )
             } else {
                 deleteTypeOptions.addAll(
                     listOf(
-                        ExpirationRadioOption(value = ExpiryMode.NONE, title = getString(R.string.expiration_off)),
-                        ExpirationRadioOption(
-                            value = ExpiryMode.Legacy(0),
-                            title = getString(R.string.expiration_type_disappear_legacy),
-                            subtitle = getString(R.string.expiration_type_disappear_legacy_description)
-                        ),
-                        ExpirationRadioOption(
-                            value = ExpiryMode.AfterSend(0),
-                            title = getString(R.string.expiration_type_disappear_after_send),
-                            subtitle = getString(R.string.expiration_type_disappear_after_send_description),
-                            enabled = false,
-                            contentDescription = getString(R.string.AccessibilityId_disappear_after_send_option)
-                        )
+                        radioOption(ExpiryMode.NONE, R.string.expiration_off),
+                        radioOption(ExpiryMode.Legacy(0), R.string.expiration_type_disappear_legacy) {
+                            subtitle(R.string.expiration_type_disappear_legacy_description)
+                        },
+                        radioOption(ExpiryMode.AfterSend(0), R.string.expiration_type_disappear_after_send) {
+                            subtitle(R.string.expiration_type_disappear_after_send_description)
+                            enabled = false
+                            contentDescription(R.string.AccessibilityId_disappear_after_send_option)
+                        }
                     )
                 )
             }
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt
index ec2ba8c1ea..394ad5316e 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt
@@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.viewModelScope
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedInject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.launchIn
@@ -27,6 +28,7 @@ import org.thoughtcrime.securesms.preferences.ExpirationRadioOption
 import org.thoughtcrime.securesms.preferences.RadioOption
 import kotlin.reflect.KClass
 
+@OptIn(ExperimentalCoroutinesApi::class)
 class ExpirationSettingsViewModel(
     private val threadId: Long,
     private val afterReadOptions: List<ExpirationRadioOption>,
diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt
index f747d3eb91..31f2782f8f 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt
@@ -46,8 +46,8 @@ class ClearAllDataDialog : DialogFragment() {
 
     private fun createView(): View {
         binding = DialogClearAllDataBinding.inflate(LayoutInflater.from(requireContext()))
-        val device = StringRadioOption("deviceOnly", requireContext().getString(R.string.dialog_clear_all_data_clear_device_only))
-        val network = StringRadioOption("deviceAndNetwork", requireContext().getString(R.string.dialog_clear_all_data_clear_device_and_network))
+        val device = radioOption("deviceOnly", R.string.dialog_clear_all_data_clear_device_only)
+        val network = radioOption("deviceAndNetwork", R.string.dialog_clear_all_data_clear_device_and_network)
         var selectedOption: RadioOption<String> = device
         val optionAdapter = RadioOptionAdapter { selectedOption = it }
         binding.recyclerView.apply {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt
index d4c4f77716..8f74d89a0b 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.preferences
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import androidx.annotation.StringRes
 import androidx.core.view.isVisible
 import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.ListAdapter
@@ -11,6 +12,7 @@ import network.loki.messenger.R
 import network.loki.messenger.databinding.ItemSelectableBinding
 import network.loki.messenger.libsession_util.util.ExpiryMode
 import org.thoughtcrime.securesms.mms.GlideApp
+import org.thoughtcrime.securesms.ui.GetString
 import java.util.Objects
 
 class RadioOptionAdapter<T>(
@@ -23,15 +25,15 @@ class RadioOptionAdapter<T>(
         override fun areContentsTheSame(oldItem: RadioOption<T>, newItem: RadioOption<T>) = Objects.equals(oldItem.value,newItem.value)
     }
 
-    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<T> {
-        val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_selectable, parent, false)
-        return ViewHolder<T>(itemView)
-    }
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<T> =
+        LayoutInflater.from(parent.context).inflate(R.layout.item_selectable, parent, false)
+            .let(::ViewHolder)
 
     override fun onBindViewHolder(holder: ViewHolder<T>, position: Int) {
-        val option = getItem(position)
-        val isSelected = position == selectedOptionPosition
-        holder.bind(option, isSelected) {
+        holder.bind(
+            option = getItem(position),
+            isSelected = position == selectedOptionPosition
+        ) {
             onClickListener(it)
             selectedOptionPosition = position
             notifyItemRangeChanged(0, itemCount)
@@ -44,21 +46,22 @@ class RadioOptionAdapter<T>(
     }
 
     class ViewHolder<T>(itemView: View): RecyclerView.ViewHolder(itemView) {
-
         val glide = GlideApp.with(itemView)
         val binding = ItemSelectableBinding.bind(itemView)
 
         fun bind(option: RadioOption<T>, isSelected: Boolean, toggleSelection: (RadioOption<T>) -> Unit) {
             val alpha = if (option.enabled) 1f else 0.5f
             binding.root.isEnabled = option.enabled
-            binding.root.contentDescription = option.contentDescription
+            binding.root.contentDescription = option.contentDescription?.string(itemView.context)
             binding.titleTextView.alpha = alpha
             binding.subtitleTextView.alpha = alpha
             binding.selectButton.alpha = alpha
 
-            binding.titleTextView.text = option.title
-            binding.subtitleTextView.text = option.subtitle
-            binding.subtitleTextView.isVisible = !option.subtitle.isNullOrEmpty()
+            binding.titleTextView.text = option.title.string(itemView.context)
+            binding.subtitleTextView.text = option.subtitle?.string(itemView.context).also {
+                binding.subtitleTextView.isVisible = !it.isNullOrBlank()
+            }
+
             binding.selectButton.isSelected = isSelected
             if (option.enabled) {
                 binding.root.setOnClickListener { toggleSelection(option) }
@@ -68,13 +71,55 @@ class RadioOptionAdapter<T>(
 
 }
 
-data class RadioOption<T>(
+data class RadioOption<out T>(
     val value: T,
-    val title: String,
-    val subtitle: String? = null,
+    val title: GetString,
+    val subtitle: GetString? = null,
     val enabled: Boolean = true,
-    val contentDescription: String = ""
+    val contentDescription: GetString? = null
 )
 
+fun <T> radioOption(value: T, @StringRes title: Int, configure: RadioOptionBuilder<T>.() -> Unit = {}) =
+    radioOption(value, GetString(title), configure)
+
+fun <T> radioOption(value: T, title: String, configure: RadioOptionBuilder<T>.() -> Unit = {}) =
+    radioOption(value, GetString(title), configure)
+
+fun <T> radioOption(value: T, title: GetString, configure: RadioOptionBuilder<T>.() -> Unit = {}) =
+    RadioOptionBuilder(value, title).also { it.configure() }.build()
+
+class RadioOptionBuilder<out T>(
+    val value: T,
+    val title: GetString
+) {
+    var subtitle: GetString? = null
+    var enabled: Boolean = true
+    var contentDescription: GetString? = null
+
+    fun subtitle(string: String) {
+        subtitle = GetString(string)
+    }
+
+    fun subtitle(@StringRes stringRes: Int) {
+        subtitle = GetString(stringRes)
+    }
+
+    fun contentDescription(string: String) {
+        contentDescription = GetString(string)
+    }
+
+    fun contentDescription(@StringRes stringRes: Int) {
+        contentDescription = GetString(stringRes)
+    }
+
+    fun build() = RadioOption(
+        value,
+        title,
+        subtitle,
+        enabled,
+        contentDescription
+    )
+}
+
 typealias StringRadioOption = RadioOption<String>
 typealias ExpirationRadioOption = RadioOption<ExpiryMode>
diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Data.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Data.kt
index 44ff4a42d8..601f2a9dc7 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/ui/Data.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Data.kt
@@ -1,5 +1,6 @@
 package org.thoughtcrime.securesms.ui
 
+import android.content.Context
 import androidx.annotation.StringRes
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.res.stringResource
@@ -10,13 +11,17 @@ import androidx.compose.ui.res.stringResource
 sealed class GetString {
     @Composable
     abstract fun string(): String
+
+    abstract fun string(context: Context): String
     data class FromString(val string: String): GetString() {
         @Composable
         override fun string(): String = string
+        override fun string(context: Context): String = string
     }
     data class FromResId(@StringRes val resId: Int): GetString() {
         @Composable
         override fun string(): String = stringResource(resId)
+        override fun string(context: Context): String = context.getString(resId)
 
     }
 }