[PM-27098] Add plurals for times (#2052)

This commit is contained in:
Katherine Bertelsen 2025-10-31 16:26:02 -05:00 committed by GitHub
parent b68624757d
commit 0c3610661c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 136 additions and 195 deletions

View File

@ -1,30 +0,0 @@
import BitwardenResources
import XCTest
@testable import AuthenticatorShared
class ClearClipboardValueTests: BitwardenTestCase {
// MARK: Tests
/// `localizedName` returns the correct values.
func test_localizedName() {
XCTAssertEqual(ClearClipboardValue.never.localizedName, Localizations.never)
XCTAssertEqual(ClearClipboardValue.tenSeconds.localizedName, Localizations.tenSeconds)
XCTAssertEqual(ClearClipboardValue.twentySeconds.localizedName, Localizations.twentySeconds)
XCTAssertEqual(ClearClipboardValue.thirtySeconds.localizedName, Localizations.thirtySeconds)
XCTAssertEqual(ClearClipboardValue.oneMinute.localizedName, Localizations.xMinutes(1))
XCTAssertEqual(ClearClipboardValue.twoMinutes.localizedName, Localizations.xMinutes(2))
XCTAssertEqual(ClearClipboardValue.fiveMinutes.localizedName, Localizations.xMinutes(5))
}
/// `rawValue` returns the correct values.
func test_rawValues() {
XCTAssertEqual(ClearClipboardValue.never.rawValue, -1)
XCTAssertEqual(ClearClipboardValue.tenSeconds.rawValue, 10)
XCTAssertEqual(ClearClipboardValue.twentySeconds.rawValue, 20)
XCTAssertEqual(ClearClipboardValue.thirtySeconds.rawValue, 30)
XCTAssertEqual(ClearClipboardValue.oneMinute.rawValue, 60)
XCTAssertEqual(ClearClipboardValue.twoMinutes.rawValue, 120)
XCTAssertEqual(ClearClipboardValue.fiveMinutes.rawValue, 300)
}
}

View File

@ -1,12 +1,11 @@
import BitwardenKit
import BitwardenResources
// MARK: - SessionTimeoutValue
/// An enumeration of session timeout values to choose from.
/// BWA does not use the custom value.
///
extension SessionTimeoutValue: @retroactive CaseIterable, Menuable {
extension SessionTimeoutValue: @retroactive CaseIterable {
/// All of the cases to show in the menu.
public static let allCases: [Self] = [
.immediately,
@ -19,30 +18,4 @@ extension SessionTimeoutValue: @retroactive CaseIterable, Menuable {
.onAppRestart,
.never,
]
/// The localized string representation of a `SessionTimeoutValue`.
public var localizedName: String {
switch self {
case .immediately:
Localizations.immediately
case .oneMinute:
Localizations.xMinutes(1)
case .fiveMinutes:
Localizations.xMinutes(5)
case .fifteenMinutes:
Localizations.xMinutes(15)
case .thirtyMinutes:
Localizations.xMinutes(30)
case .oneHour:
Localizations.oneHour
case .fourHours:
Localizations.fourHours
case .onAppRestart:
Localizations.onRestart
case .never:
Localizations.never
case .custom:
Localizations.custom
}
}
}

View File

@ -32,8 +32,8 @@ final class SessionTimeoutValueTests: BitwardenTestCase {
XCTAssertEqual(SessionTimeoutValue.fiveMinutes.localizedName, Localizations.xMinutes(5))
XCTAssertEqual(SessionTimeoutValue.fifteenMinutes.localizedName, Localizations.xMinutes(15))
XCTAssertEqual(SessionTimeoutValue.thirtyMinutes.localizedName, Localizations.xMinutes(30))
XCTAssertEqual(SessionTimeoutValue.oneHour.localizedName, Localizations.oneHour)
XCTAssertEqual(SessionTimeoutValue.fourHours.localizedName, Localizations.fourHours)
XCTAssertEqual(SessionTimeoutValue.oneHour.localizedName, Localizations.xHours(1))
XCTAssertEqual(SessionTimeoutValue.fourHours.localizedName, Localizations.xHours(4))
XCTAssertEqual(SessionTimeoutValue.onAppRestart.localizedName, Localizations.onRestart)
XCTAssertEqual(SessionTimeoutValue.never.localizedName, Localizations.never)
XCTAssertEqual(SessionTimeoutValue.custom(123).localizedName, Localizations.custom)

View File

@ -1,4 +1,5 @@
@testable import AuthenticatorShared
import BitwardenKit
class MockPasteboardService: PasteboardService {
var clearClipboardValue: ClearClipboardValue = .never

View File

@ -161,11 +161,11 @@ enum TotpPeriodOptions: Int, Menuable, CaseIterable {
var localizedName: String {
switch self {
case .thirty:
Localizations.thirtySeconds
Localizations.xSeconds(30)
case .sixty:
Localizations.sixtySeconds
Localizations.xSeconds(60)
case .ninety:
Localizations.ninetySeconds
Localizations.xSeconds(90)
}
}
}

View File

@ -1,4 +1,3 @@
import BitwardenKit
import BitwardenResources
import Foundation
@ -6,7 +5,7 @@ import Foundation
/// The time after which the clipboard should be cleared.
///
enum ClearClipboardValue: Int, Menuable {
public enum ClearClipboardValue: Int, Menuable {
/// Do not clear the clipboard.
case never = -1
@ -29,7 +28,7 @@ enum ClearClipboardValue: Int, Menuable {
case fiveMinutes = 300
/// All of the cases to show in the menu, in order.
static let allCases: [Self] = [
public static let allCases: [Self] = [
.never,
.tenSeconds,
.twentySeconds,
@ -40,16 +39,16 @@ enum ClearClipboardValue: Int, Menuable {
]
/// The name of the value to display in the menu.
var localizedName: String {
public var localizedName: String {
switch self {
case .never:
Localizations.never
case .tenSeconds:
Localizations.tenSeconds
Localizations.xSeconds(10)
case .twentySeconds:
Localizations.twentySeconds
Localizations.xSeconds(20)
case .thirtySeconds:
Localizations.thirtySeconds
Localizations.xSeconds(30)
case .oneMinute:
Localizations.xMinutes(1)
case .twoMinutes:

View File

@ -1,7 +1,6 @@
import XCTest
import BitwardenKit
import BitwardenResources
@testable import BitwardenShared
import XCTest
class ClearClipboardValueTests: BitwardenTestCase {
// MARK: Tests
@ -9,9 +8,9 @@ class ClearClipboardValueTests: BitwardenTestCase {
/// `localizedName` returns the correct values.
func test_localizedName() {
XCTAssertEqual(ClearClipboardValue.never.localizedName, Localizations.never)
XCTAssertEqual(ClearClipboardValue.tenSeconds.localizedName, Localizations.tenSeconds)
XCTAssertEqual(ClearClipboardValue.twentySeconds.localizedName, Localizations.twentySeconds)
XCTAssertEqual(ClearClipboardValue.thirtySeconds.localizedName, Localizations.thirtySeconds)
XCTAssertEqual(ClearClipboardValue.tenSeconds.localizedName, Localizations.xSeconds(10))
XCTAssertEqual(ClearClipboardValue.twentySeconds.localizedName, Localizations.xSeconds(20))
XCTAssertEqual(ClearClipboardValue.thirtySeconds.localizedName, Localizations.xSeconds(30))
XCTAssertEqual(ClearClipboardValue.oneMinute.localizedName, Localizations.xMinutes(1))
XCTAssertEqual(ClearClipboardValue.twoMinutes.localizedName, Localizations.xMinutes(2))
XCTAssertEqual(ClearClipboardValue.fiveMinutes.localizedName, Localizations.xMinutes(5))

View File

@ -1,8 +1,10 @@
import BitwardenResources
// MARK: - SessionTimeoutValue
/// An enumeration of session timeout values to choose from.
///
public enum SessionTimeoutValue: Codable, RawRepresentable, Equatable, Hashable, Sendable {
public enum SessionTimeoutValue: Codable, RawRepresentable, Equatable, Hashable, Menuable, Sendable {
/// Time out immediately.
case immediately
@ -38,6 +40,32 @@ public enum SessionTimeoutValue: Codable, RawRepresentable, Equatable, Hashable,
rawValue * 60
}
/// The localized string representation of a `SessionTimeoutValue`.
public var localizedName: String {
switch self {
case .immediately:
Localizations.immediately
case .oneMinute:
Localizations.xMinutes(1)
case .fiveMinutes:
Localizations.xMinutes(5)
case .fifteenMinutes:
Localizations.xMinutes(15)
case .thirtyMinutes:
Localizations.xMinutes(30)
case .oneHour:
Localizations.xHours(1)
case .fourHours:
Localizations.xHours(4)
case .onAppRestart:
Localizations.onRestart
case .never:
Localizations.never
case .custom:
Localizations.custom
}
}
/// The session timeout value in minutes.
public var rawValue: Int {
switch self {

View File

@ -131,8 +131,6 @@
"LastSync" = "Last sync:";
"Length" = "Length";
"Lock" = "Lock";
"OneHour" = "1 hour";
"FourHours" = "4 hours";
"Immediately" = "Immediately";
"VaultTimeout" = "Vault timeout";
"VaultTimeoutAction" = "Vault timeout action";
@ -439,9 +437,6 @@
"VaultLockedIdentity" = "Your vault is locked. Verify your identity to continue.";
"Dark" = "Dark";
"Light" = "Light";
"TenSeconds" = "10 seconds";
"ThirtySeconds" = "30 seconds";
"TwentySeconds" = "20 seconds";
"ClearClipboard" = "Clear clipboard";
"ClearClipboardDescription" = "Automatically clear copied values from your clipboard.";
"DefaultUriMatchDetection" = "Default URI match detection";
@ -584,11 +579,6 @@
"SendDeleted" = "Send deleted";
"SendUpdated" = "Send saved";
"NewSendCreated" = "Send created";
"OneDay" = "1 day";
"TwoDays" = "2 days";
"ThreeDays" = "3 days";
"SevenDays" = "7 days";
"ThirtyDays" = "30 days";
"Custom" = "Custom";
"ShareOnSave" = "Share this Send upon save";
"SendDisabledWarning" = "Due to an enterprise policy, you are only able to delete an existing Send.";
@ -841,7 +831,6 @@
"OneHourAndXMinute" = "One hour and %1$@ minutes";
"XHoursAndOneMinute" = "%1$@ hours and one minute";
"XHoursAndYMinutes" = "%1$@ hours and %2$@ minutes";
"XHours" = "%1$@ hours";
"PasskeyManagementExplanationLong" = "Use Bitwarden to save new passkeys and log in with passkeys stored in your vault.";
"AutofillServicesExplanationLong" = "The Android Autofill Framework is used to assist in filling login information into other apps on your device.";
"UseInlineAutofillExplanationLong" = "Use inline autofill if your selected keyboard supports it. Otherwise, use the default overlay.";
@ -1147,7 +1136,6 @@
"LoggingDuration" = "Logging duration";
"LogsWillBeAutomaticallyDeletedAfter30DaysDescriptionLong" = "Logs will be automatically deleted after 30 days. Bitwarden is only able to access your log data when you share it.";
"ForDetailsOnWhatIsAndIsntLoggedVisitTheBitwardenHelpCenter" = "For details on what is and isnt logged, visit the **[Bitwarden help center](%1$@)**.";
"OneWeek" = "1 week";
"ShowMore" = "Show more";
"ShowLess" = "Show less";
"ItemNameX" = "Item name, %1$@";
@ -1242,7 +1230,6 @@
"LearnMoreLink" = "[Learn more](%1$@)";
"LocalCodes" = "Local codes";
"NeedHelpVisitOurHelpCenterForGuidance" = "Need help? Visit our Help Center for guidance.";
"NinetySeconds" = "90 seconds";
"NoAskMe" = "No, ask me";
"NoCodes" = "You dont have any codes to display";
"None" = "None";
@ -1260,7 +1247,6 @@
"SetSaveLocallyAsYourDefaultSaveOption" = "Set “Save locally” as your default save option?";
"SetSaveToBitwardenAsYourDefaultSaveOption" = "Set “Save to Bitwarden” as your default save option?";
"SignInUsingUniqueCodes" = "Sign in using unique codes";
"SixtySeconds" = "60 seconds";
"Skip" = "Skip";
"Steam" = "Steam";
"StoreAllOfYourLoginsAndSyncVerificationCodesDirectlyWithTheAuthenticatorApp" = "Store all of your logins and sync verification codes directly with the Authenticator app.";

View File

@ -2,6 +2,41 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- A number of days, used in menu selections. For example, the number of days to keep a send before deletion. -->
<key>XDays</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@days@</string>
<key>days</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d day</string>
<key>other</key>
<string>%d days</string>
</dict>
</dict>
<!-- A number of hours, used in menu selections. For example, how long to log events in the flight recorder. -->
<key>XHours</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@hours@</string>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d hour</string>
<key>other</key>
<string>%d hours</string>
</dict>
</dict>
<!-- A number of minutes, used in menu selections. For example, the amount of time to keep the vault unlocked after being unlocked. -->
<key>XMinutes</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
@ -18,5 +53,39 @@
<string>%d minutes</string>
</dict>
</dict>
<!-- A number of seconds, used in menu selection. For example, how long to keep copied fields in the clipboard before clearing. -->
<key>XSeconds</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@seconds@</string>
<key>seconds</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d second</string>
<key>other</key>
<string>%d seconds</string>
</dict>
</dict>
<!-- A number of weeks, used in menu selection. For example, how long to log events in the flight recorder. -->
<key>XWeeks</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@weeks@</string>
<key>weeks</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d week</string>
<key>other</key>
<string>%d weeks</string>
</dict>
</dict>
</dict>
</plist>

View File

@ -1,61 +0,0 @@
import BitwardenKit
import BitwardenResources
import Foundation
// MARK: - ClearClipboardValue
/// The time after which the clipboard should be cleared.
///
enum ClearClipboardValue: Int, Menuable {
/// Do not clear the clipboard.
case never = -1
/// Clear the clipboard after ten seconds.
case tenSeconds = 10
/// Clear the clipboard after twenty seconds.
case twentySeconds = 20
/// Clear the clipboard after thirty seconds.
case thirtySeconds = 30
/// Clear the clipboard after one minute.
case oneMinute = 60
/// Clear the clipboard after two minutes.
case twoMinutes = 120
/// Clear the clipboard after five minutes.
case fiveMinutes = 300
/// All of the cases to show in the menu, in order.
static let allCases: [Self] = [
.never,
.tenSeconds,
.twentySeconds,
.thirtySeconds,
.oneMinute,
.twoMinutes,
.fiveMinutes,
]
/// The name of the value to display in the menu.
var localizedName: String {
switch self {
case .never:
Localizations.never
case .tenSeconds:
Localizations.tenSeconds
case .twentySeconds:
Localizations.twentySeconds
case .thirtySeconds:
Localizations.thirtySeconds
case .oneMinute:
Localizations.xMinutes(1)
case .twoMinutes:
Localizations.xMinutes(2)
case .fiveMinutes:
Localizations.xMinutes(5)
}
}
}

View File

@ -19,10 +19,10 @@ enum FlightRecorderLoggingDuration: CaseIterable, Codable, Menuable {
var localizedName: String {
switch self {
case .oneHour: Localizations.oneHour
case .oneHour: Localizations.xHours(1)
case .eightHours: Localizations.xHours(8)
case .twentyFourHours: Localizations.xHours(24)
case .oneWeek: Localizations.oneWeek
case .oneWeek: Localizations.xWeeks(1)
}
}

View File

@ -25,10 +25,10 @@ class FlightRecorderLoggingDurationTests: BitwardenTestCase {
/// `localizedName` returns the correct values.
func test_localizedName() {
XCTAssertEqual(FlightRecorderLoggingDuration.oneHour.localizedName, Localizations.oneHour)
XCTAssertEqual(FlightRecorderLoggingDuration.oneHour.localizedName, Localizations.xHours(1))
XCTAssertEqual(FlightRecorderLoggingDuration.eightHours.localizedName, Localizations.xHours(8))
XCTAssertEqual(FlightRecorderLoggingDuration.twentyFourHours.localizedName, Localizations.xHours(24))
XCTAssertEqual(FlightRecorderLoggingDuration.oneWeek.localizedName, Localizations.oneWeek)
XCTAssertEqual(FlightRecorderLoggingDuration.oneWeek.localizedName, Localizations.xWeeks(1))
}
/// `shortDescription` returns a short string representation of the logging duration.

View File

@ -1,3 +1,4 @@
import BitwardenKit
import BitwardenSdk
import Combine
import Foundation

View File

@ -1,3 +1,4 @@
import BitwardenKit
import BitwardenSdk
import Combine
import Foundation

View File

@ -1,3 +1,4 @@
import BitwardenKit
@testable import BitwardenShared
class MockPasteboardService: PasteboardService {

View File

@ -32,12 +32,12 @@ enum SendDeletionDateType: Menuable {
var localizedName: String {
switch self {
case .oneHour: Localizations.oneHour
case .oneDay: Localizations.oneDay
case .twoDays: Localizations.twoDays
case .threeDays: Localizations.threeDays
case .sevenDays: Localizations.sevenDays
case .thirtyDays: Localizations.thirtyDays
case .oneHour: Localizations.xHours(1)
case .oneDay: Localizations.xDays(1)
case .twoDays: Localizations.xDays(2)
case .threeDays: Localizations.xDays(3)
case .sevenDays: Localizations.xDays(7)
case .thirtyDays: Localizations.xDays(30)
case let .custom(customDate): customDate.dateTimeDisplay
}
}

View File

@ -37,12 +37,12 @@ class SendDeletionDateTypeTests: BitwardenTestCase {
/// `localizedName` returns the localized name of the option to display in the menu.
func test_localizedName() {
XCTAssertEqual(SendDeletionDateType.oneHour.localizedName, Localizations.oneHour)
XCTAssertEqual(SendDeletionDateType.oneDay.localizedName, Localizations.oneDay)
XCTAssertEqual(SendDeletionDateType.twoDays.localizedName, Localizations.twoDays)
XCTAssertEqual(SendDeletionDateType.threeDays.localizedName, Localizations.threeDays)
XCTAssertEqual(SendDeletionDateType.sevenDays.localizedName, Localizations.sevenDays)
XCTAssertEqual(SendDeletionDateType.thirtyDays.localizedName, Localizations.thirtyDays)
XCTAssertEqual(SendDeletionDateType.oneHour.localizedName, Localizations.xHours(1))
XCTAssertEqual(SendDeletionDateType.oneDay.localizedName, Localizations.xDays(1))
XCTAssertEqual(SendDeletionDateType.twoDays.localizedName, Localizations.xDays(2))
XCTAssertEqual(SendDeletionDateType.threeDays.localizedName, Localizations.xDays(3))
XCTAssertEqual(SendDeletionDateType.sevenDays.localizedName, Localizations.xDays(7))
XCTAssertEqual(SendDeletionDateType.thirtyDays.localizedName, Localizations.xDays(30))
XCTAssertEqual(
SendDeletionDateType.custom(Date(year: 2024, month: 1, day: 19)).localizedName,

View File

@ -21,7 +21,7 @@ public enum UnlockMethod {
/// An enumeration of session timeout values to choose from.
///
extension SessionTimeoutValue: @retroactive CaseIterable, @retroactive Menuable {
extension SessionTimeoutValue: @retroactive CaseIterable {
/// All of the cases to show in the menu.
public static let allCases: [Self] = [
.immediately,
@ -35,32 +35,6 @@ extension SessionTimeoutValue: @retroactive CaseIterable, @retroactive Menuable
.never,
.custom(-100),
]
/// The localized string representation of a `SessionTimeoutValue`.
public var localizedName: String {
switch self {
case .immediately:
Localizations.immediately
case .oneMinute:
Localizations.xMinutes(1)
case .fiveMinutes:
Localizations.xMinutes(5)
case .fifteenMinutes:
Localizations.xMinutes(15)
case .thirtyMinutes:
Localizations.xMinutes(30)
case .oneHour:
Localizations.oneHour
case .fourHours:
Localizations.fourHours
case .onAppRestart:
Localizations.onRestart
case .never:
Localizations.never
case .custom:
Localizations.custom
}
}
}
// MARK: - SessionTimeoutAction