import Foundation import Models public struct DeepLClient: Sendable { public enum DeepLError: Error { case notFound } private var deeplUserAPIKey: String? private var deeplUserAPIFree: Bool private var endpoint: String { "https://api\(deeplUserAPIFree && (deeplUserAPIKey != nil) ? "-free" : "").deepl.com/v2/translate" } private var APIKey: String { if let deeplUserAPIKey { return deeplUserAPIKey } if let path = Bundle.main.path(forResource: "Secret", ofType: "plist") { let secret = NSDictionary(contentsOfFile: path) return secret?["DEEPL_SECRET"] as? String ?? "" } return "" } private var authorizationHeaderValue: String { "DeepL-Auth-Key \(APIKey)" } public struct Response: Decodable { public struct Translation: Decodable { public let detectedSourceLanguage: String public let text: String } public let translations: [Translation] } private var decoder: JSONDecoder { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase return decoder } public init(userAPIKey: String?, userAPIFree: Bool) { deeplUserAPIKey = userAPIKey deeplUserAPIFree = userAPIFree } public func request(target: String, text: String) async throws -> Translation { do { var components = URLComponents(string: endpoint)! var queryItems: [URLQueryItem] = [] queryItems.append(.init(name: "text", value: text)) queryItems.append(.init(name: "target_lang", value: target.uppercased())) components.queryItems = queryItems var request = URLRequest(url: components.url!) request.httpMethod = "POST" request.setValue(authorizationHeaderValue, forHTTPHeaderField: "Authorization") request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") let (result, _) = try await URLSession.shared.data(for: request) let response = try decoder.decode(Response.self, from: result) if let translation = response.translations.first { return .init(content: translation.text.removingPercentEncoding ?? "", detectedSourceLanguage: translation.detectedSourceLanguage, provider: "DeepL.com") } throw DeepLError.notFound } catch { throw error } } }