mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-25 17:17:43 +00:00
feat(mac): localization support
added localizable types where necessary created Localizable.xcstrings for both the config app and the Keyman settings package
This commit is contained in:
parent
7fae710528
commit
ab30341f59
12 changed files with 571 additions and 53 deletions
|
|
@ -9,6 +9,7 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
D83272A82F57614400F71698 /* KeymanSettings in Frameworks */ = {isa = PBXBuildFile; productRef = D83272A72F57614400F71698 /* KeymanSettings */; };
|
||||
D88B79CA30472B3B00F10D14 /* Sentry-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = D88B79C930472B3B00F10D14 /* Sentry-Dynamic */; };
|
||||
D8F10B623048F12A00C1E597 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = D8F10B613048F12A00C1E597 /* Localizable.xcstrings */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
|
@ -32,6 +33,7 @@
|
|||
D88F03C62F50ED5000C02A31 /* Keyman Configuration.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Keyman Configuration.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D88F03D32F50ED5100C02A31 /* ConfigTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ConfigTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D88F03DD2F50ED5100C02A31 /* ConfigUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ConfigUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D8F10B613048F12A00C1E597 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
|
@ -95,6 +97,7 @@
|
|||
D88F03BD2F50ED5000C02A31 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D8F10B613048F12A00C1E597 /* Localizable.xcstrings */,
|
||||
D87D6F492FAF95400083A95E /* Installation */,
|
||||
D88F03C82F50ED5000C02A31 /* Config */,
|
||||
D88F03E02F50ED5100C02A31 /* ConfigUITests */,
|
||||
|
|
@ -243,6 +246,7 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D8F10B623048F12A00C1E597 /* Localizable.xcstrings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -361,6 +365,7 @@
|
|||
REGISTER_APP_GROUPS = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
|
|
@ -419,6 +424,7 @@
|
|||
REGISTER_APP_GROUPS = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,11 +32,12 @@ struct AddKeyboardView: View {
|
|||
.font(.headline)
|
||||
|
||||
// Native progress bar bound to the coordinator's value (0.0 to 1.0)
|
||||
ProgressView(value: downloadCoordinator.downloadProgress, total: 1.0)
|
||||
ProgressView(value: downloadCoordinator.downloadProgressFraction, total: 1.0)
|
||||
.progressViewStyle(.linear)
|
||||
.frame(width: 250)
|
||||
|
||||
Text("\(Int(downloadCoordinator.downloadProgress * 100))%")
|
||||
// Text("\(Int(downloadCoordinator.downloadProgressFraction * 100))%")
|
||||
Text(downloadCoordinator.downloadProgressFraction, format: .percent.precision(.fractionLength(0)))
|
||||
.font(.body)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import OSLog
|
|||
public class DownloadCoordinator: NSObject, ObservableObject, WKNavigationDelegate, WKDownloadDelegate {
|
||||
@Published var isDownloading = false
|
||||
// progress is between 0.0 and 1.0
|
||||
@Published var downloadProgress: Double = 0.0
|
||||
@Published var downloadProgressFraction: Double = 0.0
|
||||
|
||||
@Published var showConfirmPackageSheet = false
|
||||
@Published var installHelper: PackageInstallHelper?
|
||||
|
|
@ -100,13 +100,13 @@ public class DownloadCoordinator: NSObject, ObservableObject, WKNavigationDelega
|
|||
|
||||
// reset progress states
|
||||
self.isDownloading = true
|
||||
self.downloadProgress = 0.0
|
||||
self.downloadProgressFraction = 0.0
|
||||
|
||||
progressObserver = download.progress.observe(\.fractionCompleted, options: [.new]) { [weak self] _, change in
|
||||
guard let newValue = change.newValue else { return }
|
||||
|
||||
Task { @MainActor [weak self] in
|
||||
self?.downloadProgress = newValue
|
||||
self?.downloadProgressFraction = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public struct IconButtonView: View {
|
|||
let action: () -> Void
|
||||
let systemImage: String
|
||||
let font: Font
|
||||
let helpText: String
|
||||
let helpText: LocalizedStringKey
|
||||
|
||||
public var body: some View {
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ public struct IconButtonView: View {
|
|||
// the view for buttons with a label
|
||||
public struct LabelButtonView: View {
|
||||
let action: () -> Void
|
||||
let label: String
|
||||
let label: LocalizedStringKey
|
||||
let systemImage: String
|
||||
let font: Font
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
*
|
||||
* Created by Shawn Schantz on 2026-02-26
|
||||
*
|
||||
* Main view used for configuring Keyman
|
||||
* Test view used for configuring Keyman
|
||||
* Not included in ConfigApp.swift, but can be added temporarily for testing purposes
|
||||
* All text and labels are marked verbatim, so that they do not get extracted for localization
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
|
|
@ -18,7 +20,7 @@ struct InstallDebugView: View {
|
|||
Image(systemName: "gear")
|
||||
.imageScale(.large)
|
||||
.foregroundColor(.accentColor)
|
||||
Text("Current task = \(taskText)")
|
||||
Text(verbatim: "Current task = \(taskText)")
|
||||
.onAppear() {
|
||||
if let installTask = installation.currentTask() {
|
||||
taskText = installTask.taskType.rawValue
|
||||
|
|
@ -26,52 +28,78 @@ struct InstallDebugView: View {
|
|||
}
|
||||
}
|
||||
HStack {
|
||||
Button("Next...") {
|
||||
Button {
|
||||
installation.executeCurrentInstallationTask()
|
||||
if let installTask = installation.currentTask() {
|
||||
taskText = installTask.taskType.rawValue
|
||||
}
|
||||
} label: {
|
||||
Text(verbatim: "Next...")
|
||||
}
|
||||
.disabled(installation.isInstallationComplete())
|
||||
Button("Migrate Data") {
|
||||
Button {
|
||||
_ = installation.migrateData()
|
||||
} label: {
|
||||
Text(verbatim: "Migrate Data")
|
||||
}
|
||||
Button("Register Keyman") {
|
||||
Button {
|
||||
_ = installation.registerKeymanInputMethod()
|
||||
} label: {
|
||||
Text(verbatim: "Register Keyman")
|
||||
}
|
||||
Button("Enable Keyman") {
|
||||
Button {
|
||||
_ = installation.enableKeymanInputMethod()
|
||||
} label: {
|
||||
Text(verbatim: "Enable Keyman")
|
||||
}
|
||||
Button("Select Keyman") {
|
||||
Button {
|
||||
_ = installation.selectKeymanInputMethod()
|
||||
} label: {
|
||||
Text(verbatim: "Select Keyman")
|
||||
}
|
||||
Button("Check Permission") {
|
||||
Button {
|
||||
installation.checkAccessibilityPermissionGranted()
|
||||
} label: {
|
||||
Text(verbatim: "Check Permission")
|
||||
}
|
||||
Button("Request Permission") {
|
||||
Button {
|
||||
_ = installation.requestAccessibility()
|
||||
} label: {
|
||||
Text(verbatim: "Request Permission")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
HStack {
|
||||
Button("Request Restart") {
|
||||
Button {
|
||||
_ = installation.notifyUserPromptedToRestart()
|
||||
} label: {
|
||||
Text(verbatim: "Request Restart")
|
||||
}
|
||||
Button("Check Restart") {
|
||||
Button {
|
||||
_ = installation.validateUserHasRestarted()
|
||||
} label: {
|
||||
Text(verbatim: "Check Restart")
|
||||
}
|
||||
Button("Set Displayed Complete") {
|
||||
Button {
|
||||
installation.setHasDisplayedInstallationComplete()
|
||||
} label: {
|
||||
Text(verbatim: "Set Displayed Complete")
|
||||
}
|
||||
Button("debug") {
|
||||
Button {
|
||||
installation.debug()
|
||||
} label: {
|
||||
Text(verbatim: "debug")
|
||||
}
|
||||
Button("Disable Keyman") {
|
||||
Button {
|
||||
_ = installation.disableKeymanInputMethod()
|
||||
} label: {
|
||||
Text(verbatim: "Disable Keyman")
|
||||
}
|
||||
Button("Kill Keyman") {
|
||||
Button {
|
||||
_ = installation.killKeymanInputMethod()
|
||||
} label: {
|
||||
Text(verbatim: "Kill Keyman")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ struct MainConfigView: View {
|
|||
.dropDestination(for: URL.self) { urls, _ in
|
||||
// reject drop if it is more than one file
|
||||
guard let droppedFileUrl = urls.first, urls.count < 2 else {
|
||||
let error = DropKmpError.tooManyFiles
|
||||
let error = DropKmpError.exceededFileDropLimit
|
||||
self.alertMessage = error.localizedDescription
|
||||
self.isShowingDropKmpAlert = true
|
||||
return false // the drop failed
|
||||
|
|
|
|||
141
mac/Config/Localizable.xcstrings
Normal file
141
mac/Config/Localizable.xcstrings
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"Access has not been granted." : {
|
||||
|
||||
},
|
||||
"Add Keyboard" : {
|
||||
|
||||
},
|
||||
"Are you sure you want to delete the Keyman package '%@'?" : {
|
||||
|
||||
},
|
||||
"Author:" : {
|
||||
|
||||
},
|
||||
"Cancel" : {
|
||||
|
||||
},
|
||||
"Checking..." : {
|
||||
|
||||
},
|
||||
"Close" : {
|
||||
|
||||
},
|
||||
"Configuration" : {
|
||||
|
||||
},
|
||||
"Continue" : {
|
||||
|
||||
},
|
||||
"Copy link" : {
|
||||
|
||||
},
|
||||
"Copyright:" : {
|
||||
|
||||
},
|
||||
"Delete" : {
|
||||
|
||||
},
|
||||
"Downloading File..." : {
|
||||
|
||||
},
|
||||
"Enable" : {
|
||||
|
||||
},
|
||||
"Enable Keyman" : {
|
||||
|
||||
},
|
||||
"enabled" : {
|
||||
|
||||
},
|
||||
"Ensure Keyman.app is set to provide it with necessary control in System Settings > Privacy & Security > Accessibility." : {
|
||||
|
||||
},
|
||||
"Finish installation" : {
|
||||
|
||||
},
|
||||
"Fonts:" : {
|
||||
|
||||
},
|
||||
"Grant Accessibility Permission" : {
|
||||
|
||||
},
|
||||
"Help" : {
|
||||
|
||||
},
|
||||
"Help not available." : {
|
||||
|
||||
},
|
||||
"Install" : {
|
||||
|
||||
},
|
||||
"Installation" : {
|
||||
|
||||
},
|
||||
"Installation complete" : {
|
||||
|
||||
},
|
||||
"Keyboards" : {
|
||||
|
||||
},
|
||||
"Missing Keyman Components" : {
|
||||
|
||||
},
|
||||
"OK" : {
|
||||
|
||||
},
|
||||
"One or more Keyman components or permissions require your attention. Complete the following steps to restore your Keyman installation." : {
|
||||
|
||||
},
|
||||
"Open Settings" : {
|
||||
|
||||
},
|
||||
"Package Installation Failed" : {
|
||||
|
||||
},
|
||||
"Package Version:" : {
|
||||
|
||||
},
|
||||
"Proceed to continue with installation" : {
|
||||
|
||||
},
|
||||
"Read me not available." : {
|
||||
|
||||
},
|
||||
"Remove Package" : {
|
||||
|
||||
},
|
||||
"Repairs Required" : {
|
||||
|
||||
},
|
||||
"Resolve Issues" : {
|
||||
|
||||
},
|
||||
"Restart Computer" : {
|
||||
|
||||
},
|
||||
"Restart your Mac to complete the installation. After restarting, open Keyman Configuration again if it doesn't launch automatically." : {
|
||||
|
||||
},
|
||||
"Run Keyman installer" : {
|
||||
|
||||
},
|
||||
"Share Keyboard" : {
|
||||
|
||||
},
|
||||
"Show keyboard help" : {
|
||||
|
||||
},
|
||||
"To use Keyman, enable the Keyman input method in System Settings." : {
|
||||
|
||||
},
|
||||
"Website:" : {
|
||||
|
||||
},
|
||||
"Your Keyman input method is either missing or outdated. Run the Keyman installer to install a new version." : {
|
||||
|
||||
}
|
||||
},
|
||||
"version" : "1.2"
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import PackageDescription
|
|||
|
||||
let package = Package(
|
||||
name: "KeymanSettings",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.macOS(.v13)
|
||||
|
|
|
|||
237
mac/KeymanSettings/Sources/KeymanSettings/Localizable.xcstrings
Normal file
237
mac/KeymanSettings/Sources/KeymanSettings/Localizable.xcstrings
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"contains.no.files" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The keyboard package contains no files."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"contains.no.keyboards" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The keyboard package contains no keyboards."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"could.not.unzip" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The keyboard package could not be unzipped."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"error.internal" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "An internal error occurred."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceeded.file.drop.limit" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Only a single .KMP file can be installed at a time."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"font.copy.error" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "There was an error copying the font."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"font.registration.error" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "There was an error registering the font."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"installation.in.progress" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "A package installation is already in progress."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"insufficient.keyman.version" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The keyboard package '%1$@' requires Keyman version %2$@ but your version is %3$@."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"invalid.url" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The URL is not valid."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"keyboard.id.missing" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "A keyboard in the package has no ID."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"keyboard.missing.kmx.file" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "A keyboard in the package has no corresponding KMX file."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"keyboard.missing.version" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "A keyboard in the package has no version."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"keyboard.name.missing" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "A keyboard in the package has no name."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kmp.json.not.found" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The package's kmp.json file was not found."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kmp.json.unreadable" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The package's kmp.json file could not be parsed."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"non.kmp.file" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The file %@ is not a .KMP file."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"prompt.new.package" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The package '%@' is ready to install"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"prompt.replace.newer.package" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The package '%1$@' is ready to downgrade from version %2$@ to %3$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"prompt.replace.older.package" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The package '%1$@' is ready to update from version %2$@ to %3$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"prompt.replace.same.version.package" : {
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "The package '%@' is ready to re-install"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version" : "1.2"
|
||||
}
|
||||
|
|
@ -33,12 +33,35 @@ public enum InstallPackageError: LocalizedError {
|
|||
case fontRegistrationError
|
||||
case internalError // due to invalid state, should never occur
|
||||
|
||||
private var packageBundle: LocalizedStringResource.BundleDescription {
|
||||
.atURL(Bundle.module.bundleURL)
|
||||
}
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .packageInstallationAlreadyInProgress: return "A package installation is already in progress."
|
||||
case .fontCopyError: return "There was an error copying the font."
|
||||
case .fontRegistrationError: return "There was an error registering the font."
|
||||
case .internalError: return "An internal error occurred."
|
||||
case .packageInstallationAlreadyInProgress:
|
||||
let resource = LocalizedStringResource(
|
||||
"installation.in.progress",
|
||||
defaultValue: "A package installation is already in progress.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .fontCopyError:
|
||||
let resource = LocalizedStringResource(
|
||||
"font.copy.error",
|
||||
defaultValue: "There was an error copying the font.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .fontRegistrationError:
|
||||
let resource = LocalizedStringResource(
|
||||
"font.registration.error",
|
||||
defaultValue: "There was an error registering the font.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .internalError:
|
||||
let resource = LocalizedStringResource(
|
||||
"error.internal",
|
||||
defaultValue: "An internal error occurred.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,20 +80,27 @@ public extension Notification.Name {
|
|||
static let dataMigrated = Notification.Name("com.keyman.data.migrated")
|
||||
}
|
||||
|
||||
// define LocalizedError so that UI can present a localizable message
|
||||
// when the attempt to install a KMP file using drag and drop fails
|
||||
public enum DropKmpError: LocalizedError {
|
||||
case invalidFileType(String)
|
||||
case alreadyInstalled(String)
|
||||
case installFailed(String)
|
||||
case tooManyFiles
|
||||
case exceededFileDropLimit
|
||||
|
||||
private var packageBundle: LocalizedStringResource.BundleDescription {
|
||||
.atURL(Bundle.module.bundleURL)
|
||||
}
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .invalidFileType(let fileName): return "The file \(fileName) is not a .KMP file."
|
||||
case .alreadyInstalled(let fileName): return "The package \(fileName) is already installed."
|
||||
case .installFailed(let fileName): return "The file \(fileName) could not be installed."
|
||||
case .tooManyFiles: return "Only a single .KMP file can be installed at a time."
|
||||
case .invalidFileType(let fileName):
|
||||
let resource = LocalizedStringResource(
|
||||
"non.kmp.file",
|
||||
defaultValue: "The file \(fileName) is not a .KMP file.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .exceededFileDropLimit:
|
||||
let resource = LocalizedStringResource(
|
||||
"exceeded.file.drop.limit",
|
||||
defaultValue: "Only a single .KMP file can be installed at a time.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,14 +21,30 @@ public enum PackageInstallationType {
|
|||
|
||||
public var prompt: LocalizedStringResource {
|
||||
switch self {
|
||||
case .newPackage(let packageName):
|
||||
return "The package '\(packageName)' is ready to install"
|
||||
case .newPackage(let packageName):
|
||||
return LocalizedStringResource(
|
||||
"prompt.new.package",
|
||||
defaultValue: "The package '\(packageName)' is ready to install",
|
||||
bundle: .atURL(Bundle.module.bundleURL)
|
||||
)
|
||||
case .replaceSameVersionPackage(let packageName):
|
||||
return "The package '\(packageName)' is ready to re-install"
|
||||
return LocalizedStringResource(
|
||||
"prompt.replace.same.version.package",
|
||||
defaultValue: "The package '\(packageName)' is ready to re-install",
|
||||
bundle: .atURL(Bundle.module.bundleURL)
|
||||
)
|
||||
case .replaceOlderPackage(let packageName, let existingVersion, let newVersion):
|
||||
return "The package '\(packageName)' is ready to update from version \(existingVersion) to \(newVersion)"
|
||||
return LocalizedStringResource(
|
||||
"prompt.replace.older.package",
|
||||
defaultValue: "The package '\(packageName)' is ready to update from version \(existingVersion) to \(newVersion)",
|
||||
bundle: .atURL(Bundle.module.bundleURL)
|
||||
)
|
||||
case .replaceNewerPackage(let packageName, let existingVersion, let newVersion):
|
||||
return "The package '\(packageName)' is ready to downgrade from version \(existingVersion) to \(newVersion)"
|
||||
return LocalizedStringResource(
|
||||
"prompt.replace.newer.package",
|
||||
defaultValue: "The package '\(packageName)' is ready to downgrade from version \(existingVersion) to \(newVersion)",
|
||||
bundle: .atURL(Bundle.module.bundleURL)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,20 +23,78 @@ public enum LoadPackageError: LocalizedError {
|
|||
case missingKeyboardVersion
|
||||
case missingKmxFile
|
||||
case insufficientKeymanVersion(packageName: String, requiredKeymanVersion: String, actualKeymanVersion: String)
|
||||
|
||||
|
||||
private var packageBundle: LocalizedStringResource.BundleDescription {
|
||||
.atURL(Bundle.module.bundleURL)
|
||||
}
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .invalidUrl: return "The URL is not valid."
|
||||
case .unzipError: return "The keyboard package could not be unzipped."
|
||||
case .containsNoFiles: return "The keyboard package contains no files."
|
||||
case .containsNoKeyboards: return "The keyboard package contains no keyboards."
|
||||
case .kmpJsonFileUnreadable: return "The package's kmp.json file could not be parsed."
|
||||
case .kmpJsonFileNotFound: return "The package's kmp.json file was not found."
|
||||
case .missingKeyboardName: return "A keyboard in the package has no name."
|
||||
case .missingKeyboardId: return "A keyboard in the package has no ID."
|
||||
case .missingKeyboardVersion: return "A keyboard in the package has no version."
|
||||
case .missingKmxFile: return "A keyboard in the package has no corresponding KMX file."
|
||||
case .insufficientKeymanVersion(let packageName, let requiredKeymanVersion, let actualKeymanVersion): return "The keyboard package '\(packageName)' requires Keyman version \(requiredKeymanVersion) but your version is \(actualKeymanVersion)."
|
||||
case .invalidUrl:
|
||||
let resource = LocalizedStringResource(
|
||||
"invalid.url",
|
||||
defaultValue: "The URL is not valid.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .unzipError:
|
||||
let resource = LocalizedStringResource(
|
||||
"could.not.unzip",
|
||||
defaultValue: "The keyboard package could not be unzipped.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .containsNoFiles:
|
||||
let resource = LocalizedStringResource(
|
||||
"contains.no.files",
|
||||
defaultValue: "The keyboard package contains no files.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .containsNoKeyboards:
|
||||
let resource = LocalizedStringResource(
|
||||
"contains.no.keyboards",
|
||||
defaultValue: "The keyboard package contains no keyboards.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .kmpJsonFileUnreadable:
|
||||
let resource = LocalizedStringResource(
|
||||
"kmp.json.unreadable",
|
||||
defaultValue: "The package's kmp.json file could not be parsed.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .kmpJsonFileNotFound:
|
||||
let resource = LocalizedStringResource(
|
||||
"kmp.json.not.found",
|
||||
defaultValue: "The package's kmp.json file was not found.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .missingKeyboardName:
|
||||
let resource = LocalizedStringResource(
|
||||
"keyboard.name.missing",
|
||||
defaultValue: "A keyboard in the package has no name.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .missingKeyboardId:
|
||||
let resource = LocalizedStringResource(
|
||||
"keyboard.id.missing",
|
||||
defaultValue: "A keyboard in the package has no ID.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .missingKeyboardVersion:
|
||||
let resource = LocalizedStringResource(
|
||||
"keyboard.missing.version",
|
||||
defaultValue: "A keyboard in the package has no version.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .missingKmxFile:
|
||||
let resource = LocalizedStringResource(
|
||||
"keyboard.missing.kmx.file",
|
||||
defaultValue: "A keyboard in the package has no corresponding KMX file.",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
case .insufficientKeymanVersion(let packageName, let requiredKeymanVersion, let actualKeymanVersion):
|
||||
let resource = LocalizedStringResource(
|
||||
"insufficient.keyman.version",
|
||||
defaultValue: "The keyboard package '\(packageName)' requires Keyman version \(requiredKeymanVersion) but your version is \(actualKeymanVersion).",
|
||||
bundle: packageBundle)
|
||||
return String(localized: resource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue