Files
2026-07-14 17:37:17 +03:00

126 lines
3.8 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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(),
);
}
}