Add localization for 'Received a GIF' in multiple languages and update chat UI

This commit is contained in:
Zach
2026-07-08 20:38:06 -07:00
parent c683dbd8f1
commit 8765d0f4cc
26 changed files with 107 additions and 12 deletions
+1
View File
@@ -734,6 +734,7 @@
}
},
"chat_sendGif": "Send GIF",
"chat_receivedGif": "Received a GIF",
"chat_reply": "Reply",
"chat_addReaction": "Add Reaction",
"chat_me": "Me",
+6
View File
@@ -2692,6 +2692,12 @@ abstract class AppLocalizations {
/// **'Send GIF'**
String get chat_sendGif;
/// No description provided for @chat_receivedGif.
///
/// In en, this message translates to:
/// **'Received a GIF'**
String get chat_receivedGif;
/// No description provided for @chat_reply.
///
/// In en, this message translates to:
+4 -1
View File
@@ -1468,6 +1468,9 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get chat_sendGif => 'Изпрати GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Отговори';
@@ -2706,7 +2709,6 @@ class AppLocalizationsBg extends AppLocalizations {
'Байтовете, използвани за кодиране на идентификатора на този повторител в таговете за откриване на потоци/цикли, са: 0=1 байт (256 идентификатора, до 64 скока), 1=2 байта (65 000 идентификатора, до 32 скока), 2=3 байта (16 милиона идентификатора, до 21 скока). Версиите 1.13 и по-старите фърмуери използват многобайтови пътища - само след като мрежата е актуализирана до версия 1.14 или по-нова.';
@override
String get repeater_txDelay => 'Забавяне на Flood TX';
String get repeater_keySettings => 'Change Identity Keys';
@override
@@ -2742,6 +2744,7 @@ class AppLocalizationsBg extends AppLocalizations {
}
@override
String get repeater_txDelay => 'Забавяне на Flood TX';
@override
String get repeater_txDelayHelper =>
+3
View File
@@ -1469,6 +1469,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get chat_sendGif => 'GIF senden';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Antworten';
+3
View File
@@ -1438,6 +1438,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get chat_sendGif => 'Send GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Reply';
+3
View File
@@ -1465,6 +1465,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get chat_sendGif => 'Enviar GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Responder';
+3
View File
@@ -1467,6 +1467,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get chat_sendGif => 'Envoyer un GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Répondre';
+3
View File
@@ -1460,6 +1460,9 @@ class AppLocalizationsHu extends AppLocalizations {
@override
String get chat_sendGif => 'GIF küldése';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Válasz';
+3
View File
@@ -1466,6 +1466,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get chat_sendGif => 'Invia GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Rispondi';
+3
View File
@@ -1395,6 +1395,9 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get chat_sendGif => 'GIF を送信';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => '返信';
+3
View File
@@ -1398,6 +1398,9 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get chat_sendGif => 'GIF 보내기';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => '답변';
+3
View File
@@ -1454,6 +1454,9 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get chat_sendGif => 'GIF verzenden';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Reageren';
+3
View File
@@ -1477,6 +1477,9 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get chat_sendGif => 'Wyślij GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Odpowiedz';
+3
View File
@@ -1464,6 +1464,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get chat_sendGif => 'Enviar GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Responder';
+3
View File
@@ -1465,6 +1465,9 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get chat_sendGif => 'Отправить GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Ответить';
+3
View File
@@ -1454,6 +1454,9 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get chat_sendGif => 'Odoslať GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Odpovedať';
+3
View File
@@ -1453,6 +1453,9 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get chat_sendGif => 'Pošlji GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Odgovori';
+3
View File
@@ -1446,6 +1446,9 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get chat_sendGif => 'Skicka GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Svara';
+3
View File
@@ -1459,6 +1459,9 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get chat_sendGif => 'Надіслати GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => 'Відповісти';
+3
View File
@@ -1381,6 +1381,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get chat_sendGif => '发送 GIF';
@override
String get chat_receivedGif => 'Received a GIF';
@override
String get chat_reply => '回复';
+3 -1
View File
@@ -1579,9 +1579,11 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
}
String _formatPathPrefixes(Uint8List pathBytes) {
// Join with ", " (not bare ",") so long paths have soft-wrap break points
// and stay confined to the message bubble instead of overflowing right.
return pathBytes
.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
.join(',');
.join(', ');
}
Future<void> openRegionSelectDialog(Channel channel) async {
+11 -3
View File
@@ -27,6 +27,7 @@ import '../widgets/qr_code_display.dart';
import '../widgets/quick_switch_bar.dart';
import '../widgets/sync_progress_overlay.dart';
import '../widgets/unread_badge.dart';
import '../helpers/gif_helper.dart';
import '../helpers/snack_bar_builder.dart';
import 'channel_chat_screen.dart';
import 'community_qr_scanner_screen.dart';
@@ -410,7 +411,11 @@ class _ChannelsScreenState extends State<ChannelsScreen>
// Last message preview
final messages = connector.getChannelMessages(channel);
final lastMessage = messages.isNotEmpty ? messages.last : null;
final lastPreview = lastMessage?.text ?? '';
final lastMessageText = lastMessage?.text ?? '';
final lastPreview = lastMessageText.isNotEmpty &&
GifHelper.parseGif(lastMessageText) != null
? context.l10n.chat_receivedGif
: lastMessageText;
final lastTime = lastMessage?.timestamp;
final channelLabel = channel.name.isEmpty
@@ -453,7 +458,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
)
: null,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Leading avatar with optional community badge
Stack(
@@ -581,10 +586,13 @@ class _ChannelsScreenState extends State<ChannelsScreen>
const SizedBox(width: 4),
ReorderableDragStartListener(
index: dragIndex,
// Top-aligned with the "CH n" / time line. Bottom padding keeps
// a comfortable drag target without pushing the icon down.
child: Padding(
padding: const EdgeInsets.all(8),
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 16),
child: Icon(
Icons.drag_handle,
size: 18,
color: scheme.onSurfaceVariant,
),
),
+2 -2
View File
@@ -276,7 +276,7 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
return Scaffold(
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
@@ -296,7 +296,7 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
),
],
),
centerTitle: false,
centerTitle: true,
actions: [
IconButton(
icon: Icon(isFloodMode ? Icons.waves : Icons.route),
+2 -2
View File
@@ -334,7 +334,7 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
return Scaffold(
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
@@ -350,7 +350,7 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
),
],
),
centerTitle: false,
centerTitle: true,
bottom: const SyncProgressAppBarBottom(),
actions: [
IconButton(
+10 -3
View File
@@ -26,9 +26,16 @@ class QuickSwitchBar extends StatelessWidget {
final colorScheme = theme.colorScheme;
final labelStyle = theme.textTheme.labelMedium ?? const TextStyle();
final background = highContrast ? MapPalette.panelDark : Colors.transparent;
final selectedColor = highContrast
// The selected icon sits on the primary indicator pill, so it uses
// onPrimary. The label sits below on the bar background, so it must use a
// foreground color that contrasts with the surface (not onPrimary, which
// is white-on-white in the light theme).
final selectedIconColor = highContrast
? MapPalette.textPrimary
: colorScheme.onPrimary;
final selectedLabelColor = highContrast
? MapPalette.textPrimary
: colorScheme.onSurface;
final unselectedColor = highContrast
? MapPalette.textSecondary
: colorScheme.onSurfaceVariant;
@@ -59,13 +66,13 @@ class QuickSwitchBar extends StatelessWidget {
final isSelected = states.contains(WidgetState.selected);
return labelStyle.copyWith(
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w500,
color: isSelected ? selectedColor : unselectedColor,
color: isSelected ? selectedLabelColor : unselectedColor,
);
}),
iconTheme: WidgetStateProperty.resolveWith((states) {
final isSelected = states.contains(WidgetState.selected);
return IconThemeData(
color: isSelected ? selectedColor : unselectedColor,
color: isSelected ? selectedIconColor : unselectedColor,
);
}),
),