Files
xianyan/lib/l10n/translation_io_service.dart
Developer 214a0684d0 chore: 移除NFC/蓝牙相关支持,更新设备在线统计,新增功能优化
1.  移除NFC和蓝牙相关依赖、权限及功能代码,精简传输链路
2.  重构设备在线统计逻辑,使用后端7天活跃字段替代本地计算
3.  更新应用名称、权限说明和协议文档
4.  新增消息转发、缓存管理、医疗免责提示功能
5.  优化运势模块和字体管理文案,修复构建日志问题
2026-06-06 06:12:09 +08:00

426 lines
18 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// ============================================================
/// 闲言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);
}
}