From 75ae903b99b4f341d5a419ccd4aa813f46e492cf Mon Sep 17 00:00:00 2001 From: ZIER Date: Tue, 12 May 2026 10:24:01 +0200 Subject: [PATCH] implement flutter_langdetect --- lib/services/translation_service.dart | 86 ++++++--------------------- pubspec.yaml | 1 + 2 files changed, 19 insertions(+), 68 deletions(-) diff --git a/lib/services/translation_service.dart b/lib/services/translation_service.dart index 294a7848..148650ca 100644 --- a/lib/services/translation_service.dart +++ b/lib/services/translation_service.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:llamadart/llamadart.dart'; +import 'package:flutter_langdetect/flutter_langdetect.dart'; import '../models/app_settings.dart'; import '../models/translation_support.dart'; @@ -41,7 +42,10 @@ class TranslationService extends ChangeNotifier { TranslationService( this._appSettingsService, { TranslationFileStore? fileStore, - }) : _fileStore = fileStore ?? TranslationFileStore(); + }) : _fileStore = fileStore ?? TranslationFileStore() { + // Initialize langdetect once at service construction. + _langDetectInit = initLangDetect(); + } bool _isBusy = false; bool _isDownloading = false; @@ -51,6 +55,7 @@ class TranslationService extends ChangeNotifier { LlamaEngine? _engine; String? _loadedModelPath; String? _failedModelPath; + Future? _langDetectInit; int _downloadedBytes = 0; int? _downloadTotalBytes; String? _downloadFileName; @@ -436,7 +441,18 @@ class TranslationService extends ChangeNotifier { } Future detectLanguage(String text) async { - return _heuristicLanguageCode(text); + try { + // Ensure the detector is initialized (constructor starts init). + await (_langDetectInit ??= initLangDetect()); + final code = detect(text); + if (code.isEmpty) return null; + return code; + } catch (error) { + _lastError = error.toString(); + appLogger.warn('Language detection failed: $error'); + notifyListeners(); + return null; + } } Future _translateText({ @@ -518,72 +534,6 @@ class TranslationService extends ChangeNotifier { trimmed.startsWith('r:')); } - String? _heuristicLanguageCode(String text) { - final trimmed = text.trim(); - if (trimmed.isEmpty) { - return null; - } - - if (RegExp(r'[ぁ-んァ-ン]').hasMatch(text)) { - return 'ja'; - } - if (RegExp(r'[가-힣]').hasMatch(text)) { - return 'ko'; - } - if (RegExp(r'[\u4e00-\u9fff]').hasMatch(text)) { - return 'zh'; - } - - final lower = trimmed.toLowerCase(); - final patterns = { - 'uk': r'\b(привіт|дякую|будь|ласка|як|де|не|так|це|є|най|ще|може|для)\b', - 'ru': - r'\b(что|это|как|не|да|нет|он|она|они|быть|есть|для|сегодня|если|уже|может)\b', - 'bg': r'\b(ще|няма|благодаря|моля|това|какво|тук|ние|вие|не|със|за)\b', - 'de': - r'\b(der|die|das|und|ist|nicht|ein|eine|ich|für|mit|auf|zu|auch|als|an|im|am|es|dem|den|sich|von)\b', - 'en': - r'\b(the|and|is|you|for|with|from|not|that|this|have|be|are|was|were|but|can|will|your|what|when|how|they)\b', - 'es': - r'\b(el|la|los|las|es|que|de|en|con|por|para|no|un|una|se|como|su|al|del|está)\b', - 'fr': - r'\b(le|la|les|un|une|et|est|que|qui|pour|dans|pas|avec|sur|ne|vous|il|elle|des|ce|cette|je|tu|nous|vous)\b', - 'it': - r'\b(il|la|lo|un|una|che|di|da|in|per|con|non|si|mi|ti|noi|voi|lui|lei)\b', - 'pt': - r'\b(os|as|que|de|do|da|em|para|com|por|não|uma|um|se|você|também)\b', - 'nl': - r'\b(de|het|een|en|is|niet|dat|wat|je|ik|op|aan|voor|met|als|nog|zijn)\b', - 'sv': - r'\b(och|är|det|att|som|en|på|inte|har|var|men|du|jag|vi|ni|den|detta)\b', - 'pl': - r'\b(na|się|nie|jest|to|że|do|od|dla|czy|tak|ale|ma|jak|on|ona|my)\b', - 'sk': r'\b(je|na|so|že|do|od|za|si|to|ten|tá|tí|ako|má|nie|som|sa)\b', - 'sl': r'\b(in|je|na|se|da|za|od|ne|to|ta|so|kako|bo|sem|si)\b', - 'hu': - r'\b(az|és|nem|van|volt|hogy|mit|mire|ki|mi|ez|azért|is|de|ha|te|ő|mi|itt)\b', - }; - - final scores = {}; - for (final entry in patterns.entries) { - scores[entry.key] = RegExp( - entry.value, - caseSensitive: false, - ).allMatches(lower).length; - } - - final sorted = scores.entries.toList() - ..sort((a, b) => b.value.compareTo(a.value)); - if (sorted.isEmpty || sorted.first.value == 0) { - return null; - } - if (sorted.length > 1 && sorted.first.value == sorted[1].value) { - return null; - } - - return sorted.first.key; - } - String _languageLabel(String code) { for (final option in supportedTranslationLanguages) { if (option.code == code) { diff --git a/pubspec.yaml b/pubspec.yaml index 7b43dded..72cca35f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -72,6 +72,7 @@ dependencies: ml_algo: ^16.0.0 ml_dataframe: ^1.0.0 llamadart: '>=0.6.8 <0.7.0' + flutter_langdetect: ^0.0.1 hooks: user_defines: