Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion Sources/CodexBar/Localization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,22 @@ func L(_ key: String, language: String) -> String {
}

func codexBarLocalizedLocale() -> Locale {
let language = resolvedAppLanguage()
codexBarLocale(forLanguage: resolvedAppLanguage())
}

/// Returns the locale of the resource bundle currently selected by `L`.
///
/// This can differ from `Locale.current` when the app falls back to a supported language. Plural
/// formatting must use this locale so it follows the same language as the resolved strings.
func codexBarLocalizedResourceLocale() -> Locale {
let bundleURL = localizedBundle().bundleURL
guard bundleURL.pathExtension == "lproj" else {
return codexBarLocalizedLocale()
}
return codexBarLocale(forLanguage: bundleURL.deletingPathExtension().lastPathComponent)
}

private func codexBarLocale(forLanguage language: String) -> Locale {
guard !language.isEmpty else { return .current }
let normalized = language.lowercased()
if normalized == "ar" || normalized.hasPrefix("ar-") {
Expand Down
16 changes: 9 additions & 7 deletions Sources/CodexBar/UsagePaceText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,21 @@ enum UsagePaceText {

static func sessionEquivalentDetail(forecast: SessionEquivalentForecast) -> SessionEquivalentDetail {
let displayedEstimate = Self.boundedFullWindowCount(forecast.estimatedWindowsToExhaustWeekly)
let numberText = String.localizedStringWithFormat(
L("≈%d full 5h windows of weekly left · %d windows until reset"),
displayedEstimate,
forecast.windowsUntilReset)
let formattingLocale = codexBarLocalizedResourceLocale()
let numberText = String(
format: L("≈%d full 5h windows of weekly left · %d windows until reset"),
locale: formattingLocale,
arguments: [displayedEstimate, forecast.windowsUntilReset])
let verdictText: String
if forecast.estimatedWindowsToExhaustWeekly >= forecast.availableWindowsUntilReset {
verdictText = L("Weekly cannot run out before reset at this pace")
} else {
let windowsEarly = Self.boundedWindowCount(
forecast.availableWindowsUntilReset - forecast.estimatedWindowsToExhaustWeekly)
verdictText = String.localizedStringWithFormat(
L("Weekly can run out ≈%d windows early"),
max(1, windowsEarly))
verdictText = String(
format: L("Weekly can run out ≈%d windows early"),
locale: formattingLocale,
arguments: [max(1, windowsEarly)])
}
return SessionEquivalentDetail(
verdictText: verdictText,
Expand Down
13 changes: 13 additions & 0 deletions Tests/CodexBarTests/LocalizationBundleCacheTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ struct LocalizationBundleCacheTests {
#expect(bundle.bundleURL.lastPathComponent == "en.lproj")
}

@Test
func `format locale follows the resolved resource bundle`() {
let english = CodexBarLocalizationOverride.$appLanguage.withValue("en") {
codexBarLocalizedResourceLocale()
}
#expect(english.language.languageCode?.identifier == "en")

let fallback = CodexBarLocalizationOverride.$appLanguage.withValue("zz-unknown") {
codexBarLocalizedResourceLocale()
}
#expect(fallback.language.languageCode?.identifier == "en")
}

@Test
func `resolution survives an explicit cache reset`() {
let first = CodexBarLocalizationOverride.$appLanguage.withValue("uk") {
Expand Down
Loading