1. 移除NFC和蓝牙相关依赖、权限及功能代码,精简传输链路 2. 重构设备在线统计逻辑,使用后端7天活跃字段替代本地计算 3. 更新应用名称、权限说明和协议文档 4. 新增消息转发、缓存管理、医疗免责提示功能 5. 优化运势模块和字体管理文案,修复构建日志问题
426 lines
18 KiB
Dart
426 lines
18 KiB
Dart
/// ============================================================
|
||
/// 闲言APP — 翻译导出/导入服务
|
||
/// 创建时间: 2026-05-19
|
||
/// 更新时间: 2026-05-31
|
||
/// 作用: 将翻译数据导出为JSON/从JSON导入,支持协同翻译
|
||
/// 上次更新: 重构_importXxx方法,使用fromMap/fallback自动序列化替代手动逐字段映射
|
||
/// ============================================================
|
||
|
||
import 'dart:convert';
|
||
|
||
import 'translations.dart';
|
||
|
||
class TranslationIOService {
|
||
static Map<String, dynamic> _tNavToMap(TNav nav) => {
|
||
'home': nav.home,
|
||
'discover': nav.discover,
|
||
'profile': nav.profile,
|
||
};
|
||
|
||
static Map<String, dynamic> _tCommonToMap(TCommon common) => {
|
||
'cancel': common.cancel,
|
||
'ok': common.ok,
|
||
'save': common.save,
|
||
'confirm': common.confirm,
|
||
'clear': common.clear,
|
||
'reset': common.reset,
|
||
'delete': common.delete,
|
||
'success': common.success,
|
||
'failed': common.failed,
|
||
'enabled': common.enabled,
|
||
'disabled': common.disabled,
|
||
'loading': common.loading,
|
||
'view': common.view,
|
||
'search': common.search,
|
||
'entriesCountUnit': common.entriesCountUnit,
|
||
'copyright': common.copyright,
|
||
};
|
||
|
||
static Map<String, dynamic> _tProfileToMap(TProfile profile) => {
|
||
'title': profile.title,
|
||
'myFavorites': profile.myFavorites,
|
||
'readingHistory': profile.readingHistory,
|
||
'darkMode': profile.darkMode,
|
||
'accountSettings': profile.accountSettings,
|
||
'dataManagement': profile.dataManagement,
|
||
'offlineMode': profile.offlineMode,
|
||
'cacheManagement': profile.cacheManagement,
|
||
'themeCustomization': profile.themeCustomization,
|
||
'desktopWidgets': profile.desktopWidgets,
|
||
'sentenceSource': profile.sentenceSource,
|
||
'aboutApp': profile.aboutApp,
|
||
'rateApp': profile.rateApp,
|
||
'debugMode': profile.debugMode,
|
||
'tapToLogin': profile.tapToLogin,
|
||
'defaultUserName': profile.defaultUserName,
|
||
'appSlogan': profile.appSlogan,
|
||
'freeTier': profile.freeTier,
|
||
'points': profile.points,
|
||
'checkin': profile.checkin,
|
||
'notes': profile.notes,
|
||
'quickActions': profile.quickActions,
|
||
'scanQr': profile.scanQr,
|
||
'nearbyTransfer': profile.nearbyTransfer,
|
||
'payment': profile.payment,
|
||
};
|
||
|
||
static Map<String, dynamic> _tSettingsToMap(TSettings settings) => {
|
||
'generalSettings': settings.generalSettings,
|
||
'language': settings.language,
|
||
'languageSubtitle': settings.languageSubtitle,
|
||
'selectLanguage': settings.selectLanguage,
|
||
'followSystem': settings.followSystem,
|
||
'collaborativeTranslation': settings.collaborativeTranslation,
|
||
'collaborativeTranslationDesc': settings.collaborativeTranslationDesc,
|
||
'interaction': settings.interaction,
|
||
'sound': settings.sound,
|
||
'soundSubtitle': settings.soundSubtitle,
|
||
'vibration': settings.vibration,
|
||
'vibrationSubtitle': settings.vibrationSubtitle,
|
||
'soundEffect': settings.soundEffect,
|
||
'soundEffectSubtitle': settings.soundEffectSubtitle,
|
||
'pageTransitionMode': settings.pageTransitionMode,
|
||
'pageTransitionModeNavigate': settings.pageTransitionModeNavigate,
|
||
'pageTransitionModeSheet': settings.pageTransitionModeSheet,
|
||
'predictiveBack': settings.predictiveBack,
|
||
'predictiveBackSubtitle': settings.predictiveBackSubtitle,
|
||
'longPressPreview': settings.longPressPreview,
|
||
'longPressPreviewSubtitle': settings.longPressPreviewSubtitle,
|
||
'notification': settings.notification,
|
||
'pushNotification': settings.pushNotification,
|
||
'pushNotificationSubtitle': settings.pushNotificationSubtitle,
|
||
'display': settings.display,
|
||
'screenTimeout': settings.screenTimeout,
|
||
'screenTimeoutSubtitle': settings.screenTimeoutSubtitle,
|
||
'fontSize': settings.fontSize,
|
||
'fontSizeSubtitle': settings.fontSizeSubtitle,
|
||
'startupPage': settings.startupPage,
|
||
'startupPageSubtitle': settings.startupPageSubtitle,
|
||
'immersiveStatus': settings.immersiveStatus,
|
||
'immersiveStatusSubtitle': settings.immersiveStatusSubtitle,
|
||
'contentDensity': settings.contentDensity,
|
||
'contentDensitySubtitle': settings.contentDensitySubtitle,
|
||
'reduceAnimations': settings.reduceAnimations,
|
||
'reduceAnimationsSubtitle': settings.reduceAnimationsSubtitle,
|
||
'performance': settings.performance,
|
||
'smartMode': settings.smartMode,
|
||
'smartModeSubtitle': settings.smartModeSubtitle,
|
||
'preload': settings.preload,
|
||
'preloadSubtitle': settings.preloadSubtitle,
|
||
'cacheStrategy': settings.cacheStrategy,
|
||
'cacheStrategySubtitle': settings.cacheStrategySubtitle,
|
||
'imageQuality': settings.imageQuality,
|
||
'imageQualityOriginal': settings.imageQualityOriginal,
|
||
'imageQualitySaver': settings.imageQualitySaver,
|
||
'imageQualityBalanced': settings.imageQualityBalanced,
|
||
'dataSaver': settings.dataSaver,
|
||
'dataSaverSubtitle': settings.dataSaverSubtitle,
|
||
'privacyAndPermissions': settings.privacyAndPermissions,
|
||
'appLock': settings.appLock,
|
||
'appLockSubtitle': settings.appLockSubtitle,
|
||
'clipboardRead': settings.clipboardRead,
|
||
'clipboardReadSubtitle': settings.clipboardReadSubtitle,
|
||
'permissionManagement': settings.permissionManagement,
|
||
'permissionManagementSubtitle': settings.permissionManagementSubtitle,
|
||
'privacyPolicy': settings.privacyPolicy,
|
||
'privacyPolicySubtitle': settings.privacyPolicySubtitle,
|
||
'advanced': settings.advanced,
|
||
'moreSettings': settings.moreSettings,
|
||
'moreSettingsSubtitle': settings.moreSettingsSubtitle,
|
||
'autoCheckUpdate': settings.autoCheckUpdate,
|
||
'autoCheckUpdateSubtitle': settings.autoCheckUpdateSubtitle,
|
||
'syncSettings': settings.syncSettings,
|
||
'syncSettingsSubtitle': settings.syncSettingsSubtitle,
|
||
'logManagement': settings.logManagement,
|
||
'logManagementSubtitle': settings.logManagementSubtitle,
|
||
'exportImportSettings': settings.exportImportSettings,
|
||
'exportImportSettingsSubtitle': settings.exportImportSettingsSubtitle,
|
||
'dataExport': settings.dataExport,
|
||
'dataExportSubtitle': settings.dataExportSubtitle,
|
||
'clearCache': settings.clearCache,
|
||
'clearCacheSubtitle': settings.clearCacheSubtitle,
|
||
'resetSettings': settings.resetSettings,
|
||
'resetSettingsSubtitle': settings.resetSettingsSubtitle,
|
||
'youMayBeLookingFor': settings.youMayBeLookingFor,
|
||
'fontManagement': settings.fontManagement,
|
||
'clearCacheConfirm': settings.clearCacheConfirm,
|
||
'resetSettingsConfirm': settings.resetSettingsConfirm,
|
||
'exportSettings': settings.exportSettings,
|
||
'importSettings': settings.importSettings,
|
||
'importSettingsDesc': settings.importSettingsDesc,
|
||
'confirmImport': settings.confirmImport,
|
||
'importSuccess': settings.importSuccess,
|
||
'importFailed': settings.importFailed,
|
||
'pasteJson': settings.pasteJson,
|
||
'pasteJsonPlaceholder': settings.pasteJsonPlaceholder,
|
||
'vibrationStrength': settings.vibrationStrength,
|
||
'soundEffectStyle': settings.soundEffectStyle,
|
||
'screenTimeoutTitle': settings.screenTimeoutTitle,
|
||
'startupPageTitle': settings.startupPageTitle,
|
||
'pageTransitionModeTitle': settings.pageTransitionModeTitle,
|
||
'contentDensityTitle': settings.contentDensityTitle,
|
||
'cacheStrategyTitle': settings.cacheStrategyTitle,
|
||
'imageQualityTitle': settings.imageQualityTitle,
|
||
'navigateDescDetail': settings.navigateDescDetail,
|
||
'sheetDescDetail': settings.sheetDescDetail,
|
||
'standardNavigation': settings.standardNavigation,
|
||
'bottomSheet': settings.bottomSheet,
|
||
};
|
||
|
||
static Map<String, dynamic> tToMap(T t) => {
|
||
'nav': _tNavToMap(t.nav),
|
||
'common': _tCommonToMap(t.common),
|
||
'profile': _tProfileToMap(t.profile),
|
||
'settings': _tSettingsToMap(t.settings),
|
||
};
|
||
|
||
static String exportToJson(T t) {
|
||
final map = tToMap(t);
|
||
const encoder = JsonEncoder.withIndent(' ');
|
||
return encoder.convert(map);
|
||
}
|
||
|
||
static Map<String, dynamic> exportAllTranslations() {
|
||
final result = <String, dynamic>{};
|
||
final allTranslations = {
|
||
'zh_CN': getTranslations('zh_CN'),
|
||
'en': getTranslations('en'),
|
||
'ja': getTranslations('ja'),
|
||
'zh_TW': getTranslations('zh_TW'),
|
||
'es': getTranslations('es'),
|
||
'ar': getTranslations('ar'),
|
||
'bn': getTranslations('bn'),
|
||
};
|
||
for (final entry in allTranslations.entries) {
|
||
result[entry.key] = tToMap(entry.value);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
static String exportAllToJson() {
|
||
const encoder = JsonEncoder.withIndent(' ');
|
||
return encoder.convert(exportAllTranslations());
|
||
}
|
||
|
||
static Map<String, dynamic>? parseJson(String jsonStr) {
|
||
try {
|
||
return json.decode(jsonStr) as Map<String, dynamic>;
|
||
} catch (_) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
static bool validateTranslationJson(String jsonStr) {
|
||
final parsed = parseJson(jsonStr);
|
||
if (parsed == null) return false;
|
||
return parsed.containsKey('nav') ||
|
||
parsed.containsKey('common') ||
|
||
parsed.containsKey('profile') ||
|
||
parsed.containsKey('settings');
|
||
}
|
||
|
||
static Map<String, int> getCoverageReport() {
|
||
return TranslationCoverage.checkAll();
|
||
}
|
||
|
||
static int getTotalFieldCount() {
|
||
return TranslationCoverage.totalFieldCount;
|
||
}
|
||
|
||
static T? importFromJson(String jsonStr, T fallback) {
|
||
final parsed = parseJson(jsonStr);
|
||
if (parsed == null) return null;
|
||
|
||
try {
|
||
final navMap = parsed['nav'] as Map<String, dynamic>?;
|
||
final commonMap = parsed['common'] as Map<String, dynamic>?;
|
||
final homeMap = parsed['home'] as Map<String, dynamic>?;
|
||
final discoverMap = parsed['discover'] as Map<String, dynamic>?;
|
||
final profileMap = parsed['profile'] as Map<String, dynamic>?;
|
||
final settingsMap = parsed['settings'] as Map<String, dynamic>?;
|
||
|
||
final nav = _importNav(navMap, fallback.nav);
|
||
final common = _importCommon(commonMap, fallback.common);
|
||
final home = _importHome(homeMap, fallback.home);
|
||
final discover = _importDiscover(discoverMap, fallback.discover);
|
||
final profile = _importProfile(profileMap, fallback.profile);
|
||
final settings = _importSettings(settingsMap, fallback.settings);
|
||
|
||
return T(
|
||
nav: nav,
|
||
common: common,
|
||
home: home,
|
||
discover: discover,
|
||
profile: profile,
|
||
settings: settings,
|
||
about: fallback.about,
|
||
auth: fallback.auth,
|
||
onboarding: fallback.onboarding,
|
||
progress: fallback.progress,
|
||
theme: fallback.theme,
|
||
search: fallback.search,
|
||
accountSettings: fallback.accountSettings,
|
||
dataManagement: fallback.dataManagement,
|
||
source: fallback.source,
|
||
favorites: fallback.favorites,
|
||
offline: fallback.offline,
|
||
accountInsights: fallback.accountInsights,
|
||
);
|
||
} catch (_) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
static TNav _importNav(Map<String, dynamic>? map, TNav fallback) {
|
||
if (map == null) return fallback;
|
||
return TNav.fromMap(Map<String, String>.from(map), fallback: fallback);
|
||
}
|
||
|
||
static TCommon _importCommon(Map<String, dynamic>? map, TCommon fallback) {
|
||
if (map == null) return fallback;
|
||
return TCommon.fromMap(Map<String, String>.from(map), fallback: fallback);
|
||
}
|
||
|
||
static THome _importHome(Map<String, dynamic>? map, THome fallback) {
|
||
if (map == null) return fallback;
|
||
return THome.fromImportMap(map, fallback: fallback);
|
||
}
|
||
|
||
static TDiscover _importDiscover(
|
||
Map<String, dynamic>? map,
|
||
TDiscover fallback,
|
||
) {
|
||
if (map == null) return fallback;
|
||
final stringMap = map.map((k, v) => MapEntry(k, v as String? ?? ''));
|
||
final fallbackMap = fallback.toMap();
|
||
final merged = <String, String>{};
|
||
for (final key in {...fallbackMap.keys, ...stringMap.keys}) {
|
||
merged[key] = stringMap[key] ?? fallbackMap[key] ?? '';
|
||
}
|
||
return TDiscover.fromMap(merged);
|
||
}
|
||
|
||
static TProfile _importProfile(Map<String, dynamic>? map, TProfile fallback) {
|
||
if (map == null) return fallback;
|
||
return TProfile(
|
||
title: map['title'] as String? ?? fallback.title,
|
||
myFavorites: map['myFavorites'] as String? ?? fallback.myFavorites,
|
||
readingHistory:
|
||
map['readingHistory'] as String? ?? fallback.readingHistory,
|
||
darkMode: map['darkMode'] as String? ?? fallback.darkMode,
|
||
accountSettings:
|
||
map['accountSettings'] as String? ?? fallback.accountSettings,
|
||
dataManagement:
|
||
map['dataManagement'] as String? ?? fallback.dataManagement,
|
||
offlineMode: map['offlineMode'] as String? ?? fallback.offlineMode,
|
||
cacheManagement:
|
||
map['cacheManagement'] as String? ?? fallback.cacheManagement,
|
||
themeCustomization:
|
||
map['themeCustomization'] as String? ?? fallback.themeCustomization,
|
||
desktopWidgets:
|
||
map['desktopWidgets'] as String? ?? fallback.desktopWidgets,
|
||
sentenceSource:
|
||
map['sentenceSource'] as String? ?? fallback.sentenceSource,
|
||
aboutApp: map['aboutApp'] as String? ?? fallback.aboutApp,
|
||
rateApp: map['rateApp'] as String? ?? fallback.rateApp,
|
||
debugMode: map['debugMode'] as String? ?? fallback.debugMode,
|
||
tapToLogin: map['tapToLogin'] as String? ?? fallback.tapToLogin,
|
||
defaultUserName:
|
||
map['defaultUserName'] as String? ?? fallback.defaultUserName,
|
||
appSlogan: map['appSlogan'] as String? ?? fallback.appSlogan,
|
||
freeTier: map['freeTier'] as String? ?? fallback.freeTier,
|
||
points: map['points'] as String? ?? fallback.points,
|
||
checkin: map['checkin'] as String? ?? fallback.checkin,
|
||
notes: map['notes'] as String? ?? fallback.notes,
|
||
quickActions: map['quickActions'] as String? ?? fallback.quickActions,
|
||
scanQr: map['scanQr'] as String? ?? fallback.scanQr,
|
||
nearbyTransfer:
|
||
map['nearbyTransfer'] as String? ?? fallback.nearbyTransfer,
|
||
payment: map['payment'] as String? ?? fallback.payment,
|
||
selectScanMethod:
|
||
map['selectScanMethod'] as String? ?? fallback.selectScanMethod,
|
||
scanQrLogin: map['scanQrLogin'] as String? ?? fallback.scanQrLogin,
|
||
scanQrCode: map['scanQrCode'] as String? ?? fallback.scanQrCode,
|
||
appStoreNotFound:
|
||
map['appStoreNotFound'] as String? ?? fallback.appStoreNotFound,
|
||
experimentalFeature:
|
||
map['experimentalFeature'] as String? ?? fallback.experimentalFeature,
|
||
underReview: map['underReview'] as String? ?? fallback.underReview,
|
||
changeAvatar: map['changeAvatar'] as String? ?? fallback.changeAvatar,
|
||
inputAvatarUrl:
|
||
map['inputAvatarUrl'] as String? ?? fallback.inputAvatarUrl,
|
||
selectFromAlbum:
|
||
map['selectFromAlbum'] as String? ?? fallback.selectFromAlbum,
|
||
avatarUrlHint:
|
||
map['avatarUrlHint'] as String? ?? fallback.avatarUrlHint,
|
||
pleaseInputUrl:
|
||
map['pleaseInputUrl'] as String? ?? fallback.pleaseInputUrl,
|
||
urlMustStartWithHttp:
|
||
map['urlMustStartWithHttp'] as String? ??
|
||
fallback.urlMustStartWithHttp,
|
||
urlTooLong: map['urlTooLong'] as String? ?? fallback.urlTooLong,
|
||
invalidUrlFormat:
|
||
map['invalidUrlFormat'] as String? ?? fallback.invalidUrlFormat,
|
||
avatarUnderReview:
|
||
map['avatarUnderReview'] as String? ?? fallback.avatarUnderReview,
|
||
avatarReviewing:
|
||
map['avatarReviewing'] as String? ?? fallback.avatarReviewing,
|
||
avatarChangeSuccess:
|
||
map['avatarChangeSuccess'] as String? ??
|
||
fallback.avatarChangeSuccess,
|
||
avatarChangeFailed:
|
||
map['avatarChangeFailed'] as String? ?? fallback.avatarChangeFailed,
|
||
success: map['success'] as String? ?? fallback.success,
|
||
failed: map['failed'] as String? ?? fallback.failed,
|
||
ok: map['ok'] as String? ?? fallback.ok,
|
||
loading: map['loading'] as String? ?? fallback.loading,
|
||
loginToViewProfile:
|
||
map['loginToViewProfile'] as String? ?? fallback.loginToViewProfile,
|
||
goLogin: map['goLogin'] as String? ?? fallback.goLogin,
|
||
consecutiveCheckin: map['consecutiveCheckin'] as String? ?? fallback.consecutiveCheckin,
|
||
favorites: map['favorites'] as String? ?? fallback.favorites,
|
||
likes: map['likes'] as String? ?? fallback.likes,
|
||
dailyCheckin: map['dailyCheckin'] as String? ?? fallback.dailyCheckin,
|
||
learningCenter: map['learningCenter'] as String? ?? fallback.learningCenter,
|
||
achievementCenter: map['achievementCenter'] as String? ?? fallback.achievementCenter,
|
||
dailyTask: map['dailyTask'] as String? ?? fallback.dailyTask,
|
||
leaderboard: map['leaderboard'] as String? ?? fallback.leaderboard,
|
||
dataStatistics: map['dataStatistics'] as String? ?? fallback.dataStatistics,
|
||
myNotes: map['myNotes'] as String? ?? fallback.myNotes,
|
||
contentCorrection: map['contentCorrection'] as String? ?? fallback.contentCorrection,
|
||
myDevices: map['myDevices'] as String? ?? fallback.myDevices,
|
||
tagCloud: map['tagCloud'] as String? ?? fallback.tagCloud,
|
||
personalInfo: map['personalInfo'] as String? ?? fallback.personalInfo,
|
||
username: map['username'] as String? ?? fallback.username,
|
||
nickname: map['nickname'] as String? ?? fallback.nickname,
|
||
bio: map['bio'] as String? ?? fallback.bio,
|
||
notSet: map['notSet'] as String? ?? fallback.notSet,
|
||
notFilled: map['notFilled'] as String? ?? fallback.notFilled,
|
||
set: map['set'] as String? ?? fallback.set,
|
||
reviewing: map['reviewing'] as String? ?? fallback.reviewing,
|
||
editUsername: map['editUsername'] as String? ?? fallback.editUsername,
|
||
editNickname: map['editNickname'] as String? ?? fallback.editNickname,
|
||
nearbyDiscovery: map['nearbyDiscovery'] as String? ?? fallback.nearbyDiscovery,
|
||
nearbyDiscoveryDesc: map['nearbyDiscoveryDesc'] as String? ?? fallback.nearbyDiscoveryDesc,
|
||
);
|
||
}
|
||
|
||
static TSettings _importSettings(
|
||
Map<String, dynamic>? map,
|
||
TSettings fallback,
|
||
) {
|
||
if (map == null) return fallback;
|
||
final fallbackMap = fallback.toMap();
|
||
final merged = <String, String>{};
|
||
for (final key in fallbackMap.keys) {
|
||
final value = map[key];
|
||
if (value is String && value.isNotEmpty) {
|
||
merged[key] = value;
|
||
} else {
|
||
merged[key] = fallbackMap[key] ?? '';
|
||
}
|
||
}
|
||
return TSettings.fromMap(merged);
|
||
}
|
||
}
|