feat(apps): 重构 UI 架构为 presentation 层并新增 l10n 国际化支持

This commit is contained in:
qzl
2026-03-27 14:05:03 +08:00
parent b1f0eb8921
commit c592cc7854
178 changed files with 10748 additions and 5764 deletions
@@ -0,0 +1,68 @@
String formatPhoneForDisplay(String? rawPhone) {
final normalized = _normalizePhone(rawPhone);
if (normalized == null) {
return rawPhone?.trim() ?? '';
}
if (normalized.startsWith('+86') && normalized.length == 14) {
final local = normalized.substring(3);
return '${local.substring(0, 3)}****${local.substring(7)}';
}
if (!normalized.startsWith('+')) {
return normalized;
}
final digits = normalized.substring(1);
final countryCode = _detectCountryCode(digits);
if (countryCode == null) {
return normalized;
}
final localNumber = digits.substring(countryCode.length);
if (localNumber.length <= 4) {
return '+$countryCode $localNumber';
}
final tail = localNumber.substring(localNumber.length - 4);
return '+$countryCode ****$tail';
}
String? _normalizePhone(String? rawPhone) {
if (rawPhone == null) {
return null;
}
var phone = rawPhone.trim();
for (final separator in const [' ', '-', '(', ')']) {
phone = phone.replaceAll(separator, '');
}
if (phone.isEmpty) {
return null;
}
if (phone.startsWith('00') && phone.length > 2) {
phone = '+${phone.substring(2)}';
}
if (!phone.startsWith('+') && RegExp(r'^\d+$').hasMatch(phone)) {
phone = '+$phone';
}
return phone;
}
String? _detectCountryCode(String digits) {
const knownCodes = ['86', '1', '44', '81', '65', '33'];
for (final code in knownCodes) {
if (digits.startsWith(code) && digits.length > code.length + 3) {
return code;
}
}
for (int length = 3; length >= 1; length--) {
if (length >= digits.length) {
continue;
}
final candidate = digits.substring(0, length);
if (candidate.startsWith('0')) {
continue;
}
if (digits.length - length >= 4) {
return candidate;
}
}
return null;
}
@@ -0,0 +1,43 @@
import '../l10n/l10n.dart';
const Map<String, String> _toolNameAliases = {
'calendar_read': 'calendar.read',
'calendar_write': 'calendar.write',
'calendar_share': 'calendar.share',
'user_lookup': 'user.lookup',
'memory_write': 'memory.write',
'memory_forget': 'memory.forget',
};
const List<String> automationToolOptions = [
'calendar.read',
'calendar.write',
'calendar.share',
'user.lookup',
'memory.write',
'memory.forget',
];
String localizeToolName(String rawName) {
final normalized = rawName.trim().toLowerCase();
if (normalized.isEmpty) {
return rawName;
}
final canonical = _toolNameAliases[normalized] ?? normalized;
switch (canonical) {
case 'calendar.read':
return L10n.current.toolCalendarRead;
case 'calendar.write':
return L10n.current.toolCalendarWrite;
case 'calendar.share':
return L10n.current.toolCalendarShare;
case 'user.lookup':
return L10n.current.toolUserLookup;
case 'memory.write':
return L10n.current.toolMemoryWrite;
case 'memory.forget':
return L10n.current.toolMemoryForget;
default:
return rawName;
}
}
+45
View File
@@ -0,0 +1,45 @@
import '../l10n/l10n.dart';
class Validators {
Validators._();
static String? phone(String? value) {
if (value == null || value.isEmpty) {
return L10n.current.validatorPhoneRequired;
}
final phoneRegex = RegExp(r'^\+861[3-9]\d{9}$');
if (!phoneRegex.hasMatch(value)) {
return L10n.current.validatorPhoneInvalid86;
}
return null;
}
static String? password(String? value) {
if (value == null || value.isEmpty) {
return L10n.current.validatorPasswordRequired;
}
if (value.length < 8) {
return L10n.current.validatorPasswordMin8;
}
return null;
}
static String? required(String? value, [String? fieldName]) {
if (value == null || value.isEmpty) {
return L10n.current.validatorRequired(
fieldName ?? L10n.current.commonUnknown,
);
}
return null;
}
static String? nickname(String? value) {
if (value == null || value.isEmpty) {
return L10n.current.validatorNicknameRequired;
}
if (value.length < 2) {
return L10n.current.validatorNicknameMin2;
}
return null;
}
}