Localize chat day labels and gif errors
Some checks failed
Android CI / android (push) Failing after 5m57s
Android Release / release (push) Failing after 5m13s
CI / test (push) Failing after 2m56s

This commit is contained in:
2026-03-11 06:41:23 +03:00
parent 43c3fd0169
commit 6e9e580b3f
3 changed files with 53 additions and 15 deletions

View File

@@ -382,7 +382,7 @@ fun ChatScreen(
val isPrivateChat = state.chatType.equals("private", ignoreCase = true)
val canShowMembersTab = state.chatType.equals("group", ignoreCase = true) ||
(state.chatType.equals("channel", ignoreCase = true) && state.canManageMembers)
val timelineItems = remember(state.messages) { buildChatTimelineItems(state.messages) }
val timelineItems = remember(state.messages, context) { buildChatTimelineItems(state.messages, context) }
val senderNameByUserId = remember(state.messages, state.chatMembers) {
val fromMembers = state.chatMembers.associate { member ->
val resolved = member.name.ifBlank {
@@ -442,7 +442,7 @@ fun ChatScreen(
val chatInfoSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840
val adaptiveHorizontalPadding = if (isTabletLayout) 72.dp else 0.dp
val chatInfoEntries = remember(state.messages) { buildChatInfoEntries(state.messages) }
val chatInfoEntries = remember(state.messages, context) { buildChatInfoEntries(state.messages, context) }
val giphyApiKey = remember { BuildConfig.GIPHY_API_KEY.trim() }
val firstUnreadIncomingMessageId = remember(state.messages, state.chatUnreadCount) {
val unread = state.chatUnreadCount.coerceAtLeast(0)
@@ -554,7 +554,7 @@ fun ChatScreen(
}
if (giphyApiKey.isBlank()) {
giphySearchItems = emptyList()
giphyErrorMessage = "Set GIPHY_API_KEY in local.properties"
giphyErrorMessage = context.getString(R.string.chat_error_giphy_api_key_missing)
isGiphyLoading = false
return@LaunchedEffect
}
@@ -567,7 +567,11 @@ fun ChatScreen(
limit = 24,
)
giphySearchItems = found
giphyErrorMessage = if (found.isEmpty()) "No GIFs found" else null
giphyErrorMessage = if (found.isEmpty()) {
context.getString(R.string.chat_error_no_gifs_found)
} else {
null
}
isGiphyLoading = false
}
LaunchedEffect(listState, timelineItems) {
@@ -2193,6 +2197,8 @@ private fun MessageBubble(
?.takeIf { it.isNotBlank() }
?: message.senderUsername?.takeIf { it.isNotBlank() }?.let { "@$it" }
?: senderNameByUserId[message.senderId]
val unknownUserText = stringResource(id = R.string.chat_unknown_user)
val mediaPlaceholder = stringResource(id = R.string.chat_media_placeholder)
val legacyTextUrl = message.text?.trim()?.takeIf { it.startsWith("http", ignoreCase = true) }
val hasLegacyStickerImage = message.attachments.isEmpty() &&
message.type.equals("image", ignoreCase = true) &&
@@ -2264,7 +2270,11 @@ private fun MessageBubble(
if (message.forwardedFromMessageId != null || !message.forwardedFromDisplayName.isNullOrBlank()) {
Text(
text = "Forwarded from ${message.forwardedFromDisplayName?.takeIf { it.isNotBlank() } ?: "#${message.forwardedFromMessageId ?: "?"}"}",
text = stringResource(
id = R.string.chat_forwarded_from,
message.forwardedFromDisplayName?.takeIf { it.isNotBlank() }
?: "#${message.forwardedFromMessageId ?: "?"}",
),
style = MaterialTheme.typography.labelSmall,
color = secondaryTextColor,
)
@@ -2287,8 +2297,8 @@ private fun MessageBubble(
}
val replyAuthor = replyAuthorFromPreview
?: replyAuthorByMessageId[message.replyToMessageId]
?: "Unknown user"
val replySnippet = message.replyPreviewText?.takeIf { it.isNotBlank() } ?: "[media]"
?: unknownUserText
val replySnippet = message.replyPreviewText?.takeIf { it.isNotBlank() } ?: mediaPlaceholder
Row(
modifier = Modifier
.fillMaxWidth()
@@ -2761,7 +2771,10 @@ private val defaultStickerItems = listOf(
RemotePickerItem("Sticker 6", "https://raw.githubusercontent.com/twitter/twemoji/master/assets/72x72/1f602.png", "sticker_lol"),
)
private fun buildChatTimelineItems(messages: List<MessageItem>): List<ChatTimelineItem> {
private fun buildChatTimelineItems(
messages: List<MessageItem>,
context: Context,
): List<ChatTimelineItem> {
if (messages.isEmpty()) return emptyList()
val items = mutableListOf<ChatTimelineItem>()
var previousDate: LocalDate? = null
@@ -2773,7 +2786,7 @@ private fun buildChatTimelineItems(messages: List<MessageItem>): List<ChatTimeli
items += ChatTimelineItem.DayHeader(
headerId = "${date}_$dayHeaderCounter",
dateKey = date.toString(),
label = formatDateSeparatorLabel(date),
label = formatDateSeparatorLabel(date, context),
)
previousDate = date
}
@@ -2885,11 +2898,14 @@ private fun parseMessageLocalDate(createdAt: String): LocalDate? {
private val daySeparatorFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("d MMMM", Locale.getDefault())
private fun formatDateSeparatorLabel(date: LocalDate): String {
private fun formatDateSeparatorLabel(
date: LocalDate,
context: Context,
): String {
val today = LocalDate.now()
return when {
date == today -> "Сегодня"
date == today.minusDays(1) -> "Вчера"
date == today -> context.getString(R.string.chat_day_today)
date == today.minusDays(1) -> context.getString(R.string.chat_day_yesterday)
else -> date.format(daySeparatorFormatter)
}
}
@@ -2914,7 +2930,10 @@ private fun VoiceRecordingStatusRow(
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "Voice ${formatDuration(durationMs.toInt())}",
text = stringResource(
id = R.string.chat_voice_recording_duration,
formatDuration(durationMs.toInt()),
),
style = MaterialTheme.typography.labelMedium,
fontWeight = FontWeight.SemiBold,
)
@@ -4014,7 +4033,10 @@ private fun ChatMembersTabContent(
}
}
private fun buildChatInfoEntries(messages: List<MessageItem>): List<ChatInfoEntry> {
private fun buildChatInfoEntries(
messages: List<MessageItem>,
context: Context,
): List<ChatInfoEntry> {
val entries = mutableListOf<ChatInfoEntry>()
messages
.sortedByDescending { it.id }
@@ -4063,7 +4085,7 @@ private fun buildChatInfoEntries(messages: List<MessageItem>): List<ChatInfoEntr
entries += ChatInfoEntry(
type = ChatInfoEntryType.Link,
title = match.value,
subtitle = "${message.senderDisplayName?.takeIf { it.isNotBlank() } ?: message.senderUsername?.let { "@$it" } ?: "Unknown"} • $time",
subtitle = "${message.senderDisplayName?.takeIf { it.isNotBlank() } ?: message.senderUsername?.let { "@$it" } ?: context.getString(R.string.chat_unknown_user)} • $time",
resourceUrl = match.value,
sourceMessageId = message.id,
)

View File

@@ -130,6 +130,14 @@
<string name="chat_voice_hint_slide">Проведите вверх, чтобы закрепить, и влево, чтобы отменить</string>
<string name="chat_voice_hint_locked">Запись закреплена</string>
<string name="chat_title_fallback">Чат #%1$d</string>
<string name="chat_day_today">Сегодня</string>
<string name="chat_day_yesterday">Вчера</string>
<string name="chat_voice_recording_duration">Голос %1$s</string>
<string name="chat_unknown_user">Неизвестный пользователь</string>
<string name="chat_media_placeholder">[медиа]</string>
<string name="chat_forwarded_from">Переслано от %1$s</string>
<string name="chat_error_giphy_api_key_missing">Укажите GIPHY_API_KEY в local.properties</string>
<string name="chat_error_no_gifs_found">GIF не найдены</string>
<string name="settings_user_fallback">Пользователь</string>
<string name="settings_accounts_header">АККАУНТЫ</string>

View File

@@ -130,6 +130,14 @@
<string name="chat_voice_hint_slide">Slide up to lock, slide left to cancel</string>
<string name="chat_voice_hint_locked">Recording locked</string>
<string name="chat_title_fallback">Chat #%1$d</string>
<string name="chat_day_today">Today</string>
<string name="chat_day_yesterday">Yesterday</string>
<string name="chat_voice_recording_duration">Voice %1$s</string>
<string name="chat_unknown_user">Unknown user</string>
<string name="chat_media_placeholder">[media]</string>
<string name="chat_forwarded_from">Forwarded from %1$s</string>
<string name="chat_error_giphy_api_key_missing">Set GIPHY_API_KEY in local.properties</string>
<string name="chat_error_no_gifs_found">No GIFs found</string>
<string name="settings_user_fallback">User</string>
<string name="settings_accounts_header">ACCOUNTS</string>