Files
xianyan/lib/l10n/translation_coverage.dart
Developer 9ea8d3d606 chore: 汇总批量提交的功能优化与bug修复
本次提交包含多项迭代优化和问题修复:
1. 新增缩略图图片组件、数字格式化工具类,补充多语言翻译类型与本地化支持
2. 优化底部导航栏主题色统一使用动态accent色值
3. 修复多处图表动画、路由跳转、API请求相关问题
4. 简化服务器公告文案,调整默认分屏状态为关闭
5. 新增安卓/iOS桌面快捷方式配置
6. 重构多处状态管理类使用SafeNotifierInit统一异常保护
7. 替换硬编码蓝色为主题色,更新版本号获取方式为动态读取
8. 优化缓存预加载逻辑,移除无用代码
9. 调整默认设置项,优化用户体验细节
2026-05-31 12:24:05 +08:00

228 lines
6.2 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-29
/// 更新时间: 2026-05-31
/// 作用: 各语言翻译完成度检测与覆盖率报告
/// 上次更新: 新增ko/de/it语言覆盖率检测基于toMap自动检测所有子模块
/// ============================================================
import 'types/t.dart';
import 'languages/zh_cn.dart';
import 'languages/en.dart';
import 'languages/ja.dart';
import 'languages/zh_tw.dart';
import 'languages/ko.dart';
import 'languages/de.dart';
import 'languages/it.dart';
import 'languages/es.dart';
import 'languages/ar.dart';
import 'languages/bn.dart';
import 'languages/hi.dart';
import 'languages/pt.dart';
import 'languages/ru.dart';
import 'languages/fr.dart';
/// 翻译覆盖率检测工具
class TranslationCoverage {
/// 总翻译字段数(自动计算基于zhCN的toMap)
static int get totalFieldCount {
final map = zhCN.toMap();
var count = 0;
for (final section in map.values) {
count += section.length;
}
return count;
}
/// 各语言实际翻译完成度(0-100),覆盖自动检测的空字段计数
/// 自动检测仅判断字段是否为空,无法区分"机器翻译占位"与"人工精校"
/// 因此由维护者手动标注真实进度
static const Map<String, int> _progressOverride = {
'zh_CN': 100,
'en': 95,
'ja': 75,
'zh_TW': 90,
'ko': 70,
'de': 60,
'it': 55,
'es': 60,
'ar': 55,
'bn': 50,
'hi': 45,
'pt': 55,
'ru': 60,
'fr': 65,
};
/// 检测所有语言的翻译完成数量
static Map<String, int> checkAll() {
final result = <String, int>{};
result['zh_CN'] = totalFieldCount;
final languages = <String, T>{
'en': en,
'ja': ja,
'zh_TW': zhTW,
'ko': ko,
'de': de,
'it': it,
'es': es,
'ar': ar,
'bn': bn,
'hi': hi,
'pt': pt,
'ru': ru,
'fr': fr,
};
for (final entry in languages.entries) {
final overridePercent = _progressOverride[entry.key];
if (overridePercent != null) {
result[entry.key] = (totalFieldCount * overridePercent / 100).round();
} else {
final missing = checkCoverage(entry.value, zhCN);
result[entry.key] = totalFieldCount - missing.length;
}
}
return result;
}
/// 获取某语言的实际翻译完成百分比(0-100)
static int getProgressPercent(String langId) {
final percent = _progressOverride[langId];
if (percent != null) return percent;
return 100;
}
/// 检测目标语言相对于参考语言的缺失翻译字段
/// 基于 toMap() 自动遍历,新增字段无需手动添加检测代码
static List<String> checkCoverage(T target, T reference) {
final targetMap = target.toMap();
final missing = <String>[];
for (final sectionEntry in targetMap.entries) {
final sectionName = sectionEntry.key;
final sectionMap = sectionEntry.value;
for (final fieldEntry in sectionMap.entries) {
if (fieldEntry.value.isEmpty) {
missing.add('$sectionName.${fieldEntry.key}');
}
}
}
return missing;
}
/// 获取某语言的缺失翻译字段详情
static Map<String, List<String>> checkCoverageDetail(T target) {
final targetMap = target.toMap();
final result = <String, List<String>>{};
for (final sectionEntry in targetMap.entries) {
final sectionName = sectionEntry.key;
final sectionMap = sectionEntry.value;
final missingFields = <String>[];
for (final fieldEntry in sectionMap.entries) {
if (fieldEntry.value.isEmpty) {
missingFields.add(fieldEntry.key);
}
}
if (missingFields.isNotEmpty) {
result[sectionName] = missingFields;
}
}
return result;
}
/// 获取所有语言的覆盖率报告
static Map<String, Map<String, dynamic>> fullReport() {
final languages = <String, T>{
'zh_CN': zhCN,
'en': en,
'ja': ja,
'zh_TW': zhTW,
'ko': ko,
'de': de,
'it': it,
'es': es,
'ar': ar,
'bn': bn,
'hi': hi,
'pt': pt,
'ru': ru,
'fr': fr,
};
final total = totalFieldCount;
final report = <String, Map<String, dynamic>>{};
for (final entry in languages.entries) {
final missing = checkCoverage(entry.value, zhCN);
final diff = diffFields(entry.value, zhCN);
final overridePercent = _progressOverride[entry.key];
final effectivePercent =
overridePercent ?? ((total - missing.length) * 100 / total).round();
report[entry.key] = {
'total': total,
'translated': total - missing.length,
'missing': missing,
'percent': effectivePercent,
'missingFields': diff,
};
}
return report;
}
/// 检测目标语言与参考语言的字段差异
/// 返回参考语言中存在但目标语言中缺失或为空的字段
static Map<String, List<String>> diffFields(T target, T reference) {
final targetMap = target.toMap();
final referenceMap = reference.toMap();
final result = <String, List<String>>{};
for (final sectionEntry in referenceMap.entries) {
final sectionName = sectionEntry.key;
final refSection = sectionEntry.value;
final targetSection = targetMap[sectionName] ?? {};
final missing = <String>[];
for (final fieldEntry in refSection.entries) {
final targetValue = targetSection[fieldEntry.key];
if (targetValue == null || targetValue.isEmpty) {
missing.add(fieldEntry.key);
}
}
if (missing.isNotEmpty) {
result[sectionName] = missing;
}
}
return result;
}
/// 获取所有语言ID列表(不含zh_CN)
static List<String> get allLanguageIds => [
'en',
'ja',
'zh_TW',
'ko',
'de',
'it',
'es',
'ar',
'bn',
'hi',
'pt',
'ru',
'fr',
];
/// 获取所有语言的翻译实例映射
static Map<String, T> get allLanguages => {
'zh_CN': zhCN,
'en': en,
'ja': ja,
'zh_TW': zhTW,
'ko': ko,
'de': de,
'it': it,
'es': es,
'ar': ar,
'bn': bn,
'hi': hi,
'pt': pt,
'ru': ru,
'fr': fr,
};
}