为什么:a) 应用对外名称要统一成 FruitShorts(产品名,nontonki 是原型代号); b) 首发市场是印尼,界面必须有印尼语;c) 归档要求"新增 id-ID 与改名合并生成一次 l10n,别分两次"。 改名: - pubspec name: nontonki → fruitshorts(Dart 包名),description 同步;28 个文件的 package: 导入前缀替换 - Android android:label、iOS CFBundleDisplayName/CFBundleName → FruitShorts - 图标资源 assets/icon/nontonki.png → fruitshorts.png(含 pubspec 两处引用) - README 同步(补印尼语说明,顺带修掉写错的模块名 reelshort-app-api → fruitshorts-app-api) 文案: - 三份 arb 的 appName → FruitShorts、brandMark → FRUITSHORTS、shareFooter → FruitShorts(不留 demo 标记) - 新增 lib/l10n/app_id.arb:174 个值键与 en/zh 完全一致,按印尼语习惯翻译(Coin→Koin、Episode→Episode) - 一次性 flutter gen-l10n 生成 app_localizations_id.dart;id 自动进入 supportedLocales,默认 locale 未改 - 新增 test/config/l10n_id_test.dart:钉住 id 文案、supportedLocales 含 id、品牌文案无 demo 标记、占位符渲染 不动:applicationId / bundle id(lumadrama 遗留,等裁定)、目录名、业务逻辑与页面结构、语言选择页(仍只有 en/zh) 门禁:dart analyze lib test 零告警;flutter test 159 全过(基线 155 + 新增 4);残留扫描 0 命中; 三份 arb 键集一致(174/174/174);真机(iPhone iOS 26.6.1)构建安装启动成功,devicectl 列出显示名 FruitShorts
555 lines
11 KiB
Dart
555 lines
11 KiB
Dart
// ignore: unused_import
|
|
import 'package:intl/intl.dart' as intl;
|
|
import 'app_localizations.dart';
|
|
|
|
// ignore_for_file: type=lint
|
|
|
|
/// The translations for English (`en`).
|
|
class AppLocalizationsEn extends AppLocalizations {
|
|
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
|
|
|
@override
|
|
String get appName => 'FruitShorts';
|
|
|
|
@override
|
|
String get demo => 'DEMO';
|
|
|
|
@override
|
|
String get homeHeadline => 'Watch now';
|
|
|
|
@override
|
|
String get homeSubtitle => 'Free short drama preview.';
|
|
|
|
@override
|
|
String get mediaNotice => 'User-provided local demo video · Free preview';
|
|
|
|
@override
|
|
String get featuredDemo => 'FEATURED · TECHNICAL DEMO';
|
|
|
|
@override
|
|
String get demoTitle => 'The Signal';
|
|
|
|
@override
|
|
String get demoSynopsis =>
|
|
'A short local video preview. More episodes can be added when media is available.';
|
|
|
|
@override
|
|
String get homeTab => 'Home';
|
|
|
|
@override
|
|
String get openQuickMenu => 'Open quick menu';
|
|
|
|
@override
|
|
String get quickAccess => 'Quick access';
|
|
|
|
@override
|
|
String get currentSeries => 'Current series';
|
|
|
|
@override
|
|
String get discoverTab => 'Discover';
|
|
|
|
@override
|
|
String get searchDiscover => 'Search dramas, actors, genres...';
|
|
|
|
@override
|
|
String get featured => 'Featured';
|
|
|
|
@override
|
|
String get suspense => 'Suspense';
|
|
|
|
@override
|
|
String get romance => 'Romance';
|
|
|
|
@override
|
|
String get actionGenre => 'Action';
|
|
|
|
@override
|
|
String get filter => 'Filter';
|
|
|
|
@override
|
|
String get topCharts => 'Top Charts';
|
|
|
|
@override
|
|
String get newReleases => 'New Releases';
|
|
|
|
@override
|
|
String get trendingMicroDramas => 'Trending Micro-Dramas';
|
|
|
|
@override
|
|
String get seeAll => 'See All';
|
|
|
|
@override
|
|
String get artworkOnly => 'Artwork';
|
|
|
|
@override
|
|
String get artworkOnlyNotice =>
|
|
'This is a visual catalog item. No video is available yet.';
|
|
|
|
@override
|
|
String get videoPending => 'Video pending';
|
|
|
|
@override
|
|
String get watchNow => 'Watch free';
|
|
|
|
@override
|
|
String get noSearchResults => 'No matching dramas.';
|
|
|
|
@override
|
|
String get demoCatalogNotice =>
|
|
'Three demo dramas share the same local video.';
|
|
|
|
@override
|
|
String get myTab => 'My';
|
|
|
|
@override
|
|
String get localViewer => 'Local Viewer';
|
|
|
|
@override
|
|
String get localDemoProfile => 'Local demo profile';
|
|
|
|
@override
|
|
String get continueWatching => 'Continue Watching';
|
|
|
|
@override
|
|
String watchedPercent(int percent) {
|
|
return 'Watched $percent%';
|
|
}
|
|
|
|
@override
|
|
String get resume => 'Resume';
|
|
|
|
@override
|
|
String get history => 'History';
|
|
|
|
@override
|
|
String get watchlist => 'Watchlist';
|
|
|
|
@override
|
|
String get likedTab => 'Liked';
|
|
|
|
@override
|
|
String get all => 'All';
|
|
|
|
@override
|
|
String get inProgress => 'In Progress';
|
|
|
|
@override
|
|
String get completed => 'Completed';
|
|
|
|
@override
|
|
String get noSavedShows => 'No shows here yet.';
|
|
|
|
@override
|
|
String get preferences => 'Preferences';
|
|
|
|
@override
|
|
String get appLanguage => 'App Language';
|
|
|
|
@override
|
|
String get followingTab => 'Following';
|
|
|
|
@override
|
|
String get animatedTab => 'Animated';
|
|
|
|
@override
|
|
String get recommendedTab => 'Recommended';
|
|
|
|
@override
|
|
String get noCategoryDramas => 'No dramas in this category yet.';
|
|
|
|
@override
|
|
String get trendingTab => 'Trending';
|
|
|
|
@override
|
|
String get liveActionTab => 'Live Action';
|
|
|
|
@override
|
|
String get search => 'Search';
|
|
|
|
@override
|
|
String get free => 'Free';
|
|
|
|
@override
|
|
String get mute => 'Mute';
|
|
|
|
@override
|
|
String get unmute => 'Unmute';
|
|
|
|
@override
|
|
String get like => 'Like';
|
|
|
|
@override
|
|
String get save => 'Save';
|
|
|
|
@override
|
|
String get comments => 'Comments';
|
|
|
|
@override
|
|
String get share => 'Share';
|
|
|
|
@override
|
|
String get localOnlyComments => 'Only visible on this device';
|
|
|
|
@override
|
|
String get noComments => 'No comments yet.';
|
|
|
|
@override
|
|
String get writeComment => 'Write a comment';
|
|
|
|
@override
|
|
String get postComment => 'Post';
|
|
|
|
@override
|
|
String get shareFailed => 'Could not open the share sheet.';
|
|
|
|
@override
|
|
String get nowPlaying => 'Playing now';
|
|
|
|
@override
|
|
String get watchFullscreen => 'Watch in landscape';
|
|
|
|
@override
|
|
String get exitFullscreen => 'Exit fullscreen';
|
|
|
|
@override
|
|
String get pictureInPicture => 'Picture in picture';
|
|
|
|
@override
|
|
String get pipUnavailable =>
|
|
'Picture in picture is unavailable on this device';
|
|
|
|
@override
|
|
String get originalTag => 'Original';
|
|
|
|
@override
|
|
String get dramaTag => 'Short drama';
|
|
|
|
@override
|
|
String episodeLabel(int number) {
|
|
return 'Episode $number';
|
|
}
|
|
|
|
@override
|
|
String get exploreSeries => 'Explore series';
|
|
|
|
@override
|
|
String episodeCount(int count) {
|
|
return '$count EPISODES';
|
|
}
|
|
|
|
@override
|
|
String technicalDemoCount(int count) {
|
|
return 'FREE PREVIEW · $count EPISODES';
|
|
}
|
|
|
|
@override
|
|
String get episodes => 'Episodes';
|
|
|
|
@override
|
|
String get watchEpisodeOne => 'Watch episode 1';
|
|
|
|
@override
|
|
String continueEpisode(int number) {
|
|
return 'Continue episode $number';
|
|
}
|
|
|
|
@override
|
|
String get freeTestMedia => 'Same demo clip · free';
|
|
|
|
@override
|
|
String get lockedStoreUnavailable => 'Locked · store unavailable';
|
|
|
|
@override
|
|
String get freeTestClip => 'Free test clip';
|
|
|
|
@override
|
|
String get locked => 'Locked';
|
|
|
|
@override
|
|
String get freeSamplesNotice => 'Two free samples · store setup pending';
|
|
|
|
@override
|
|
String get demoTestMedia => 'FREE · SAME DEMO CLIP';
|
|
|
|
@override
|
|
String episodeProgress(int number, int total) {
|
|
return 'EPISODE $number / $total';
|
|
}
|
|
|
|
@override
|
|
String get testVideoNotice => 'Self-generated technical test video';
|
|
|
|
@override
|
|
String lockedTitle(int number) {
|
|
return 'Episode $number is locked';
|
|
}
|
|
|
|
@override
|
|
String get lockedBody =>
|
|
'Unlocking will be available after store and verification setup. This demo does not process payments.';
|
|
|
|
@override
|
|
String get playbackError => 'This clip could not be played.';
|
|
|
|
@override
|
|
String get catalogError => 'The catalog could not be loaded.';
|
|
|
|
@override
|
|
String get detailError => 'The episode list could not be loaded.';
|
|
|
|
@override
|
|
String get noMediaNotice => 'This episode has no playable media yet.';
|
|
|
|
@override
|
|
String get noStories => 'No stories available yet.';
|
|
|
|
@override
|
|
String get retry => 'Retry';
|
|
|
|
@override
|
|
String get backToSeries => 'Back to series';
|
|
|
|
@override
|
|
String get nextEpisode => 'Next episode';
|
|
|
|
@override
|
|
String get pause => 'Pause';
|
|
|
|
@override
|
|
String get play => 'Play';
|
|
|
|
@override
|
|
String get seekVideo => 'Seek video';
|
|
|
|
@override
|
|
String get seekFailed => 'Could not change playback position.';
|
|
|
|
@override
|
|
String get captions => 'Captions';
|
|
|
|
@override
|
|
String get captionsOff => 'Captions off';
|
|
|
|
@override
|
|
String get englishCaptions => 'English captions';
|
|
|
|
@override
|
|
String get chineseCaptions => 'Chinese captions';
|
|
|
|
@override
|
|
String get language => 'Language';
|
|
|
|
@override
|
|
String get systemLanguage => 'System';
|
|
|
|
@override
|
|
String get english => 'English';
|
|
|
|
@override
|
|
String get simplifiedChinese => '简体中文';
|
|
|
|
@override
|
|
String get episodeOneTitle => 'First Light';
|
|
|
|
@override
|
|
String get episodeTwoTitle => 'A New Frequency';
|
|
|
|
@override
|
|
String get episodeThreeTitle => 'Crossing Over';
|
|
|
|
@override
|
|
String get episodeFourTitle => 'After the Echo';
|
|
|
|
@override
|
|
String get dramaSignalTitle => 'The Signal';
|
|
|
|
@override
|
|
String get dramaSignalSynopsis =>
|
|
'A demo series for vertical playback. Every episode reuses one local clip.';
|
|
|
|
@override
|
|
String get dramaCityTitle => 'City Lights & Velvet Nights';
|
|
|
|
@override
|
|
String get dramaCitySynopsis =>
|
|
'A demo romance series. The same local video is used to try switching and resume.';
|
|
|
|
@override
|
|
String get dramaVengeanceTitle => 'Vengeance Rising';
|
|
|
|
@override
|
|
String get dramaVengeanceSynopsis =>
|
|
'A demo action series. The same local video is used to try switching and resume.';
|
|
|
|
@override
|
|
String get dramaEchoesTitle => 'Echoes of Fate';
|
|
|
|
@override
|
|
String get genreSuspenseMystery => 'Suspense · Mystery';
|
|
|
|
@override
|
|
String get genreUrbanRomance => 'Urban Romance';
|
|
|
|
@override
|
|
String get genreActionThriller => 'Action · Thriller';
|
|
|
|
@override
|
|
String get genreDramaMystery => 'Drama · Mystery';
|
|
|
|
@override
|
|
String get continueWatchingHeading => 'CONTINUE WATCHING';
|
|
|
|
@override
|
|
String get preferencesHeading => 'PREFERENCES';
|
|
|
|
@override
|
|
String get brandMark => 'FRUITSHORTS';
|
|
|
|
@override
|
|
String get shareFooter => 'FruitShorts';
|
|
|
|
@override
|
|
String get shortsTab => 'Shorts';
|
|
|
|
@override
|
|
String get myListTab => 'My List';
|
|
|
|
@override
|
|
String get watchFull => 'Watch Full';
|
|
|
|
@override
|
|
String get swipeForNext => 'Swipe up for next';
|
|
|
|
@override
|
|
String get clearScreen => 'Clear screen';
|
|
|
|
@override
|
|
String get exitClearScreen => 'Exit clear screen';
|
|
|
|
@override
|
|
String get logIn => 'Log in';
|
|
|
|
@override
|
|
String get logInHint => 'Keep your coins and list';
|
|
|
|
@override
|
|
String get rewards => 'Rewards';
|
|
|
|
@override
|
|
String get forYou => 'For You';
|
|
|
|
@override
|
|
String get hot => 'Hot';
|
|
|
|
@override
|
|
String get newest => 'New';
|
|
|
|
@override
|
|
String get favorite => 'Favorite';
|
|
|
|
@override
|
|
String get myAccount => 'My account';
|
|
|
|
@override
|
|
String get coins => 'Coins';
|
|
|
|
@override
|
|
String get topUpWallet => 'Top Up Wallet';
|
|
|
|
@override
|
|
String get vipMembership => 'VIP Membership';
|
|
|
|
@override
|
|
String get notActive => 'Not Active';
|
|
|
|
@override
|
|
String get joinVip => 'Join VIP';
|
|
|
|
@override
|
|
String get earnRewards => 'Earn Rewards';
|
|
|
|
@override
|
|
String get freeBonus => 'Get Free Bonus';
|
|
|
|
@override
|
|
String get watchHistory => 'Watch History';
|
|
|
|
@override
|
|
String get settings => 'Settings';
|
|
|
|
@override
|
|
String get signOut => 'Sign Out';
|
|
|
|
@override
|
|
String get mockUserName => 'Vicky sheng';
|
|
|
|
@override
|
|
String get mockUserId => 'ID: M1CZWVYE';
|
|
|
|
@override
|
|
String get mockNotice => 'Sample data only.';
|
|
|
|
@override
|
|
String get topBadge => 'TOP 1';
|
|
|
|
@override
|
|
String get signIn => 'Sign In';
|
|
|
|
@override
|
|
String get signUp => 'Sign Up';
|
|
|
|
@override
|
|
String get authSignInTitle => 'Welcome back';
|
|
|
|
@override
|
|
String get authSignUpTitle => 'Create your account';
|
|
|
|
@override
|
|
String get authSubtitle => 'Sign in with your email and password.';
|
|
|
|
@override
|
|
String get authEmailLabel => 'Email';
|
|
|
|
@override
|
|
String get authPasswordLabel => 'Password';
|
|
|
|
@override
|
|
String get authPasswordHint => 'At least 8 characters, letters and numbers';
|
|
|
|
@override
|
|
String get authEmailRequired => 'Enter your email';
|
|
|
|
@override
|
|
String get authEmailInvalid => 'That email doesn\'t look right';
|
|
|
|
@override
|
|
String get authPasswordRequired => 'Enter your password';
|
|
|
|
@override
|
|
String get authPasswordLength => 'Password must be 8-64 characters';
|
|
|
|
@override
|
|
String get authPasswordWeak => 'Password needs both letters and numbers';
|
|
|
|
@override
|
|
String get authNetworkError => 'Network error, please try again';
|
|
|
|
@override
|
|
String get authSwitchToSignUp => 'New here? Create an account';
|
|
|
|
@override
|
|
String get authSwitchToSignIn => 'Already have an account? Sign in';
|
|
|
|
@override
|
|
String get authGateTitle => 'Sign in to continue';
|
|
|
|
@override
|
|
String get authGateMessage => 'Favorites and comments need an account.';
|
|
|
|
@override
|
|
String get authGateAction => 'Sign In';
|
|
|
|
@override
|
|
String get cancel => 'Not now';
|
|
}
|