import Foundation public enum MyMemoryAPI { public static func translate( _ text: String, sourceLanguage: String = "en", targetLanguage: String = "ja" ) async throws -> String { try await translate(text, sourceLanguage: sourceLanguage, targetLanguage: targetLanguage) { try await URLSession.shared.data(for: $0) } } static func translate( _ text: String, sourceLanguage: String = "en", targetLanguage: String = "ja", load: @Sendable (URLRequest) async throws -> (Data, URLResponse) ) async throws -> String { guard !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { throw Failure(message: "Enter text to translate.") } guard text.utf8.count <= 500 else { throw Failure(message: "MyMemory allows at most 500 UTF-8 bytes per translation. Shorten the text or choose OpenAI.") } var url = URLComponents(string: "https://api.mymemory.translated.net/get")! url.queryItems = [URLQueryItem(name: "q", value: text), URLQueryItem(name: "langpair", value: "\(sourceLanguage)|\(targetLanguage)")] let request = URLRequest(url: url.url!, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30) let (data, response) = try await load(request) try Task.checkCancellation() guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else { throw Failure(message: "MyMemory request failed. Try again later or choose OpenAI.") } let result = try JSONDecoder().decode(Response.self, from: data) guard result.quotaFinished != true, result.responseStatus != 429 else { throw Failure(message: "MyMemory's anonymous daily quota is exhausted. Try later or choose OpenAI.") } guard result.responseStatus == 200 else { throw Failure(message: "MyMemory returned no successful translation. Try again later or choose OpenAI.") } if let translation = result.responseData?.translatedText, !translation.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { return translation } // The service can rank an empty memory first; only use another exact match for the requested pair. if let match = result.matches?.first(where: { $0.segment == text && $0.source.split(separator: "-").first == Substring(sourceLanguage) && $0.target.split(separator: "-").first == Substring(targetLanguage) && !$0.translation.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }) { return match.translation } throw Failure(message: "MyMemory returned no successful translation. Try again later or choose OpenAI.") } private struct Response: Decodable { let responseStatus: Int let quotaFinished: Bool? let responseData: Translation? let matches: [Match]? struct Match: Decodable { let segment: String let translation: String let source: String let target: String } struct Translation: Decodable { let translatedText: String? } } private struct Failure: LocalizedError { let message: String var errorDescription: String? { message } } }