Files
ReelShort/test/formatting/localized_content_test.dart
T
Vicky 61454e36b1 feat(client): 改名为 FruitShorts 并新增 id-ID 印尼语文案(1-3 + 1-4 合并)
为什么: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
2026-10-16 10:16:59 +08:00

97 lines
3.6 KiB
Dart

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/l10n/app_localizations_en.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 [],
);
void main() {
final en = AppLocalizationsEn();
final zh = AppLocalizationsZh();
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(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 = {
localizedDramaTitle(en, _drama(id: 'the-signal-demo')),
localizedDramaTitle(en, _drama(id: 'city-lights-demo')),
localizedDramaTitle(en, _drama(id: 'vengeance-demo')),
};
final synopses = {
localizedDramaSynopsis(en, _drama(id: 'the-signal-demo')),
localizedDramaSynopsis(en, _drama(id: 'city-lights-demo')),
localizedDramaSynopsis(en, _drama(id: 'vengeance-demo')),
};
expect(titles, hasLength(3));
expect(synopses, hasLength(3));
});
test('should_translate_localized_copy_when_content_is_looked_up', () {
final signal = _drama(id: 'the-signal-demo');
expect(localizedDramaTitle(zh, signal), zh.dramaSignalTitle);
expect(localizedDramaSynopsis(zh, signal), zh.dramaSignalSynopsis);
expect(localizedDramaTitle(zh, signal), isNot(en.dramaSignalTitle));
});
test('should_keep_catalog_text_in_every_locale_when_drama_is_unknown', () {
final custom = _drama(
id: 'custom-demo',
title: 'Custom Title',
synopsis: 'Custom synopsis',
);
expect(localizedDramaTitle(en, custom), 'Custom Title');
expect(localizedDramaTitle(zh, custom), 'Custom Title');
expect(localizedDramaSynopsis(en, custom), 'Custom synopsis');
expect(localizedDramaSynopsis(zh, custom), 'Custom synopsis');
});
test('should_ignore_raw_catalog_text_when_drama_is_seeded', () {
final signal = _drama(
id: 'the-signal-demo',
title: 'stale title',
synopsis: 'stale synopsis',
);
expect(localizedDramaTitle(en, signal), isNot('stale title'));
expect(localizedDramaSynopsis(en, signal), isNot('stale synopsis'));
});
test('should_use_named_titles_when_episode_number_is_one_to_four', () {
expect(localizedEpisodeTitle(en, 1), en.episodeOneTitle);
expect(localizedEpisodeTitle(en, 2), en.episodeTwoTitle);
expect(localizedEpisodeTitle(en, 3), en.episodeThreeTitle);
expect(localizedEpisodeTitle(en, 4), en.episodeFourTitle);
});
test('should_fall_back_to_numbered_label_when_episode_number_is_later', () {
for (final number in <int>[5, 12, 100]) {
expect(localizedEpisodeTitle(en, number), en.episodeLabel(number));
expect(localizedEpisodeTitle(en, number), contains('$number'));
}
expect(localizedEpisodeTitle(zh, 12), zh.episodeLabel(12));
});
}