126 lines
3.8 KiB
Dart
126 lines
3.8 KiB
Dart
import 'dart:convert';
|
||
import 'package:http/http.dart' as http;
|
||
import '../models/language.dart';
|
||
import 'settings_service.dart';
|
||
|
||
class TranslateException implements Exception {
|
||
final String message;
|
||
TranslateException(this.message);
|
||
@override
|
||
String toString() => message;
|
||
}
|
||
|
||
class DetectResult {
|
||
final String language;
|
||
final int confidence;
|
||
DetectResult({required this.language, required this.confidence});
|
||
}
|
||
|
||
/// Kullanıcının kendi sunucusundaki LibreTranslate API'sine istek atar.
|
||
class TranslateService {
|
||
final SettingsService _settings;
|
||
TranslateService(this._settings);
|
||
|
||
Future<String> _baseUrl() async {
|
||
final url = await _settings.getServerUrl();
|
||
if (url == null || url.isEmpty) {
|
||
throw TranslateException('Sunucu adresi ayarlanmamış. Ayarlar\'dan ekleyin.');
|
||
}
|
||
return url;
|
||
}
|
||
|
||
Map<String, dynamic> _withApiKey(Map<String, dynamic> body, String? apiKey) {
|
||
if (apiKey != null && apiKey.isNotEmpty) {
|
||
body['api_key'] = apiKey;
|
||
}
|
||
return body;
|
||
}
|
||
|
||
String _errorFromBody(String body, int statusCode) {
|
||
try {
|
||
final decoded = jsonDecode(body);
|
||
if (decoded is Map && decoded['error'] != null) {
|
||
return decoded['error'].toString();
|
||
}
|
||
} catch (_) {
|
||
// JSON değilse aşağıda genel mesaj kullanılır.
|
||
}
|
||
return 'Sunucu hatası (HTTP $statusCode)';
|
||
}
|
||
|
||
/// Sunucuya bağlanıp desteklenen dilleri getirir.
|
||
/// Hem "bağlantı testi" hem de dil listesini doldurmak için kullanılır.
|
||
Future<List<Language>> fetchLanguages() async {
|
||
final base = await _baseUrl();
|
||
final apiKey = await _settings.getApiKey();
|
||
final uri = Uri.parse('$base/languages').replace(
|
||
queryParameters: apiKey != null ? {'api_key': apiKey} : null,
|
||
);
|
||
|
||
final res = await http.get(uri).timeout(const Duration(seconds: 10));
|
||
if (res.statusCode != 200) {
|
||
throw TranslateException(_errorFromBody(res.body, res.statusCode));
|
||
}
|
||
final List<dynamic> data = jsonDecode(utf8.decode(res.bodyBytes));
|
||
return data.map((e) => Language.fromJson(e as Map<String, dynamic>)).toList();
|
||
}
|
||
|
||
Future<String> translate({
|
||
required String text,
|
||
required String sourceLang,
|
||
required String targetLang,
|
||
}) async {
|
||
if (text.trim().isEmpty) return '';
|
||
final base = await _baseUrl();
|
||
final apiKey = await _settings.getApiKey();
|
||
|
||
final body = _withApiKey({
|
||
'q': text,
|
||
'source': sourceLang,
|
||
'target': targetLang,
|
||
'format': 'text',
|
||
}, apiKey);
|
||
|
||
final res = await http
|
||
.post(
|
||
Uri.parse('$base/translate'),
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: jsonEncode(body),
|
||
)
|
||
.timeout(const Duration(seconds: 15));
|
||
|
||
if (res.statusCode != 200) {
|
||
throw TranslateException(_errorFromBody(res.body, res.statusCode));
|
||
}
|
||
final decoded = jsonDecode(utf8.decode(res.bodyBytes));
|
||
return decoded['translatedText'] as String;
|
||
}
|
||
|
||
Future<DetectResult?> detect(String text) async {
|
||
if (text.trim().isEmpty) return null;
|
||
final base = await _baseUrl();
|
||
final apiKey = await _settings.getApiKey();
|
||
|
||
final body = _withApiKey({'q': text}, apiKey);
|
||
|
||
final res = await http
|
||
.post(
|
||
Uri.parse('$base/detect'),
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: jsonEncode(body),
|
||
)
|
||
.timeout(const Duration(seconds: 10));
|
||
|
||
if (res.statusCode != 200) {
|
||
throw TranslateException(_errorFromBody(res.body, res.statusCode));
|
||
}
|
||
final List<dynamic> data = jsonDecode(utf8.decode(res.bodyBytes));
|
||
if (data.isEmpty) return null;
|
||
final best = data.first as Map<String, dynamic>;
|
||
return DetectResult(
|
||
language: best['language'] as String,
|
||
confidence: ((best['confidence'] ?? 0) as num).round(),
|
||
);
|
||
}
|
||
}
|