Skip to content
This repository has been archived by the owner on Aug 12, 2022. It is now read-only.

Commit

Permalink
Merge pull request #51 from readium/fixes/l10n
Browse files Browse the repository at this point in the history
Localize the framework
  • Loading branch information
aferditamuriqi authored Jun 16, 2019
2 parents 69ab841 + 3b07851 commit 9eac337
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 59 deletions.
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
github "readium/r2-shared-swift" == 1.2.11
github "readium/r2-shared-swift" == 1.2.12
github "stephencelis/SQLite.swift" == 0.11.5
github "krzyzanowskim/CryptoSwift" == 0.15.0
github "weichsel/ZIPFoundation" == 0.9.8
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
github "krzyzanowskim/CryptoSwift" "0.15.0"
github "readium/r2-shared-swift" "1.2.11"
github "readium/r2-shared-swift" "1.2.12"
github "stephencelis/SQLite.swift" "0.11.5"
github "weichsel/ZIPFoundation" "0.9.8"
37 changes: 36 additions & 1 deletion r2-lcp-swift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
CA4A388E220988C800599297 /* LCPLLicenseContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA4A388D220988C800599297 /* LCPLLicenseContainer.swift */; };
CA4A389022098A9400599297 /* ZIPLicenseContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA4A388F22098A9400599297 /* ZIPLicenseContainer.swift */; };
CA4A3892220994E300599297 /* EPUBLicenseContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA4A3891220994E300599297 /* EPUBLicenseContainer.swift */; };
CA50B88022B2A777003AFF24 /* R2LCPLocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA50B87F22B2A777003AFF24 /* R2LCPLocalizedString.swift */; };
CA50B88422B2A822003AFF24 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = CA50B88622B2A822003AFF24 /* Localizable.strings */; };
CAB131F6220D81E60097DFB5 /* LicensesRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB131F5220D81E60097DFB5 /* LicensesRepository.swift */; };
CAB1320B220D9B200097DFB5 /* LCPService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB1320A220D9B200097DFB5 /* LCPService.swift */; };
CAB13211220DB2B10097DFB5 /* LCPAuthenticating.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB13210220DB2B10097DFB5 /* LCPAuthenticating.swift */; };
Expand Down Expand Up @@ -57,6 +59,8 @@
CA4A388D220988C800599297 /* LCPLLicenseContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LCPLLicenseContainer.swift; sourceTree = "<group>"; };
CA4A388F22098A9400599297 /* ZIPLicenseContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZIPLicenseContainer.swift; sourceTree = "<group>"; };
CA4A3891220994E300599297 /* EPUBLicenseContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPUBLicenseContainer.swift; sourceTree = "<group>"; };
CA50B87F22B2A777003AFF24 /* R2LCPLocalizedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = R2LCPLocalizedString.swift; sourceTree = "<group>"; };
CA50B88522B2A822003AFF24 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
CAB131F5220D81E60097DFB5 /* LicensesRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensesRepository.swift; sourceTree = "<group>"; };
CAB1320A220D9B200097DFB5 /* LCPService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LCPService.swift; sourceTree = "<group>"; };
CAB13210220DB2B10097DFB5 /* LCPAuthenticating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LCPAuthenticating.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -104,6 +108,23 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
CA50B87E22B2A768003AFF24 /* Toolkit */ = {
isa = PBXGroup;
children = (
CA50B87F22B2A777003AFF24 /* R2LCPLocalizedString.swift */,
);
path = Toolkit;
sourceTree = "<group>";
};
CA50B88122B2A7F2003AFF24 /* Resources */ = {
isa = PBXGroup;
children = (
CAB214E02269C241007E989D /* prod-license.lcpl */,
CA50B88622B2A822003AFF24 /* Localizable.strings */,
);
path = Resources;
sourceTree = "<group>";
};
CAB13212220DB55C0097DFB5 /* Public */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -232,8 +253,9 @@
CAB13214220DB5930097DFB5 /* License */,
CAB1321B220DB6430097DFB5 /* Services */,
CAB1321C220DB6570097DFB5 /* Persistence */,
CA50B87E22B2A768003AFF24 /* Toolkit */,
CA50B88122B2A7F2003AFF24 /* Resources */,
F3B2C88B1F667223007601E4 /* Info.plist */,
CAB214E02269C241007E989D /* prod-license.lcpl */,
F3B2C88A1F667223007601E4 /* readium-lcp-swift.h */,
);
path = "readium-lcp-swift";
Expand Down Expand Up @@ -329,6 +351,7 @@
buildActionMask = 2147483647;
files = (
CAB214E12269C241007E989D /* prod-license.lcpl in Resources */,
CA50B88422B2A822003AFF24 /* Localizable.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -361,6 +384,7 @@
F38FB0381F66839600F9D602 /* Rights.swift in Sources */,
F36F9E281F8270FC001D0DB4 /* Transactions.swift in Sources */,
F3B2C8A81F66727C007601E4 /* StatusDocument.swift in Sources */,
CA50B88022B2A777003AFF24 /* R2LCPLocalizedString.swift in Sources */,
CA2AE328221C3CFB008BD18F /* Deprecated.swift in Sources */,
F38FB0361F66837800F9D602 /* User.swift in Sources */,
F38FB0421F66847E00F9D602 /* Signature.swift in Sources */,
Expand All @@ -379,6 +403,17 @@
};
/* End PBXSourcesBuildPhase section */

/* Begin PBXVariantGroup section */
CA50B88622B2A822003AFF24 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
CA50B88522B2A822003AFF24 /* en */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
F3B2C8991F667223007601E4 /* Debug */ = {
isa = XCBuildConfiguration;
Expand Down
113 changes: 57 additions & 56 deletions readium-lcp-swift/Public/LCPError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
//

import Foundation
import R2LCPClient

public enum LCPError: Error {
public enum LCPError: LocalizedError {
// The operation can't be done right now because another License operation is running.
case licenseIsBusy
// An error occured while checking the integrity of the License, it can't be retrieved.
case licenseIntegrity(Error)
case licenseIntegrity(LCPClientError)
// The status of the License is not valid, it can't be used to decrypt the publication.
case licenseStatus(StatusError)
// Can't read or write the License Document from its container.
Expand All @@ -41,34 +42,54 @@ public enum LCPError: Error {
// An unknown low-level error was reported.
case unknown(Error?)

}

extension LCPError: LocalizedError {

public var errorDescription: String? {
switch self {
case .licenseIsBusy:
return "Can't perform this operation at the moment."
return R2LCPLocalizedString("LCPError.licenseIsBusy")
case .licenseIntegrity(let error):
return error.localizedDescription
let description: String = {
switch error {
case .licenseOutOfDate:
return R2LCPLocalizedString("LCPClientError.licenseOutOfDate")
case .certificateRevoked:
return R2LCPLocalizedString("LCPClientError.certificateRevoked")
case .certificateSignatureInvalid:
return R2LCPLocalizedString("LCPClientError.certificateSignatureInvalid")
case .licenseSignatureDateInvalid:
return R2LCPLocalizedString("LCPClientError.licenseSignatureDateInvalid")
case .licenseSignatureInvalid:
return R2LCPLocalizedString("LCPClientError.licenseSignatureInvalid")
case .contextInvalid:
return R2LCPLocalizedString("LCPClientError.contextInvalid")
case .contentKeyDecryptError:
return R2LCPLocalizedString("LCPClientError.contentKeyDecryptError")
case .userKeyCheckInvalid:
return R2LCPLocalizedString("LCPClientError.userKeyCheckInvalid")
case .contentDecryptError:
return R2LCPLocalizedString("LCPClientError.contentDecryptError")
case .unknown:
return R2LCPLocalizedString("LCPClientError.unknown")
}
}()
return R2LCPLocalizedString("LCPError.licenseIntegrity", description)
case .licenseStatus(let error):
return error.localizedDescription
case .licenseContainer:
return "Can't access the License Document."
return R2LCPLocalizedString("LCPError.licenseContainer")
case .licenseInteractionNotAvailable:
return "This interaction is not available."
return R2LCPLocalizedString("LCPError.licenseInteractionNotAvailable")
case .licenseProfileNotSupported:
return "This License has a profile identifier that this app cannot handle, the publication cannot be processed."
return R2LCPLocalizedString("LCPError.licenseProfileNotSupported")
case .crlFetching:
return "Can't retrieve the Certificate Revocation List."
return R2LCPLocalizedString("LCPError.crlFetching")
case .licenseRenew(let error):
return error.localizedDescription
case .licenseReturn(let error):
return error.localizedDescription
case .parsing(let error):
return error.localizedDescription
case .parsing(_):
return R2LCPLocalizedString("LCPError.parsing")
case .network(let error):
return error?.localizedDescription ?? "Network error."
return error?.localizedDescription ?? R2LCPLocalizedString("LCPError.network")
case .runtime(let error):
return error
case .unknown(let error):
Expand All @@ -80,38 +101,34 @@ extension LCPError: LocalizedError {


/// Errors while checking the status of the License, using the Status Document.
public enum StatusError: Error {
public enum StatusError: LocalizedError {
// For the case (revoked, returned, cancelled, expired), app should notify the user and stop there. The message to the user must be clear about the status of the license: don't display "expired" if the status is "revoked". The date and time corresponding to the new status should be displayed (e.g. "The license expired on 01 January 2018").
case cancelled(Date)
case returned(Date)
case expired(start: Date, end: Date)
// If the license has been revoked, the user message should display the number of devices which registered to the server. This count can be calculated from the number of "register" events in the status document. If no event is logged in the status document, no such message should appear (certainly not "The license was registered by 0 devices").
case revoked(Date, devicesCount: Int)
}

extension StatusError: LocalizedError {

public var errorDescription: String? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM dd, yyyy HH:mm"
dateFormatter.locale = Locale(identifier:"en")

dateFormatter.dateStyle = .medium

switch self {
case .cancelled(let date):
return "You have cancelled this license on \(dateFormatter.string(from: date))."
return R2LCPLocalizedString("StatusError.cancelled", dateFormatter.string(from: date))

case .returned(let date):
return "This license has been returned on \(dateFormatter.string(from: date))."
return R2LCPLocalizedString("StatusError.returned", dateFormatter.string(from: date))

case .expired(start: let start, end: let end):
if start > Date() {
return "This license starts on \(dateFormatter.string(from: start))."
return R2LCPLocalizedString("StatusError.expired.start", dateFormatter.string(from: start))
} else {
return "This license expired on \(dateFormatter.string(from: end))."
return R2LCPLocalizedString("StatusError.expired.end", dateFormatter.string(from: end))
}

case .revoked(let date, let devicesCount):
return "This license has been revoked by its provider on \(dateFormatter.string(from: date)).\nThe license was registered by \(devicesCount) device\(devicesCount > 1 ? "s" : "")."
return R2LCPLocalizedString("StatusError.revoked", dateFormatter.string(from: date), devicesCount)
}
}

Expand All @@ -130,11 +147,11 @@ public enum RenewError: LocalizedError {
public var errorDescription: String? {
switch self {
case .renewFailed:
return "Your publication could not be renewed properly."
return R2LCPLocalizedString("RenewError.renewFailed")
case .invalidRenewalPeriod(maxRenewDate: _):
return "Incorrect renewal period, your publication could not be renewed."
return R2LCPLocalizedString("RenewError.invalidRenewalPeriod")
case .unexpectedServerError:
return "An unexpected error has occurred on the server."
return R2LCPLocalizedString("RenewError.unexpectedServerError")
}
}

Expand All @@ -153,11 +170,11 @@ public enum ReturnError: LocalizedError {
public var errorDescription: String? {
switch self {
case .returnFailed:
return "Your publication could not be returned properly."
return R2LCPLocalizedString("ReturnError.returnFailed")
case .alreadyReturnedOrExpired:
return "Your publication has already been returned before or is expired."
return R2LCPLocalizedString("ReturnError.alreadyReturnedOrExpired")
case .unexpectedServerError:
return "An unexpected error has occurred on the server."
return R2LCPLocalizedString("ReturnError.unexpectedServerError")
}
}

Expand All @@ -166,34 +183,18 @@ public enum ReturnError: LocalizedError {

/// Errors while parsing the License or Status JSON Documents.
public enum ParsingError: Error {
// The JSON is malformed and can't be parsed.
case malformedJSON
// The JSON is not representing a valid License Document.
case licenseDocument
// The JSON is not representing a valid Status Document.
case statusDocument
// Invalid Link.
case link
// Invalid Encryption.
case encryption
// Invalid License Document Signature.
case signature
// Invalid URL for link with rel %@.
case url(rel: String)
}

extension ParsingError: LocalizedError {

public var errorDescription: String? {
switch self {
case .malformedJSON:
return "The JSON is malformed and can't be parsed."
case .licenseDocument:
return "The JSON is not representing a valid License Document."
case .statusDocument:
return "The JSON is not representing a valid Status Document."
case .link:
return "Invalid Link."
case .encryption:
return "Invalid Encryption."
case .signature:
return "Invalid License Document Signature."
case .url(let rel):
return "Invalid URL for link with rel \(rel)."
}
}

}
49 changes: 49 additions & 0 deletions readium-lcp-swift/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
Localizable.strings
r2-lcp-swift

Created by Mickaël Menu on 13.06.19.

Copyright 2019 Readium Foundation. All rights reserved.
Use of this source code is governed by a BSD-style license which is detailed
in the LICENSE file present in the project repository where this source code is maintained.
*/

/* LCPError: General error messages */
"ReadiumLCP.LCPError.licenseIsBusy" = "Can't perform this operation at the moment.";
"ReadiumLCP.LCPError.licenseIntegrity" = "License integrity: %@";
"ReadiumLCP.LCPError.licenseContainer" = "Can't access the License Document.";
"ReadiumLCP.LCPError.licenseInteractionNotAvailable" = "This interaction is not available.";
"ReadiumLCP.LCPError.licenseProfileNotSupported" = "This License has a profile identifier that this app cannot handle, the publication cannot be processed.";
"ReadiumLCP.LCPError.crlFetching" = "Can't retrieve the Certificate Revocation List.";
"ReadiumLCP.LCPError.parsing" = "Failed to parse the License Document.";
"ReadiumLCP.LCPError.network" = "Network error.";

/* LCPClientError: Errors while checking the integrity of the License */
"ReadiumLCP.LCPClientError.licenseOutOfDate" = "License is out of date (check start and end date).";
"ReadiumLCP.LCPClientError.certificateRevoked" = "Certificate has been revoked in the CRL.";
"ReadiumLCP.LCPClientError.certificateSignatureInvalid" = "Certificate has not been signed by CA.";
"ReadiumLCP.LCPClientError.licenseSignatureDateInvalid" = "License has been issued by an expired certificate.";
"ReadiumLCP.LCPClientError.licenseSignatureInvalid" = "License signature does not match.";
"ReadiumLCP.LCPClientError.contextInvalid" = "The DRM context is invalid.";
"ReadiumLCP.LCPClientError.contentKeyDecryptError" = "Unable to decrypt encrypted content key from user key.";
"ReadiumLCP.LCPClientError.userKeyCheckInvalid" = "User key check invalid.";
"ReadiumLCP.LCPClientError.contentDecryptError" = "Unable to decrypt encrypted content from content key.";
"ReadiumLCP.LCPClientError.unknown" = "Unknown error.";

/* StatusError: Errors while checking the status of the License, using the Status Document. */
"ReadiumLCP.StatusError.cancelled" = "You have cancelled this license on %@.";
"ReadiumLCP.StatusError.returned" = "This license has been returned on %@.";
"ReadiumLCP.StatusError.expired.start" = "This license starts on %@.";
"ReadiumLCP.StatusError.expired.end" = "This license expired on %@.";
"ReadiumLCP.StatusError.revoked" = "This license has been revoked by its provider on %1$@.\nThe license was registered by %1$d device(s)";

/* RenewError: Errors while renewing a loan. */
"ReadiumLCP.RenewError.renewFailed" = "Your publication could not be renewed properly.";
"ReadiumLCP.RenewError.invalidRenewalPeriod" = "Incorrect renewal period, your publication could not be renewed.";
"ReadiumLCP.RenewError.unexpectedServerError" = "An unexpected error has occurred on the server.";

/* ReturnError: Errors while returning a loan. */
"ReadiumLCP.ReturnError.returnFailed" = "Your publication could not be returned properly.";
"ReadiumLCP.ReturnError.alreadyReturnedOrExpired" = "Your publication has already been returned before or is expired.";
"ReadiumLCP.ReturnError.unexpectedServerError" = "An unexpected error has occurred on the server.";
17 changes: 17 additions & 0 deletions readium-lcp-swift/Toolkit/R2LCPLocalizedString.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// R2LCPLocalizedString.swift
// r2-lcp-swift
//
// Created by Mickaël Menu on 13.06.19.
//
// Copyright 2019 Readium Foundation. All rights reserved.
// Use of this source code is governed by a BSD-style license which is detailed
// in the LICENSE file present in the project repository where this source code is maintained.
//

import Foundation
import R2Shared

func R2LCPLocalizedString(_ key: String, _ values: CVarArg...) -> String {
return R2LocalizedString("ReadiumLCP.\(key)", in: "org.readium.readium-lcp-swift", values)
}

0 comments on commit 9eac337

Please sign in to comment.