diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index 58822303fd..a370a00105 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -56,6 +56,10 @@ "@profileButtonSendDirectMessage": { "description": "Label for button in profile screen to navigate to DMs with the shown user." }, + "errorShowUserProfile": "Could not show user profile.", + "@errorShowUserProfile": { + "description": "Message that appears on the user profile page when the profile cannot be shown." + }, "permissionsNeededTitle": "Permissions needed", "@permissionsNeededTitle": { "description": "Title for dialog asking the user to grant additional permissions." @@ -321,8 +325,8 @@ "@composeBoxSendTooltip": { "description": "Tooltip for send button in compose box." }, - "composeBoxUnknownChannelName": "(unknown channel)", - "@composeBoxUnknownChannelName": { + "unknownChannelName": "(unknown channel)", + "@unknownChannelName": { "description": "Replacement name for channel when it cannot be found in the store." }, "composeBoxTopicHintText": "Topic", @@ -340,6 +344,10 @@ "@unknownUserName": { "description": "Name placeholder to use for a user when we don't know their name." }, + "messageListYouWithYourselfTitle": "DMs with yourself", + "@messageListYouWithYourselfTitle": { + "description": "Message list page title for a DM group that only includes yourself." + }, "messageListGroupYouAndOthers": "You and {others}", "@messageListGroupYouAndOthers": { "description": "Message list recipient header for a DM group with others.", @@ -610,6 +618,14 @@ "@channelFeedButtonTooltip": { "description": "Tooltip for button to navigate to a given channel's feed" }, + "notifChannelConversationLabel": "#{channel} > {topic}", + "@notifChannelConversationLabel": { + "description": "Label for a channel conversation notification.", + "placeholders": { + "channel": {"type": "String", "example": "channel name"}, + "topic": {"type": "String", "example": "topic name"} + } + }, "notifGroupDmConversationLabel": "{senderFullName} to you and {numOthers, plural, =1{1 other} other{{numOthers} others}}", "@notifGroupDmConversationLabel": { "description": "Label for a group DM conversation notification.", @@ -618,6 +634,18 @@ "numOthers": {"type": "int", "example": "4"} } }, + "pinnedSubscriptionsLabel": "Pinned", + "@pinnedSubscriptionsLabel": { + "description": "Label for the list of pinned subscribed channels." + }, + "unpinnedSubscriptionsLabel": "Unpinned", + "@unpinnedSubscriptionsLabel": { + "description": "Label for the list of unpinned subscribed channels." + }, + "noSubscriptions": "No channels found", + "@noSubscriptions": { + "description": "Text to display when there are no subscribed channels." + }, "notifSelfUser": "You", "@notifSelfUser": { "description": "Display name for the user themself, to show after replying in an Android notification" diff --git a/lib/generated/l10n/zulip_localizations.dart b/lib/generated/l10n/zulip_localizations.dart index 00d7cfde72..b9eef21071 100644 --- a/lib/generated/l10n/zulip_localizations.dart +++ b/lib/generated/l10n/zulip_localizations.dart @@ -187,6 +187,12 @@ abstract class ZulipLocalizations { /// **'Send direct message'** String get profileButtonSendDirectMessage; + /// Message that appears on the user profile page when the profile cannot be shown. + /// + /// In en, this message translates to: + /// **'Could not show user profile.'** + String get errorShowUserProfile; + /// Title for dialog asking the user to grant additional permissions. /// /// In en, this message translates to: @@ -533,7 +539,7 @@ abstract class ZulipLocalizations { /// /// In en, this message translates to: /// **'(unknown channel)'** - String get composeBoxUnknownChannelName; + String get unknownChannelName; /// Hint text for topic input widget in compose box. /// @@ -553,6 +559,12 @@ abstract class ZulipLocalizations { /// **'(unknown user)'** String get unknownUserName; + /// Message list page title for a DM group that only includes yourself. + /// + /// In en, this message translates to: + /// **'DMs with yourself'** + String get messageListYouWithYourselfTitle; + /// Message list recipient header for a DM group with others. /// /// In en, this message translates to: @@ -925,12 +937,36 @@ abstract class ZulipLocalizations { /// **'Channel feed'** String get channelFeedButtonTooltip; + /// Label for a channel conversation notification. + /// + /// In en, this message translates to: + /// **'#{channel} > {topic}'** + String notifChannelConversationLabel(String channel, String topic); + /// Label for a group DM conversation notification. /// /// In en, this message translates to: /// **'{senderFullName} to you and {numOthers, plural, =1{1 other} other{{numOthers} others}}'** String notifGroupDmConversationLabel(String senderFullName, int numOthers); + /// Label for the list of pinned subscribed channels. + /// + /// In en, this message translates to: + /// **'Pinned'** + String get pinnedSubscriptionsLabel; + + /// Label for the list of unpinned subscribed channels. + /// + /// In en, this message translates to: + /// **'Unpinned'** + String get unpinnedSubscriptionsLabel; + + /// Text to display when there are no subscribed channels. + /// + /// In en, this message translates to: + /// **'No channels found'** + String get noSubscriptions; + /// Display name for the user themself, to show after replying in an Android notification /// /// In en, this message translates to: diff --git a/lib/generated/l10n/zulip_localizations_ar.dart b/lib/generated/l10n/zulip_localizations_ar.dart index 542b85031b..2fac2565af 100644 --- a/lib/generated/l10n/zulip_localizations_ar.dart +++ b/lib/generated/l10n/zulip_localizations_ar.dart @@ -52,6 +52,9 @@ class ZulipLocalizationsAr extends ZulipLocalizations { @override String get profileButtonSendDirectMessage => 'Send direct message'; + @override + String get errorShowUserProfile => 'Could not show user profile.'; + @override String get permissionsNeededTitle => 'Permissions needed'; @@ -256,7 +259,7 @@ class ZulipLocalizationsAr extends ZulipLocalizations { String get composeBoxSendTooltip => 'Send'; @override - String get composeBoxUnknownChannelName => '(unknown channel)'; + String get unknownChannelName => '(unknown channel)'; @override String get composeBoxTopicHintText => 'Topic'; @@ -269,6 +272,9 @@ class ZulipLocalizationsAr extends ZulipLocalizations { @override String get unknownUserName => '(unknown user)'; + @override + String get messageListYouWithYourselfTitle => 'DMs with yourself'; + @override String messageListGroupYouAndOthers(String others) { return 'You and $others'; @@ -481,6 +487,11 @@ class ZulipLocalizationsAr extends ZulipLocalizations { @override String get channelFeedButtonTooltip => 'Channel feed'; + @override + String notifChannelConversationLabel(String channel, String topic) { + return '#$channel > $topic'; + } + @override String notifGroupDmConversationLabel(String senderFullName, int numOthers) { String _temp0 = intl.Intl.pluralLogic( @@ -492,6 +503,15 @@ class ZulipLocalizationsAr extends ZulipLocalizations { return '$senderFullName to you and $_temp0'; } + @override + String get pinnedSubscriptionsLabel => 'Pinned'; + + @override + String get unpinnedSubscriptionsLabel => 'Unpinned'; + + @override + String get noSubscriptions => 'No channels found'; + @override String get notifSelfUser => 'You'; diff --git a/lib/generated/l10n/zulip_localizations_en.dart b/lib/generated/l10n/zulip_localizations_en.dart index b6bc9f72e7..7918499a8a 100644 --- a/lib/generated/l10n/zulip_localizations_en.dart +++ b/lib/generated/l10n/zulip_localizations_en.dart @@ -52,6 +52,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations { @override String get profileButtonSendDirectMessage => 'Send direct message'; + @override + String get errorShowUserProfile => 'Could not show user profile.'; + @override String get permissionsNeededTitle => 'Permissions needed'; @@ -256,7 +259,7 @@ class ZulipLocalizationsEn extends ZulipLocalizations { String get composeBoxSendTooltip => 'Send'; @override - String get composeBoxUnknownChannelName => '(unknown channel)'; + String get unknownChannelName => '(unknown channel)'; @override String get composeBoxTopicHintText => 'Topic'; @@ -269,6 +272,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations { @override String get unknownUserName => '(unknown user)'; + @override + String get messageListYouWithYourselfTitle => 'DMs with yourself'; + @override String messageListGroupYouAndOthers(String others) { return 'You and $others'; @@ -481,6 +487,11 @@ class ZulipLocalizationsEn extends ZulipLocalizations { @override String get channelFeedButtonTooltip => 'Channel feed'; + @override + String notifChannelConversationLabel(String channel, String topic) { + return '#$channel > $topic'; + } + @override String notifGroupDmConversationLabel(String senderFullName, int numOthers) { String _temp0 = intl.Intl.pluralLogic( @@ -492,6 +503,15 @@ class ZulipLocalizationsEn extends ZulipLocalizations { return '$senderFullName to you and $_temp0'; } + @override + String get pinnedSubscriptionsLabel => 'Pinned'; + + @override + String get unpinnedSubscriptionsLabel => 'Unpinned'; + + @override + String get noSubscriptions => 'No channels found'; + @override String get notifSelfUser => 'You'; diff --git a/lib/generated/l10n/zulip_localizations_fr.dart b/lib/generated/l10n/zulip_localizations_fr.dart index c857da2c82..5079caad41 100644 --- a/lib/generated/l10n/zulip_localizations_fr.dart +++ b/lib/generated/l10n/zulip_localizations_fr.dart @@ -52,6 +52,9 @@ class ZulipLocalizationsFr extends ZulipLocalizations { @override String get profileButtonSendDirectMessage => 'Send direct message'; + @override + String get errorShowUserProfile => 'Could not show user profile.'; + @override String get permissionsNeededTitle => 'Permissions needed'; @@ -256,7 +259,7 @@ class ZulipLocalizationsFr extends ZulipLocalizations { String get composeBoxSendTooltip => 'Send'; @override - String get composeBoxUnknownChannelName => '(unknown channel)'; + String get unknownChannelName => '(unknown channel)'; @override String get composeBoxTopicHintText => 'Topic'; @@ -269,6 +272,9 @@ class ZulipLocalizationsFr extends ZulipLocalizations { @override String get unknownUserName => '(unknown user)'; + @override + String get messageListYouWithYourselfTitle => 'DMs with yourself'; + @override String messageListGroupYouAndOthers(String others) { return 'You and $others'; @@ -481,6 +487,11 @@ class ZulipLocalizationsFr extends ZulipLocalizations { @override String get channelFeedButtonTooltip => 'Channel feed'; + @override + String notifChannelConversationLabel(String channel, String topic) { + return '#$channel > $topic'; + } + @override String notifGroupDmConversationLabel(String senderFullName, int numOthers) { String _temp0 = intl.Intl.pluralLogic( @@ -492,6 +503,15 @@ class ZulipLocalizationsFr extends ZulipLocalizations { return '$senderFullName to you and $_temp0'; } + @override + String get pinnedSubscriptionsLabel => 'Pinned'; + + @override + String get unpinnedSubscriptionsLabel => 'Unpinned'; + + @override + String get noSubscriptions => 'No channels found'; + @override String get notifSelfUser => 'You'; diff --git a/lib/generated/l10n/zulip_localizations_ja.dart b/lib/generated/l10n/zulip_localizations_ja.dart index 7adbc9ae8a..dce85b0148 100644 --- a/lib/generated/l10n/zulip_localizations_ja.dart +++ b/lib/generated/l10n/zulip_localizations_ja.dart @@ -52,6 +52,9 @@ class ZulipLocalizationsJa extends ZulipLocalizations { @override String get profileButtonSendDirectMessage => 'ダイレクトメッセージを送信'; + @override + String get errorShowUserProfile => 'Could not show user profile.'; + @override String get permissionsNeededTitle => 'Permissions needed'; @@ -256,7 +259,7 @@ class ZulipLocalizationsJa extends ZulipLocalizations { String get composeBoxSendTooltip => 'Send'; @override - String get composeBoxUnknownChannelName => '(unknown channel)'; + String get unknownChannelName => '(unknown channel)'; @override String get composeBoxTopicHintText => 'Topic'; @@ -269,6 +272,9 @@ class ZulipLocalizationsJa extends ZulipLocalizations { @override String get unknownUserName => '(unknown user)'; + @override + String get messageListYouWithYourselfTitle => 'DMs with yourself'; + @override String messageListGroupYouAndOthers(String others) { return 'You and $others'; @@ -481,6 +487,11 @@ class ZulipLocalizationsJa extends ZulipLocalizations { @override String get channelFeedButtonTooltip => 'Channel feed'; + @override + String notifChannelConversationLabel(String channel, String topic) { + return '#$channel > $topic'; + } + @override String notifGroupDmConversationLabel(String senderFullName, int numOthers) { String _temp0 = intl.Intl.pluralLogic( @@ -492,6 +503,15 @@ class ZulipLocalizationsJa extends ZulipLocalizations { return '$senderFullName to you and $_temp0'; } + @override + String get pinnedSubscriptionsLabel => 'Pinned'; + + @override + String get unpinnedSubscriptionsLabel => 'Unpinned'; + + @override + String get noSubscriptions => 'No channels found'; + @override String get notifSelfUser => 'You'; diff --git a/lib/generated/l10n/zulip_localizations_pl.dart b/lib/generated/l10n/zulip_localizations_pl.dart index 07746b3f27..bfb0d6cc78 100644 --- a/lib/generated/l10n/zulip_localizations_pl.dart +++ b/lib/generated/l10n/zulip_localizations_pl.dart @@ -52,6 +52,9 @@ class ZulipLocalizationsPl extends ZulipLocalizations { @override String get profileButtonSendDirectMessage => 'Wyślij wiadomość bezpośrednią'; + @override + String get errorShowUserProfile => 'Could not show user profile.'; + @override String get permissionsNeededTitle => 'Wymagane uprawnienia'; @@ -256,7 +259,7 @@ class ZulipLocalizationsPl extends ZulipLocalizations { String get composeBoxSendTooltip => 'Wyślij'; @override - String get composeBoxUnknownChannelName => '(nieznany kanał)'; + String get unknownChannelName => '(unknown channel)'; @override String get composeBoxTopicHintText => 'Wątek'; @@ -269,6 +272,9 @@ class ZulipLocalizationsPl extends ZulipLocalizations { @override String get unknownUserName => '(nieznany użytkownik)'; + @override + String get messageListYouWithYourselfTitle => 'DMs with yourself'; + @override String messageListGroupYouAndOthers(String others) { return 'Ty i $others'; @@ -481,6 +487,11 @@ class ZulipLocalizationsPl extends ZulipLocalizations { @override String get channelFeedButtonTooltip => 'Strumień kanału'; + @override + String notifChannelConversationLabel(String channel, String topic) { + return '#$channel > $topic'; + } + @override String notifGroupDmConversationLabel(String senderFullName, int numOthers) { String _temp0 = intl.Intl.pluralLogic( @@ -492,6 +503,15 @@ class ZulipLocalizationsPl extends ZulipLocalizations { return '$senderFullName do ciebie i $_temp0'; } + @override + String get pinnedSubscriptionsLabel => 'Pinned'; + + @override + String get unpinnedSubscriptionsLabel => 'Unpinned'; + + @override + String get noSubscriptions => 'No channels found'; + @override String get notifSelfUser => 'Ty'; diff --git a/lib/generated/l10n/zulip_localizations_ru.dart b/lib/generated/l10n/zulip_localizations_ru.dart index 9c2065376b..040e1d93fb 100644 --- a/lib/generated/l10n/zulip_localizations_ru.dart +++ b/lib/generated/l10n/zulip_localizations_ru.dart @@ -52,6 +52,9 @@ class ZulipLocalizationsRu extends ZulipLocalizations { @override String get profileButtonSendDirectMessage => 'Отправить личное сообщение'; + @override + String get errorShowUserProfile => 'Could not show user profile.'; + @override String get permissionsNeededTitle => 'Требуются разрешения'; @@ -256,7 +259,7 @@ class ZulipLocalizationsRu extends ZulipLocalizations { String get composeBoxSendTooltip => 'Send'; @override - String get composeBoxUnknownChannelName => '(unknown channel)'; + String get unknownChannelName => '(unknown channel)'; @override String get composeBoxTopicHintText => 'Topic'; @@ -269,6 +272,9 @@ class ZulipLocalizationsRu extends ZulipLocalizations { @override String get unknownUserName => '(unknown user)'; + @override + String get messageListYouWithYourselfTitle => 'DMs with yourself'; + @override String messageListGroupYouAndOthers(String others) { return 'You and $others'; @@ -481,6 +487,11 @@ class ZulipLocalizationsRu extends ZulipLocalizations { @override String get channelFeedButtonTooltip => 'Channel feed'; + @override + String notifChannelConversationLabel(String channel, String topic) { + return '#$channel > $topic'; + } + @override String notifGroupDmConversationLabel(String senderFullName, int numOthers) { String _temp0 = intl.Intl.pluralLogic( @@ -492,6 +503,15 @@ class ZulipLocalizationsRu extends ZulipLocalizations { return '$senderFullName to you and $_temp0'; } + @override + String get pinnedSubscriptionsLabel => 'Pinned'; + + @override + String get unpinnedSubscriptionsLabel => 'Unpinned'; + + @override + String get noSubscriptions => 'No channels found'; + @override String get notifSelfUser => 'You'; diff --git a/lib/notifications/display.dart b/lib/notifications/display.dart index 46ea95486a..35f1b0ecc8 100644 --- a/lib/notifications/display.dart +++ b/lib/notifications/display.dart @@ -263,9 +263,10 @@ class NotificationDisplayManager { // the first. messagingStyle.conversationTitle = switch (data.recipient) { FcmMessageChannelRecipient(:var streamName?, :var topic) => - '#$streamName > $topic', + zulipLocalizations.notifChannelConversationLabel(streamName, topic), FcmMessageChannelRecipient(:var topic) => - '#(unknown channel) > $topic', // TODO get stream name from data + zulipLocalizations.notifChannelConversationLabel( + zulipLocalizations.unknownChannelName, topic), // TODO get stream name from data FcmMessageDmRecipient(:var allRecipientIds) when allRecipientIds.length > 2 => zulipLocalizations.notifGroupDmConversationLabel( data.senderFullName, allRecipientIds.length - 2), // TODO use others' names, from data diff --git a/lib/widgets/app.dart b/lib/widgets/app.dart index 75fec7bc8b..9525dffdfe 100644 --- a/lib/widgets/app.dart +++ b/lib/widgets/app.dart @@ -323,6 +323,7 @@ class ChooseAccountPageOverflowButton extends StatelessWidget { @override Widget build(BuildContext context) { + final zulipLocalizations = ZulipLocalizations.of(context); final materialLocalizations = MaterialLocalizations.of(context); return MenuAnchor( menuChildren: [ @@ -330,7 +331,7 @@ class ChooseAccountPageOverflowButton extends StatelessWidget { onPressed: () { Navigator.push(context, AboutZulipPage.buildRoute(context)); }, - child: const Text('About Zulip')), // TODO(i18n) + child: Text(zulipLocalizations.aboutPageTitle)), ], builder: (BuildContext context, MenuController controller, Widget? child) { return IconButton( diff --git a/lib/widgets/compose_box.dart b/lib/widgets/compose_box.dart index 114e392bc7..2e05b6aec9 100644 --- a/lib/widgets/compose_box.dart +++ b/lib/widgets/compose_box.dart @@ -483,7 +483,7 @@ class _StreamContentInputState extends State<_StreamContentInput> { final store = PerAccountStoreWidget.of(context); final zulipLocalizations = ZulipLocalizations.of(context); final streamName = store.streams[widget.narrow.streamId]?.name - ?? zulipLocalizations.composeBoxUnknownChannelName; + ?? zulipLocalizations.unknownChannelName; return _ContentInput( narrow: widget.narrow, destination: TopicNarrow(widget.narrow.streamId, _topicTextNormalized), @@ -545,7 +545,7 @@ class _FixedDestinationContentInput extends StatelessWidget { case TopicNarrow(:final streamId, :final topic): final store = PerAccountStoreWidget.of(context); final streamName = store.streams[streamId]?.name - ?? zulipLocalizations.composeBoxUnknownChannelName; + ?? zulipLocalizations.unknownChannelName; return zulipLocalizations.composeBoxChannelContentHint(streamName, topic); case DmNarrow(otherRecipientIds: []): // The self-1:1 thread. diff --git a/lib/widgets/emoji_reaction.dart b/lib/widgets/emoji_reaction.dart index 98147a54d7..5c1b855c2a 100644 --- a/lib/widgets/emoji_reaction.dart +++ b/lib/widgets/emoji_reaction.dart @@ -149,6 +149,7 @@ class ReactionChip extends StatelessWidget { @override Widget build(BuildContext context) { final store = PerAccountStoreWidget.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); final reactionType = reactionWithVotes.reactionType; final emojiCode = reactionWithVotes.emojiCode; @@ -163,7 +164,7 @@ class ReactionChip extends StatelessWidget { ? userIds.map((id) { return id == store.selfUserId ? 'You' - : store.users[id]?.fullName ?? '(unknown user)'; // TODO(i18n) + : store.users[id]?.fullName ?? zulipLocalizations.unknownUserName; }).join(', ') : userIds.length.toString(); diff --git a/lib/widgets/inbox.dart b/lib/widgets/inbox.dart index 12ec8751a1..ad74c47c12 100644 --- a/lib/widgets/inbox.dart +++ b/lib/widgets/inbox.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../api/model/model.dart'; +import '../generated/l10n/zulip_localizations.dart'; import '../model/narrow.dart'; import '../model/recent_dm_conversations.dart'; import '../model/unreads.dart'; @@ -218,7 +219,7 @@ abstract class _HeaderItem extends StatelessWidget { required this.sectionContext, }); - String get title; + String title(ZulipLocalizations zulipLocalizations); IconData get icon; Color collapsedIconColor(BuildContext context); Color uncollapsedIconColor(BuildContext context); @@ -238,6 +239,7 @@ abstract class _HeaderItem extends StatelessWidget { @override Widget build(BuildContext context) { + final zulipLocalizations = ZulipLocalizations.of(context); final designVariables = DesignVariables.of(context); return Material( color: collapsed @@ -272,7 +274,7 @@ abstract class _HeaderItem extends StatelessWidget { ).merge(weightVariableTextStyle(context, wght: 600)), maxLines: 1, overflow: TextOverflow.ellipsis, - title))), + title(zulipLocalizations)))), const SizedBox(width: 12), if (hasMention) const _IconMarker(icon: ZulipIcons.at_sign), Padding(padding: const EdgeInsetsDirectional.only(end: 16), @@ -293,7 +295,8 @@ class _AllDmsHeaderItem extends _HeaderItem { required super.sectionContext, }); - @override String get title => 'Direct messages'; // TODO(i18n) + @override String title(ZulipLocalizations zulipLocalizations) => + zulipLocalizations.recentDmConversationsPageTitle; @override IconData get icon => ZulipIcons.user; // TODO(design) check if this is the right variable for these @@ -362,16 +365,20 @@ class _DmItem extends StatelessWidget { final store = PerAccountStoreWidget.of(context); final selfUser = store.users[store.selfUserId]!; + final zulipLocalizations = ZulipLocalizations.of(context); final designVariables = DesignVariables.of(context); final title = switch (narrow.otherRecipientIds) { // TODO dedupe with [RecentDmConversationsItem] [] => selfUser.fullName, - [var otherUserId] => store.users[otherUserId]?.fullName ?? '(unknown user)', + [var otherUserId] => + store.users[otherUserId]?.fullName ?? zulipLocalizations.unknownUserName, // TODO(i18n): List formatting, like you can do in JavaScript: // new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya', 'Shu']) // // 'Chris、Greg、Alya、Shu' - _ => narrow.otherRecipientIds.map((id) => store.users[id]?.fullName ?? '(unknown user)').join(', '), + _ => narrow.otherRecipientIds.map( + (id) => store.users[id]?.fullName ?? zulipLocalizations.unknownUserName + ).join(', '), }; return Material( @@ -417,7 +424,8 @@ class _StreamHeaderItem extends _HeaderItem { required super.sectionContext, }); - @override String get title => subscription.name; + @override String title(ZulipLocalizations zulipLocalizations) => + subscription.name; @override IconData get icon => iconDataForStream(subscription); @override Color collapsedIconColor(context) => colorSwatchFor(context, subscription).iconOnPlainBackground; diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index 8c32a12115..8bceca646a 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -317,6 +317,7 @@ class MessageListAppBarTitle extends StatelessWidget { Widget _buildStreamRow(BuildContext context, { ZulipStream? stream, }) { + final zulipLocalizations = ZulipLocalizations.of(context); // A null [Icon.icon] makes a blank space. final icon = stream != null ? iconDataForStream(stream) : null; return Row( @@ -328,7 +329,8 @@ class MessageListAppBarTitle extends StatelessWidget { children: [ Icon(size: 16, icon), const SizedBox(width: 4), - Flexible(child: Text(stream?.name ?? '(unknown channel)')), + Flexible(child: Text( + stream?.name ?? zulipLocalizations.unknownChannelName)), ]); } @@ -420,9 +422,10 @@ class MessageListAppBarTitle extends StatelessWidget { case DmNarrow(:var otherRecipientIds): final store = PerAccountStoreWidget.of(context); if (otherRecipientIds.isEmpty) { - return const Text("DMs with yourself"); + return Text(zulipLocalizations.messageListYouWithYourselfTitle); } else { - final names = otherRecipientIds.map((id) => store.users[id]?.fullName ?? '(unknown user)'); + final names = otherRecipientIds.map( + (id) => store.users[id]?.fullName ?? zulipLocalizations.unknownUserName); return Text("DMs with ${names.join(", ")}"); // TODO show avatars } } @@ -1027,6 +1030,7 @@ class StreamMessageRecipientHeader extends StatelessWidget { // https://github.com/zulip/zulip-mobile/issues/5511 final store = PerAccountStoreWidget.of(context); final designVariables = DesignVariables.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); final topic = message.topic; @@ -1051,7 +1055,7 @@ class StreamMessageRecipientHeader extends StatelessWidget { final stream = store.streams[message.streamId]; final streamName = stream?.name ?? message.displayRecipient - ?? '(unknown channel)'; // TODO(log) + ?? zulipLocalizations.unknownChannelName; // TODO(log) streamWidget = GestureDetector( onTap: () => Navigator.push(context, diff --git a/lib/widgets/profile.dart b/lib/widgets/profile.dart index 57dd76a0ab..ce23dfa37f 100644 --- a/lib/widgets/profile.dart +++ b/lib/widgets/profile.dart @@ -121,17 +121,18 @@ class _ProfileErrorPage extends StatelessWidget { @override Widget build(BuildContext context) { + final zulipLocalizations = ZulipLocalizations.of(context); return Scaffold( - appBar: ZulipAppBar(title: const Text('Error')), - body: const SingleChildScrollView( + appBar: ZulipAppBar(title: Text(zulipLocalizations.errorDialogTitle)), + body: SingleChildScrollView( child: Padding( - padding: EdgeInsets.symmetric(horizontal: 16, vertical: 32), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 32), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(Icons.error), - SizedBox(width: 4), - Text('Could not show user profile.'), + const Icon(Icons.error), + const SizedBox(width: 4), + Text(zulipLocalizations.errorShowUserProfile), ])))); } } @@ -290,8 +291,9 @@ class _UserWidget extends StatelessWidget { @override Widget build(BuildContext context) { final store = PerAccountStoreWidget.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); final user = store.users[userId]; - final fullName = user?.fullName ?? '(unknown user)'; + final fullName = user?.fullName ?? zulipLocalizations.unknownUserName; return InkWell( onTap: () => Navigator.push(context, ProfilePage.buildRoute(context: context, diff --git a/lib/widgets/recent_dm_conversations.dart b/lib/widgets/recent_dm_conversations.dart index c9d3131591..ddc32861a3 100644 --- a/lib/widgets/recent_dm_conversations.dart +++ b/lib/widgets/recent_dm_conversations.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import '../generated/l10n/zulip_localizations.dart'; import '../model/narrow.dart'; import '../model/recent_dm_conversations.dart'; import '../model/unreads.dart'; @@ -81,6 +82,7 @@ class RecentDmConversationsItem extends StatelessWidget { final store = PerAccountStoreWidget.of(context); final selfUser = store.users[store.selfUserId]!; + final zulipLocalizations = ZulipLocalizations.of(context); final designVariables = DesignVariables.of(context); final String title; @@ -94,13 +96,15 @@ class RecentDmConversationsItem extends StatelessWidget { // (should we offer a "spam folder" style summary screen of recent // 1:1 DM conversations from muted users?) final otherUser = store.users[otherUserId]; - title = otherUser?.fullName ?? '(unknown user)'; + title = otherUser?.fullName ?? zulipLocalizations.unknownUserName; avatar = AvatarImage(userId: otherUserId, size: _avatarSize); default: // TODO(i18n): List formatting, like you can do in JavaScript: // new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya']) // // 'Chris、Greg、Alya' - title = narrow.otherRecipientIds.map((id) => store.users[id]?.fullName ?? '(unknown user)').join(', '); + title = narrow.otherRecipientIds.map( + (id) => store.users[id]?.fullName ?? zulipLocalizations.unknownUserName + ).join(', '); avatar = ColoredBox(color: designVariables.groupDmConversationIconBg, child: Center( child: Icon(color: designVariables.groupDmConversationIcon, diff --git a/lib/widgets/subscription_list.dart b/lib/widgets/subscription_list.dart index d2faf03b2c..05af0387ea 100644 --- a/lib/widgets/subscription_list.dart +++ b/lib/widgets/subscription_list.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../api/model/model.dart'; +import '../generated/l10n/zulip_localizations.dart'; import '../model/narrow.dart'; import '../model/unreads.dart'; import 'icons.dart'; @@ -65,10 +66,8 @@ class _SubscriptionListPageBodyState extends State wit // TODO: Implement collapsible topics - // TODO(i18n): localize strings on page - // Strings here left unlocalized as they likely will not - // exist in the settled design. final store = PerAccountStoreWidget.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); final List pinned = []; final List unpinned = []; @@ -90,11 +89,11 @@ class _SubscriptionListPageBodyState extends State wit if (pinned.isEmpty && unpinned.isEmpty) const _NoSubscriptionsItem(), if (pinned.isNotEmpty) ...[ - const _SubscriptionListHeader(label: "Pinned"), + _SubscriptionListHeader(label: zulipLocalizations.pinnedSubscriptionsLabel), _SubscriptionList(unreadsModel: unreadsModel, subscriptions: pinned), ], if (unpinned.isNotEmpty) ...[ - const _SubscriptionListHeader(label: "Unpinned"), + _SubscriptionListHeader(label: zulipLocalizations.unpinnedSubscriptionsLabel), _SubscriptionList(unreadsModel: unreadsModel, subscriptions: unpinned), ], @@ -112,11 +111,12 @@ class _NoSubscriptionsItem extends StatelessWidget { @override Widget build(BuildContext context) { final designVariables = DesignVariables.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); return SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(10), - child: Text("No channels found", + child: Text(zulipLocalizations.noSubscriptions, textAlign: TextAlign.center, style: TextStyle( color: designVariables.subscriptionListHeaderText,