后端接口从 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)
244 lines
8.1 KiB
Plaintext
244 lines
8.1 KiB
Plaintext
{
|
|
"@@locale": "id",
|
|
"appName": "FruitShorts",
|
|
"demo": "Demo",
|
|
"homeHeadline": "Tonton sekarang",
|
|
"homeSubtitle": "Pratinjau drama pendek gratis.",
|
|
"mediaNotice": "Video demo lokal dari pengguna · Pratinjau gratis",
|
|
"featuredDemo": "UNGGULAN · DEMO TEKNIS",
|
|
"demoTitle": "Sinyal",
|
|
"demoSynopsis": "Pratinjau video lokal singkat. Episode lain bisa ditambahkan saat medianya tersedia.",
|
|
"homeTab": "Beranda",
|
|
"openQuickMenu": "Buka menu cepat",
|
|
"quickAccess": "Akses cepat",
|
|
"currentSeries": "Serial saat ini",
|
|
"discoverTab": "Jelajahi",
|
|
"searchDiscover": "Cari drama, aktor, genre...",
|
|
"featured": "Unggulan",
|
|
"suspense": "Thriller",
|
|
"romance": "Romantis",
|
|
"actionGenre": "Aksi",
|
|
"filter": "Filter",
|
|
"topCharts": "Peringkat Teratas",
|
|
"newReleases": "Rilisan Baru",
|
|
"trendingMicroDramas": "Drama Mini Populer",
|
|
"seeAll": "Lihat Semua",
|
|
"artworkOnly": "Gambar",
|
|
"artworkOnlyNotice": "Ini hanya gambar katalog. Videonya belum tersedia.",
|
|
"videoPending": "Video belum siap",
|
|
"watchNow": "Tonton gratis",
|
|
"noSearchResults": "Tidak ada drama yang cocok.",
|
|
"demoCatalogNotice": "Tiga drama demo memakai video lokal yang sama.",
|
|
"myTab": "Saya",
|
|
"localViewer": "Penonton Lokal",
|
|
"localDemoProfile": "Profil demo lokal",
|
|
"continueWatching": "Lanjut Tonton",
|
|
"watchedPercent": "Sudah ditonton {percent}%",
|
|
"@watchedPercent": {
|
|
"placeholders": {
|
|
"percent": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
"resume": "Lanjutkan",
|
|
"history": "Riwayat",
|
|
"watchlist": "Daftar Tonton",
|
|
"likedTab": "Disukai",
|
|
"all": "Semua",
|
|
"inProgress": "Sedang Berjalan",
|
|
"completed": "Selesai",
|
|
"noSavedShows": "Belum ada tontonan di sini.",
|
|
"preferences": "Preferensi",
|
|
"appLanguage": "Bahasa Aplikasi",
|
|
"followingTab": "Mengikuti",
|
|
"animatedTab": "Animasi",
|
|
"recommendedTab": "Rekomendasi",
|
|
"noCategoryDramas": "Belum ada drama di kategori ini.",
|
|
"trendingTab": "Sedang Tren",
|
|
"liveActionTab": "Live Action",
|
|
"search": "Cari",
|
|
"free": "Gratis",
|
|
"mute": "Bisukan",
|
|
"unmute": "Nyalakan suara",
|
|
"like": "Suka",
|
|
"save": "Simpan",
|
|
"comments": "Komentar",
|
|
"share": "Bagikan",
|
|
"localOnlyComments": "Hanya terlihat di perangkat ini",
|
|
"noComments": "Belum ada komentar.",
|
|
"writeComment": "Tulis komentar",
|
|
"postComment": "Kirim",
|
|
"shareFailed": "Tidak bisa membuka menu berbagi.",
|
|
"nowPlaying": "Sedang diputar",
|
|
"watchFullscreen": "Tonton mode lanskap",
|
|
"exitFullscreen": "Keluar dari layar penuh",
|
|
"pictureInPicture": "Gambar dalam gambar",
|
|
"pipUnavailable": "Gambar dalam gambar tidak tersedia di perangkat ini",
|
|
"originalTag": "Original",
|
|
"dramaTag": "Drama pendek",
|
|
"episodeLabel": "Episode {number}",
|
|
"@episodeLabel": {
|
|
"placeholders": {
|
|
"number": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
"exploreSeries": "Lihat serial",
|
|
"episodeCount": "{count} EPISODE",
|
|
"@episodeCount": {
|
|
"placeholders": {
|
|
"count": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
"technicalDemoCount": "PRATINJAU GRATIS · {count} EPISODE",
|
|
"@technicalDemoCount": {
|
|
"placeholders": {
|
|
"count": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
"episodes": "Episode",
|
|
"watchEpisodeOne": "Tonton episode 1",
|
|
"continueEpisode": "Lanjut episode {number}",
|
|
"@continueEpisode": {
|
|
"placeholders": {
|
|
"number": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
"freeTestMedia": "Klip demo sama · gratis",
|
|
"lockedStoreUnavailable": "Terkunci · toko belum tersedia",
|
|
"freeTestClip": "Klip uji gratis",
|
|
"locked": "Terkunci",
|
|
"freeSamplesNotice": "Dua sampel gratis · pengaturan toko belum selesai",
|
|
"demoTestMedia": "GRATIS · KLIP DEMO SAMA",
|
|
"episodeProgress": "EPISODE {number} / {total}",
|
|
"@episodeProgress": {
|
|
"placeholders": {
|
|
"number": {
|
|
"type": "int"
|
|
},
|
|
"total": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
"testVideoNotice": "Video uji teknis buatan sendiri",
|
|
"lockedTitle": "Episode {number} terkunci",
|
|
"@lockedTitle": {
|
|
"placeholders": {
|
|
"number": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
"lockedBody": "Pembukaan kunci tersedia setelah toko dan verifikasi disiapkan. Demo ini tidak memproses pembayaran.",
|
|
"playbackError": "Klip ini tidak bisa diputar.",
|
|
"catalogError": "Katalog gagal dimuat.",
|
|
"detailError": "Daftar episode gagal dimuat.",
|
|
"noMediaNotice": "Episode ini belum punya media yang bisa diputar.",
|
|
"noStories": "Belum ada cerita.",
|
|
"retry": "Coba lagi",
|
|
"backToSeries": "Kembali ke serial",
|
|
"nextEpisode": "Episode berikutnya",
|
|
"pause": "Jeda",
|
|
"play": "Putar",
|
|
"seekVideo": "Geser posisi video",
|
|
"seekFailed": "Gagal mengubah posisi putar.",
|
|
"captions": "Subtitle",
|
|
"captionsOff": "Subtitle nonaktif",
|
|
"englishCaptions": "Subtitle bahasa Inggris",
|
|
"chineseCaptions": "Subtitle bahasa Mandarin",
|
|
"language": "Bahasa",
|
|
"systemLanguage": "Sistem",
|
|
"english": "English",
|
|
"simplifiedChinese": "简体中文",
|
|
"episodeOneTitle": "Cahaya Pertama",
|
|
"episodeTwoTitle": "Frekuensi Baru",
|
|
"episodeThreeTitle": "Menyeberang",
|
|
"episodeFourTitle": "Setelah Gema",
|
|
"dramaSignalTitle": "Sinyal",
|
|
"dramaSignalSynopsis": "Serial demo untuk tontonan vertikal. Setiap episode memakai satu klip lokal yang sama.",
|
|
"dramaCityTitle": "Cahaya Kota & Malam Beludru",
|
|
"dramaCitySynopsis": "Serial romansa demo. Video lokal yang sama dipakai untuk mencoba pindah episode dan lanjut tonton.",
|
|
"dramaVengeanceTitle": "Badai Pembalasan",
|
|
"dramaVengeanceSynopsis": "Serial aksi demo. Video lokal yang sama dipakai untuk mencoba pindah episode dan lanjut tonton.",
|
|
"dramaEchoesTitle": "Gema Takdir",
|
|
"genreSuspenseMystery": "Thriller · Misteri",
|
|
"genreUrbanRomance": "Romansa Kota",
|
|
"genreActionThriller": "Aksi · Thriller",
|
|
"genreDramaMystery": "Drama · Misteri",
|
|
"continueWatchingHeading": "LANJUT TONTON",
|
|
"preferencesHeading": "PREFERENSI",
|
|
"brandMark": "FRUITSHORTS",
|
|
"shareFooter": "FruitShorts",
|
|
"shortsTab": "Drama Pendek",
|
|
"myListTab": "Daftar Saya",
|
|
"watchFull": "Tonton Penuh",
|
|
"swipeForNext": "Geser ke atas untuk lanjut",
|
|
"clearScreen": "Bersihkan layar",
|
|
"exitClearScreen": "Keluar dari mode bersih",
|
|
"logIn": "Masuk",
|
|
"logInHint": "Simpan koin dan daftar kamu",
|
|
"rewards": "Hadiah",
|
|
"forYou": "Untuk Kamu",
|
|
"hot": "Populer",
|
|
"newest": "Terbaru",
|
|
"favorite": "Favorit",
|
|
"myAccount": "Akun saya",
|
|
"coins": "Koin",
|
|
"topUpWallet": "Isi Ulang Dompet",
|
|
"vipMembership": "Keanggotaan VIP",
|
|
"notActive": "Tidak Aktif",
|
|
"joinVip": "Gabung VIP",
|
|
"earnRewards": "Dapatkan Hadiah",
|
|
"freeBonus": "Bonus Gratis",
|
|
"watchHistory": "Riwayat Tontonan",
|
|
"settings": "Pengaturan",
|
|
"signOut": "Keluar",
|
|
"mockUserName": "Vicky sheng",
|
|
"mockUserId": "ID: M1CZWVYE",
|
|
"mockNotice": "Hanya data contoh.",
|
|
"topBadge": "TOP 1",
|
|
"signIn": "Masuk",
|
|
"signUp": "Daftar",
|
|
"authSignInTitle": "Selamat datang kembali",
|
|
"authSignUpTitle": "Buat akun kamu",
|
|
"authSubtitle": "Masuk dengan email dan kata sandi.",
|
|
"authEmailLabel": "Email",
|
|
"authPasswordLabel": "Kata sandi",
|
|
"authPasswordHint": "Minimal 8 karakter, berisi huruf dan angka",
|
|
"authEmailRequired": "Masukkan email kamu",
|
|
"authEmailInvalid": "Email itu sepertinya tidak benar",
|
|
"authPasswordRequired": "Masukkan kata sandi kamu",
|
|
"authPasswordLength": "Kata sandi harus 8-64 karakter",
|
|
"authPasswordWeak": "Kata sandi perlu huruf dan angka",
|
|
"authNetworkError": "Jaringan bermasalah, coba lagi",
|
|
"authSwitchToSignUp": "Baru di sini? Buat akun",
|
|
"authSwitchToSignIn": "Sudah punya akun? Masuk",
|
|
"authGateTitle": "Masuk untuk melanjutkan",
|
|
"authGateMessage": "Favorit dan komentar perlu akun.",
|
|
"authGateAction": "Masuk",
|
|
"cancel": "Nanti saja",
|
|
"noEpisodesYet": "Belum ada episode yang bisa diputar.",
|
|
"networkError": "Jaringan tidak tersedia. Periksa koneksi lalu coba lagi.",
|
|
"progressError": "Posisi tonton gagal dimuat.",
|
|
"favoritesError": "Daftar favorit gagal dimuat.",
|
|
"favoriteToggleFailed": "Gagal memperbarui favorit. Coba lagi.",
|
|
"remove": "Hapus",
|
|
"unlockComingSoon": "Pembukaan dengan koin segera hadir.",
|
|
"resumeProgress": "Lanjut di {position} / {total}",
|
|
"@resumeProgress": {
|
|
"placeholders": {
|
|
"position": {},
|
|
"total": {}
|
|
}
|
|
}
|
|
}
|