Files
xianyan/lib/l10n/translation_io_service.dart
Developer 88a3f6d65f feat: 新增仪表盘页面与macOS多项优化
1. 新增TDashboard翻译类型与多语言文案
2. 完善macOS权限管理与Impeller渲染适配
3. 更新服务器部署配置与协议文件上传脚本
4. 修复翻译导入服务与根类型编译问题
2026-06-26 06:34:05 +08:00

1711 lines
68 KiB
Dart
Raw Permalink 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-06-26
/// 作用: 将翻译数据导出为JSON/从JSON导入支持协同翻译
/// 上次更新: importFromJson 的 return T(...) 补 dashboard: fallback.dashboard修复 required 字段缺失编译错误)
/// ============================================================
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,
'selectScanMethod': profile.selectScanMethod,
'scanQrLogin': profile.scanQrLogin,
'scanQrCode': profile.scanQrCode,
'appStoreNotFound': profile.appStoreNotFound,
'experimentalFeature': profile.experimentalFeature,
'underReview': profile.underReview,
'changeAvatar': profile.changeAvatar,
'inputAvatarUrl': profile.inputAvatarUrl,
'selectFromAlbum': profile.selectFromAlbum,
'avatarUrlHint': profile.avatarUrlHint,
'pleaseInputUrl': profile.pleaseInputUrl,
'urlMustStartWithHttp': profile.urlMustStartWithHttp,
'urlTooLong': profile.urlTooLong,
'invalidUrlFormat': profile.invalidUrlFormat,
'avatarUnderReview': profile.avatarUnderReview,
'avatarReviewing': profile.avatarReviewing,
'avatarChangeSuccess': profile.avatarChangeSuccess,
'avatarChangeFailed': profile.avatarChangeFailed,
'success': profile.success,
'failed': profile.failed,
'ok': profile.ok,
'loading': profile.loading,
'loginToViewProfile': profile.loginToViewProfile,
'goLogin': profile.goLogin,
'consecutiveCheckin': profile.consecutiveCheckin,
'favorites': profile.favorites,
'likes': profile.likes,
'dailyCheckin': profile.dailyCheckin,
'learningCenter': profile.learningCenter,
'achievementCenter': profile.achievementCenter,
'dailyTask': profile.dailyTask,
'leaderboard': profile.leaderboard,
'dataStatistics': profile.dataStatistics,
'myNotes': profile.myNotes,
'contentCorrection': profile.contentCorrection,
'myDevices': profile.myDevices,
'tagCloud': profile.tagCloud,
'deviceRemoveCurrentTitle': profile.deviceRemoveCurrentTitle,
'deviceRemoveCurrentWarning': profile.deviceRemoveCurrentWarning,
'deviceContinueRemove': profile.deviceContinueRemove,
'deviceVerifyIdentityRemove': profile.deviceVerifyIdentityRemove,
'deviceRemoveTitle': profile.deviceRemoveTitle,
'deviceRemoveConfirm': profile.deviceRemoveConfirm,
'deviceRemoved': profile.deviceRemoved,
'deviceRemoveFailed': profile.deviceRemoveFailed,
'personalInfo': profile.personalInfo,
'username': profile.username,
'nickname': profile.nickname,
'bio': profile.bio,
'notSet': profile.notSet,
'notFilled': profile.notFilled,
'set': profile.set,
'reviewing': profile.reviewing,
'editUsername': profile.editUsername,
'editNickname': profile.editNickname,
'nearbyDiscovery': profile.nearbyDiscovery,
'nearbyDiscoveryDesc': profile.nearbyDiscoveryDesc,
'totalTasks': profile.totalTasks,
'taskClaimed': profile.taskClaimed,
'perfectDay': profile.perfectDay,
'perfectDayAllDone': profile.perfectDayAllDone,
'perfectDayReward': profile.perfectDayReward,
'perfectDayRewardDesc': profile.perfectDayRewardDesc,
'claimPerfectDayReward': profile.claimPerfectDayReward,
'rewardSuffix': profile.rewardSuffix,
'expUnit': profile.expUnit,
'scoreUnit': profile.scoreUnit,
'noTasks': profile.noTasks,
'noTasksDesc': profile.noTasksDesc,
'great': profile.great,
'loginToCheckin': profile.loginToCheckin,
'loginToCheckinDesc': profile.loginToCheckinDesc,
'viewAchievementCenter': profile.viewAchievementCenter,
'todaySigned': profile.todaySigned,
'tapToCheckin': profile.tapToCheckin,
'signed': profile.signed,
'weeklyCheckin': profile.weeklyCheckin,
'totalCheckinDays': profile.totalCheckinDays,
'checkinHistory': profile.checkinHistory,
'noCheckinRecord': profile.noCheckinRecord,
'todayLabel': profile.todayLabel,
'checkinDate': profile.checkinDate,
'status': profile.status,
'signedStatus': profile.signedStatus,
'remark': profile.remark,
'dailyCheckinTaskDone': profile.dailyCheckinTaskDone,
'makeupCheckin': profile.makeupCheckin,
'makeupCostInfo': profile.makeupCostInfo,
'makeupDate': profile.makeupDate,
'costPoints': profile.costPoints,
'currentPoints': profile.currentPoints,
'makeupInfo': profile.makeupInfo,
'makeupLimitInfo': profile.makeupLimitInfo,
'confirmMakeup': profile.confirmMakeup,
'insufficientPoints': profile.insufficientPoints,
'insufficientPointsDesc': profile.insufficientPointsDesc,
'makeupSuccess': profile.makeupSuccess,
'makeupSuccessDesc': profile.makeupSuccessDesc,
'makeupFailed': profile.makeupFailed,
'makeupFailedRetry': profile.makeupFailedRetry,
'timesUnit': profile.timesUnit,
'daysUnit': profile.daysUnit,
'accountAndData': profile.accountAndData,
'editProfile': profile.editProfile,
'edit': profile.edit,
'editBio': profile.editBio,
'save': profile.save,
'pleaseInput': profile.pleaseInput,
'modifySuccess': profile.modifySuccess,
'modifyFailed': profile.modifyFailed,
'userProfile': profile.userProfile,
'goBack': profile.goBack,
'userNotExist': profile.userNotExist,
'retry': profile.retry,
'anonymousUser': profile.anonymousUser,
'articles': profile.articles,
'follow': profile.follow,
'followed': profile.followed,
'theUser': profile.theUser,
'privateMessage': profile.privateMessage,
'gotIt': profile.gotIt,
'shareProfile': profile.shareProfile,
'blockUser': profile.blockUser,
'personalBio': profile.personalBio,
'titleLevel': profile.titleLevel,
'activeData': profile.activeData,
'beginner': profile.beginner,
'apprentice': profile.apprentice,
'skilled': profile.skilled,
'expert': profile.expert,
'master': profile.master,
'signInCount': profile.signInCount,
'noteCount': profile.noteCount,
'likeCount': profile.likeCount,
'commentCount': profile.commentCount,
'viewCount': profile.viewCount,
'readLaterCount': profile.readLaterCount,
'modifyField': profile.modifyField,
'pleaseInputField': profile.pleaseInputField,
'fieldModifySuccess': profile.fieldModifySuccess,
'fieldModifyFailed': profile.fieldModifyFailed,
'debugInfo': profile.debugInfo,
'defaultBio': profile.defaultBio,
};
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> _tNoteToMap(TNote note) => {
// 页面标题与导航
'title': note.title,
'searchNotes': note.searchNotes,
'editNote': note.editNote,
'newNote': note.newNote,
// 登录守卫
'loginRequired': note.loginRequired,
'loginToUseNotes': note.loginToUseNotes,
'goLogin': note.goLogin,
// 搜索
'searchPlaceholder': note.searchPlaceholder,
// 筛选类型
'all': note.all,
'note': note.note,
'excerpt': note.excerpt,
'checklist': note.checklist,
// 排序与分组
'sortBy': note.sortBy,
'sortByUpdateTime': note.sortByUpdateTime,
'sortByCreateTime': note.sortByCreateTime,
'sortByTitle': note.sortByTitle,
'groupBy': note.groupBy,
'groupByDate': note.groupByDate,
'groupByCategory': note.groupByCategory,
'groupByType': note.groupByType,
'groupBySource': note.groupBySource,
// 布局
'switchLayout': note.switchLayout,
'layoutList': note.layoutList,
'layoutGrid': note.layoutGrid,
'layoutTimeline': note.layoutTimeline,
// 选择模式
'selectedCount': note.selectedCount,
'selectAll': note.selectAll,
'deselectAll': note.deselectAll,
'batchSelect': note.batchSelect,
'multiSelect': note.multiSelect,
// 批量操作
'batchDelete': note.batchDelete,
'batchDeleteConfirm': note.batchDeleteConfirm,
'deletedCount': note.deletedCount,
// 拖拽排序
'dragReorder': note.dragReorder,
'dragToReorder': note.dragToReorder,
// 上下文菜单
'starFavorite': note.starFavorite,
'unfavorite': note.unfavorite,
'exportNote': note.exportNote,
'copyNote': note.copyNote,
// 菜单项
'newNoteMenu': note.newNoteMenu,
'sortMenu': note.sortMenu,
'groupMenu': note.groupMenu,
'showStarOnly': note.showStarOnly,
'showAll': note.showAll,
'statsPanel': note.statsPanel,
'reorderMenu': note.reorderMenu,
// 清空全部
'confirmClear': note.confirmClear,
'clearAllWarning': note.clearAllWarning,
'clearAll': note.clearAll,
'allCleared': note.allCleared,
// 空状态
'noNotes': note.noNotes,
'tapToStart': note.tapToStart,
// 底部
'reachedBottom': note.reachedBottom,
'notesCount': note.notesCount,
// 日期标签
'unknownDate': note.unknownDate,
'today': note.today,
'yesterday': note.yesterday,
// 分组标签
'uncategorized': note.uncategorized,
'noSource': note.noSource,
// 笔记卡片
'noTitle': note.noTitle,
// 删除
'deleteNote': note.deleteNote,
'deleteNoteConfirm': note.deleteNoteConfirm,
// 编辑页
'noteLoadFailed': note.noteLoadFailed,
'titlePlaceholder': note.titlePlaceholder,
'categoryLabel': note.categoryLabel,
'sourceLabel': note.sourceLabel,
'publicLabel': note.publicLabel,
'optionalPlaceholder': note.optionalPlaceholder,
'visibleToAll': note.visibleToAll,
'visibleToSelf': note.visibleToSelf,
'selectSourceType': note.selectSourceType,
'excerptPlaceholder': note.excerptPlaceholder,
'checklistPlaceholder': note.checklistPlaceholder,
'notePlaceholder': note.notePlaceholder,
'noPreviewContent': note.noPreviewContent,
'switchToEdit': note.switchToEdit,
'selectPreviewFont': note.selectPreviewFont,
'systemDefault': note.systemDefault,
'noTitleNote': note.noTitleNote,
'noteSaved': note.noteSaved,
'unsaved': note.unsaved,
'saved': note.saved,
'charCount': note.charCount,
// 来源类型
'sourcePoetry': note.sourcePoetry,
'sourceArticle': note.sourceArticle,
'sourceHanzi': note.sourceHanzi,
'sourceIdiom': note.sourceIdiom,
// Markdown 工具栏
'bold': note.bold,
'italic': note.italic,
'heading': note.heading,
'list': note.list,
'orderedList': note.orderedList,
'quote': note.quote,
'link': note.link,
'code': note.code,
'divider': note.divider,
// 统计面板
'noteStats': note.noteStats,
'totalNotes': note.totalNotes,
'weekNew': note.weekNew,
'totalWords': note.totalWords,
'typeDistribution': note.typeDistribution,
'categoryDistribution': note.categoryDistribution,
'noCategoryData': note.noCategoryData,
'starredNotes': note.starredNotes,
// 导出
'exportTitle': note.exportTitle,
'exportCountNote': note.exportCountNote,
'copiedToClipboard': note.copiedToClipboard,
'exportTypeLabel': note.exportTypeLabel,
'exportCategoryLabel': note.exportCategoryLabel,
'exportPublicYes': note.exportPublicYes,
'exportSourceLabel': note.exportSourceLabel,
'exportCreateTime': note.exportCreateTime,
'exportUpdateTime': note.exportUpdateTime,
'exportShareSubject': note.exportShareSubject,
// 置顶
'pinToDiscover': note.pinToDiscover,
'pinToDiscoverDesc': note.pinToDiscoverDesc,
'close': note.close,
'pinnedToDiscover': note.pinnedToDiscover,
'unpinnedFromDiscover': note.unpinnedFromDiscover,
'confirmPin': note.confirmPin,
'confirmUnpin': note.confirmUnpin,
};
static Map<String, dynamic> _tBetaToMap(TBeta beta) => {
// 页面级
'pageTitle': beta.pageTitle,
'back': beta.back,
'previewTab': beta.previewTab,
'issuesTab': beta.issuesTab,
// 对话框
'confirmClose': beta.confirmClose,
'confirmOpen': beta.confirmOpen,
'cancel': beta.cancel,
'close': beta.close,
'open': beta.open,
// 空状态/错误
'emptyFeatures': beta.emptyFeatures,
'reload': beta.reload,
'loadFailed': beta.loadFailed,
'retry': beta.retry,
// 筛选标签
'filterAll': beta.filterAll,
'filterPending': beta.filterPending,
'filterFixing': beta.filterFixing,
'filterFixed': beta.filterFixed,
// 问题列表
'emptyIssues': beta.emptyIssues,
'rolloutPercentage': beta.rolloutPercentage,
'targetGroup': beta.targetGroup,
'issueStats': beta.issueStats,
// 严重程度
'severityHigh': beta.severityHigh,
'severityMedium': beta.severityMedium,
'severityLow': beta.severityLow,
// 状态
'statusPending': beta.statusPending,
'statusFixing': beta.statusFixing,
'statusFixed': beta.statusFixed,
'statusDeveloping': beta.statusDeveloping,
'statusTesting': beta.statusTesting,
'statusPreview': beta.statusPreview,
'statusReleased': beta.statusReleased,
// 通用
'comingSoon': beta.comingSoon,
'gotIt': beta.gotIt,
};
static Map<String, dynamic> _tSubmitToMap(TSubmit submit) => {
'title': submit.title,
'contentLabel': submit.contentLabel,
'titleLabel': submit.titleLabel,
'authorLabel': submit.authorLabel,
'categoryLabel': submit.categoryLabel,
'titleHint': submit.titleHint,
'authorHint': submit.authorHint,
'contentHint': submit.contentHint,
'submit': submit.submit,
'submitting': submit.submitting,
'contentRequired': submit.contentRequired,
'contentTooShort': submit.contentTooShort,
'contentTooLong': submit.contentTooLong,
'reviewing': submit.reviewing,
'reviewDesc': submit.reviewDesc,
'historyTitle': submit.historyTitle,
'historyEmpty': submit.historyEmpty,
'statusReviewing': submit.statusReviewing,
'statusApproved': submit.statusApproved,
'statusRejected': submit.statusRejected,
'catYiyan': submit.catYiyan,
'catXinde': submit.catXinde,
'catYiju': submit.catYiju,
'catSignature': submit.catSignature,
'deleteRecord': submit.deleteRecord,
'deleteConfirm': submit.deleteConfirm,
};
static Map<String, dynamic> tToMap(T t) => {
'nav': _tNavToMap(t.nav),
'common': _tCommonToMap(t.common),
'profile': _tProfileToMap(t.profile),
'settings': _tSettingsToMap(t.settings),
'note': _tNoteToMap(t.note),
'beta': _tBetaToMap(t.beta),
'submit': _tSubmitToMap(t.submit),
'studyPlan': t.studyPlan.toMap(),
'correction': t.correction.toMap(),
'leisure': t.leisure.toMap(),
};
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') ||
parsed.containsKey('note');
}
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 noteMap = parsed['note'] as Map<String, dynamic>?;
final betaMap = parsed['beta'] as Map<String, dynamic>?;
final submitMap = parsed['submit'] as Map<String, dynamic>?;
final studyPlanMap = parsed['studyPlan'] as Map<String, dynamic>?;
final correctionMap = parsed['correction'] as Map<String, dynamic>?;
final leisureMap = parsed['leisure'] 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);
final note = _importNote(noteMap, fallback.note);
final beta = _importBeta(betaMap, fallback.beta);
final submit = _importSubmit(submitMap, fallback.submit);
final studyPlan = TStudyPlan.fromMap(
studyPlanMap?.map((k, v) => MapEntry(k, v.toString())) ?? {},
fallback: fallback.studyPlan,
);
final correction = TCorrection.fromMap(
correctionMap?.map((k, v) => MapEntry(k, v.toString())) ?? {},
fallback: fallback.correction,
);
final leisure = TLeisure.fromMap(
leisureMap?.map((k, v) => MapEntry(k, v.toString())) ?? {},
);
return T(
nav: nav,
common: common,
home: home,
discover: discover,
profile: profile,
settings: settings,
note: note,
beta: beta,
submit: submit,
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,
widget: fallback.widget,
fileTransfer: fallback.fileTransfer,
studyPlan: studyPlan,
correction: correction,
leisure: leisure,
quickCard: fallback.quickCard,
dashboard: fallback.dashboard,
);
} 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;
// ---- 字段同步检查(防止新增字段遗漏) ----
_checkProfileFieldsSync(map, 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,
deviceRemoveCurrentTitle:
map['deviceRemoveCurrentTitle'] as String? ?? fallback.deviceRemoveCurrentTitle,
deviceRemoveCurrentWarning:
map['deviceRemoveCurrentWarning'] as String? ?? fallback.deviceRemoveCurrentWarning,
deviceContinueRemove:
map['deviceContinueRemove'] as String? ?? fallback.deviceContinueRemove,
deviceVerifyIdentityRemove:
map['deviceVerifyIdentityRemove'] as String? ?? fallback.deviceVerifyIdentityRemove,
deviceRemoveTitle:
map['deviceRemoveTitle'] as String? ?? fallback.deviceRemoveTitle,
deviceRemoveConfirm:
map['deviceRemoveConfirm'] as String? ?? fallback.deviceRemoveConfirm,
deviceRemoved:
map['deviceRemoved'] as String? ?? fallback.deviceRemoved,
deviceRemoveFailed:
map['deviceRemoveFailed'] as String? ?? fallback.deviceRemoveFailed,
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,
totalTasks: map['totalTasks'] as String? ?? fallback.totalTasks,
taskClaimed: map['taskClaimed'] as String? ?? fallback.taskClaimed,
perfectDay: map['perfectDay'] as String? ?? fallback.perfectDay,
perfectDayAllDone:
map['perfectDayAllDone'] as String? ?? fallback.perfectDayAllDone,
perfectDayReward:
map['perfectDayReward'] as String? ?? fallback.perfectDayReward,
perfectDayRewardDesc:
map['perfectDayRewardDesc'] as String? ??
fallback.perfectDayRewardDesc,
claimPerfectDayReward:
map['claimPerfectDayReward'] as String? ??
fallback.claimPerfectDayReward,
rewardSuffix: map['rewardSuffix'] as String? ?? fallback.rewardSuffix,
expUnit: map['expUnit'] as String? ?? fallback.expUnit,
scoreUnit: map['scoreUnit'] as String? ?? fallback.scoreUnit,
noTasks: map['noTasks'] as String? ?? fallback.noTasks,
noTasksDesc: map['noTasksDesc'] as String? ?? fallback.noTasksDesc,
great: map['great'] as String? ?? fallback.great,
loginToCheckin:
map['loginToCheckin'] as String? ?? fallback.loginToCheckin,
loginToCheckinDesc:
map['loginToCheckinDesc'] as String? ?? fallback.loginToCheckinDesc,
viewAchievementCenter:
map['viewAchievementCenter'] as String? ??
fallback.viewAchievementCenter,
todaySigned: map['todaySigned'] as String? ?? fallback.todaySigned,
tapToCheckin: map['tapToCheckin'] as String? ?? fallback.tapToCheckin,
signed: map['signed'] as String? ?? fallback.signed,
weeklyCheckin: map['weeklyCheckin'] as String? ?? fallback.weeklyCheckin,
totalCheckinDays:
map['totalCheckinDays'] as String? ?? fallback.totalCheckinDays,
checkinHistory:
map['checkinHistory'] as String? ?? fallback.checkinHistory,
noCheckinRecord:
map['noCheckinRecord'] as String? ?? fallback.noCheckinRecord,
todayLabel: map['todayLabel'] as String? ?? fallback.todayLabel,
checkinDate: map['checkinDate'] as String? ?? fallback.checkinDate,
status: map['status'] as String? ?? fallback.status,
signedStatus: map['signedStatus'] as String? ?? fallback.signedStatus,
remark: map['remark'] as String? ?? fallback.remark,
dailyCheckinTaskDone:
map['dailyCheckinTaskDone'] as String? ??
fallback.dailyCheckinTaskDone,
makeupCheckin: map['makeupCheckin'] as String? ?? fallback.makeupCheckin,
makeupCostInfo:
map['makeupCostInfo'] as String? ?? fallback.makeupCostInfo,
makeupDate: map['makeupDate'] as String? ?? fallback.makeupDate,
costPoints: map['costPoints'] as String? ?? fallback.costPoints,
currentPoints: map['currentPoints'] as String? ?? fallback.currentPoints,
makeupInfo: map['makeupInfo'] as String? ?? fallback.makeupInfo,
makeupLimitInfo:
map['makeupLimitInfo'] as String? ?? fallback.makeupLimitInfo,
confirmMakeup: map['confirmMakeup'] as String? ?? fallback.confirmMakeup,
insufficientPoints:
map['insufficientPoints'] as String? ?? fallback.insufficientPoints,
insufficientPointsDesc:
map['insufficientPointsDesc'] as String? ??
fallback.insufficientPointsDesc,
makeupSuccess: map['makeupSuccess'] as String? ?? fallback.makeupSuccess,
makeupSuccessDesc:
map['makeupSuccessDesc'] as String? ?? fallback.makeupSuccessDesc,
makeupFailed: map['makeupFailed'] as String? ?? fallback.makeupFailed,
makeupFailedRetry:
map['makeupFailedRetry'] as String? ?? fallback.makeupFailedRetry,
timesUnit: map['timesUnit'] as String? ?? fallback.timesUnit,
daysUnit: map['daysUnit'] as String? ?? fallback.daysUnit,
accountAndData:
map['accountAndData'] as String? ?? fallback.accountAndData,
editProfile: map['editProfile'] as String? ?? fallback.editProfile,
edit: map['edit'] as String? ?? fallback.edit,
editBio: map['editBio'] as String? ?? fallback.editBio,
save: map['save'] as String? ?? fallback.save,
pleaseInput: map['pleaseInput'] as String? ?? fallback.pleaseInput,
modifySuccess: map['modifySuccess'] as String? ?? fallback.modifySuccess,
modifyFailed: map['modifyFailed'] as String? ?? fallback.modifyFailed,
userProfile: map['userProfile'] as String? ?? fallback.userProfile,
goBack: map['goBack'] as String? ?? fallback.goBack,
userNotExist: map['userNotExist'] as String? ?? fallback.userNotExist,
retry: map['retry'] as String? ?? fallback.retry,
anonymousUser: map['anonymousUser'] as String? ?? fallback.anonymousUser,
articles: map['articles'] as String? ?? fallback.articles,
follow: map['follow'] as String? ?? fallback.follow,
followed: map['followed'] as String? ?? fallback.followed,
theUser: map['theUser'] as String? ?? fallback.theUser,
privateMessage:
map['privateMessage'] as String? ?? fallback.privateMessage,
gotIt: map['gotIt'] as String? ?? fallback.gotIt,
shareProfile: map['shareProfile'] as String? ?? fallback.shareProfile,
blockUser: map['blockUser'] as String? ?? fallback.blockUser,
personalBio: map['personalBio'] as String? ?? fallback.personalBio,
titleLevel: map['titleLevel'] as String? ?? fallback.titleLevel,
activeData: map['activeData'] as String? ?? fallback.activeData,
beginner: map['beginner'] as String? ?? fallback.beginner,
apprentice: map['apprentice'] as String? ?? fallback.apprentice,
skilled: map['skilled'] as String? ?? fallback.skilled,
expert: map['expert'] as String? ?? fallback.expert,
master: map['master'] as String? ?? fallback.master,
signInCount: map['signInCount'] as String? ?? fallback.signInCount,
noteCount: map['noteCount'] as String? ?? fallback.noteCount,
likeCount: map['likeCount'] as String? ?? fallback.likeCount,
commentCount: map['commentCount'] as String? ?? fallback.commentCount,
viewCount: map['viewCount'] as String? ?? fallback.viewCount,
readLaterCount:
map['readLaterCount'] as String? ?? fallback.readLaterCount,
modifyField: map['modifyField'] as String? ?? fallback.modifyField,
pleaseInputField:
map['pleaseInputField'] as String? ?? fallback.pleaseInputField,
fieldModifySuccess:
map['fieldModifySuccess'] as String? ?? fallback.fieldModifySuccess,
fieldModifyFailed:
map['fieldModifyFailed'] as String? ?? fallback.fieldModifyFailed,
debugInfo: map['debugInfo'] as String? ?? fallback.debugInfo,
defaultBio: map['defaultBio'] as String? ?? fallback.defaultBio,
exitSubProject:
map['exitSubProject'] as String? ?? fallback.exitSubProject,
exitApp: map['exitApp'] as String? ?? fallback.exitApp,
exitAccount: map['exitAccount'] as String? ?? fallback.exitAccount,
closeAppToDesktop:
map['closeAppToDesktop'] as String? ?? fallback.closeAppToDesktop,
closeAppKillBackground:
map['closeAppKillBackground'] as String? ??
fallback.closeAppKillBackground,
backToDesktop:
map['backToDesktop'] as String? ?? fallback.backToDesktop,
selectExitMethod:
map['selectExitMethod'] as String? ?? fallback.selectExitMethod,
);
}
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);
}
static TNote _importNote(Map<String, dynamic>? map, TNote fallback) {
if (map == null) return fallback;
// ---- 字段同步检查(防止新增字段遗漏) ----
_checkNoteFieldsSync(map, fallback);
return TNote(
// 页面标题与导航
title: map['title'] as String? ?? fallback.title,
searchNotes: map['searchNotes'] as String? ?? fallback.searchNotes,
editNote: map['editNote'] as String? ?? fallback.editNote,
newNote: map['newNote'] as String? ?? fallback.newNote,
// 登录守卫
loginRequired: map['loginRequired'] as String? ?? fallback.loginRequired,
loginToUseNotes:
map['loginToUseNotes'] as String? ?? fallback.loginToUseNotes,
goLogin: map['goLogin'] as String? ?? fallback.goLogin,
// 搜索
searchPlaceholder:
map['searchPlaceholder'] as String? ?? fallback.searchPlaceholder,
// 筛选类型
all: map['all'] as String? ?? fallback.all,
note: map['note'] as String? ?? fallback.note,
excerpt: map['excerpt'] as String? ?? fallback.excerpt,
checklist: map['checklist'] as String? ?? fallback.checklist,
// 排序与分组
sortBy: map['sortBy'] as String? ?? fallback.sortBy,
sortByUpdateTime:
map['sortByUpdateTime'] as String? ?? fallback.sortByUpdateTime,
sortByCreateTime:
map['sortByCreateTime'] as String? ?? fallback.sortByCreateTime,
sortByTitle: map['sortByTitle'] as String? ?? fallback.sortByTitle,
groupBy: map['groupBy'] as String? ?? fallback.groupBy,
groupByDate: map['groupByDate'] as String? ?? fallback.groupByDate,
groupByCategory:
map['groupByCategory'] as String? ?? fallback.groupByCategory,
groupByType: map['groupByType'] as String? ?? fallback.groupByType,
groupBySource:
map['groupBySource'] as String? ?? fallback.groupBySource,
// 布局
switchLayout:
map['switchLayout'] as String? ?? fallback.switchLayout,
layoutList: map['layoutList'] as String? ?? fallback.layoutList,
layoutGrid: map['layoutGrid'] as String? ?? fallback.layoutGrid,
layoutTimeline:
map['layoutTimeline'] as String? ?? fallback.layoutTimeline,
// 选择模式
selectedCount:
map['selectedCount'] as String? ?? fallback.selectedCount,
selectAll: map['selectAll'] as String? ?? fallback.selectAll,
deselectAll: map['deselectAll'] as String? ?? fallback.deselectAll,
batchSelect: map['batchSelect'] as String? ?? fallback.batchSelect,
multiSelect: map['multiSelect'] as String? ?? fallback.multiSelect,
// 批量操作
batchDelete: map['batchDelete'] as String? ?? fallback.batchDelete,
batchDeleteConfirm:
map['batchDeleteConfirm'] as String? ?? fallback.batchDeleteConfirm,
deletedCount: map['deletedCount'] as String? ?? fallback.deletedCount,
// 拖拽排序
dragReorder: map['dragReorder'] as String? ?? fallback.dragReorder,
dragToReorder:
map['dragToReorder'] as String? ?? fallback.dragToReorder,
// 上下文菜单
starFavorite:
map['starFavorite'] as String? ?? fallback.starFavorite,
unfavorite: map['unfavorite'] as String? ?? fallback.unfavorite,
exportNote: map['exportNote'] as String? ?? fallback.exportNote,
copyNote: map['copyNote'] as String? ?? fallback.copyNote,
// 菜单项
newNoteMenu: map['newNoteMenu'] as String? ?? fallback.newNoteMenu,
sortMenu: map['sortMenu'] as String? ?? fallback.sortMenu,
groupMenu: map['groupMenu'] as String? ?? fallback.groupMenu,
showStarOnly: map['showStarOnly'] as String? ?? fallback.showStarOnly,
showAll: map['showAll'] as String? ?? fallback.showAll,
statsPanel: map['statsPanel'] as String? ?? fallback.statsPanel,
reorderMenu: map['reorderMenu'] as String? ?? fallback.reorderMenu,
// 清空全部
confirmClear: map['confirmClear'] as String? ?? fallback.confirmClear,
clearAllWarning:
map['clearAllWarning'] as String? ?? fallback.clearAllWarning,
clearAll: map['clearAll'] as String? ?? fallback.clearAll,
allCleared: map['allCleared'] as String? ?? fallback.allCleared,
// 空状态
noNotes: map['noNotes'] as String? ?? fallback.noNotes,
tapToStart: map['tapToStart'] as String? ?? fallback.tapToStart,
// 底部
reachedBottom:
map['reachedBottom'] as String? ?? fallback.reachedBottom,
notesCount: map['notesCount'] as String? ?? fallback.notesCount,
// 日期标签
unknownDate: map['unknownDate'] as String? ?? fallback.unknownDate,
today: map['today'] as String? ?? fallback.today,
yesterday: map['yesterday'] as String? ?? fallback.yesterday,
// 分组标签
uncategorized:
map['uncategorized'] as String? ?? fallback.uncategorized,
noSource: map['noSource'] as String? ?? fallback.noSource,
// 笔记卡片
noTitle: map['noTitle'] as String? ?? fallback.noTitle,
// 删除
deleteNote: map['deleteNote'] as String? ?? fallback.deleteNote,
deleteNoteConfirm:
map['deleteNoteConfirm'] as String? ?? fallback.deleteNoteConfirm,
// 编辑页
noteLoadFailed:
map['noteLoadFailed'] as String? ?? fallback.noteLoadFailed,
titlePlaceholder:
map['titlePlaceholder'] as String? ?? fallback.titlePlaceholder,
categoryLabel:
map['categoryLabel'] as String? ?? fallback.categoryLabel,
sourceLabel: map['sourceLabel'] as String? ?? fallback.sourceLabel,
publicLabel: map['publicLabel'] as String? ?? fallback.publicLabel,
optionalPlaceholder:
map['optionalPlaceholder'] as String? ?? fallback.optionalPlaceholder,
visibleToAll:
map['visibleToAll'] as String? ?? fallback.visibleToAll,
visibleToSelf:
map['visibleToSelf'] as String? ?? fallback.visibleToSelf,
selectSourceType:
map['selectSourceType'] as String? ?? fallback.selectSourceType,
excerptPlaceholder:
map['excerptPlaceholder'] as String? ?? fallback.excerptPlaceholder,
checklistPlaceholder:
map['checklistPlaceholder'] as String? ??
fallback.checklistPlaceholder,
notePlaceholder:
map['notePlaceholder'] as String? ?? fallback.notePlaceholder,
noPreviewContent:
map['noPreviewContent'] as String? ?? fallback.noPreviewContent,
switchToEdit:
map['switchToEdit'] as String? ?? fallback.switchToEdit,
selectPreviewFont:
map['selectPreviewFont'] as String? ?? fallback.selectPreviewFont,
systemDefault:
map['systemDefault'] as String? ?? fallback.systemDefault,
noTitleNote: map['noTitleNote'] as String? ?? fallback.noTitleNote,
noteSaved: map['noteSaved'] as String? ?? fallback.noteSaved,
unsaved: map['unsaved'] as String? ?? fallback.unsaved,
saved: map['saved'] as String? ?? fallback.saved,
charCount: map['charCount'] as String? ?? fallback.charCount,
// 来源类型
sourcePoetry: map['sourcePoetry'] as String? ?? fallback.sourcePoetry,
sourceArticle:
map['sourceArticle'] as String? ?? fallback.sourceArticle,
sourceHanzi: map['sourceHanzi'] as String? ?? fallback.sourceHanzi,
sourceIdiom: map['sourceIdiom'] as String? ?? fallback.sourceIdiom,
// Markdown 工具栏
bold: map['bold'] as String? ?? fallback.bold,
italic: map['italic'] as String? ?? fallback.italic,
heading: map['heading'] as String? ?? fallback.heading,
list: map['list'] as String? ?? fallback.list,
orderedList: map['orderedList'] as String? ?? fallback.orderedList,
quote: map['quote'] as String? ?? fallback.quote,
link: map['link'] as String? ?? fallback.link,
code: map['code'] as String? ?? fallback.code,
divider: map['divider'] as String? ?? fallback.divider,
// 统计面板
noteStats: map['noteStats'] as String? ?? fallback.noteStats,
totalNotes: map['totalNotes'] as String? ?? fallback.totalNotes,
weekNew: map['weekNew'] as String? ?? fallback.weekNew,
totalWords: map['totalWords'] as String? ?? fallback.totalWords,
typeDistribution:
map['typeDistribution'] as String? ?? fallback.typeDistribution,
categoryDistribution:
map['categoryDistribution'] as String? ??
fallback.categoryDistribution,
noCategoryData:
map['noCategoryData'] as String? ?? fallback.noCategoryData,
starredNotes:
map['starredNotes'] as String? ?? fallback.starredNotes,
// 导出
exportTitle: map['exportTitle'] as String? ?? fallback.exportTitle,
exportCountNote:
map['exportCountNote'] as String? ?? fallback.exportCountNote,
copiedToClipboard:
map['copiedToClipboard'] as String? ?? fallback.copiedToClipboard,
exportTypeLabel:
map['exportTypeLabel'] as String? ?? fallback.exportTypeLabel,
exportCategoryLabel:
map['exportCategoryLabel'] as String? ?? fallback.exportCategoryLabel,
exportPublicYes:
map['exportPublicYes'] as String? ?? fallback.exportPublicYes,
exportSourceLabel:
map['exportSourceLabel'] as String? ?? fallback.exportSourceLabel,
exportCreateTime:
map['exportCreateTime'] as String? ?? fallback.exportCreateTime,
exportUpdateTime:
map['exportUpdateTime'] as String? ?? fallback.exportUpdateTime,
exportShareSubject:
map['exportShareSubject'] as String? ?? fallback.exportShareSubject,
// 置顶
pinToDiscover:
map['pinToDiscover'] as String? ?? fallback.pinToDiscover,
pinToDiscoverDesc:
map['pinToDiscoverDesc'] as String? ?? fallback.pinToDiscoverDesc,
close: map['close'] as String? ?? fallback.close,
pinnedToDiscover:
map['pinnedToDiscover'] as String? ?? fallback.pinnedToDiscover,
unpinnedFromDiscover:
map['unpinnedFromDiscover'] as String? ??
fallback.unpinnedFromDiscover,
confirmPin: map['confirmPin'] as String? ?? fallback.confirmPin,
confirmUnpin: map['confirmUnpin'] as String? ?? fallback.confirmUnpin,
);
}
/// 检查 TProfile 字段同步
///
/// 当 TProfile 新增字段但 _importProfile 未同步时,
/// 会在 debug 模式下打印警告,帮助开发者及时发现遗漏。
static void _checkProfileFieldsSync(
Map<String, dynamic> map,
TProfile? fallback,
) {
assert(() {
const expectedKeys = <String>{
'title',
'myFavorites',
'readingHistory',
'darkMode',
'accountSettings',
'dataManagement',
'offlineMode',
'cacheManagement',
'themeCustomization',
'desktopWidgets',
'sentenceSource',
'aboutApp',
'rateApp',
'debugMode',
'tapToLogin',
'defaultUserName',
'appSlogan',
'freeTier',
'points',
'checkin',
'notes',
'quickActions',
'scanQr',
'nearbyTransfer',
'payment',
'selectScanMethod',
'scanQrLogin',
'scanQrCode',
'appStoreNotFound',
'experimentalFeature',
'underReview',
'changeAvatar',
'inputAvatarUrl',
'selectFromAlbum',
'avatarUrlHint',
'pleaseInputUrl',
'urlMustStartWithHttp',
'urlTooLong',
'invalidUrlFormat',
'avatarUnderReview',
'avatarReviewing',
'avatarChangeSuccess',
'avatarChangeFailed',
'success',
'failed',
'ok',
'loading',
'loginToViewProfile',
'goLogin',
'consecutiveCheckin',
'favorites',
'likes',
'dailyCheckin',
'learningCenter',
'achievementCenter',
'dailyTask',
'leaderboard',
'dataStatistics',
'myNotes',
'contentCorrection',
'myDevices',
'tagCloud',
'deviceRemoveCurrentTitle',
'deviceRemoveCurrentWarning',
'deviceContinueRemove',
'deviceVerifyIdentityRemove',
'deviceRemoveTitle',
'deviceRemoveConfirm',
'deviceRemoved',
'deviceRemoveFailed',
'personalInfo',
'username',
'nickname',
'bio',
'notSet',
'notFilled',
'set',
'reviewing',
'editUsername',
'editNickname',
'nearbyDiscovery',
'nearbyDiscoveryDesc',
'totalTasks',
'taskClaimed',
'perfectDay',
'perfectDayAllDone',
'perfectDayReward',
'perfectDayRewardDesc',
'claimPerfectDayReward',
'rewardSuffix',
'expUnit',
'scoreUnit',
'noTasks',
'noTasksDesc',
'great',
'loginToCheckin',
'loginToCheckinDesc',
'viewAchievementCenter',
'todaySigned',
'tapToCheckin',
'signed',
'weeklyCheckin',
'totalCheckinDays',
'checkinHistory',
'noCheckinRecord',
'todayLabel',
'checkinDate',
'status',
'signedStatus',
'remark',
'dailyCheckinTaskDone',
'makeupCheckin',
'makeupCostInfo',
'makeupDate',
'costPoints',
'currentPoints',
'makeupInfo',
'makeupLimitInfo',
'confirmMakeup',
'insufficientPoints',
'insufficientPointsDesc',
'makeupSuccess',
'makeupSuccessDesc',
'makeupFailed',
'makeupFailedRetry',
'timesUnit',
'daysUnit',
};
final mapKeys = map.keys.toSet();
final missingInImport = expectedKeys.difference(mapKeys);
if (missingInImport.isNotEmpty) {
// ignore: avoid_print
print(
'⚠️ [TranslationIO] TProfile _importProfile 缺失字段: $missingInImport',
);
}
final extraInImport = mapKeys.difference(expectedKeys);
if (extraInImport.isNotEmpty) {
// ignore: avoid_print
print(
'⚠️ [TranslationIO] TProfile _importProfile 多余字段: $extraInImport',
);
}
return true; // assert 总是通过,仅打印警告
}());
}
/// 检查 TNote 字段同步
///
/// 当 TNote 新增字段但 _importNote 未同步时,
/// 会在 debug 模式下打印警告,帮助开发者及时发现遗漏。
static void _checkNoteFieldsSync(
Map<String, dynamic> map,
TNote? fallback,
) {
assert(() {
const expectedKeys = <String>{
'title',
'searchNotes',
'editNote',
'newNote',
'loginRequired',
'loginToUseNotes',
'goLogin',
'searchPlaceholder',
'all',
'note',
'excerpt',
'checklist',
'sortBy',
'sortByUpdateTime',
'sortByCreateTime',
'sortByTitle',
'groupBy',
'groupByDate',
'groupByCategory',
'groupByType',
'groupBySource',
'switchLayout',
'layoutList',
'layoutGrid',
'layoutTimeline',
'selectedCount',
'selectAll',
'deselectAll',
'batchSelect',
'multiSelect',
'batchDelete',
'batchDeleteConfirm',
'deletedCount',
'dragReorder',
'dragToReorder',
'starFavorite',
'unfavorite',
'exportNote',
'copyNote',
'newNoteMenu',
'sortMenu',
'groupMenu',
'showStarOnly',
'showAll',
'statsPanel',
'reorderMenu',
'confirmClear',
'clearAllWarning',
'clearAll',
'allCleared',
'noNotes',
'tapToStart',
'reachedBottom',
'notesCount',
'unknownDate',
'today',
'yesterday',
'uncategorized',
'noSource',
'noTitle',
'deleteNote',
'deleteNoteConfirm',
'noteLoadFailed',
'titlePlaceholder',
'categoryLabel',
'sourceLabel',
'publicLabel',
'optionalPlaceholder',
'visibleToAll',
'visibleToSelf',
'selectSourceType',
'excerptPlaceholder',
'checklistPlaceholder',
'notePlaceholder',
'noPreviewContent',
'switchToEdit',
'selectPreviewFont',
'systemDefault',
'noTitleNote',
'noteSaved',
'unsaved',
'saved',
'charCount',
'sourcePoetry',
'sourceArticle',
'sourceHanzi',
'sourceIdiom',
'bold',
'italic',
'heading',
'list',
'orderedList',
'quote',
'link',
'code',
'divider',
'noteStats',
'totalNotes',
'weekNew',
'totalWords',
'typeDistribution',
'categoryDistribution',
'noCategoryData',
'starredNotes',
'exportTitle',
'exportCountNote',
'copiedToClipboard',
'exportTypeLabel',
'exportCategoryLabel',
'exportPublicYes',
'exportSourceLabel',
'exportCreateTime',
'exportUpdateTime',
'exportShareSubject',
'pinToDiscover',
'pinToDiscoverDesc',
'close',
'pinnedToDiscover',
'unpinnedFromDiscover',
'confirmPin',
'confirmUnpin',
};
final mapKeys = map.keys.toSet();
final missingInImport = expectedKeys.difference(mapKeys);
if (missingInImport.isNotEmpty) {
// ignore: avoid_print
print(
'⚠️ [TranslationIO] TNote _importNote 缺失字段: $missingInImport',
);
}
final extraInImport = mapKeys.difference(expectedKeys);
if (extraInImport.isNotEmpty) {
// ignore: avoid_print
print(
'⚠️ [TranslationIO] TNote _importNote 多余字段: $extraInImport',
);
}
return true; // assert 总是通过,仅打印警告
}());
}
static TBeta _importBeta(Map<String, dynamic>? map, TBeta fallback) {
if (map == null) return fallback;
// ---- 字段同步检查(防止新增字段遗漏) ----
_checkBetaFieldsSync(map, fallback);
return TBeta(
// 页面级
pageTitle: map['pageTitle'] as String? ?? fallback.pageTitle,
back: map['back'] as String? ?? fallback.back,
previewTab: map['previewTab'] as String? ?? fallback.previewTab,
issuesTab: map['issuesTab'] as String? ?? fallback.issuesTab,
// 对话框
confirmClose: map['confirmClose'] as String? ?? fallback.confirmClose,
confirmOpen: map['confirmOpen'] as String? ?? fallback.confirmOpen,
cancel: map['cancel'] as String? ?? fallback.cancel,
close: map['close'] as String? ?? fallback.close,
open: map['open'] as String? ?? fallback.open,
// 空状态/错误
emptyFeatures: map['emptyFeatures'] as String? ?? fallback.emptyFeatures,
reload: map['reload'] as String? ?? fallback.reload,
loadFailed: map['loadFailed'] as String? ?? fallback.loadFailed,
retry: map['retry'] as String? ?? fallback.retry,
// 筛选标签
filterAll: map['filterAll'] as String? ?? fallback.filterAll,
filterPending: map['filterPending'] as String? ?? fallback.filterPending,
filterFixing: map['filterFixing'] as String? ?? fallback.filterFixing,
filterFixed: map['filterFixed'] as String? ?? fallback.filterFixed,
// 问题列表
emptyIssues: map['emptyIssues'] as String? ?? fallback.emptyIssues,
rolloutPercentage: map['rolloutPercentage'] as String? ?? fallback.rolloutPercentage,
targetGroup: map['targetGroup'] as String? ?? fallback.targetGroup,
issueStats: map['issueStats'] as String? ?? fallback.issueStats,
// 严重程度
severityHigh: map['severityHigh'] as String? ?? fallback.severityHigh,
severityMedium: map['severityMedium'] as String? ?? fallback.severityMedium,
severityLow: map['severityLow'] as String? ?? fallback.severityLow,
// 状态
statusPending: map['statusPending'] as String? ?? fallback.statusPending,
statusFixing: map['statusFixing'] as String? ?? fallback.statusFixing,
statusFixed: map['statusFixed'] as String? ?? fallback.statusFixed,
statusDeveloping: map['statusDeveloping'] as String? ?? fallback.statusDeveloping,
statusTesting: map['statusTesting'] as String? ?? fallback.statusTesting,
statusPreview: map['statusPreview'] as String? ?? fallback.statusPreview,
statusReleased: map['statusReleased'] as String? ?? fallback.statusReleased,
// 通用
comingSoon: map['comingSoon'] as String? ?? fallback.comingSoon,
gotIt: map['gotIt'] as String? ?? fallback.gotIt,
// 问卷
questionnaireBtn: map['questionnaireBtn'] as String? ?? fallback.questionnaireBtn,
questionnaireTitle: map['questionnaireTitle'] as String? ?? fallback.questionnaireTitle,
q1KnowGooglePlay: map['q1KnowGooglePlay'] as String? ?? fallback.q1KnowGooglePlay,
q2HasGmsDevice: map['q2HasGmsDevice'] as String? ?? fallback.q2HasGmsDevice,
q3WillingToBeta: map['q3WillingToBeta'] as String? ?? fallback.q3WillingToBeta,
q4EnterGmail: map['q4EnterGmail'] as String? ?? fallback.q4EnterGmail,
q4GmailHint: map['q4GmailHint'] as String? ?? fallback.q4GmailHint,
qYes: map['qYes'] as String? ?? fallback.qYes,
qNo: map['qNo'] as String? ?? fallback.qNo,
qSubmit: map['qSubmit'] as String? ?? fallback.qSubmit,
qNext: map['qNext'] as String? ?? fallback.qNext,
qEndTitle: map['qEndTitle'] as String? ?? fallback.qEndTitle,
qEndThanks: map['qEndThanks'] as String? ?? fallback.qEndThanks,
qEndNotQualified: map['qEndNotQualified'] as String? ?? fallback.qEndNotQualified,
qInvalidEmail: map['qInvalidEmail'] as String? ?? fallback.qInvalidEmail,
qSubmitting: map['qSubmitting'] as String? ?? fallback.qSubmitting,
qSubmitSuccess: map['qSubmitSuccess'] as String? ?? fallback.qSubmitSuccess,
qSubmitFailed: map['qSubmitFailed'] as String? ?? fallback.qSubmitFailed,
qEndThankYou: map['qEndThankYou'] as String? ?? fallback.qEndThankYou,
);
}
/// 检查 TBeta 字段同步
///
/// 当 TBeta 新增字段但 _importBeta 未同步时,
/// 会在 debug 模式下打印警告,帮助开发者及时发现遗漏。
static void _checkBetaFieldsSync(
Map<String, dynamic> map,
TBeta? fallback,
) {
assert(() {
const expectedKeys = <String>{
'pageTitle',
'back',
'previewTab',
'issuesTab',
'confirmClose',
'confirmOpen',
'cancel',
'close',
'open',
'emptyFeatures',
'reload',
'loadFailed',
'retry',
'filterAll',
'filterPending',
'filterFixing',
'filterFixed',
'emptyIssues',
'rolloutPercentage',
'targetGroup',
'issueStats',
'severityHigh',
'severityMedium',
'severityLow',
'statusPending',
'statusFixing',
'statusFixed',
'statusDeveloping',
'statusTesting',
'statusPreview',
'statusReleased',
'comingSoon',
'gotIt',
};
final mapKeys = map.keys.toSet();
final missingInImport = expectedKeys.difference(mapKeys);
if (missingInImport.isNotEmpty) {
// ignore: avoid_print
print(
'⚠️ [TranslationIO] TBeta _importBeta 缺失字段: $missingInImport',
);
}
final extraInImport = mapKeys.difference(expectedKeys);
if (extraInImport.isNotEmpty) {
// ignore: avoid_print
print(
'⚠️ [TranslationIO] TBeta _importBeta 多余字段: $extraInImport',
);
}
return true; // assert 总是通过,仅打印警告
}());
}
static TSubmit _importSubmit(Map<String, dynamic>? map, TSubmit fallback) {
if (map == null) return fallback;
// ---- 字段同步检查(防止新增字段遗漏) ----
_checkSubmitFieldsSync(map, fallback);
return TSubmit(
title: map['title'] as String? ?? fallback.title,
contentLabel: map['contentLabel'] as String? ?? fallback.contentLabel,
titleLabel: map['titleLabel'] as String? ?? fallback.titleLabel,
authorLabel: map['authorLabel'] as String? ?? fallback.authorLabel,
categoryLabel: map['categoryLabel'] as String? ?? fallback.categoryLabel,
titleHint: map['titleHint'] as String? ?? fallback.titleHint,
authorHint: map['authorHint'] as String? ?? fallback.authorHint,
contentHint: map['contentHint'] as String? ?? fallback.contentHint,
submit: map['submit'] as String? ?? fallback.submit,
submitting: map['submitting'] as String? ?? fallback.submitting,
contentRequired: map['contentRequired'] as String? ?? fallback.contentRequired,
contentTooShort: map['contentTooShort'] as String? ?? fallback.contentTooShort,
contentTooLong: map['contentTooLong'] as String? ?? fallback.contentTooLong,
reviewing: map['reviewing'] as String? ?? fallback.reviewing,
reviewDesc: map['reviewDesc'] as String? ?? fallback.reviewDesc,
historyTitle: map['historyTitle'] as String? ?? fallback.historyTitle,
historyEmpty: map['historyEmpty'] as String? ?? fallback.historyEmpty,
statusReviewing: map['statusReviewing'] as String? ?? fallback.statusReviewing,
statusApproved: map['statusApproved'] as String? ?? fallback.statusApproved,
statusRejected: map['statusRejected'] as String? ?? fallback.statusRejected,
catYiyan: map['catYiyan'] as String? ?? fallback.catYiyan,
catXinde: map['catXinde'] as String? ?? fallback.catXinde,
catYiju: map['catYiju'] as String? ?? fallback.catYiju,
catSignature: map['catSignature'] as String? ?? fallback.catSignature,
deleteRecord: map['deleteRecord'] as String? ?? fallback.deleteRecord,
deleteConfirm: map['deleteConfirm'] as String? ?? fallback.deleteConfirm,
);
}
/// 检查 TSubmit 字段同步
///
/// 当 TSubmit 新增字段但 _importSubmit 未同步时,
/// 会在 debug 模式下打印警告,帮助开发者及时发现遗漏。
static void _checkSubmitFieldsSync(
Map<String, dynamic> map,
TSubmit? fallback,
) {
assert(() {
const expectedKeys = <String>{
'title',
'contentLabel',
'titleLabel',
'authorLabel',
'categoryLabel',
'titleHint',
'authorHint',
'contentHint',
'submit',
'submitting',
'contentRequired',
'contentTooShort',
'contentTooLong',
'reviewing',
'reviewDesc',
'historyTitle',
'historyEmpty',
'statusReviewing',
'statusApproved',
'statusRejected',
'catYiyan',
'catXinde',
'catYiju',
'catSignature',
'deleteRecord',
'deleteConfirm',
};
final mapKeys = map.keys.toSet();
final missingInImport = expectedKeys.difference(mapKeys);
if (missingInImport.isNotEmpty) {
// ignore: avoid_print
print(
'⚠️ [TranslationIO] TSubmit _importSubmit 缺失字段: $missingInImport',
);
}
final extraInImport = mapKeys.difference(expectedKeys);
if (extraInImport.isNotEmpty) {
// ignore: avoid_print
print(
'⚠️ [TranslationIO] TSubmit _importSubmit 多余字段: $extraInImport',
);
}
return true; // assert 总是通过,仅打印警告
}());
}
}