23 changed files with 747 additions and 280 deletions
@ -0,0 +1,87 @@ |
|||||
|
import 'dart:io'; |
||||
|
import 'dart:ui'; |
||||
|
import 'package:flutter/services.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
import '../translations/app_translations.dart'; |
||||
|
|
||||
|
class NativeLocalizationSync { |
||||
|
static const MethodChannel _channel = |
||||
|
MethodChannel('com.saitong.voitrans/native_localization'); |
||||
|
|
||||
|
static const List<String> nativeKeys = <String>[ |
||||
|
'native.notification.appName', |
||||
|
'native.notification.navigation.title', |
||||
|
'native.notification.navigation.keepRunning', |
||||
|
'native.notification.bleAgent.running', |
||||
|
'native.notification.audioRecording.recording', |
||||
|
'native.notification.music.waiting', |
||||
|
'native.notification.music.previous', |
||||
|
'native.notification.music.next', |
||||
|
'native.notification.music.play', |
||||
|
'native.notification.music.pause', |
||||
|
'native.notification.qqMusic.playing', |
||||
|
'unknownSong', |
||||
|
'unknownArtist', |
||||
|
]; |
||||
|
|
||||
|
static String? _lastLocaleKey; |
||||
|
static int? _lastPayloadHash; |
||||
|
static bool _isSyncing = false; |
||||
|
|
||||
|
static Future<void> syncToNative({bool force = false}) async { |
||||
|
if (!Platform.isAndroid) return; |
||||
|
if (_isSyncing) return; |
||||
|
|
||||
|
final Locale locale = Get.locale ?? |
||||
|
AppTranslations.deviceLocale() ?? |
||||
|
AppTranslations.fallbackLocale; |
||||
|
final String localeKey = _toAppTranslationsLocaleKey(locale); |
||||
|
|
||||
|
final Map<String, String> payload = _buildPayload(localeKey); |
||||
|
final int payloadHash = Object.hashAll( |
||||
|
payload.entries.map((e) => Object.hash(e.key, e.value)), |
||||
|
); |
||||
|
|
||||
|
if (!force && |
||||
|
_lastLocaleKey == localeKey && |
||||
|
_lastPayloadHash == payloadHash) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_isSyncing = true; |
||||
|
try { |
||||
|
await _channel.invokeMethod('updateNativeStrings', <String, dynamic>{ |
||||
|
'locale': localeKey, |
||||
|
'strings': payload, |
||||
|
}); |
||||
|
_lastLocaleKey = localeKey; |
||||
|
_lastPayloadHash = payloadHash; |
||||
|
} finally { |
||||
|
_isSyncing = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static Map<String, String> _buildPayload(String localeKey) { |
||||
|
final Map<String, Map<String, String>> all = AppTranslations().keys; |
||||
|
final Map<String, String>? current = all[localeKey]; |
||||
|
final Map<String, String>? fallback = |
||||
|
all['en_US'] ?? all[AppTranslations.fallbackLocale.toString()]; |
||||
|
|
||||
|
final Map<String, String> result = <String, String>{}; |
||||
|
for (final String key in nativeKeys) { |
||||
|
final String? value = current?[key] ?? fallback?[key]; |
||||
|
if (value == null || value.isEmpty) continue; |
||||
|
result[key] = value; |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
static String _toAppTranslationsLocaleKey(Locale locale) { |
||||
|
final String language = locale.languageCode; |
||||
|
final String? country = locale.countryCode; |
||||
|
if (country == null || country.isEmpty) { |
||||
|
return language.toLowerCase(); |
||||
|
} |
||||
|
return '${language.toLowerCase()}_${country.toUpperCase()}'; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue