Skip to content

Commit

Permalink
refactor: remove AppleTranslation swift package
Browse files Browse the repository at this point in the history
  • Loading branch information
tisfeng committed Oct 21, 2024
1 parent 6b8e93e commit 85a5af0
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 31 deletions.
37 changes: 20 additions & 17 deletions Easydict.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
033C31002A74CECE0095926A /* EZAppleDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 033C30FF2A74CECE0095926A /* EZAppleDictionary.m */; };
033E181F2C5A970A0099A7B0 /* Vapor in Frameworks */ = {isa = PBXBuildFile; productRef = 033E181E2C5A970A0099A7B0 /* Vapor */; };
033EAA9F2CBBBE2B004DC199 /* ForceGetSelectedTextType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 033EAA9E2CBBBE2B004DC199 /* ForceGetSelectedTextType.swift */; };
03404B2E2CC3643F0024CF69 /* AppleTranslation in Frameworks */ = {isa = PBXBuildFile; productRef = 03404B2D2CC3643F0024CF69 /* AppleTranslation */; };
0340D38E2C8EEEA2004C9910 /* AppleScriptTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0340D38D2C8EEEA2004C9910 /* AppleScriptTask.swift */; };
0340D3912C8EEEE3004C9910 /* Data+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0340D3902C8EEEE3004C9910 /* Data+Extension.swift */; };
0340D3942C8EEF58004C9910 /* Dictionary+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0340D3932C8EEF58004C9910 /* Dictionary+Extension.swift */; };
Expand Down Expand Up @@ -118,6 +117,9 @@
0396D611292C932F006A11D9 /* EZSelectLanguageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0396D610292C932F006A11D9 /* EZSelectLanguageCell.m */; };
0396D615292CC4C3006A11D9 /* EZLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 0396D614292CC4C3006A11D9 /* EZLocalStorage.m */; };
0396DE552BB5844A009FD2A5 /* BaseOpenAIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0396DE542BB5844A009FD2A5 /* BaseOpenAIService.swift */; };
039785282CC678F400A49087 /* TranslationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039785242CC678F400A49087 /* TranslationManager.swift */; };
039785292CC678F400A49087 /* TranslationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039785252CC678F400A49087 /* TranslationService.swift */; };
0397852A2CC678F400A49087 /* TranslationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039785262CC678F400A49087 /* TranslationView.swift */; };
03991158292927E000E1B06D /* EZTitlebar.m in Sources */ = {isa = PBXBuildFile; fileRef = 03991157292927E000E1B06D /* EZTitlebar.m */; };
03991166292A8A4400E1B06D /* EZTitleBarMoveView.m in Sources */ = {isa = PBXBuildFile; fileRef = 03991165292A8A4400E1B06D /* EZTitleBarMoveView.m */; };
0399116A292AA2EF00E1B06D /* EZLayoutManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 03991169292AA2EF00E1B06D /* EZLayoutManager.m */; };
Expand Down Expand Up @@ -524,6 +526,9 @@
0396D613292CC4C3006A11D9 /* EZLocalStorage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EZLocalStorage.h; sourceTree = "<group>"; };
0396D614292CC4C3006A11D9 /* EZLocalStorage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZLocalStorage.m; sourceTree = "<group>"; };
0396DE542BB5844A009FD2A5 /* BaseOpenAIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseOpenAIService.swift; sourceTree = "<group>"; };
039785242CC678F400A49087 /* TranslationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslationManager.swift; sourceTree = "<group>"; };
039785252CC678F400A49087 /* TranslationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslationService.swift; sourceTree = "<group>"; };
039785262CC678F400A49087 /* TranslationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslationView.swift; sourceTree = "<group>"; };
03991156292927E000E1B06D /* EZTitlebar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EZTitlebar.h; sourceTree = "<group>"; };
03991157292927E000E1B06D /* EZTitlebar.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZTitlebar.m; sourceTree = "<group>"; };
03991164292A8A4400E1B06D /* EZTitleBarMoveView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EZTitleBarMoveView.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -914,7 +919,6 @@
03B63ABF2A86967800E155ED /* CoreServices.framework in Frameworks */,
038030972B4106800009230C /* CocoaLumberjackSwift in Frameworks */,
038EA1AA2B41169C008A6DD1 /* ZipArchive in Frameworks */,
03404B2E2CC3643F0024CF69 /* AppleTranslation in Frameworks */,
0364EC8D2C208FB70036B61B /* AXSwift in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -1242,6 +1246,7 @@
036463682CB62B6600D0D6CC /* Apple */ = {
isa = PBXGroup;
children = (
039785272CC678F400A49087 /* TranslationService */,
036463692CB62B8900D0D6CC /* AppleService.swift */,
);
path = Apple;
Expand Down Expand Up @@ -1472,6 +1477,16 @@
path = Storage;
sourceTree = "<group>";
};
039785272CC678F400A49087 /* TranslationService */ = {
isa = PBXGroup;
children = (
039785252CC678F400A49087 /* TranslationService.swift */,
039785242CC678F400A49087 /* TranslationManager.swift */,
039785262CC678F400A49087 /* TranslationView.swift */,
);
path = TranslationService;
sourceTree = "<group>";
};
03991155292927A100E1B06D /* Titlebar */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2665,7 +2680,6 @@
C4BE8E1B2C72E61B00F53204 /* LaunchAtLogin */,
036806572CA829F100800B76 /* SFSafeSymbols */,
0346F3D52CAECEFC006A6CDF /* AXSwiftExt */,
03404B2D2CC3643F0024CF69 /* AppleTranslation */,
);
productName = Bob;
productReference = C99EEB182385796700FEE666 /* Easydict-debug.app */;
Expand Down Expand Up @@ -2733,7 +2747,6 @@
C4BE8E1A2C72E5C500F53204 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */,
036806562CA829F100800B76 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
0346F3D42CAECEFC006A6CDF /* XCRemoteSwiftPackageReference "AXSwiftExt" */,
03404B2C2CC3643F0024CF69 /* XCRemoteSwiftPackageReference "AppleTranslation" */,
);
productRefGroup = C99EEB192385796700FEE666 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -2991,6 +3004,9 @@
03FA677E2C2EFB10000FEA64 /* LLMStreamService+Configuration.swift in Sources */,
03882F8E29D95044005B5A52 /* ToastWindowController.m in Sources */,
03B0231929231FA6001C7E63 /* SnipViewController.m in Sources */,
039785282CC678F400A49087 /* TranslationManager.swift in Sources */,
039785292CC678F400A49087 /* TranslationService.swift in Sources */,
0397852A2CC678F400A49087 /* TranslationView.swift in Sources */,
039CC910292F86F40037B91E /* NSImage+EZResize.m in Sources */,
03B0232529231FA6001C7E63 /* NSButton+MM.m in Sources */,
03D0434E292886D200E7559E /* EZMiniQueryWindow.m in Sources */,
Expand Down Expand Up @@ -3616,14 +3632,6 @@
minimumVersion = 4.102.1;
};
};
03404B2C2CC3643F0024CF69 /* XCRemoteSwiftPackageReference "AppleTranslation" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/tisfeng/AppleTranslation";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 1.0.0;
};
};
0346F3D42CAECEFC006A6CDF /* XCRemoteSwiftPackageReference "AXSwiftExt" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/s1ntoneli/AXSwiftExt";
Expand Down Expand Up @@ -3791,11 +3799,6 @@
package = 033E181D2C5A970A0099A7B0 /* XCRemoteSwiftPackageReference "vapor" */;
productName = Vapor;
};
03404B2D2CC3643F0024CF69 /* AppleTranslation */ = {
isa = XCSwiftPackageProductDependency;
package = 03404B2C2CC3643F0024CF69 /* XCRemoteSwiftPackageReference "AppleTranslation" */;
productName = AppleTranslation;
};
0346F3D52CAECEFC006A6CDF /* AXSwiftExt */ = {
isa = XCSwiftPackageProductDependency;
package = 0346F3D42CAECEFC006A6CDF /* XCRemoteSwiftPackageReference "AXSwiftExt" */;
Expand Down
11 changes: 1 addition & 10 deletions Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "1b5bf214999fc88f3a79515b2a6f574f8e5d9dc30f91419ecc416ae5a8f4acc9",
"originHash" : "d88b5b8250adbaea6c584fb14d5173d28fde088b92e123e49eb7f087b8d44d3d",
"pins" : [
{
"identity" : "abseil-cpp-binary",
Expand Down Expand Up @@ -37,15 +37,6 @@
"version" : "5.0.4"
}
},
{
"identity" : "appletranslation",
"kind" : "remoteSourceControl",
"location" : "https://github.com/tisfeng/AppleTranslation",
"state" : {
"revision" : "7618a4e8ba391e52df9de4124bfbbebf934556ab",
"version" : "1.0.2"
}
},
{
"identity" : "async-http-client",
"kind" : "remoteSourceControl",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// TranslationManager.swift
// AppleTranslation
//
// Created by tisfeng on 2024/10/10.
// Copyright © 2024 izual. All rights reserved.
//

import Foundation
import Translation

// MARK: - TranslationManager

@available(macOS 15.0, *)
class TranslationManager: ObservableObject {
// MARK: Internal

@Published var sourceText: String = ""
@Published var targetText: String = ""
@Published var configuration: TranslationSession.Configuration?

@MainActor
func translate(
text: String,
sourceLanguage: Locale.Language?,
targetLanguage: Locale.Language?
) async throws
-> TranslationSession.Response {
sourceText = text

return try await withCheckedThrowingContinuation { continuation in
translationContinuation = continuation

if configuration == nil {
configuration = .init(source: sourceLanguage, target: targetLanguage)
return
}

configuration?.source = sourceLanguage
configuration?.target = targetLanguage
configuration?.invalidate()
}
}

func performTranslation(_ session: TranslationSession) {
Task {
do {
let response = try await session.translate(sourceText)
await MainActor.run {
self.targetText = response.targetText
}
translationContinuation?.resume(returning: response)
} catch {
translationContinuation?.resume(throwing: error)
}
translationContinuation = nil
}
}

// MARK: Private

private var translationContinuation: CheckedContinuation<TranslationSession.Response, Error>?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
//
// TranslationService.swift
// AppleTranslation
//
// Created by tisfeng on 2024/10/10.
// Copyright © 2024 izual. All rights reserved.
//

import Foundation
import SwiftUI
import Translation

// MARK: - TranslationService

@objcMembers
@available(macOS 15.0, *)
public class TranslationService: NSObject {
// MARK: Lifecycle

@MainActor
public init(
attachedWindow: NSWindow? = nil,
configuration: TranslationSession.Configuration? = nil
) {
self.attachedWindow = attachedWindow
self.configuration = configuration
super.init()
setupTranslationView()
}

/// Initializer for objc, since objc cannot use Swift TranslationSession.Configuration.
///
/// - Note: If attachedWindow is nil, the translation view will be attached to the translationWindow we created.
@MainActor
public convenience init(attachedWindow: NSWindow? = nil) {
self.init(attachedWindow: attachedWindow, configuration: nil)
}

// MARK: Public

public var enableTranslateSameLanguage = false
public var configuration: TranslationSession.Configuration?

/// The window that the translation view is attached to.
///
/// - Note: If attachedWindow is nil, the translation view will be attached to the translationWindow we created.
public var attachedWindow: NSWindow?

public var translationView: NSView {
translationController.view
}

/// Translate text with specified source and target languages.
///
/// If no languages are provided, it uses the current configuration.
public func translate(
text: String,
sourceLanguage: Locale.Language? = nil,
targetLanguage: Locale.Language? = nil
) async throws
-> TranslationSession.Response {
let source = sourceLanguage ?? configuration?.source
let target = targetLanguage ?? configuration?.target

do {
// Check if the translation is ready for use.
let isReady = try await translationIsReadyforUse(text: text, from: source, to: target)
await MainActor.run {
translationWindow?.alphaValue = isReady ? 0 : 1
}
} catch {
await MainActor.run {
translationWindow?.level = .floating
}
}

do {
return try await manager.translate(
text: text,
sourceLanguage: source,
targetLanguage: target
)
} catch {
guard let translationError = error as? TranslationError else { throw error }

switch translationError {
case .unsupportedLanguagePairing:
if source == target, enableTranslateSameLanguage {
return TranslationSession.Response(
sourceLanguage: source ?? .init(identifier: ""),
targetLanguage: target ?? .init(identifier: ""),
sourceText: text,
targetText: text
)
}

fallthrough

default:
throw error
}
}
}

/// Translate text with language codes, providing a more flexible api.
///
/// - Parameters
/// - sourceLanguageCode: ISO 639 code, such as zh, en, etc.
///
/// - Note: Currently Apple Translate does not support language script, so zh-Hans and zh-Hant is the same as zh.
public func translate(
text: String,
sourceLanguageCode: String,
targetLanguageCode: String
) async throws
-> String {
let response = try await translate(
text: text,
sourceLanguage: .init(identifier: sourceLanguageCode),
targetLanguage: .init(identifier: targetLanguageCode)
)
return response.targetText
}

// MARK: Private

private let manager = TranslationManager()
private var translationWindow: NSWindow?

private lazy var translationController = NSHostingController(
rootView: TranslationView(manager: manager)
)

/// Check if the translation is ready for use.
private func translationIsReadyforUse(
text: String, from source: Locale.Language?, to target: Locale.Language?
) async throws
-> Bool {
let status = try await LanguageAvailability().status(for: text, from: source, to: target)
return status == .installed
}

@MainActor
private func setupTranslationView() {
// TranslationView must be added to a window, otherwise it will not work.
if let attachedWindow {
let translationView = translationController.view
attachedWindow.contentView?.addSubview(translationView)
translationView.isHidden = true
} else {
translationWindow = NSWindow(contentViewController: translationController)
translationWindow?.title = "Translation"
translationWindow?.setContentSize(CGSize(width: 200, height: 200))
translationWindow?.makeKeyAndOrderFront(nil)
}
}
}

@available(macOS 15.0, *)
extension LanguageAvailability {
public func status(for text: String, from source: Locale.Language?, to target: Locale.Language?)
async throws
-> LanguageAvailability.Status {
if let source {
return await status(from: source, to: target)
}
return try await status(for: text, to: target)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// TranslationExample.swift
// AppleTranslation
//
// Created by tisfeng on 2024/10/10.
// Copyright © 2024 izual. All rights reserved.
//

import SwiftUI
import Translation

// MARK: - TranslationView

@available(macOS 15.0, *)
struct TranslationView: View {
@ObservedObject var manager: TranslationManager

var body: some View {
VStack {
TextField("", text: $manager.sourceText)
Text(manager.targetText)
}
.padding()
.translationTask(manager.configuration) { session in
manager.performTranslation(session)
}
}
}
Loading

0 comments on commit 85a5af0

Please sign in to comment.