feat(apps): 重构 UI 架构为 presentation 层并新增 l10n 国际化支持
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user