后端接口从 6 个扩到 13 个,全部走真数据:
- 新增 api:playback_api(/app/episodes/{id}/play)、watch_api(/app/watch/*)、
library_api(/app/favorites*);票据从 CatalogApi 搬到 PlaybackApi
- 新增 repository:PlaybackRepository(含票据 TTL 缓存,默认 10 分钟,时钟可注入)、
FavoriteRepository、WatchHistoryRepository 换成服务端模型(每剧一条续看记录,删掉 demoProfileId)
- PlayerLogic:起播前取票(拿不到给三态 UI,失败可重试);accessType 驱动锁定文案,
不在前端猜;签名地址原样交给播放器(只补 host,不动 path/query);
进度上报节流 5s → 10s,切集/退出/切后台各补一次,播完带 finished
- 续看定位:服务端记录里的那一集才 seek(其它集从头播),续看位置进详情/播放页时读一次
- FavoriteController:收藏以服务端为准,乐观更新 + 失败回滚;退出登录清空
- UI 三态:详情页(加载/失败可重试/空剧集 + 续看进度条 + 收藏按钮)、
播放页(票据加载中/失败可重试/后端判定不可播)、我的页(续看失败可重试、收藏三态、最近观看)
- l10n 新增 8 个键(en/zh/id 三份):网络/进度/收藏失败、锁定提示、续看文案等
测试:flutter test 188 全过(存量 27 个测试文件未跑红);新增票据过期重取、进度节流、
续看定位、收藏回滚、签名 query 原样保留、真实后端端到端联调(live_watch_flow)
573 lines
12 KiB
Dart
573 lines
12 KiB
Dart
// ignore: unused_import
|
|
import 'package:intl/intl.dart' as intl;
|
|
import 'app_localizations.dart';
|
|
|
|
// ignore_for_file: type=lint
|
|
|
|
/// The translations for Chinese (`zh`).
|
|
class AppLocalizationsZh extends AppLocalizations {
|
|
AppLocalizationsZh([String locale = 'zh']) : super(locale);
|
|
|
|
@override
|
|
String get appName => 'FruitShorts';
|
|
|
|
@override
|
|
String get demo => '演示';
|
|
|
|
@override
|
|
String get homeHeadline => '即刻观看';
|
|
|
|
@override
|
|
String get homeSubtitle => '免费短剧预览。';
|
|
|
|
@override
|
|
String get mediaNotice => '用户提供的本地演示视频 · 免费观看';
|
|
|
|
@override
|
|
String get featuredDemo => '精选 · 技术演示';
|
|
|
|
@override
|
|
String get demoTitle => '信号';
|
|
|
|
@override
|
|
String get demoSynopsis => '使用本地视频进行播放预览,后续有素材再增加剧集。';
|
|
|
|
@override
|
|
String get homeTab => '首页';
|
|
|
|
@override
|
|
String get openQuickMenu => '打开快捷菜单';
|
|
|
|
@override
|
|
String get quickAccess => '快捷入口';
|
|
|
|
@override
|
|
String get currentSeries => '当前短剧';
|
|
|
|
@override
|
|
String get discoverTab => '找剧';
|
|
|
|
@override
|
|
String get searchDiscover => '搜索短剧、演员、类型…';
|
|
|
|
@override
|
|
String get featured => '精选';
|
|
|
|
@override
|
|
String get suspense => '悬疑';
|
|
|
|
@override
|
|
String get romance => '都市爱情';
|
|
|
|
@override
|
|
String get actionGenre => '动作';
|
|
|
|
@override
|
|
String get filter => '筛选';
|
|
|
|
@override
|
|
String get topCharts => '排行榜';
|
|
|
|
@override
|
|
String get newReleases => '新剧';
|
|
|
|
@override
|
|
String get trendingMicroDramas => '热门短剧';
|
|
|
|
@override
|
|
String get seeAll => '查看全部';
|
|
|
|
@override
|
|
String get artworkOnly => '海报展示';
|
|
|
|
@override
|
|
String get artworkOnlyNotice => '这张海报仅用于目录展示,暂时没有可播放视频。';
|
|
|
|
@override
|
|
String get videoPending => '视频待提供';
|
|
|
|
@override
|
|
String get watchNow => '免费观看';
|
|
|
|
@override
|
|
String get noSearchResults => '没有匹配的短剧。';
|
|
|
|
@override
|
|
String get demoCatalogNotice => '三部演示短剧共用同一条本地视频。';
|
|
|
|
@override
|
|
String get myTab => '我的';
|
|
|
|
@override
|
|
String get localViewer => '本地观众';
|
|
|
|
@override
|
|
String get localDemoProfile => '本地演示档案';
|
|
|
|
@override
|
|
String get continueWatching => '继续观看';
|
|
|
|
@override
|
|
String watchedPercent(int percent) {
|
|
return '已观看 $percent%';
|
|
}
|
|
|
|
@override
|
|
String get resume => '继续播放';
|
|
|
|
@override
|
|
String get history => '历史';
|
|
|
|
@override
|
|
String get watchlist => '追剧';
|
|
|
|
@override
|
|
String get likedTab => '喜欢';
|
|
|
|
@override
|
|
String get all => '全部';
|
|
|
|
@override
|
|
String get inProgress => '未看完';
|
|
|
|
@override
|
|
String get completed => '已看完';
|
|
|
|
@override
|
|
String get noSavedShows => '这里还没有剧集。';
|
|
|
|
@override
|
|
String get preferences => '偏好设置';
|
|
|
|
@override
|
|
String get appLanguage => '应用语言';
|
|
|
|
@override
|
|
String get followingTab => '关注';
|
|
|
|
@override
|
|
String get animatedTab => '漫剧';
|
|
|
|
@override
|
|
String get recommendedTab => '推荐';
|
|
|
|
@override
|
|
String get noCategoryDramas => '这个分类暂时没有剧集。';
|
|
|
|
@override
|
|
String get trendingTab => '热门';
|
|
|
|
@override
|
|
String get liveActionTab => '真人剧';
|
|
|
|
@override
|
|
String get search => '搜索';
|
|
|
|
@override
|
|
String get free => '免费';
|
|
|
|
@override
|
|
String get mute => '静音';
|
|
|
|
@override
|
|
String get unmute => '取消静音';
|
|
|
|
@override
|
|
String get like => '喜欢';
|
|
|
|
@override
|
|
String get save => '收藏';
|
|
|
|
@override
|
|
String get comments => '评论';
|
|
|
|
@override
|
|
String get share => '分享';
|
|
|
|
@override
|
|
String get localOnlyComments => '仅在本机可见';
|
|
|
|
@override
|
|
String get noComments => '还没有评论。';
|
|
|
|
@override
|
|
String get writeComment => '写评论';
|
|
|
|
@override
|
|
String get postComment => '发布';
|
|
|
|
@override
|
|
String get shareFailed => '无法打开系统分享面板。';
|
|
|
|
@override
|
|
String get nowPlaying => '正在播放';
|
|
|
|
@override
|
|
String get watchFullscreen => '横屏全屏观看';
|
|
|
|
@override
|
|
String get exitFullscreen => '退出全屏';
|
|
|
|
@override
|
|
String get pictureInPicture => '画中画';
|
|
|
|
@override
|
|
String get pipUnavailable => '此设备无法使用画中画';
|
|
|
|
@override
|
|
String get originalTag => '原创';
|
|
|
|
@override
|
|
String get dramaTag => '短剧';
|
|
|
|
@override
|
|
String episodeLabel(int number) {
|
|
return '第 $number 集';
|
|
}
|
|
|
|
@override
|
|
String get exploreSeries => '查看剧集';
|
|
|
|
@override
|
|
String episodeCount(int count) {
|
|
return '$count 集';
|
|
}
|
|
|
|
@override
|
|
String technicalDemoCount(int count) {
|
|
return '免费试看 · $count 集';
|
|
}
|
|
|
|
@override
|
|
String get episodes => '选集';
|
|
|
|
@override
|
|
String get watchEpisodeOne => '观看第 1 集';
|
|
|
|
@override
|
|
String continueEpisode(int number) {
|
|
return '续看第 $number 集';
|
|
}
|
|
|
|
@override
|
|
String get freeTestMedia => '复用同一演示视频 · 免费';
|
|
|
|
@override
|
|
String get lockedStoreUnavailable => '已锁定 · 商店未配置';
|
|
|
|
@override
|
|
String get freeTestClip => '免费测试片';
|
|
|
|
@override
|
|
String get locked => '已锁定';
|
|
|
|
@override
|
|
String get freeSamplesNotice => '前两集免费 · 商店配置待完成';
|
|
|
|
@override
|
|
String get demoTestMedia => '免费 · 同一演示视频';
|
|
|
|
@override
|
|
String episodeProgress(int number, int total) {
|
|
return '第 $number / $total 集';
|
|
}
|
|
|
|
@override
|
|
String get testVideoNotice => '自行生成的技术测试视频';
|
|
|
|
@override
|
|
String lockedTitle(int number) {
|
|
return '第 $number 集已锁定';
|
|
}
|
|
|
|
@override
|
|
String get lockedBody => '完成商店与服务端验单配置后才能解锁。本演示目前不处理付款。';
|
|
|
|
@override
|
|
String get playbackError => '此视频暂时无法播放。';
|
|
|
|
@override
|
|
String get catalogError => '剧目列表加载失败。';
|
|
|
|
@override
|
|
String get detailError => '剧集信息加载失败。';
|
|
|
|
@override
|
|
String get noMediaNotice => '该剧集还没有可播放的片源。';
|
|
|
|
@override
|
|
String get noStories => '暂无剧目。';
|
|
|
|
@override
|
|
String get retry => '重试';
|
|
|
|
@override
|
|
String get backToSeries => '返回剧集详情';
|
|
|
|
@override
|
|
String get nextEpisode => '下一集';
|
|
|
|
@override
|
|
String get pause => '暂停';
|
|
|
|
@override
|
|
String get play => '播放';
|
|
|
|
@override
|
|
String get seekVideo => '拖动播放进度';
|
|
|
|
@override
|
|
String get seekFailed => '无法调整播放进度。';
|
|
|
|
@override
|
|
String get captions => '字幕';
|
|
|
|
@override
|
|
String get captionsOff => '关闭字幕';
|
|
|
|
@override
|
|
String get englishCaptions => '英文字幕';
|
|
|
|
@override
|
|
String get chineseCaptions => '简体中文字幕';
|
|
|
|
@override
|
|
String get language => '界面语言';
|
|
|
|
@override
|
|
String get systemLanguage => '跟随系统';
|
|
|
|
@override
|
|
String get english => 'English';
|
|
|
|
@override
|
|
String get simplifiedChinese => '简体中文';
|
|
|
|
@override
|
|
String get episodeOneTitle => '初见微光';
|
|
|
|
@override
|
|
String get episodeTwoTitle => '新的频率';
|
|
|
|
@override
|
|
String get episodeThreeTitle => '越过边界';
|
|
|
|
@override
|
|
String get episodeFourTitle => '回声之后';
|
|
|
|
@override
|
|
String get dramaSignalTitle => '信号';
|
|
|
|
@override
|
|
String get dramaSignalSynopsis => '一部竖屏连播的演示短剧,每一集都使用同一段本地视频。';
|
|
|
|
@override
|
|
String get dramaCityTitle => '都市灯火与丝绒夜';
|
|
|
|
@override
|
|
String get dramaCitySynopsis => '一部都市爱情演示短剧,用同一段本地视频体验切换和续看。';
|
|
|
|
@override
|
|
String get dramaVengeanceTitle => '复仇崛起';
|
|
|
|
@override
|
|
String get dramaVengeanceSynopsis => '一部动作演示短剧,用同一段本地视频体验切换和续看。';
|
|
|
|
@override
|
|
String get dramaEchoesTitle => '命运回声';
|
|
|
|
@override
|
|
String get genreSuspenseMystery => '悬疑 · 推理';
|
|
|
|
@override
|
|
String get genreUrbanRomance => '都市爱情';
|
|
|
|
@override
|
|
String get genreActionThriller => '动作 · 惊悚';
|
|
|
|
@override
|
|
String get genreDramaMystery => '剧情 · 悬疑';
|
|
|
|
@override
|
|
String get continueWatchingHeading => '继续观看';
|
|
|
|
@override
|
|
String get preferencesHeading => '偏好设置';
|
|
|
|
@override
|
|
String get brandMark => 'FRUITSHORTS';
|
|
|
|
@override
|
|
String get shareFooter => 'FruitShorts';
|
|
|
|
@override
|
|
String get shortsTab => '短剧';
|
|
|
|
@override
|
|
String get myListTab => '片单';
|
|
|
|
@override
|
|
String get watchFull => '看全集';
|
|
|
|
@override
|
|
String get swipeForNext => '上滑看下一部';
|
|
|
|
@override
|
|
String get clearScreen => '清屏';
|
|
|
|
@override
|
|
String get exitClearScreen => '退出清屏';
|
|
|
|
@override
|
|
String get logIn => '登录';
|
|
|
|
@override
|
|
String get logInHint => '登录后保存金币和片单';
|
|
|
|
@override
|
|
String get rewards => '奖励';
|
|
|
|
@override
|
|
String get forYou => '为你推荐';
|
|
|
|
@override
|
|
String get hot => '热门';
|
|
|
|
@override
|
|
String get newest => '最新';
|
|
|
|
@override
|
|
String get favorite => '收藏';
|
|
|
|
@override
|
|
String get myAccount => '我的账户';
|
|
|
|
@override
|
|
String get coins => '金币';
|
|
|
|
@override
|
|
String get topUpWallet => '充值';
|
|
|
|
@override
|
|
String get vipMembership => '会员';
|
|
|
|
@override
|
|
String get notActive => '未开通';
|
|
|
|
@override
|
|
String get joinVip => '开通会员';
|
|
|
|
@override
|
|
String get earnRewards => '赚奖励';
|
|
|
|
@override
|
|
String get freeBonus => '领免费奖励';
|
|
|
|
@override
|
|
String get watchHistory => '观看历史';
|
|
|
|
@override
|
|
String get settings => '设置';
|
|
|
|
@override
|
|
String get signOut => '退出登录';
|
|
|
|
@override
|
|
String get mockUserName => 'Vicky sheng';
|
|
|
|
@override
|
|
String get mockUserId => 'ID: M1CZWVYE';
|
|
|
|
@override
|
|
String get mockNotice => '当前是示例数据。';
|
|
|
|
@override
|
|
String get topBadge => 'TOP 1';
|
|
|
|
@override
|
|
String get signIn => '登录';
|
|
|
|
@override
|
|
String get signUp => '注册';
|
|
|
|
@override
|
|
String get authSignInTitle => '欢迎回来';
|
|
|
|
@override
|
|
String get authSignUpTitle => '创建账号';
|
|
|
|
@override
|
|
String get authSubtitle => '用邮箱和密码登录。';
|
|
|
|
@override
|
|
String get authEmailLabel => '邮箱';
|
|
|
|
@override
|
|
String get authPasswordLabel => '密码';
|
|
|
|
@override
|
|
String get authPasswordHint => '至少 8 位,需包含字母和数字';
|
|
|
|
@override
|
|
String get authEmailRequired => '请输入邮箱';
|
|
|
|
@override
|
|
String get authEmailInvalid => '邮箱格式不正确';
|
|
|
|
@override
|
|
String get authPasswordRequired => '请输入密码';
|
|
|
|
@override
|
|
String get authPasswordLength => '密码长度需在 8-64 位之间';
|
|
|
|
@override
|
|
String get authPasswordWeak => '密码需同时包含字母和数字';
|
|
|
|
@override
|
|
String get authNetworkError => '网络异常,请稍后重试';
|
|
|
|
@override
|
|
String get authSwitchToSignUp => '还没有账号?去注册';
|
|
|
|
@override
|
|
String get authSwitchToSignIn => '已有账号?去登录';
|
|
|
|
@override
|
|
String get authGateTitle => '登录后可继续';
|
|
|
|
@override
|
|
String get authGateMessage => '收藏、评论等操作需要登录。';
|
|
|
|
@override
|
|
String get authGateAction => '去登录';
|
|
|
|
@override
|
|
String get cancel => '暂不';
|
|
|
|
@override
|
|
String get noEpisodesYet => '还没有可播放的剧集。';
|
|
|
|
@override
|
|
String get networkError => '网络异常,请检查网络后重试。';
|
|
|
|
@override
|
|
String get progressError => '续看进度加载失败。';
|
|
|
|
@override
|
|
String get favoritesError => '收藏列表加载失败。';
|
|
|
|
@override
|
|
String get favoriteToggleFailed => '收藏操作失败,请重试。';
|
|
|
|
@override
|
|
String get remove => '移除';
|
|
|
|
@override
|
|
String get unlockComingSoon => '金币解锁功能即将开放。';
|
|
|
|
@override
|
|
String resumeProgress(Object position, Object total) {
|
|
return '续看至 $position / $total';
|
|
}
|
|
}
|