浏览代码

Localize paywall, PDF export, and purchase error alerts.

Route StoreKit and file failures through UserFacingErrorMessage so alert bodies use the app language instead of system descriptions, and drop developer-only subscription recovery text from customer-facing UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 月之前
父节点
当前提交
803cfa2142

+ 7 - 15
App for Indeed/Controllers/PremiumPlansWindowController.swift

@@ -1243,7 +1243,7 @@ private final class PremiumPlansViewController: NSViewController {
             }
         } catch {
             await MainActor.run {
-                self.presentPurchaseError(error)
+                self.presentPurchaseError(error, context: .purchase)
             }
         }
     }
@@ -1278,26 +1278,18 @@ private final class PremiumPlansViewController: NSViewController {
             }
         } catch {
             await MainActor.run {
-                self.presentPurchaseError(error)
+                self.presentPurchaseError(error, context: .restore)
             }
         }
     }
 
-    private func presentPurchaseError(_ error: Error) {
+    private func presentPurchaseError(
+        _ error: Error,
+        context: UserFacingErrorMessage.PurchaseContext
+    ) {
         let alert = NSAlert()
         alert.messageText = L("Something went wrong")
-        if let localized = error as? LocalizedError {
-            var parts: [String] = []
-            if let description = localized.errorDescription {
-                parts.append(description)
-            }
-            if let recovery = localized.recoverySuggestion {
-                parts.append(recovery)
-            }
-            alert.informativeText = parts.isEmpty ? error.localizedDescription : parts.joined(separator: "\n\n")
-        } else {
-            alert.informativeText = error.localizedDescription
-        }
+        alert.informativeText = UserFacingErrorMessage.purchaseFailure(error, context: context)
         alert.alertStyle = .warning
         alert.addButton(withTitle: L("OK"))
         if let window = view.window {

+ 144 - 11
App for Indeed/Services/UserFacingErrorMessage.swift

@@ -1,23 +1,156 @@
 import Foundation
+import StoreKit
 
-/// Maps backend and network errors to short, safe copy for the UI.
+/// Maps backend, StoreKit, and file errors to short, safe copy for the UI (always via `L()`).
 enum UserFacingErrorMessage {
+    enum PurchaseContext {
+        case purchase
+        case restore
+    }
+
     static func jobSearchFailure(_ error: Error) -> String {
+        let ns = error as NSError
+        if ns.domain == "OpenAIJobSearchService", ns.code == 1 {
+            return L("Job search is unavailable.")
+        }
+        if let urlError = error as? URLError, urlError.code == .cancelled {
+            return L("The search was cancelled. Try again when you're ready.")
+        }
+        if let message = connectivityFailure(for: error) {
+            return message
+        }
+        return L("Something went wrong while searching. Please try again in a moment.")
+    }
+
+    static func purchaseFailure(_ error: Error, context: PurchaseContext = .purchase) -> String {
+        if let subscription = error as? SubscriptionStoreError {
+            return subscription.userFacingMessage
+        }
+        if let storeKit = error as? StoreKitError {
+            return message(for: storeKit, context: context)
+        }
+        if let purchase = error as? Product.PurchaseError {
+            return message(for: purchase)
+        }
+        if let message = connectivityFailure(for: error) {
+            return message
+        }
+        switch context {
+        case .purchase:
+            return L("We couldn't complete your purchase. Please try again.")
+        case .restore:
+            return L("We couldn't restore your purchases. Please try again.")
+        }
+    }
+
+    static func pdfSaveFailure(_ error: Error) -> String {
+        if let cocoa = error as? CocoaError {
+            switch cocoa.code {
+            case .fileWriteNoPermission:
+                return L("We don't have permission to save to that folder. Choose another location or grant access.")
+            case .fileWriteVolumeReadOnly:
+                return L("This disk is read-only. Choose another folder to save your PDF.")
+            case .fileWriteOutOfSpace:
+                return L("The disk is full. Free up space, then try again.")
+            default:
+                break
+            }
+        }
         if let urlError = error as? URLError {
             switch urlError.code {
-            case .notConnectedToInternet,
-                 .networkConnectionLost,
-                 .timedOut,
-                 .cannotFindHost,
-                 .cannotConnectToHost,
-                 .dnsLookupFailed:
-                return L("We couldn't reach the server. Check your internet connection and try again.")
-            case .cancelled:
-                return L("The search was cancelled. Try again when you're ready.")
+            case .fileDoesNotExist, .noPermissionsToReadFile:
+                return L("We don't have permission to save to that folder. Choose another location or grant access.")
             default:
                 break
             }
         }
-        return L("Something went wrong while searching. Please try again in a moment.")
+        let ns = error as NSError
+        if ns.domain == NSCocoaErrorDomain,
+           ns.code == NSFileWriteOutOfSpaceError || ns.code == NSFileWriteVolumeReadOnlyError {
+            if ns.code == NSFileWriteOutOfSpaceError {
+                return L("The disk is full. Free up space, then try again.")
+            }
+            return L("This disk is read-only. Choose another folder to save your PDF.")
+        }
+        if let message = connectivityFailure(for: error) {
+            return message
+        }
+        return L("We couldn't save the PDF. Try again or choose a different folder.")
+    }
+
+    private static func connectivityFailure(for error: Error) -> String? {
+        if let storeKit = error as? StoreKitError,
+           case .networkError(let urlError) = storeKit {
+            return connectivityFailure(for: urlError)
+        }
+        if let urlError = error as? URLError {
+            return connectivityFailure(for: urlError)
+        }
+        return nil
+    }
+
+    private static func connectivityFailure(for urlError: URLError) -> String? {
+        switch urlError.code {
+        case .notConnectedToInternet,
+             .networkConnectionLost,
+             .timedOut,
+             .cannotFindHost,
+             .cannotConnectToHost,
+             .dnsLookupFailed:
+            return L("We couldn't reach the server. Check your internet connection and try again.")
+        case .cancelled:
+            return L("The request was cancelled. Try again when you're ready.")
+        default:
+            return nil
+        }
+    }
+
+    private static func message(for error: StoreKitError, context: PurchaseContext) -> String {
+        switch error {
+        case .userCancelled:
+            return L("Purchase was cancelled.")
+        case .networkError(let urlError):
+            return connectivityFailure(for: urlError)
+                ?? (context == .restore
+                    ? L("We couldn't restore your purchases. Please try again.")
+                    : L("We couldn't complete your purchase. Please try again."))
+        case .notAvailableInStorefront:
+            return L("This subscription isn't available in your App Store region.")
+        case .notEntitled:
+            return L("The purchase couldn't be verified. Please try again.")
+        case .unsupported, .unknown, .systemError:
+            return context == .restore
+                ? L("We couldn't restore your purchases. Please try again.")
+                : L("We couldn't complete your purchase. Please try again.")
+        @unknown default:
+            return context == .restore
+                ? L("We couldn't restore your purchases. Please try again.")
+                : L("We couldn't complete your purchase. Please try again.")
+        }
+    }
+
+    private static func message(for error: Product.PurchaseError) -> String {
+        switch error {
+        case .productUnavailable:
+            return L("That subscription isn’t available from the App Store right now.")
+        case .purchaseNotAllowed:
+            return L("Purchases aren't allowed on this account or device.")
+        case .ineligibleForOffer:
+            return L("This introductory offer isn't available for your account.")
+        case .invalidQuantity, .invalidOfferIdentifier, .invalidOfferPrice,
+             .invalidOfferSignature, .missingOfferParameters:
+            return L("We couldn't complete your purchase. Please try again.")
+        @unknown default:
+            return L("We couldn't complete your purchase. Please try again.")
+        }
+    }
+}
+
+private extension SubscriptionStoreError {
+    var userFacingMessage: String {
+        switch self {
+        case .productUnavailable:
+            return L("That subscription isn’t available from the App Store right now.")
+        }
     }
 }

+ 2 - 10
App for Indeed/Subscription/SubscriptionStore.swift

@@ -161,14 +161,6 @@ enum SubscriptionStoreError: LocalizedError {
         }
     }
 
-    var recoverySuggestion: String? {
-        switch self {
-        case .productUnavailable:
-            return """
-            For local testing in Xcode: Product → Scheme → Edit Scheme → Run → Options → StoreKit Configuration → choose Paywall.storekit.
-
-            For TestFlight / App Store: In App Store Connect, create auto-renewable subscriptions whose Product IDs exactly match SubscriptionProductIDs.swift (same spelling as com.hwaccount.appforindeed.pro.*), then submit them with the app version.
-            """
-        }
-    }
+    /// Developer setup notes belong in docs or console logs—not in customer-facing alerts.
+    var recoverySuggestion: String? { nil }
 }

+ 1 - 1
App for Indeed/Views/CVFilledPreviewPageView.swift

@@ -316,7 +316,7 @@ final class CVFilledPreviewPageView: NSView {
         do {
             try data.write(to: url, options: .atomic)
         } catch {
-            presentExportError(error.localizedDescription)
+            presentExportError(UserFacingErrorMessage.pdfSaveFailure(error))
         }
     }
 

+ 12 - 0
App for Indeed/ar.lproj/Localizable.strings

@@ -347,8 +347,20 @@
 // MARK: - الأخطاء
 "We couldn't reach the server. Check your internet connection and try again." = "لم نتمكن من الوصول إلى الخادم. تحقق من اتصالك بالإنترنت وحاول مرة أخرى.";
 "The search was cancelled. Try again when you're ready." = "تم إلغاء البحث. حاول مرة أخرى عندما تكون مستعداً.";
+"The request was cancelled. Try again when you're ready." = "تم إلغاء الطلب. حاول مرة أخرى عندما تكون مستعداً.";
 "Something went wrong while searching. Please try again in a moment." = "حدث خطأ ما أثناء البحث. يرجى المحاولة مرة أخرى بعد قليل.";
 "Job search is unavailable." = "البحث عن الوظائف غير متوفر.";
+"We couldn't complete your purchase. Please try again." = "تعذر إكمال عملية الشراء. يرجى المحاولة مرة أخرى.";
+"We couldn't restore your purchases. Please try again." = "تعذر استعادة مشترياتك. يرجى المحاولة مرة أخرى.";
+"Purchase was cancelled." = "تم إلغاء الشراء.";
+"This subscription isn't available in your App Store region." = "هذا الاشتراك غير متوفر في منطقة متجر التطبيقات الخاصة بك.";
+"Purchases aren't allowed on this account or device." = "الشراء غير مسموح به على هذا الحساب أو الجهاز.";
+"This introductory offer isn't available for your account." = "عرض الترحيب هذا غير متاح لحسابك.";
+"The purchase couldn't be verified. Please try again." = "تعذر التحقق من الشراء. يرجى المحاولة مرة أخرى.";
+"We couldn't save the PDF. Try again or choose a different folder." = "تعذر حفظ ملف بي دي إف. حاول مرة أخرى أو اختر مجلداً مختلفاً.";
+"We don't have permission to save to that folder. Choose another location or grant access." = "ليس لدينا إذن للحفظ في ذلك المجلد. اختر موقعاً آخر أو امنح الوصول.";
+"This disk is read-only. Choose another folder to save your PDF." = "هذا القرص للقراءة فقط. اختر مجلداً آخر لحفظ ملف بي دي إف.";
+"The disk is full. Free up space, then try again." = "القرص ممتلئ. حرر مساحة ثم حاول مرة أخرى.";
 
 // MARK: - التنبيهات
 "This profile will be removed from this Mac." = "سيتم إزالة هذا الملف الشخصي من جهاز ماك هذا.";

+ 12 - 0
App for Indeed/de.lproj/Localizable.strings

@@ -347,8 +347,20 @@
 // MARK: - Fehler
 "We couldn't reach the server. Check your internet connection and try again." = "Wir konnten den Server nicht erreichen. Überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.";
 "The search was cancelled. Try again when you're ready." = "Die Suche wurde abgebrochen. Versuchen Sie es erneut, wenn Sie bereit sind.";
+"The request was cancelled. Try again when you're ready." = "Die Anfrage wurde abgebrochen. Versuchen Sie es erneut, wenn Sie bereit sind.";
 "Something went wrong while searching. Please try again in a moment." = "Bei der Suche ist etwas schiefgelaufen. Bitte versuchen Sie es in einem Moment erneut.";
 "Job search is unavailable." = "Jobsuche ist nicht verfügbar.";
+"We couldn't complete your purchase. Please try again." = "Ihr Kauf konnte nicht abgeschlossen werden. Bitte versuchen Sie es erneut.";
+"We couldn't restore your purchases. Please try again." = "Ihre Käufe konnten nicht wiederhergestellt werden. Bitte versuchen Sie es erneut.";
+"Purchase was cancelled." = "Kauf abgebrochen.";
+"This subscription isn't available in your App Store region." = "Dieses Abonnement ist in Ihrer App-Store-Region nicht verfügbar.";
+"Purchases aren't allowed on this account or device." = "Käufe sind für dieses Konto oder Gerät nicht erlaubt.";
+"This introductory offer isn't available for your account." = "Dieses Einführungsangebot ist für Ihr Konto nicht verfügbar.";
+"The purchase couldn't be verified. Please try again." = "Der Kauf konnte nicht verifiziert werden. Bitte versuchen Sie es erneut.";
+"We couldn't save the PDF. Try again or choose a different folder." = "PDF konnte nicht gespeichert werden. Versuchen Sie es erneut oder wählen Sie einen anderen Ordner.";
+"We don't have permission to save to that folder. Choose another location or grant access." = "Keine Berechtigung zum Speichern in diesem Ordner. Wählen Sie einen anderen Speicherort oder gewähren Sie Zugriff.";
+"This disk is read-only. Choose another folder to save your PDF." = "Dieses Laufwerk ist schreibgeschützt. Wählen Sie einen anderen Ordner für Ihr PDF.";
+"The disk is full. Free up space, then try again." = "Der Speicher ist voll. Schaffen Sie Speicherplatz und versuchen Sie es erneut.";
 
 // MARK: - Benachrichtigungen
 "This profile will be removed from this Mac." = "Dieses Profil wird von diesem Mac entfernt.";

+ 12 - 0
App for Indeed/en.lproj/Localizable.strings

@@ -325,8 +325,20 @@
 // MARK: - Errors
 "We couldn't reach the server. Check your internet connection and try again." = "We couldn't reach the server. Check your internet connection and try again.";
 "The search was cancelled. Try again when you're ready." = "The search was cancelled. Try again when you're ready.";
+"The request was cancelled. Try again when you're ready." = "The request was cancelled. Try again when you're ready.";
 "Something went wrong while searching. Please try again in a moment." = "Something went wrong while searching. Please try again in a moment.";
 "Job search is unavailable." = "Job search is unavailable.";
+"We couldn't complete your purchase. Please try again." = "We couldn't complete your purchase. Please try again.";
+"We couldn't restore your purchases. Please try again." = "We couldn't restore your purchases. Please try again.";
+"Purchase was cancelled." = "Purchase was cancelled.";
+"This subscription isn't available in your App Store region." = "This subscription isn't available in your App Store region.";
+"Purchases aren't allowed on this account or device." = "Purchases aren't allowed on this account or device.";
+"This introductory offer isn't available for your account." = "This introductory offer isn't available for your account.";
+"The purchase couldn't be verified. Please try again." = "The purchase couldn't be verified. Please try again.";
+"We couldn't save the PDF. Try again or choose a different folder." = "We couldn't save the PDF. Try again or choose a different folder.";
+"We don't have permission to save to that folder. Choose another location or grant access." = "We don't have permission to save to that folder. Choose another location or grant access.";
+"This disk is read-only. Choose another folder to save your PDF." = "This disk is read-only. Choose another folder to save your PDF.";
+"The disk is full. Free up space, then try again." = "The disk is full. Free up space, then try again.";
 
 // MARK: - Alerts
 "This profile will be removed from this Mac." = "This profile will be removed from this Mac.";

+ 12 - 0
App for Indeed/fr-CA.lproj/Localizable.strings

@@ -347,8 +347,20 @@
 // MARK: - Erreurs
 "We couldn't reach the server. Check your internet connection and try again." = "Nous ne pouvons pas joindre le serveur. Vérifiez votre connexion Internet et réessayez.";
 "The search was cancelled. Try again when you're ready." = "La recherche a été annulée. Réessayez quand vous êtes prêt.";
+"The request was cancelled. Try again when you're ready." = "La requête a été annulée. Réessayez quand vous êtes prêt.";
 "Something went wrong while searching. Please try again in a moment." = "Une erreur s'est produite lors de la recherche. Veuillez réessayer dans un instant.";
 "Job search is unavailable." = "La recherche d'emploi n'est pas disponible.";
+"We couldn't complete your purchase. Please try again." = "Nous n'avons pas pu finaliser votre achat. Veuillez réessayer.";
+"We couldn't restore your purchases. Please try again." = "Nous n'avons pas pu restaurer vos achats. Veuillez réessayer.";
+"Purchase was cancelled." = "Achat annulé.";
+"This subscription isn't available in your App Store region." = "Cet abonnement n'est pas disponible dans votre région de l'App Store.";
+"Purchases aren't allowed on this account or device." = "Les achats ne sont pas autorisés sur ce compte ou cet appareil.";
+"This introductory offer isn't available for your account." = "Cette offre d'introduction n'est pas disponible pour votre compte.";
+"The purchase couldn't be verified. Please try again." = "L'achat n'a pas pu être vérifié. Veuillez réessayer.";
+"We couldn't save the PDF. Try again or choose a different folder." = "Impossible d'enregistrer le PDF. Réessayez ou choisissez un autre dossier.";
+"We don't have permission to save to that folder. Choose another location or grant access." = "Nous n'avons pas l'autorisation d'enregistrer dans ce dossier. Choisissez un autre emplacement ou accordez l'accès.";
+"This disk is read-only. Choose another folder to save your PDF." = "Ce disque est en lecture seule. Choisissez un autre dossier pour enregistrer votre PDF.";
+"The disk is full. Free up space, then try again." = "Le disque est plein. Libérez de l'espace, puis réessayez.";
 
 // MARK: - Alertes
 "This profile will be removed from this Mac." = "Ce profil sera supprimé de ce Mac.";

+ 12 - 0
App for Indeed/fr.lproj/Localizable.strings

@@ -347,8 +347,20 @@
 // MARK: - Erreurs
 "We couldn't reach the server. Check your internet connection and try again." = "Nous ne pouvons pas joindre le serveur. Vérifiez votre connexion Internet et réessayez.";
 "The search was cancelled. Try again when you're ready." = "La recherche a été annulée. Réessayez quand vous êtes prêt.";
+"The request was cancelled. Try again when you're ready." = "La requête a été annulée. Réessayez quand vous êtes prêt.";
 "Something went wrong while searching. Please try again in a moment." = "Une erreur s'est produite lors de la recherche. Veuillez réessayer dans un instant.";
 "Job search is unavailable." = "La recherche d'emploi n'est pas disponible.";
+"We couldn't complete your purchase. Please try again." = "Nous n'avons pas pu finaliser votre achat. Veuillez réessayer.";
+"We couldn't restore your purchases. Please try again." = "Nous n'avons pas pu restaurer vos achats. Veuillez réessayer.";
+"Purchase was cancelled." = "Achat annulé.";
+"This subscription isn't available in your App Store region." = "Cet abonnement n'est pas disponible dans votre région de l'App Store.";
+"Purchases aren't allowed on this account or device." = "Les achats ne sont pas autorisés sur ce compte ou cet appareil.";
+"This introductory offer isn't available for your account." = "Cette offre d'introduction n'est pas disponible pour votre compte.";
+"The purchase couldn't be verified. Please try again." = "L'achat n'a pas pu être vérifié. Veuillez réessayer.";
+"We couldn't save the PDF. Try again or choose a different folder." = "Impossible d'enregistrer le PDF. Réessayez ou choisissez un autre dossier.";
+"We don't have permission to save to that folder. Choose another location or grant access." = "Nous n'avons pas l'autorisation d'enregistrer dans ce dossier. Choisissez un autre emplacement ou accordez l'accès.";
+"This disk is read-only. Choose another folder to save your PDF." = "Ce disque est en lecture seule. Choisissez un autre dossier pour enregistrer votre PDF.";
+"The disk is full. Free up space, then try again." = "Le disque est plein. Libérez de l'espace, puis réessayez.";
 
 // MARK: - Alertes
 "This profile will be removed from this Mac." = "Ce profil sera supprimé de ce Mac.";

+ 12 - 0
App for Indeed/sv.lproj/Localizable.strings

@@ -347,8 +347,20 @@
 // MARK: - Fel
 "We couldn't reach the server. Check your internet connection and try again." = "Vi kunde inte nå servern. Kontrollera din internetanslutning och försök igen.";
 "The search was cancelled. Try again when you're ready." = "Sökningen avbröts. Försök igen när du är redo.";
+"The request was cancelled. Try again when you're ready." = "Begäran avbröts. Försök igen när du är redo.";
 "Something went wrong while searching. Please try again in a moment." = "Något gick fel under sökningen. Försök igen om en stund.";
 "Job search is unavailable." = "Jobbsökning är inte tillgänglig.";
+"We couldn't complete your purchase. Please try again." = "Vi kunde inte slutföra ditt köp. Försök igen.";
+"We couldn't restore your purchases. Please try again." = "Vi kunde inte återställa dina köp. Försök igen.";
+"Purchase was cancelled." = "Köpet avbröts.";
+"This subscription isn't available in your App Store region." = "Den här prenumerationen är inte tillgänglig i din App Store-region.";
+"Purchases aren't allowed on this account or device." = "Köp är inte tillåtna på det här kontot eller enheten.";
+"This introductory offer isn't available for your account." = "Det här introduktionserbjudandet är inte tillgängligt för ditt konto.";
+"The purchase couldn't be verified. Please try again." = "Köpet kunde inte verifieras. Försök igen.";
+"We couldn't save the PDF. Try again or choose a different folder." = "Vi kunde inte spara PDF-filen. Försök igen eller välj en annan mapp.";
+"We don't have permission to save to that folder. Choose another location or grant access." = "Vi har inte behörighet att spara i den mappen. Välj en annan plats eller bevilja åtkomst.";
+"This disk is read-only. Choose another folder to save your PDF." = "Disken är skrivskyddad. Välj en annan mapp för din PDF.";
+"The disk is full. Free up space, then try again." = "Disken är full. Frigör utrymme och försök igen.";
 
 // MARK: - Aviseringar
 "This profile will be removed from this Mac." = "Den här profilen kommer att tas bort från den här Mac-datorn.";

+ 12 - 0
App for Indeed/zh-Hans.lproj/Localizable.strings

@@ -348,8 +348,20 @@
 // MARK: - 错误
 "We couldn't reach the server. Check your internet connection and try again." = "无法连接服务器。请检查您的网络连接后重试。";
 "The search was cancelled. Try again when you're ready." = "搜索已取消。准备就绪后请重试。";
+"The request was cancelled. Try again when you're ready." = "请求已取消。准备就绪后请重试。";
 "Something went wrong while searching. Please try again in a moment." = "搜索时出错。请稍后再试。";
 "Job search is unavailable." = "工作搜索不可用。";
+"We couldn't complete your purchase. Please try again." = "无法完成购买。请重试。";
+"We couldn't restore your purchases. Please try again." = "无法恢复购买。请重试。";
+"Purchase was cancelled." = "购买已取消。";
+"This subscription isn't available in your App Store region." = "此订阅在您的 App Store 地区不可用。";
+"Purchases aren't allowed on this account or device." = "此账户或设备不允许购买。";
+"This introductory offer isn't available for your account." = "此介绍性优惠不适用于您的账户。";
+"The purchase couldn't be verified. Please try again." = "无法验证购买。请重试。";
+"We couldn't save the PDF. Try again or choose a different folder." = "无法保存 PDF。请重试或选择其他文件夹。";
+"We don't have permission to save to that folder. Choose another location or grant access." = "无权保存到该文件夹。请选择其他位置或授予访问权限。";
+"This disk is read-only. Choose another folder to save your PDF." = "此磁盘为只读。请选择其他文件夹保存 PDF。";
+"The disk is full. Free up space, then try again." = "磁盘已满。请释放空间后重试。";
 
 // MARK: - 提示
 "This profile will be removed from this Mac." = "此个人资料将从这台 Mac 上移除。";

+ 12 - 0
App for Indeed/zh-Hant.lproj/Localizable.strings

@@ -347,8 +347,20 @@
 // MARK: - 錯誤
 "We couldn't reach the server. Check your internet connection and try again." = "無法連線到伺服器。請檢查您的網路連線後再試一次。";
 "The search was cancelled. Try again when you're ready." = "搜尋已取消。準備就緒後請再試一次。";
+"The request was cancelled. Try again when you're ready." = "請求已取消。準備就緒後請再試一次。";
 "Something went wrong while searching. Please try again in a moment." = "搜尋時發生錯誤。請稍後再試。";
 "Job search is unavailable." = "工作搜尋無法使用。";
+"We couldn't complete your purchase. Please try again." = "無法完成購買。請再試一次。";
+"We couldn't restore your purchases. Please try again." = "無法回復購買。請再試一次。";
+"Purchase was cancelled." = "購買已取消。";
+"This subscription isn't available in your App Store region." = "此訂閱在您的 App Store 地區不可用。";
+"Purchases aren't allowed on this account or device." = "此帳號或裝置不允許購買。";
+"This introductory offer isn't available for your account." = "此介紹性優惠不適用於您的帳號。";
+"The purchase couldn't be verified. Please try again." = "無法驗證購買。請再試一次。";
+"We couldn't save the PDF. Try again or choose a different folder." = "無法儲存 PDF。請再試一次或選擇其他資料夾。";
+"We don't have permission to save to that folder. Choose another location or grant access." = "沒有權限儲存到該資料夾。請選擇其他位置或授予存取權限。";
+"This disk is read-only. Choose another folder to save your PDF." = "此磁碟為唯讀。請選擇其他資料夾儲存 PDF。";
+"The disk is full. Free up space, then try again." = "磁碟已滿。請釋出空間後再試一次。";
 
 // MARK: - 提示
 "This profile will be removed from this Mac." = "此個人資料將從這台 Mac 上移除。";