diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0faad799..3bb5a923 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,6 +52,8 @@ Stream's Flutter code is kept in a single mono-repository consisting of multiple `stream_chat_persistence` - This package provides a persistence client for fetching and saving chat data locally. Stream Chat Persistence uses Moor as a disk cache. +`stream_chat_localizations` - This package provides a set of localizations for the SDK. + ### Local Setup Congratulations! 🎉. You've successfully cloned our repo, and you are ready to make your first contribution. Before you can start making code changes, there are a few things to configure. diff --git a/README.md b/README.md index 6758b189..62358d57 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This repository contains code for our [Dart](https://dart.dev/) and [Flutter](ht Stream allows developers to rapidly deploy scalable feeds and chat messaging with an industry leading 99.999% uptime SLA guarantee. ## Sample apps and demos -Our team maintains a dedicated repository for fully-fledged sample applications and demos. Consider checking out [GetStream/flutter-samples](https://github.com/GetStream/flutter-samples) to learn more or get started by looking at our latest [Stream Chat demo](https://github.com/GetStream/flutter-samples/tree/main/stream_chat_v1). +Our team maintains a dedicated repository for fully-fledged sample applications and demos. Consider checking out [GetStream/flutter-samples](https://github.com/GetStream/flutter-samples) to learn more or get started by looking at our latest [Stream Chat demo](https://github.com/GetStream/flutter-samples/tree/main/packages/stream_chat_v1). ## Free for Makers diff --git a/docusaurus/docs/Flutter/basics/introduction.mdx b/docusaurus/docs/Flutter/basics/introduction.mdx index 756bd33f..99170774 100644 --- a/docusaurus/docs/Flutter/basics/introduction.mdx +++ b/docusaurus/docs/Flutter/basics/introduction.mdx @@ -8,13 +8,14 @@ Exploring The Basics Of Stream Chat ![](../assets/sdk_title.png) -Stream Chat is a service that helps you easily build a full chat experience in your Flutter (and more) apps. +Stream Chat is a service that helps you easily build a full chat experience in your Flutter apps. +We also support a variety of other SDKs. This section of the documentation focuses on our Flutter SDK which helps you easily -ship high quality messaging experiences in apps and programs built with the [Flutter toolkit made -by Google](https://flutter.dev). +ship high quality messaging experiences in apps and programs built with the [Flutter toolkit +made by Google](https://flutter.dev). -The Stream Chat Flutter SDK comprises of four different packages to choose from ranging from ones +The Stream Chat Flutter SDK comprises five different packages to choose from, ranging from ones giving you complete control to ones that give you a rich out-of-the-box chat experience. The packages that make up the Stream Chat SDK are: @@ -30,28 +31,28 @@ reusable and customisable UI components. saving chat data locally. 5. Localizations (stream_chat_localizations): provides a set of localizations for the SDK. -We recommend building prototypes using the full UI package since it contains UI widgets already -integrated with Stream's API. [stream_chat_flutter](https://pub.dev/packages/stream_chat_flutter) -is the fastest way to get up and running using Stream chat in your app. +We recommend building prototypes using the full UI package, [stream_chat_flutter](https://pub.dev/packages/stream_chat_flutter), +since it contains UI widgets already integrated with Stream's API. It is the fastest way to get up +and running using Stream chat in your app. The Flutter SDK enables you to build any type of chat or messaging experience for Android, iOS, Web and Desktop. If you're building a very custom UI and would prefer a more lean package, -our [core package](https://pub.dev/packages/stream_chat_flutter) will be suited to this use case. Core allows you to build custom, -expressive UIs while retaining the benefits of our full Flutter SDK. -APIs for accessing and controlling users, sending messages, etc are seamlessly integrated into -this package and accessible via providers and builders. +[stream_chat_flutter_core](https://pub.dev/packages/stream_chat_flutter_core) will be suited to this +use case. Core allows you to build custom, expressive UIs while retaining the benefits of our full +Flutter SDK. APIs for accessing and controlling users, sending messages, and so forth are seamlessly integrated +into this package and accessible via providers and builders. Before going into the docs, let's take a small detour to look at how the elements of Stream Chat are structured. ### Basic Structure -There are two core elements in chat, Users and Channels. +There are two core elements in chat, Users and Channels. Channels are groups of one or more users that can message each other. In an app, you need to have a user connected to query channels. -There is no specific distinction between a chat between two people and a group chat, +There is no specific distinction between a chat with only two people and a group chat, but there is a way to create a unique chat between a certain number of people by creating a distinct channel. ![](../assets/chat_basics.png) @@ -68,7 +69,7 @@ While this is a simplistic overview of the service, the Flutter SDK handles the Before reading the docs, consider trying our [online API tour](https://getstream.io/chat/get_started/), it is a nice way to learn how the API works. -It's in-browser so Javascript-based but the ideas are pretty much the same as Dart. +It's in-browser so you'll need to use Javascript but the core conceps are pretty much the same as Dart. You may also like to look at the [Flutter tutorial](https://getstream.io/chat/flutter/tutorial/) which focuses on using the UI package to get Stream Chat integrated into a Flutter app. diff --git a/docusaurus/docs/Flutter/guides/adding_localization.mdx b/docusaurus/docs/Flutter/guides/adding_localization.mdx index 8ecc0cce..4ad5eede 100644 --- a/docusaurus/docs/Flutter/guides/adding_localization.mdx +++ b/docusaurus/docs/Flutter/guides/adding_localization.mdx @@ -26,7 +26,8 @@ At the moment we support the following languages: - [Italian](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart) - [French](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart) - [Spanish](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart) - +- [Japanese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart) +- [Korean](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart) More languages will be added in the future. Feel free to [contribute](https://github.com/GetStream/stream-chat-flutter/blob/master/CONTRIBUTING.md) to add more languages. ### Add dependency @@ -64,6 +65,8 @@ class MyApp extends StatelessWidget { Locale('fr'), Locale('it'), Locale('es'), + Locale('ja'), + Locale('ko'), ], // Add GlobalStreamChatLocalizations.delegates localizationsDelegates: GlobalStreamChatLocalizations.delegates, @@ -133,6 +136,8 @@ Here is an example of how that would look like: Locale('fr'), Locale('it'), Locale('es'), + Locale('ja'), + Locale('ko'), ], // locales are the locales of the device // supportedLocales are the app supported locales @@ -175,6 +180,8 @@ Example: nb fr it - es + es + ja + ko ``` diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index 3190dc1b..22738794 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -153,9 +153,9 @@ class Channel { true; /// Returns true if the channel is muted, as a stream. - Stream? get isMutedStream => _client.state.currentUserStream + Stream get isMutedStream => _client.state.currentUserStream .map((event) => - event!.channelMutes.any((element) => element.channel.cid == cid) == + event?.channelMutes.any((element) => element.channel.cid == cid) == true) .distinct(); @@ -168,37 +168,37 @@ class Channel { /// Channel configuration. ChannelConfig? get config { _checkInitialized(); - return state?._channelState.channel?.config; + return state!._channelState.channel?.config; } /// Channel configuration as a stream. - Stream? get configStream { + Stream get configStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs.channel?.config); + return state!.channelStateStream.map((cs) => cs.channel?.config); } /// Channel user creator. User? get createdBy { _checkInitialized(); - return state?._channelState.channel?.createdBy; + return state!._channelState.channel?.createdBy; } /// Channel user creator as a stream. - Stream? get createdByStream { + Stream get createdByStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs.channel?.createdBy); + return state!.channelStateStream.map((cs) => cs.channel?.createdBy); } /// Channel frozen status. - bool? get frozen { + bool get frozen { _checkInitialized(); - return state?._channelState.channel?.frozen; + return state!._channelState.channel?.frozen == true; } /// Channel frozen status as a stream. - Stream? get frozenStream { + Stream get frozenStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs.channel?.frozen); + return state!.channelStateStream.map((cs) => cs.channel?.frozen == true); } /// Cooldown count @@ -219,69 +219,61 @@ class Channel { /// Channel creation date. DateTime? get createdAt { _checkInitialized(); - return state?._channelState.channel?.createdAt; + return state!._channelState.channel?.createdAt; } /// Channel creation date as a stream. - Stream? get createdAtStream { + Stream get createdAtStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs.channel?.createdAt); + return state!.channelStateStream.map((cs) => cs.channel?.createdAt); } /// Channel last message date. DateTime? get lastMessageAt { _checkInitialized(); - - return state?._channelState.channel?.lastMessageAt; + return state!._channelState.channel?.lastMessageAt; } /// Channel last message date as a stream. - Stream? get lastMessageAtStream { + Stream get lastMessageAtStream { _checkInitialized(); - - return state?.channelStateStream.map((cs) => cs.channel?.lastMessageAt); + return state!.channelStateStream.map((cs) => cs.channel?.lastMessageAt); } /// Channel updated date. DateTime? get updatedAt { _checkInitialized(); - - return state?._channelState.channel?.updatedAt; + return state!._channelState.channel?.updatedAt; } /// Channel updated date as a stream. - Stream? get updatedAtStream { + Stream get updatedAtStream { _checkInitialized(); - - return state?.channelStateStream.map((cs) => cs.channel?.updatedAt); + return state!.channelStateStream.map((cs) => cs.channel?.updatedAt); } /// Channel deletion date. DateTime? get deletedAt { _checkInitialized(); - - return state?._channelState.channel?.deletedAt; + return state!._channelState.channel?.deletedAt; } /// Channel deletion date as a stream. - Stream? get deletedAtStream { + Stream get deletedAtStream { _checkInitialized(); - - return state?.channelStateStream.map((cs) => cs.channel?.deletedAt); + return state!.channelStateStream.map((cs) => cs.channel?.deletedAt); } /// Channel member count. int? get memberCount { _checkInitialized(); - - return state?._channelState.channel?.memberCount; + return state!._channelState.channel?.memberCount; } /// Channel member count as a stream. - Stream? get memberCountStream { + Stream get memberCountStream { _checkInitialized(); - - return state?.channelStateStream.map((cs) => cs.channel?.memberCount); + return state!.channelStateStream.map((cs) => cs.channel?.memberCount); } /// Channel id. @@ -296,7 +288,7 @@ class Channel { /// Channel team. String? get team { _checkInitialized(); - return state?._channelState.channel?.team; + return state!._channelState.channel?.team; } /// Channel extra data. @@ -309,7 +301,7 @@ class Channel { } /// Channel extra data as a stream. - Stream> get extraDataStream { + Stream> get extraDataStream { _checkInitialized(); return state!.channelStateStream.map( (cs) => cs.channel?.extraData ?? _extraData, @@ -1760,9 +1752,9 @@ class ChannelClientState { (event) { final readList = List.from(_channelState.read); final userReadIndex = - read?.indexWhere((r) => r.user.id == event.user!.id); + read.indexWhere((r) => r.user.id == event.user!.id); - if (userReadIndex != null && userReadIndex != -1) { + if (userReadIndex != -1) { final userRead = readList.removeAt(userReadIndex); if (userRead.user.id == _channel._client.state.currentUser!.id) { unreadCount = 0; @@ -1783,15 +1775,15 @@ class ChannelClientState { List get messages => _channelState.messages; /// Channel message list as a stream. - Stream?> get messagesStream => channelStateStream + Stream> get messagesStream => channelStateStream .map((cs) => cs.messages) .distinct(const ListEquality().equals); /// Channel pinned message list. - List? get pinnedMessages => _channelState.pinnedMessages.toList(); + List get pinnedMessages => _channelState.pinnedMessages.toList(); /// Channel pinned message list as a stream. - Stream?> get pinnedMessagesStream => + Stream> get pinnedMessagesStream => channelStateStream.map((cs) => cs.pinnedMessages.toList()); /// Get channel last message. @@ -1800,8 +1792,8 @@ class ChannelClientState { : null; /// Get channel last message. - Stream get lastMessageStream => messagesStream - .map((event) => event?.isNotEmpty == true ? event!.last : null); + Stream get lastMessageStream => + messagesStream.map((event) => event.isNotEmpty ? event.last : null); /// Channel members list. List get members => _channelState.members @@ -1838,10 +1830,10 @@ class ChannelClientState { ); /// Channel read list. - List? get read => _channelState.read; + List get read => _channelState.read; /// Channel read list as a stream. - Stream?> get readStream => channelStateStream.map((cs) => cs.read); + Stream> get readStream => channelStateStream.map((cs) => cs.read); final BehaviorSubject _unreadCountController = BehaviorSubject.seeded(0); @@ -2092,7 +2084,7 @@ class ChannelClientState { .toList(); updateChannelState(_channelState.copyWith( - pinnedMessages: pinnedMessages!.where(_pinIsValid()).toList(), + pinnedMessages: pinnedMessages.where(_pinIsValid()).toList(), messages: expiredMessages, )); } diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index add2c3cf..b21acc7a 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -45,6 +45,7 @@ breakdown: 🐞 Fixed - Fixed `MessageInput` textField case where `input` is not enabled if the file picked from the camera is null. +- Fixed date dividers position/alignment in non reversed `MessageListView`. ## 2.1.2 diff --git a/packages/stream_chat_flutter/lib/src/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel_list_view.dart index eadbed59..d6cd454b 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_view.dart @@ -584,11 +584,16 @@ class _ChannelListViewState extends State { ), ], child: widget.channelPreviewBuilder?.call(context, channel) ?? - ChannelPreview( - onLongPress: widget.onChannelLongPress, - channel: channel, - onImageTap: () => widget.onImageTap?.call(channel), - onTap: (channel) => onTap(channel, widget.channelWidget), + DecoratedBox( + decoration: BoxDecoration( + color: chatThemeData.channelListViewTheme.backgroundColor, + ), + child: ChannelPreview( + onLongPress: widget.onChannelLongPress, + channel: channel, + onImageTap: () => widget.onImageTap?.call(channel), + onTap: (channel) => onTap(channel, widget.channelWidget), + ), ), ), ); diff --git a/packages/stream_chat_flutter/lib/src/channel_preview.dart b/packages/stream_chat_flutter/lib/src/channel_preview.dart index 333ee886..e01cd008 100644 --- a/packages/stream_chat_flutter/lib/src/channel_preview.dart +++ b/packages/stream_chat_flutter/lib/src/channel_preview.dart @@ -132,7 +132,7 @@ class ChannelPreview extends StatelessWidget { message: lastMessage!, size: channelPreviewTheme.indicatorIconSize, isMessageRead: channel.state!.read - ?.where((element) => + .where((element) => element.user.id != channel .client.state.currentUser!.id) diff --git a/packages/stream_chat_flutter/lib/src/gallery_footer.dart b/packages/stream_chat_flutter/lib/src/gallery_footer.dart index 365368ac..2b2858f8 100644 --- a/packages/stream_chat_flutter/lib/src/gallery_footer.dart +++ b/packages/stream_chat_flutter/lib/src/gallery_footer.dart @@ -142,9 +142,9 @@ class _GalleryFooterState extends State { mainAxisSize: MainAxisSize.min, children: [ Text( - '${widget.currentPage + 1} ' - '${context.translations.ofText} ' - '${widget.totalPages}', + context.translations.galleryPaginationText( + currentPage: widget.currentPage, + totalPages: widget.totalPages), style: galleryFooterThemeData.titleTextStyle, ), ], diff --git a/packages/stream_chat_flutter/lib/src/localization/translations.dart b/packages/stream_chat_flutter/lib/src/localization/translations.dart index 4c86e6e2..e603656c 100644 --- a/packages/stream_chat_flutter/lib/src/localization/translations.dart +++ b/packages/stream_chat_flutter/lib/src/localization/translations.dart @@ -299,8 +299,9 @@ abstract class Translations { /// The text shown for "You" String get youText; - /// The text shown for "Of" - String get ofText; + /// Gallery footer pagination text + String galleryPaginationText( + {required int currentPage, required int totalPages}); /// The text shown for "File" String get fileText; @@ -660,7 +661,9 @@ class DefaultTranslations implements Translations { String get youText => 'You'; @override - String get ofText => 'of'; + String galleryPaginationText( + {required int currentPage, required int totalPages}) => + '${currentPage + 1} of $totalPages'; @override String get fileText => 'File'; diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index a3830966..75d45707 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -505,8 +505,14 @@ class _MessageListViewState extends State { if (i == 1 || i == itemCount - 4) return const Offstage(); - final message = messages[i - 1]; - final nextMessage = messages[i - 2]; + late final Message message, nextMessage; + if (widget.reverse) { + message = messages[i - 1]; + nextMessage = messages[i - 2]; + } else { + message = messages[i - 2]; + nextMessage = messages[i - 1]; + } if (!Jiffy(message.createdAt.toLocal()).isSame( nextMessage.createdAt.toLocal(), Units.DAY, @@ -636,8 +642,7 @@ class _MessageListViewState extends State { } Positioned _buildFloatingDateDivider(int itemCount) => Positioned( - top: widget.reverse ? 20 : null, - bottom: widget.reverse ? null : 20, + top: 20, left: 0, right: 0, child: BetterStreamBuilder>( @@ -647,19 +652,36 @@ class _MessageListViewState extends State { if (a == null || b == null) { return false; } - final aTop = _getTopElementIndex(a); - final bTop = _getTopElementIndex(b); - return aTop == bTop; + if (widget.reverse) { + final aTop = _getTopElementIndex(a); + final bTop = _getTopElementIndex(b); + return aTop == bTop; + } else { + final aBottom = _getBottomElementIndex(a); + final bBottom = _getBottomElementIndex(b); + return aBottom == bBottom; + } }, builder: (context, values) { if (values.isEmpty || messages.isEmpty) { return const Offstage(); } - final index = _getTopElementIndex(values); + int? index; + if (widget.reverse) { + index = _getTopElementIndex(values); + } else { + index = _getBottomElementIndex(values); + } - if (index == null || index <= 2 || index >= itemCount - 3) { - return const Offstage(); + if (index == null) return const Offstage(); + + if (index <= 2 || index >= itemCount - 3) { + if (widget.reverse) { + index = itemCount - 4; + } else { + index = 2; + } } final message = messages[index - 2]; @@ -685,6 +707,15 @@ class _MessageListViewState extends State { .index; } + int? _getBottomElementIndex(Iterable values) { + final inView = values.where((position) => position.itemLeadingEdge < 1); + if (inView.isEmpty) return null; + return inView + .reduce((min, position) => + position.itemLeadingEdge < min.itemLeadingEdge ? position : min) + .index; + } + Widget _buildScrollToBottom() => StreamBuilder>( stream: Rx.combineLatest2( streamChannel!.channel.state!.isUpToDateStream.distinct(), @@ -915,7 +946,7 @@ class _MessageListViewState extends State { } final channel = streamChannel!.channel; - final readList = channel.state?.read?.where((read) { + final readList = channel.state?.read.where((read) { if (read.user.id == userId) return false; return read.lastRead.isAfter(message.createdAt) || read.lastRead.isAtSameMomentAs(message.createdAt); @@ -1203,8 +1234,7 @@ class _MessageListViewState extends State { MaterialPageRoute( builder: (_) => BetterStreamBuilder( stream: streamChannel!.channel.state!.messagesStream.map( - (messages) => - messages!.firstWhere((m) => m.id == message.id)), + (messages) => messages.firstWhere((m) => m.id == message.id)), initialData: message, builder: (_, data) => StreamChannel( channel: streamChannel!.channel, diff --git a/packages/stream_chat_flutter/test/src/channel_preview_test.dart b/packages/stream_chat_flutter/test/src/channel_preview_test.dart index 7d0c549e..d6f3a5ea 100644 --- a/packages/stream_chat_flutter/test/src/channel_preview_test.dart +++ b/packages/stream_chat_flutter/test/src/channel_preview_test.dart @@ -21,6 +21,8 @@ void main() { when(() => clientState.currentUser).thenReturn(user); when(() => clientState.currentUserStream) .thenAnswer((_) => Stream.value(user)); + when(() => channel.lastMessageAtStream) + .thenAnswer((_) => Stream.value(lastMessageAt)); when(() => channel.lastMessageAt).thenReturn(lastMessageAt); when(() => channel.state).thenReturn(channelState); when(() => channel.client).thenReturn(client); diff --git a/packages/stream_chat_flutter/test/src/default_translations_test.dart b/packages/stream_chat_flutter/test/src/default_translations_test.dart index e78c0115..14a4a318 100644 --- a/packages/stream_chat_flutter/test/src/default_translations_test.dart +++ b/packages/stream_chat_flutter/test/src/default_translations_test.dart @@ -167,7 +167,7 @@ void main() { expect(translations.withText, isNotNull); expect(translations.inText, isNotNull); expect(translations.youText, isNotNull); - expect(translations.ofText, isNotNull); + expect(translations.galleryPaginationText, isNotNull); expect(translations.fileText, isNotNull); expect(translations.replyToMessageLabel, isNotNull); }); diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index ef24e401..65280674 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,3 +1,8 @@ +## Upcoming + +🐞 Fixed +- [#612](https://github.com/GetStream/stream-chat-flutter/issues/612) `ChannelListView` pagination doesn't work after refresh + ## 2.1.1 - Updated llc dependency diff --git a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart index 46aaccee..6687eef5 100644 --- a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart @@ -106,6 +106,9 @@ class ChannelsBlocState extends State final client = _streamChatCoreState!.client; final clear = paginationParams.offset == 0; + if (clear && _paginationEnded) { + _paginationEnded = false; + } if ((!clear && _paginationEnded) || _queryChannelsLoadingController.value == true) { diff --git a/packages/stream_chat_localizations/CHANGELOG.md b/packages/stream_chat_localizations/CHANGELOG.md index 865741c4..d718ce12 100644 --- a/packages/stream_chat_localizations/CHANGELOG.md +++ b/packages/stream_chat_localizations/CHANGELOG.md @@ -1,7 +1,19 @@ ## Upcoming -* Added support for [Spanish](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart) locale. +✅ Added +* Added support for [Spanish](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart) locale. +* Added support for [Korean](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart) locale. +* Added support for [Japanese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart) locale. + +🔄 Changed + +* Some of the `Hindi` translations have been updated/changed for better understanding. + - 'रिप्लाई' -> 'जवाब दें' + - 'तस्वीरें' -> 'फ़ोटोज' + - 'बिता हुआ कल' -> 'कल' + - 'चैनल मौन है' -> 'चैनल म्यूट है' + ## 1.0.2 * Updated `stream_chat_flutter` dependency diff --git a/packages/stream_chat_localizations/README.md b/packages/stream_chat_localizations/README.md index c2ea8a72..131d6b13 100644 --- a/packages/stream_chat_localizations/README.md +++ b/packages/stream_chat_localizations/README.md @@ -35,6 +35,8 @@ At the moment we support the following languages: - [Italian](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart) - [French](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart) - [Spanish](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart) +- [Japanese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart) +- [Korean](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart) More languages will be added in the future. Feel free to [contribute](https://github.com/GetStream/stream-chat-flutter/blob/master/CONTRIBUTING.md) to add more languages. @@ -70,6 +72,8 @@ class MyApp extends StatelessWidget { Locale('fr'), Locale('it'), Locale('es'), + Locale('ja'), + Locale('ko'), ], // Add GlobalStreamChatLocalizations.delegates localizationsDelegates: GlobalStreamChatLocalizations.delegates, @@ -113,6 +117,8 @@ Example: fr it es + ja + ko ``` diff --git a/packages/stream_chat_localizations/example/lib/add_new_lang.dart b/packages/stream_chat_localizations/example/lib/add_new_lang.dart index f7eb280e..eced0616 100644 --- a/packages/stream_chat_localizations/example/lib/add_new_lang.dart +++ b/packages/stream_chat_localizations/example/lib/add_new_lang.dart @@ -374,7 +374,9 @@ class NnStreamChatLocalizations extends GlobalStreamChatLocalizations { String get youText => 'You'; @override - String get ofText => 'of'; + String galleryPaginationText( + {required int currentPage, required int totalPages}) => + '$currentPage of $totalPages'; @override String get fileText => 'File'; @@ -456,6 +458,9 @@ class MyApp extends StatelessWidget { Locale('hi'), Locale('fr'), Locale('it'), + Locale('es'), + Locale('ja'), + Locale('ko'), // Add support for additional 'nn' locale Locale('nn'), ], diff --git a/packages/stream_chat_localizations/example/lib/main.dart b/packages/stream_chat_localizations/example/lib/main.dart index c99a4c6c..c71ceb01 100644 --- a/packages/stream_chat_localizations/example/lib/main.dart +++ b/packages/stream_chat_localizations/example/lib/main.dart @@ -72,6 +72,9 @@ class MyApp extends StatelessWidget { Locale('hi'), Locale('fr'), Locale('it'), + Locale('es'), + Locale('ja'), + Locale('ko'), ], // Add GlobalStreamChatLocalizations.delegates localizationsDelegates: GlobalStreamChatLocalizations.delegates, diff --git a/packages/stream_chat_localizations/example/lib/override_lang.dart b/packages/stream_chat_localizations/example/lib/override_lang.dart index 5be8f9a6..a52fcf9f 100644 --- a/packages/stream_chat_localizations/example/lib/override_lang.dart +++ b/packages/stream_chat_localizations/example/lib/override_lang.dart @@ -97,6 +97,9 @@ class MyApp extends StatelessWidget { Locale('hi'), Locale('fr'), Locale('it'), + Locale('es'), + Locale('ja'), + Locale('ko'), ], // Add overridden "CustomStreamChatLocalizationsEn.delegate" along with // "GlobalStreamChatLocalizations.delegates" diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations.dart index a6a66b93..4094dcaa 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations.dart @@ -11,6 +11,10 @@ part 'stream_chat_localizations_fr.dart'; part 'stream_chat_localizations_it.dart'; +part 'stream_chat_localizations_ja.dart'; + +part 'stream_chat_localizations_ko.dart'; + part 'stream_chat_localizations_hi.dart'; /// The set of supported languages, as language code strings. @@ -27,6 +31,8 @@ const kStreamChatSupportedLanguages = { 'fr', 'it', 'es', + 'ja', + 'ko' }; /// Creates a [GlobalStreamChatLocalizations] instance for the given `locale`. @@ -59,6 +65,10 @@ GlobalStreamChatLocalizations? getStreamChatTranslation(Locale locale) { return const StreamChatLocalizationsIt(); case 'es': return const StreamChatLocalizationsEs(); + case 'ja': + return const StreamChatLocalizationsJa(); + case 'ko': + return const StreamChatLocalizationsKo(); } } diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart index 56f2ccfe..aa9b4249 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart @@ -350,7 +350,9 @@ class StreamChatLocalizationsEn extends GlobalStreamChatLocalizations { String get youText => 'You'; @override - String get ofText => 'of'; + String galleryPaginationText( + {required int currentPage, required int totalPages}) => + '${currentPage + 1} of $totalPages'; @override String get fileText => 'File'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart index 5602a4a5..7244e3d0 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart @@ -355,7 +355,9 @@ class StreamChatLocalizationsEs extends GlobalStreamChatLocalizations { String get youText => 'Usted'; @override - String get ofText => 'de'; + String galleryPaginationText( + {required int currentPage, required int totalPages}) => + '${currentPage + 1} de $totalPages'; @override String get fileText => 'Archivo'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart index f3ab6070..0a0b551a 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart @@ -207,7 +207,7 @@ class StreamChatLocalizationsFr extends GlobalStreamChatLocalizations { @override String togglePinUnpinText({required bool pinned}) { if (pinned) return 'Décrocher de la conversation'; - return 'Épingler à la discussion'; + return 'Épingler à la conversation'; } @override @@ -354,7 +354,9 @@ class StreamChatLocalizationsFr extends GlobalStreamChatLocalizations { String get youText => 'Vous'; @override - String get ofText => 'de'; + String galleryPaginationText( + {required int currentPage, required int totalPages}) => + '${currentPage + 1} de $totalPages'; @override String get fileText => 'Fichier'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart index 386651cb..5da076af 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart @@ -197,7 +197,7 @@ class StreamChatLocalizationsHi extends GlobalStreamChatLocalizations { 'कार्रवाई पूरी नहीं की जा सकी.'; @override - String get replyLabel => 'रिप्लाई'; + String get replyLabel => 'जवाब दें'; @override String togglePinUnpinText({required bool pinned}) { @@ -224,7 +224,7 @@ class StreamChatLocalizationsHi extends GlobalStreamChatLocalizations { } @override - String get photosLabel => 'तस्वीरें'; + String get photosLabel => 'फ़ोटोज'; String _getDay(DateTime dateTime) { final now = DateTime.now(); @@ -250,10 +250,10 @@ class StreamChatLocalizationsHi extends GlobalStreamChatLocalizations { String get todayLabel => 'आज'; @override - String get yesterdayLabel => 'बिता हुआ कल'; + String get yesterdayLabel => 'कल'; @override - String get channelIsMutedText => 'चैनल मौन है'; + String get channelIsMutedText => 'चैनल म्यूट है'; @override String get noTitleText => 'कोई शीर्षक नहीं'; @@ -340,16 +340,18 @@ class StreamChatLocalizationsHi extends GlobalStreamChatLocalizations { String get sendLabel => 'भेजें'; @override - String get withText => 'विद'; + String get withText => 'विद'; //TODO: break? @override - String get inText => 'इन'; + String get inText => 'इन'; //TODO: break? @override String get youText => 'आप'; @override - String get ofText => 'ऑफ़'; + String galleryPaginationText( + {required int currentPage, required int totalPages}) => + '${currentPage + 1} ऑफ़ $totalPages'; @override String get fileText => 'फ़ाइल'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart index b3740f23..84cf76f6 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart @@ -351,7 +351,9 @@ Il file è troppo grande per essere caricato. Il limite è di $limitInMB MB.'''; String get youText => 'te'; @override - String get ofText => 'di'; + String galleryPaginationText( + {required int currentPage, required int totalPages}) => + '${currentPage + 1} di $totalPages'; @override String get fileText => 'file'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart new file mode 100644 index 00000000..e6946125 --- /dev/null +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart @@ -0,0 +1,349 @@ +part of 'stream_chat_localizations.dart'; + +/// The translations for Japanese (`ja`). +class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations { + /// Create an instance of the translation bundle for Japanese. + const StreamChatLocalizationsJa({String localeName = 'ja'}) + : super(localeName: localeName); + + @override + String get launchUrlError => 'URLの起動ができません'; + + @override + String get loadingUsersError => 'ユーザーの読み込みができません'; + + @override + String get noUsersLabel => '現在、ユーザーはいません。'; + + @override + String get retryLabel => '再試行'; + + @override + String get userLastOnlineText => '前回のオンライン'; + + @override + String get userOnlineText => 'オンライン'; + + @override + String userTypingText(Iterable users) { + if (users.isEmpty) return ''; + final first = users.first; + if (users.length == 1) { + return '${first.name}が入力しています'; + } + return '${first.name}と${users.length - 1}人が入力しています'; + } + + @override + String get threadReplyLabel => 'スレッド返信'; + + @override + String get onlyVisibleToYouText => '自分しか見れません'; + + @override + String threadReplyCountText(int count) => '$countつのスレッド返信'; + + @override + String attachmentsUploadProgressText({ + required int remaining, + required int total, + }) => + '$remaining/${total}mbのアップロード中 。。。'; + + @override + String pinnedByUserText({ + required User pinnedBy, + required User currentUser, + }) { + final pinnedByCurrentUser = currentUser.id == pinnedBy.id; + if (pinnedByCurrentUser) return 'あなたのピン'; + return '${pinnedBy.name}のピン'; + } + + @override + String get emptyMessagesText => '現在、メッセージはありません。'; + + @override + String get genericErrorText => 'エラーが発生しました'; + + @override + String get loadingMessagesError => 'メッセージの読み込みエラー'; + + @override + String resultCountText(int count) => '$count件の結果'; + + @override + String get messageDeletedText => 'このメッセージは削除されました。'; + + @override + String get messageDeletedLabel => 'メッセージ削除'; + + @override + String get messageReactionsLabel => 'メッセージのリアクション'; + + @override + String get emptyChatMessagesText => 'チャットがありませんが。。。'; + + @override + String threadSeparatorText(int replyCount) => '$replyCount件の返信'; + + @override + String get connectedLabel => '接続しています'; + + @override + String get disconnectedLabel => '接続切れ'; + + @override + String get reconnectingLabel => '再接続中。。。'; + + @override + String get alsoSendAsDirectMessageLabel => 'ダイレクトメッセージでも送信'; + + @override + String get addACommentOrSendLabel => 'コメントの追加や送信'; + + @override + String get searchGifLabel => 'GIFの検索'; + + @override + String get writeAMessageLabel => 'メッセージを書く'; + + @override + String get instantCommandsLabel => 'インスタントコマンド'; + + @override + String fileTooLargeAfterCompressionError(double limitInMB) => + 'ファイルのサイズが大きすぎてアップロードできません。' + 'ファイルサイズの制限は${limitInMB}MBです。' + '圧縮してみましたが、十分ではありませんでした。'; + + @override + String fileTooLargeError(double limitInMB) => + 'ファイルが大きすぎてアップロードできません。ファイルサイズの制限は${limitInMB}MBです。'; + + @override + String emojiMatchingQueryText(String query) => '「"$query"」とお揃いの絵文字'; + + @override + String get addAFileLabel => 'ファイルの追加'; + + @override + String get photoFromCameraLabel => 'カメラからの写真'; + + @override + String get uploadAFileLabel => 'ファイルのアップロード'; + + @override + String get uploadAPhotoLabel => '写真のアップロード'; + + @override + String get uploadAVideoLabel => '動画のアップロード'; + + @override + String get videoFromCameraLabel => 'カメラからの動画'; + + @override + String get okLabel => 'よし'; + + @override + String get somethingWentWrongError => 'エラーが発生しました'; + + @override + String get addMoreFilesLabel => 'ファイルの追加'; + + @override + String get enablePhotoAndVideoAccessMessage => 'お友達と共有できるように、写真' + '\nやビデオへのアクセスを有効にしてください。'; + @override + String get allowGalleryAccessMessage => 'ギャラリーへのアクセスを許可する'; + + @override + String get flagMessageLabel => 'メッセージをフラグする'; + + @override + String get flagMessageQuestion => 'このメッセージのコピーを' + '\nモデレーターに送って、さらに調査してもらいますか?'; + + @override + String get flagLabel => 'フラグする'; + + @override + String get cancelLabel => 'キャンセル'; + + @override + String get flagMessageSuccessfulLabel => 'メッセージにフラグが付けられました'; + + @override + String get flagMessageSuccessfulText => 'このメッセージはモデレーターに報告されました。'; + + @override + String get deleteLabel => '削除'; + + @override + String get deleteMessageLabel => 'メッセージを削除する '; + + @override + String get deleteMessageQuestion => 'このメッセージ' + '\nを完全に削除してもよろしいですか?'; + + @override + String get operationCouldNotBeCompletedText => '操作を完了できませんでした。'; + + @override + String get replyLabel => '返信'; + + @override + String togglePinUnpinText({required bool pinned}) { + if (pinned) return '会話のピンを外す'; + return '会話にピンする'; + } + + @override + String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { + if (isDeleteFailed) return 'メッセージの削除を再試行する'; + return 'メッセージを削除する'; + } + + @override + String get copyMessageLabel => 'メッセージをコピーする'; + + @override + String get editMessageLabel => 'メッセージを編集する'; + + @override + String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { + if (isUpdateFailed) return '編集したメッセージを再送する'; + return '再送'; + } + + @override + String get photosLabel => '写真'; + + String _getDay(DateTime dateTime) { + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final yesterday = DateTime(now.year, now.month, now.day - 1); + + final date = DateTime(dateTime.year, dateTime.month, dateTime.day); + + if (date == today) { + return '今日'; + } else if (date == yesterday) { + return '昨日'; + } else { + return '${Jiffy(date).MMMd}に'; + } + } + + @override + String sentAtText({required DateTime date, required DateTime time}) => + '${_getDay(date)}の${Jiffy(time.toLocal()).format('HH:mm')}に送信しました '; + + @override + String get todayLabel => '今日'; + + @override + String get yesterdayLabel => '昨日'; + + @override + String get channelIsMutedText => 'チャンネルが無音されています'; + + @override + String get noTitleText => 'タイトル無し'; + + @override + String get letsStartChattingLabel => 'チャットを始めよう!'; + + @override + String get sendingFirstMessageLabel => '友人に最初のメッセージを送りましょうか?'; + + @override + String get startAChatLabel => 'チャットを開始する'; + + @override + String get loadingChannelsError => 'チャネルのロード中にエラーが発生しました'; + + @override + String get deleteConversationLabel => '会話を削除する'; + + @override + String get deleteConversationQuestion => '本当に会話を削除しますか?'; + + @override + String get streamChatLabel => 'ストリームチャット'; + + @override + String get searchingForNetworkText => 'ネットワークを検索中'; + + @override + String get offlineLabel => 'オフライン。。。'; + + @override + String get tryAgainLabel => '再試行する'; + + @override + String membersCountText(int count) => '$count人のメンバー'; + + @override + String watchersCountText(int count) => '$count人がオンライン'; + + @override + String get viewInfoLabel => '情報を見る'; + + @override + String get leaveGroupLabel => 'グループを離れる'; + + @override + String get leaveLabel => '離れる'; + + @override + String get leaveConversationLabel => '会話を離れる'; + + @override + String get leaveConversationQuestion => '本当に会話を離れますか?'; + + @override + String get showInChatLabel => 'チャットで表示'; + + @override + String get saveImageLabel => '画像を保存'; + + @override + String get saveVideoLabel => 'ビデオを保存'; + + @override + String get uploadErrorLabel => 'アップロードエラー'; + + @override + String get giphyLabel => 'ギフィー'; + + @override + String get shuffleLabel => 'ミックス'; + + @override + String get sendLabel => '送信'; + + @override + String get withText => 'と'; + + @override + String get inText => 'に'; + + // This is the word for 'customer' or 'user' because saying 'you' directly + //is too informal and rude + @override + String get youText => 'あなた'; + + @override + String galleryPaginationText({ + required int currentPage, + required int totalPages, + }) => + '${currentPage + 1} / $totalPages'; + + @override + String get fileText => 'ファイル'; + + @override + String get replyToMessageLabel => 'メッセージに返信'; +} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart new file mode 100644 index 00000000..2642b991 --- /dev/null +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart @@ -0,0 +1,347 @@ +part of 'stream_chat_localizations.dart'; + +/// The translations for Korean (`ko`). +class StreamChatLocalizationsKo extends GlobalStreamChatLocalizations { + /// Create an instance of the translation bundle for Korean. + const StreamChatLocalizationsKo({String localeName = 'ko'}) + : super(localeName: localeName); + + @override + String get launchUrlError => 'URL을 시작할 수 없습니다'; + + @override + String get loadingUsersError => '사용자를 로드하는 중 오류 발생'; + + @override + String get noUsersLabel => '현재 사용자가 없습니다'; + + @override + String get retryLabel => '다시 시도하십시오'; + + @override + String get userLastOnlineText => '마지막 온라인입니다'; + + @override + String get userOnlineText => '온라인'; + + @override + String userTypingText(Iterable users) { + if (users.isEmpty) return ''; + final first = users.first; + if (users.length == 1) { + return '${first.name} 타이핑중'; + } + return '${first.name}하고 ${users.length - 1}명 타이핑중'; + } + + @override + String get threadReplyLabel => '스레드 응답입니다'; + + @override + String get onlyVisibleToYouText => '당신만 볼 수 있습니다'; + + @override + String threadReplyCountText(int count) => '$count스레드 답장'; + + @override + String attachmentsUploadProgressText({ + required int remaining, + required int total, + }) => + '$remaining/${total}mb를 업로드중...'; + + @override + String pinnedByUserText({ + required User pinnedBy, + required User currentUser, + }) { + final pinnedByCurrentUser = currentUser.id == pinnedBy.id; + if (pinnedByCurrentUser) return '당신의 핀'; + return '${pinnedBy.name}의 핀'; + } + + @override + String get emptyMessagesText => '현재 메시지가 없습니다'; + + @override + String get genericErrorText => '뭔가 잘못됐습니다'; + + @override + String get loadingMessagesError => '메시지를 로드하는 동안 오류가 발생했습니다'; + + @override + String resultCountText(int count) => '$count개의 결과'; + + @override + String get messageDeletedText => '이 메시지는 삭제되었습니다.'; + + @override + String get messageDeletedLabel => '메시지가 삭제되었습니다'; + + @override + String get messageReactionsLabel => '메시지에 대한 응답'; + + @override + String get emptyChatMessagesText => '아직 채팅이 없습니다...'; + + @override + String threadSeparatorText(int replyCount) => '$replyCount개의 답장'; + + @override + String get connectedLabel => '연결중'; + + @override + String get disconnectedLabel => '연결이 끊겼습니다'; + + @override + String get reconnectingLabel => '다시 연결하는 중...'; + + @override + String get alsoSendAsDirectMessageLabel => '다이렉트 메시지로도 보냅니다'; + + @override + String get addACommentOrSendLabel => '주석을 추가하거나 보냅니다'; + + @override + String get searchGifLabel => 'GIF 검색'; + + @override + String get writeAMessageLabel => '메시지 쓰기'; + + @override + String get instantCommandsLabel => '인스턴트 커맨즈'; + + @override + String fileTooLargeAfterCompressionError(double limitInMB) => + '파일이 너무 커서 업로드할 수 없습니다. ' + '파일 크기 제한은 ${limitInMB}MB입니다. ' + '우리는 압축해 보았지만 충분하지 않았습니다.'; + + @override + String fileTooLargeError(double limitInMB) => + '파일이 너무 커서 업로드할 수 없습니다. 파일 크기 제한은 ${limitInMB}MB입니다.'; + + @override + String emojiMatchingQueryText(String query) => '"$query"과 일치하는 이모티콘입니다'; + + @override + String get addAFileLabel => '파일을 추가함'; + + @override + String get photoFromCameraLabel => '카메라에서 찍은 사진'; + + @override + String get uploadAFileLabel => '파일을 업로드함'; + + @override + String get uploadAPhotoLabel => '사진을 업로드함'; + + @override + String get uploadAVideoLabel => '비디오를 업로드함'; + + @override + String get videoFromCameraLabel => '카메라의 비디오.'; + + @override + String get okLabel => '확인'; + + @override + String get somethingWentWrongError => '뭔가 잘못됐습느다'; + + @override + String get addMoreFilesLabel => '파일을 추가함'; + + @override + String get enablePhotoAndVideoAccessMessage => '친구와 공유할 수 있도록 사진과' + '\n동영상에 액세스할 수 있도록 설정하십시오.'; + + @override + String get allowGalleryAccessMessage => '갤러리에 대한 액세스를 허용합니다'; + + @override + String get flagMessageLabel => ' 메시지를 플래그함'; + + @override + String get flagMessageQuestion => '추가 조사를 위해 진행자에게 이 메시지의 복사본을 전송하시겠습니까?'; + + @override + String get flagLabel => '플래그함'; + + @override + String get cancelLabel => '취소'; + + @override + String get flagMessageSuccessfulLabel => '메시지에 플래그가 지정되었습니다'; + + @override + String get flagMessageSuccessfulText => '메시지가 진행자에게 보고되었습니다.'; + + @override + String get deleteLabel => '삭제'; + + @override + String get deleteMessageLabel => '메시지를 삭제합니다.'; + + @override + String get deleteMessageQuestion => '이 메시지를 완전히 삭제하시겠습니까?'; + + @override + String get operationCouldNotBeCompletedText => '작업을 완료할 수 없습니다.'; + + @override + String get replyLabel => '답글'; + + @override + String togglePinUnpinText({required bool pinned}) { + if (pinned) return '대화의 핀을 분리합니다'; + return '대화에 고정합니다'; + } + + @override + String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { + if (isDeleteFailed) return '메시지 삭제를 다시 시도합니다'; + return '메시지를 삭제합니다'; + } + + @override + String get copyMessageLabel => '메시지를 복사합니다.'; + + @override + String get editMessageLabel => '메시지를 편집합니다.'; + + @override + String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { + if (isUpdateFailed) return '편집된 메시지를 다시 보냅니다.'; + return '다시 보냅니다.'; + } + + @override + String get photosLabel => '사진'; + + String _getDay(DateTime dateTime) { + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final yesterday = DateTime(now.year, now.month, now.day - 1); + + final date = DateTime(dateTime.year, dateTime.month, dateTime.day); + + if (date == today) { + return '오늘'; + } else if (date == yesterday) { + return '어제'; + } else { + return '${Jiffy(date).MMMd}에'; + } + } + + @override + String sentAtText({required DateTime date, required DateTime time}) => + '${_getDay(date)} ${Jiffy(time.toLocal()).format('HH:mm')}에 보냈습니다'; + + @override + String get todayLabel => '오늘'; + + @override + String get yesterdayLabel => '어제'; + + @override + String get channelIsMutedText => '채널이 음소거됩니다.'; + + @override + String get noTitleText => '제목이 없습니다.'; + + @override + String get letsStartChattingLabel => '채팅 시작해요!'; + + @override + String get sendingFirstMessageLabel => '친구에게 첫 메시지를 보내 볼까요?'; + + @override + String get startAChatLabel => '대화를 시작합니다.'; + + @override + String get loadingChannelsError => '채널을 로드하는 동안 오류가 발생했습니다.'; + + @override + String get deleteConversationLabel => '대화를 삭제합니다.'; + + @override + String get deleteConversationQuestion => '대화를 삭제하시겠습니까?'; + + @override + String get streamChatLabel => '스트림 채팅'; + + @override + String get searchingForNetworkText => '네트워크를 검색하는 중입니다.'; + + @override + String get offlineLabel => '오프라인...'; + + @override + String get tryAgainLabel => '다시 시도합니다'; + + @override + String membersCountText(int count) => '$count명'; + + @override + String watchersCountText(int count) => '$count명이 온라인'; + + @override + String get viewInfoLabel => '정보를 보기'; + + @override + String get leaveGroupLabel => '그룹을 떠납니다.'; + + @override + String get leaveLabel => '떠나다'; + + @override + String get leaveConversationLabel => '대화에서 떠납니다.'; + + @override + String get leaveConversationQuestion => '정말 이 대화에서 나가시겠습니까?'; + + @override + String get showInChatLabel => '채팅에 표시합니다.'; + + @override + String get saveImageLabel => '이미지를 저장합니다.'; + + @override + String get saveVideoLabel => '비디오를 저장합니다.'; + + @override + String get uploadErrorLabel => '업로드 오류'; + + @override + String get giphyLabel => '지피'; + + @override + String get shuffleLabel => '섞기'; + + @override + String get sendLabel => '보내기'; + + @override + String get withText => '함께'; + + @override + String get inText => '에'; + + // This is the word for 'customer' or 'user' because saying 'you' directly + // is too informal and rude + @override + String get youText => '당신'; + + @override + String galleryPaginationText( + {required int currentPage, required int totalPages}) => + '${currentPage + 1} / $totalPages'; + //3 / 11 + + @override + String get fileText => '파일'; + + @override + String get replyToMessageLabel => '메시지에 회신합니다.'; +} diff --git a/packages/stream_chat_localizations/test/translations_test.dart b/packages/stream_chat_localizations/test/translations_test.dart index 0e621159..c7314a58 100644 --- a/packages/stream_chat_localizations/test/translations_test.dart +++ b/packages/stream_chat_localizations/test/translations_test.dart @@ -174,7 +174,7 @@ void main() { expect(localizations.withText, isNotNull); expect(localizations.inText, isNotNull); expect(localizations.youText, isNotNull); - expect(localizations.ofText, isNotNull); + expect(localizations.galleryPaginationText, isNotNull); expect(localizations.fileText, isNotNull); expect(localizations.replyToMessageLabel, isNotNull); });