feat: offer no-key MyMemory translation provider
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import Foundation
|
||||
|
||||
public enum MyMemoryAPI {
|
||||
public static func translate(_ text: String) async throws -> String {
|
||||
try await translate(text) { try await URLSession.shared.data(for: $0) }
|
||||
}
|
||||
|
||||
static func translate(
|
||||
_ text: String, load: @Sendable (URLRequest) async throws -> (Data, URLResponse)
|
||||
) async throws -> String {
|
||||
guard !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
||||
throw Failure(message: "Enter English 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: "en|ja")]
|
||||
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,
|
||||
let translation = result.responseData?.translatedText,
|
||||
!translation.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
||||
throw Failure(message: "MyMemory returned no successful translation. Try again later or choose OpenAI.")
|
||||
}
|
||||
return translation
|
||||
}
|
||||
|
||||
private struct Response: Decodable {
|
||||
let responseStatus: Int
|
||||
let quotaFinished: Bool?
|
||||
let responseData: Translation?
|
||||
struct Translation: Decodable { let translatedText: String? }
|
||||
}
|
||||
private struct Failure: LocalizedError {
|
||||
let message: String
|
||||
var errorDescription: String? { message }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user