From 19196ceb8eebf1339aa21a3a1ea85e25aadc37d6 Mon Sep 17 00:00:00 2001 From: Vicky <10212746+haifeng-jiang@user.noreply.gitee.com> Date: Fri, 30 Oct 2026 17:14:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(i18n):=20=E5=8D=B0=E5=B0=BC=E8=AF=AD?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E7=BC=BA=E5=A4=B1=E6=97=B6=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=9B=9E=E9=80=80=E4=B8=AD=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/data/models/drama.dart | 4 +- lib/core/data/models/locale_text.dart | 19 +++- lib/core/formatting/content_locale_guard.dart | 21 +++++ .../widgets/discover_search_results.dart | 13 ++- .../home/widgets/home_category_dramas.dart | 11 ++- lib/pages/home/widgets/home_feed_grid.dart | 16 +++- .../recommend/recommend_discover_page.dart | 6 +- lib/pages/recommend/recommend_page.dart | 14 ++- .../widgets/all_dramas_filter_grid.dart | 39 +++++--- .../widgets/playlist_detail_page.dart | 32 ++++--- .../widgets/recommend_sections_view.dart | 87 ++++++++++-------- test/config/l10n_id_test.dart | 6 +- test/formatting/localized_content_test.dart | 88 ++++++++++++++----- 13 files changed, 261 insertions(+), 95 deletions(-) create mode 100644 lib/core/formatting/content_locale_guard.dart diff --git a/lib/core/data/models/drama.dart b/lib/core/data/models/drama.dart index 6611f72..23e30dd 100644 --- a/lib/core/data/models/drama.dart +++ b/lib/core/data/models/drama.dart @@ -241,11 +241,11 @@ class Drama { } String localizedTitle(String languageCode) { - return localizedText(languageCode, title, titleLocales); + return localizedRequiredText(languageCode, title, titleLocales); } String localizedSynopsis(String languageCode) { - return localizedText(languageCode, synopsis, synopsisLocales); + return localizedRequiredText(languageCode, synopsis, synopsisLocales); } String localizedTag(String languageCode) { diff --git a/lib/core/data/models/locale_text.dart b/lib/core/data/models/locale_text.dart index 38d4860..058004d 100644 --- a/lib/core/data/models/locale_text.dart +++ b/lib/core/data/models/locale_text.dart @@ -7,10 +7,27 @@ Map localeMap(Object? raw) { } /// 按当前语言取文案。没有对应语言时用中文,中文也没有时用 fallback。 -String localizedText(String languageCode, String fallback, Map locales) { +String localizedText( + String languageCode, + String fallback, + Map locales, +) { final selected = locales[languageCode] ?? ''; if (selected.isNotEmpty) return selected; final chinese = locales['zh'] ?? ''; if (chinese.isNotEmpty) return chinese; return fallback; } + +/// 按当前语言取必须本地化的文案;印尼语缺失时不回退中文。 +String localizedRequiredText( + String languageCode, + String fallback, + Map locales, +) { + final primaryCode = languageCode.split(RegExp(r'[_-]')).first; + final selected = locales[languageCode] ?? locales[primaryCode] ?? ''; + if (selected.trim().isNotEmpty) return selected; + if (primaryCode == 'id') return ''; + return localizedText(primaryCode, fallback, locales); +} diff --git a/lib/core/formatting/content_locale_guard.dart b/lib/core/formatting/content_locale_guard.dart new file mode 100644 index 0000000..8735d83 --- /dev/null +++ b/lib/core/formatting/content_locale_guard.dart @@ -0,0 +1,21 @@ +// Author: fruit.com.cn + +import '../data/models/drama.dart'; + +const String _indonesianLanguageCode = 'id'; + +/// 判断短剧是否满足当前界面语言的基础文案完整度。 +bool isDramaCompleteForLocale(Drama drama, String languageCode) { + final primaryCode = languageCode.split(RegExp(r'[_-]')).first; + if (primaryCode != _indonesianLanguageCode) return true; + return drama.localizedTitle(primaryCode).trim().isNotEmpty && + drama.localizedSynopsis(primaryCode).trim().isNotEmpty; +} + +/// 过滤不满足首发语言文案完整度的短剧,保持原顺序。 +List visibleDramasForLocale( + Iterable dramas, + String languageCode, +) => dramas + .where((drama) => isDramaCompleteForLocale(drama, languageCode)) + .toList(growable: false); diff --git a/lib/pages/discover/widgets/discover_search_results.dart b/lib/pages/discover/widgets/discover_search_results.dart index 076c427..dd46451 100644 --- a/lib/pages/discover/widgets/discover_search_results.dart +++ b/lib/pages/discover/widgets/discover_search_results.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import '../../../core/data/models/drama.dart'; import '../../../core/formatting/error_text.dart'; +import '../../../core/formatting/content_locale_guard.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/widgets/empty_state.dart'; import '../../../l10n/app_localizations.dart'; @@ -33,10 +34,18 @@ class DiscoverSearchResults extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; - if (!loading && error == null && results.isNotEmpty) { + final visibleResults = visibleDramasForLocale( + results, + Localizations.localeOf(context).languageCode, + ); + if (!loading && error == null && visibleResults.isNotEmpty) { return SliverPadding( padding: _topPadding, - sliver: SliverDramaGrid(l10n: l10n, dramas: results, onOpen: onOpen), + sliver: SliverDramaGrid( + l10n: l10n, + dramas: visibleResults, + onOpen: onOpen, + ), ); } return SliverPadding( diff --git a/lib/pages/home/widgets/home_category_dramas.dart b/lib/pages/home/widgets/home_category_dramas.dart index 8abcbb7..9e0bafc 100644 --- a/lib/pages/home/widgets/home_category_dramas.dart +++ b/lib/pages/home/widgets/home_category_dramas.dart @@ -7,6 +7,7 @@ import 'package:provider/provider.dart'; import '../../../core/data/models/drama.dart'; import '../../../core/data/services/category_controller.dart'; +import '../../../core/formatting/content_locale_guard.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/app_pull_to_refresh.dart'; import '../../../core/widgets/drama_grid_skeleton.dart'; @@ -77,7 +78,10 @@ class _HomeCategoryDramasState extends State { final controller = context.watch(); final categoryId = widget.lockedCategoryId ?? ''; final slot = categoryId.isEmpty ? null : controller.slotOf(categoryId); - final dramas = slot?.dramas ?? const []; + final dramas = visibleDramasForLocale( + slot?.dramas ?? const [], + Localizations.localeOf(context).languageCode, + ); return Column( children: [ Expanded( @@ -164,9 +168,8 @@ class _HomeCategoryDramasState extends State { hasMore: slot?.hasMore ?? false, hasItems: dramas.isNotEmpty, errorMessage: null, - onRetry: () => context.read().loadMore( - categoryId, - ), + onRetry: () => + context.read().loadMore(categoryId), ), ), ), diff --git a/lib/pages/home/widgets/home_feed_grid.dart b/lib/pages/home/widgets/home_feed_grid.dart index 3afbc7f..a07b7d8 100644 --- a/lib/pages/home/widgets/home_feed_grid.dart +++ b/lib/pages/home/widgets/home_feed_grid.dart @@ -12,6 +12,7 @@ import '../../../core/data/services/catalog_controller.dart'; import '../../../core/data/services/category_controller.dart'; import '../../../core/data/services/home_feed_controller.dart'; import '../../../core/data/services/wallet_controller.dart'; +import '../../../core/formatting/content_locale_guard.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/app_pull_to_refresh.dart'; @@ -194,7 +195,10 @@ class _HomeFeedGridState extends State { mainAxisSpacing: AppDimens.homeGridRowGap, ); } - final dramas = _dramasFor(category); + final dramas = _dramasFor( + category, + Localizations.localeOf(context).languageCode, + ); if (widget.feedController.loadingFirstPage && dramas.isEmpty) { return const DramaGridSkeleton( key: Key('home-grid-skeleton'), @@ -324,8 +328,14 @@ class _HomeFeedGridState extends State { ]; } - List _dramasFor(HomeGridCategoryShortcut category) { - final dramas = widget.feedController.dramas; + List _dramasFor( + HomeGridCategoryShortcut category, + String languageCode, + ) { + final dramas = visibleDramasForLocale( + widget.feedController.dramas, + languageCode, + ); final categoryName = category.categoryName.toLowerCase(); if (categoryName.isEmpty) return dramas; final matched = dramas diff --git a/lib/pages/recommend/recommend_discover_page.dart b/lib/pages/recommend/recommend_discover_page.dart index ce9a17f..8178700 100644 --- a/lib/pages/recommend/recommend_discover_page.dart +++ b/lib/pages/recommend/recommend_discover_page.dart @@ -9,6 +9,7 @@ import '../../core/data/models/drama.dart'; import '../../core/data/models/home_feed.dart'; import '../../core/data/remote/catalog_api.dart'; import '../../core/data/services/category_controller.dart'; +import '../../core/formatting/content_locale_guard.dart'; import '../../core/theme/app_colors.dart'; import '../../core/widgets/app_pull_to_refresh.dart'; import '../../core/widgets/empty_state.dart'; @@ -284,7 +285,10 @@ class _ChannelFeedState extends State<_ChannelFeed> super.build(context); final categories = context.watch(); final slot = categories.slotOf(widget.categoryId); - final dramas = slot.dramas; + final dramas = visibleDramasForLocale( + slot.dramas, + Localizations.localeOf(context).languageCode, + ); final showCarousel = widget.isFeatured && widget.banners.isNotEmpty; final bottomInset = MediaQuery.of(context).padding.bottom; diff --git a/lib/pages/recommend/recommend_page.dart b/lib/pages/recommend/recommend_page.dart index cb580b0..b2f69a5 100644 --- a/lib/pages/recommend/recommend_page.dart +++ b/lib/pages/recommend/recommend_page.dart @@ -13,6 +13,7 @@ import '../../core/data/services/download_controller.dart'; import '../../core/data/services/home_feed_controller.dart'; import '../../core/data/services/playlist_controller.dart'; import '../../core/data/services/wallet_controller.dart'; +import '../../core/formatting/content_locale_guard.dart'; import '../../core/theme/app_colors.dart'; import '../../core/widgets/empty_state.dart'; import '../../l10n/app_localizations.dart'; @@ -289,7 +290,10 @@ class _RecommendPageState extends State Widget _recommendFeed(BuildContext context) { final feed = widget.feedController; - final dramas = widget.feedController.dramas; + final dramas = visibleDramasForLocale( + widget.feedController.dramas, + Localizations.localeOf(context).languageCode, + ); if (dramas.isNotEmpty) { return PlayerPage( key: const Key('recommend-feed-player'), @@ -327,6 +331,9 @@ class _RecommendPageState extends State key: const Key('recommend-feed-empty'), icon: Icons.wifi_off_rounded, title: l10n.networkError, + actionLabel: l10n.retry, + actionKey: const Key('recommend-feed-retry'), + onAction: _retryRecommendData, ), ); } @@ -340,6 +347,11 @@ class _RecommendPageState extends State ); } + void _retryRecommendData() { + _cancelRecommendRetries(); + unawaited(_refreshRecommendData()); + } + PlaylistController? _watchPlaylists(BuildContext context) { try { return context.watch(); diff --git a/lib/pages/recommend/widgets/all_dramas_filter_grid.dart b/lib/pages/recommend/widgets/all_dramas_filter_grid.dart index aeff9b7..623bb22 100644 --- a/lib/pages/recommend/widgets/all_dramas_filter_grid.dart +++ b/lib/pages/recommend/widgets/all_dramas_filter_grid.dart @@ -10,6 +10,7 @@ import '../../../core/data/models/drama.dart'; import '../../../core/data/remote/catalog_api.dart'; import '../../../core/data/services/all_dramas_controller.dart'; import '../../../core/data/services/home_feed_controller.dart'; +import '../../../core/formatting/content_locale_guard.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/app_pull_to_refresh.dart'; @@ -49,7 +50,8 @@ class _AllDramasFilterGridState extends State { @override void initState() { super.initState(); - _library = widget.controller ?? + _library = + widget.controller ?? AllDramasController( api: widget.catalogApi == null ? null @@ -74,8 +76,9 @@ class _AllDramasFilterGridState extends State { await widget.feedController?.loadMore(); } - List get _dramas => - _library.usesRemote ? _library.dramas : widget.feedController?.dramas ?? _library.dramas; + List get _dramas => _library.usesRemote + ? _library.dramas + : widget.feedController?.dramas ?? _library.dramas; @override Widget build(BuildContext context) { @@ -86,7 +89,9 @@ class _AllDramasFilterGridState extends State { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _filters(l10n), - Expanded(child: _dramaList(l10n, _itemExtent(constraints.maxWidth))), + Expanded( + child: _dramaList(l10n, _itemExtent(constraints.maxWidth)), + ), ], ); }, @@ -95,13 +100,18 @@ class _AllDramasFilterGridState extends State { double _itemExtent(double width) { final itemWidth = - (width - AppDimens.allDramasHorizontal * 2 - AppDimens.allDramasGridGap * 2) / + (width - + AppDimens.allDramasHorizontal * 2 - + AppDimens.allDramasGridGap * 2) / _gridColumns; return itemWidth / AppDimens.cardCoverAspectRatio + _cardBodyExtent; } Widget _dramaList(AppLocalizations l10n, double itemExtent) { - final dramas = _dramas; + final dramas = visibleDramasForLocale( + _dramas, + Localizations.localeOf(context).languageCode, + ); return NotificationListener( onNotification: _onScroll, child: AppPullToRefresh( @@ -140,7 +150,9 @@ class _AllDramasFilterGridState extends State { Widget _grid(List dramas, double itemExtent) { return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: AppDimens.allDramasHorizontal), + padding: const EdgeInsets.symmetric( + horizontal: AppDimens.allDramasHorizontal, + ), sliver: SliverGrid( key: const Key('all-dramas-grid'), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( @@ -229,7 +241,11 @@ class _AllDramasFilterGridState extends State { ); } - Widget _optionRow(_FilterRow spec, String languageCode, AppLocalizations l10n) { + Widget _optionRow( + _FilterRow spec, + String languageCode, + AppLocalizations l10n, + ) { return _filterRow( row: spec.row, child: _horizontalChipScroll( @@ -302,7 +318,9 @@ class _AllDramasFilterGridState extends State { ), alignment: Alignment.center, decoration: BoxDecoration( - color: selected ? AppColors.filterChipSelected : AppColors.transparent, + color: selected + ? AppColors.filterChipSelected + : AppColors.transparent, borderRadius: BorderRadius.circular(AppDimens.filterChipRadius), ), child: Text( @@ -323,7 +341,8 @@ class _AllDramasFilterGridState extends State { bool _onScroll(ScrollNotification notification) { if (notification.depth != 0) return false; if (notification.metrics.pixels <= 0) return false; - final reachedBottom = notification.metrics.extentAfter <= _loadMoreThreshold; + final reachedBottom = + notification.metrics.extentAfter <= _loadMoreThreshold; if (reachedBottom && (notification is ScrollUpdateNotification || notification is ScrollEndNotification || diff --git a/lib/pages/recommend/widgets/playlist_detail_page.dart b/lib/pages/recommend/widgets/playlist_detail_page.dart index ae0f13c..4b9933b 100644 --- a/lib/pages/recommend/widgets/playlist_detail_page.dart +++ b/lib/pages/recommend/widgets/playlist_detail_page.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import '../../../core/data/models/drama.dart'; import '../../../core/data/models/playlist_detail.dart'; +import '../../../core/formatting/content_locale_guard.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/app_pull_to_refresh.dart'; @@ -54,8 +55,7 @@ class _PlaylistDetailPageState extends State { bool _loadingMore = false; bool _failed = false; - bool get _hasMore => - widget.loadPage != null && _dramas.length < _dramaCount; + bool get _hasMore => widget.loadPage != null && _dramas.length < _dramaCount; Future _refresh() async { final loadPage = widget.loadPage; @@ -112,7 +112,8 @@ class _PlaylistDetailPageState extends State { Widget build(BuildContext context) { final width = MediaQuery.sizeOf(context).width; final itemWidth = (width - _pageHorizontal * 2 - _gridGap) / 2; - final itemExtent = itemWidth / AppDimens.cardCoverAspectRatio + _cardBodyExtent; + final itemExtent = + itemWidth / AppDimens.cardCoverAspectRatio + _cardBodyExtent; return Scaffold( key: const Key('playlist-detail-page'), backgroundColor: AppColors.ink, @@ -157,6 +158,10 @@ class _PlaylistDetailPageState extends State { } Widget _body(double itemExtent) { + final visibleDramas = visibleDramasForLocale( + _dramas, + Localizations.localeOf(context).languageCode, + ); return CustomScrollView( key: const Key('playlist-detail-scroll'), physics: AppPullToRefresh.physics, @@ -168,7 +173,9 @@ class _PlaylistDetailPageState extends State { _pageHorizontal, 12, ), - sliver: SliverToBoxAdapter(child: _PlaylistCollage(dramas: _dramas)), + sliver: SliverToBoxAdapter( + child: _PlaylistCollage(dramas: visibleDramas), + ), ), SliverPadding( padding: const EdgeInsets.symmetric(horizontal: _pageHorizontal), @@ -192,10 +199,11 @@ class _PlaylistDetailPageState extends State { ), delegate: SliverChildBuilderDelegate( (context, index) => HomeFeedCard( - drama: _dramas[index], - onTap: () => playDramaFromCatalog(context, _dramas[index]), + drama: visibleDramas[index], + onTap: () => + playDramaFromCatalog(context, visibleDramas[index]), ), - childCount: _dramas.length, + childCount: visibleDramas.length, ), ), ), @@ -205,9 +213,13 @@ class _PlaylistDetailPageState extends State { child: PagedListFooter( loading: _loadingMore, hasMore: _hasMore, - hasItems: _dramas.isNotEmpty, - errorMessage: _failed ? AppLocalizations.of(context)!.catalogError : null, - onRetry: _loadingMore ? _loadMore : (_hasMore ? _loadMore : _refresh), + hasItems: visibleDramas.isNotEmpty, + errorMessage: _failed + ? AppLocalizations.of(context)!.catalogError + : null, + onRetry: _loadingMore + ? _loadMore + : (_hasMore ? _loadMore : _refresh), ), ), ), diff --git a/lib/pages/recommend/widgets/recommend_sections_view.dart b/lib/pages/recommend/widgets/recommend_sections_view.dart index e64527d..87be83b 100644 --- a/lib/pages/recommend/widgets/recommend_sections_view.dart +++ b/lib/pages/recommend/widgets/recommend_sections_view.dart @@ -8,6 +8,7 @@ import '../../../core/data/models/playlist_detail.dart'; import '../../../core/data/services/category_controller.dart'; import '../../../core/data/services/home_feed_controller.dart'; import '../../../core/data/services/playlist_controller.dart'; +import '../../../core/formatting/content_locale_guard.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/app_pull_to_refresh.dart'; @@ -43,7 +44,8 @@ class RecommendSectionsView extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; - final source = feedController.dramas; + final languageCode = Localizations.localeOf(context).languageCode; + final source = visibleDramasForLocale(feedController.dramas, languageCode); final remote = playlists; return AppPullToRefresh( key: const Key('recommend-sections-refresh'), @@ -83,39 +85,39 @@ class RecommendSectionsView extends StatelessWidget { source: source, ); return [ - _section( - context, - l10n, - key: 'hits', - title: l10n.weeklyHitsTitle, - subtitle: l10n.weeklyHitsSubtitle, - dramas: hits.take(_sectionPreviewCount).toList(), - onMore: () => onMore(RecommendSectionKind.hits), - ), - const SizedBox(height: AppDimens.recommendSectionGap), - _section( - context, - l10n, - key: 'new', - title: l10n.weeklyNewTitle, - subtitle: l10n.weeklyNewSubtitle, - dramas: newest.take(_sectionPreviewCount).toList(), - onMore: () => onMore(RecommendSectionKind.newReleases), - ), - const SizedBox(height: AppDimens.recommendSectionGap), - _section( - context, - l10n, - key: 'costume', - title: l10n.costumeAwardsTitle, - subtitle: l10n.costumeAwardsSubtitle, - dramas: recommendSectionDramas( - kind: RecommendSectionKind.costume, - source: source, - categories: categories, - ).take(_sectionPreviewCount).toList(), - onMore: () => onMore(RecommendSectionKind.costume), - ), + _section( + context, + l10n, + key: 'hits', + title: l10n.weeklyHitsTitle, + subtitle: l10n.weeklyHitsSubtitle, + dramas: hits.take(_sectionPreviewCount).toList(), + onMore: () => onMore(RecommendSectionKind.hits), + ), + const SizedBox(height: AppDimens.recommendSectionGap), + _section( + context, + l10n, + key: 'new', + title: l10n.weeklyNewTitle, + subtitle: l10n.weeklyNewSubtitle, + dramas: newest.take(_sectionPreviewCount).toList(), + onMore: () => onMore(RecommendSectionKind.newReleases), + ), + const SizedBox(height: AppDimens.recommendSectionGap), + _section( + context, + l10n, + key: 'costume', + title: l10n.costumeAwardsTitle, + subtitle: l10n.costumeAwardsSubtitle, + dramas: recommendSectionDramas( + kind: RecommendSectionKind.costume, + source: source, + categories: categories, + ).take(_sectionPreviewCount).toList(), + onMore: () => onMore(RecommendSectionKind.costume), + ), ]; } @@ -163,7 +165,10 @@ class RecommendSectionsView extends StatelessWidget { subtitle: playlist.localizedDescription( Localizations.localeOf(context).languageCode, ), - dramas: playlist.dramas.take(_sectionPreviewCount).toList(), + dramas: visibleDramasForLocale( + playlist.dramas, + Localizations.localeOf(context).languageCode, + ).take(_sectionPreviewCount).toList(), onMore: () => onOpenPlaylist?.call(playlist), ), ); @@ -320,9 +325,15 @@ List recommendSectionDramas({ int _newestFirst(Drama leftDrama, Drama rightDrama) { final left = publishedAtForDrama(leftDrama); final right = publishedAtForDrama(rightDrama); - if (left == null && right == null) return rightDrama.id.compareTo(leftDrama.id); - if (left == null) return 1; - if (right == null) return -1; + if (left == null && right == null) { + return rightDrama.id.compareTo(leftDrama.id); + } + if (left == null) { + return 1; + } + if (right == null) { + return -1; + } return right.compareTo(left); } diff --git a/test/config/l10n_id_test.dart b/test/config/l10n_id_test.dart index 0686c0b..88bd0ff 100644 --- a/test/config/l10n_id_test.dart +++ b/test/config/l10n_id_test.dart @@ -29,9 +29,9 @@ void main() { for (final locale in const [Locale('en'), Locale('zh'), Locale('id')]) { final l10n = await AppLocalizations.delegate.load(locale); - expect(l10n.appName, 'FruitShorts', reason: '$locale appName'); - expect(l10n.brandMark, 'FRUITSHORTS', reason: '$locale brandMark'); - expect(l10n.shareFooter, 'FruitShorts', reason: '$locale shareFooter'); + expect(l10n.appName, 'ymb', reason: '$locale appName'); + expect(l10n.brandMark, 'YMB', reason: '$locale brandMark'); + expect(l10n.shareFooter, 'ymb', reason: '$locale shareFooter'); expect(l10n.brandMark.toLowerCase(), isNot(contains('demo'))); expect(l10n.shareFooter.toLowerCase(), isNot(contains('demo'))); } diff --git a/test/formatting/localized_content_test.dart b/test/formatting/localized_content_test.dart index efe7bb7..9e1e834 100644 --- a/test/formatting/localized_content_test.dart +++ b/test/formatting/localized_content_test.dart @@ -1,37 +1,44 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:fruitshorts/core/data/models/drama.dart'; import 'package:fruitshorts/core/formatting/localized_content.dart'; +import 'package:fruitshorts/core/formatting/content_locale_guard.dart'; import 'package:fruitshorts/l10n/app_localizations_en.dart'; import 'package:fruitshorts/l10n/app_localizations_id.dart'; import 'package:fruitshorts/l10n/app_localizations_zh.dart'; -Drama _drama({required String id, String title = 'raw', String synopsis = 'raw'}) => - Drama( - id: id, - title: title, - synopsis: synopsis, - posterUrl: '/files/poster.png', - episodes: const [], - ); +Drama _drama({ + required String id, + String title = 'raw', + String synopsis = 'raw', +}) => Drama( + id: id, + title: title, + synopsis: synopsis, + posterUrl: '/files/poster.png', + episodes: const [], +); void main() { final en = AppLocalizationsEn(); final zh = AppLocalizationsZh(); final id = AppLocalizationsId(); - test('should_map_each_seeded_drama_to_its_own_localized_copy_when_looked_up', () { - final signal = _drama(id: 'the-signal-demo'); - final city = _drama(id: 'city-lights-demo'); - final vengeance = _drama(id: 'vengeance-demo'); + test( + 'should_map_each_seeded_drama_to_its_own_localized_copy_when_looked_up', + () { + final signal = _drama(id: 'the-signal-demo'); + final city = _drama(id: 'city-lights-demo'); + final vengeance = _drama(id: 'vengeance-demo'); - expect(localizedDramaTitle(en, signal), en.dramaSignalTitle); - expect(localizedDramaTitle(en, city), en.dramaCityTitle); - expect(localizedDramaTitle(en, vengeance), en.dramaVengeanceTitle); + expect(localizedDramaTitle(en, signal), en.dramaSignalTitle); + expect(localizedDramaTitle(en, city), en.dramaCityTitle); + expect(localizedDramaTitle(en, vengeance), en.dramaVengeanceTitle); - expect(localizedDramaSynopsis(en, signal), en.dramaSignalSynopsis); - expect(localizedDramaSynopsis(en, city), en.dramaCitySynopsis); - expect(localizedDramaSynopsis(en, vengeance), en.dramaVengeanceSynopsis); - }); + expect(localizedDramaSynopsis(en, signal), en.dramaSignalSynopsis); + expect(localizedDramaSynopsis(en, city), en.dramaCitySynopsis); + expect(localizedDramaSynopsis(en, vengeance), en.dramaVengeanceSynopsis); + }, + ); test('should_keep_two_dramas_apart_when_localized_copy_is_mapped', () { final titles = { @@ -70,6 +77,47 @@ void main() { expect(localizedDramaSynopsis(zh, custom), 'Custom synopsis'); }); + test('should_not_fall_back_to_chinese_when_indonesian_copy_is_missing', () { + const drama = Drama( + id: 'missing-id-copy', + title: '古城', + synopsis: '中文简介', + titleLocales: {'zh': '古城'}, + synopsisLocales: {'zh': '中文简介'}, + posterUrl: '/files/poster.png', + ); + + expect(drama.localizedTitle('id'), isEmpty); + expect(drama.localizedSynopsis('id'), isEmpty); + }); + + test('should_filter_incomplete_indonesian_content_from_visible_list', () { + const complete = Drama( + id: 'complete-id-copy', + title: 'Kota Lama', + synopsis: 'Kisah dari kota lama.', + titleLocales: {'id': 'Kota Lama'}, + synopsisLocales: {'id': 'Kisah dari kota lama.'}, + posterUrl: '/files/complete.png', + ); + const incomplete = Drama( + id: 'incomplete-id-copy', + title: '古城', + synopsis: '中文简介', + titleLocales: {'zh': '古城'}, + synopsisLocales: {'zh': '中文简介'}, + posterUrl: '/files/incomplete.png', + ); + + expect(isDramaCompleteForLocale(complete, 'id'), isTrue); + expect(isDramaCompleteForLocale(incomplete, 'id'), isFalse); + expect(visibleDramasForLocale([complete, incomplete], 'id'), [complete]); + expect(visibleDramasForLocale([complete, incomplete], 'en'), [ + complete, + incomplete, + ]); + }); + test('should_ignore_raw_catalog_text_when_drama_is_seeded', () { final signal = _drama( id: 'the-signal-demo', @@ -107,7 +155,7 @@ void main() { expect(copies, hasLength(3)); for (final copy in copies) { - expect(copy, contains('FruitShorts')); + expect(copy, contains('ymb')); expect(copy.split('\n'), hasLength(3)); expect(copy.split('\n').every((line) => line.isNotEmpty), isTrue); }