Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 0.33.1 — Unreleased

### Added
- Localization: add native Korean language support across the app and language picker (#1460). Thanks @soohanpark!
- Devin: add daily and weekly quota tracking from the signed-in Chrome session or a manual Bearer token (#1264, fixes #800). Thanks @coygeek!
- Menu bar: add an optional reset-time display for the selected quota metric, with percent fallback when reset metadata is unavailable (#1223, fixes #1185). Thanks @Yuxin-Qiao!
- Menu bar: move the highlighted Overview provider with trackpad or mouse-wheel scrolling while preserving native submenu and keyboard behavior (#1436). Thanks @joshuavial!
Expand Down
2 changes: 2 additions & 0 deletions Sources/CodexBar/PreferencesGeneralPane.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ enum AppLanguage: String, CaseIterable, Identifiable {
case ukrainian = "uk"
case vietnamese = "vi"
case japanese = "ja"
case korean = "ko"

var id: String {
self.rawValue
Expand All @@ -36,6 +37,7 @@ enum AppLanguage: String, CaseIterable, Identifiable {
case .ukrainian: L("language_ukrainian")
case .vietnamese: L("language_vietnamese")
case .japanese: L("language_japanese")
case .korean: L("language_korean")
}
}
}
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/Resources/ca.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@
"language_french" = "Francès";
"language_ukrainian" = "Ucraïnès";
"language_japanese" = "Japonès";
"language_korean" = "Coreà";
"start_at_login_title" = "Obrir en iniciar la sessió";
"start_at_login_subtitle" = "Obre el CodexBar automàticament en iniciar el Mac.";
"show_cost_summary" = "Mostra el resum de cost";
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@
"language_french" = "French";
"language_ukrainian" = "Українська";
"language_japanese" = "Japanese";
"language_korean" = "Korean";
"start_at_login_title" = "Start at Login";
"start_at_login_subtitle" = "Automatically opens CodexBar when you start your Mac.";
"show_cost_summary" = "Show cost summary";
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/Resources/es.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@
"language_french" = "Francés";
"language_ukrainian" = "Ucraniano";
"language_japanese" = "Japonés";
"language_korean" = "Coreano";
"start_at_login_title" = "Abrir al iniciar sesión";
"start_at_login_subtitle" = "Abre CodexBar automáticamente al iniciar tu Mac.";
"show_cost_summary" = "Mostrar resumen de coste";
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/Resources/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@
"language_dutch" = "Néerlandais";
"language_ukrainian" = "Ukrainien";
"language_japanese" = "Japonais";
"language_korean" = "Coréen";
"language_vietnamese" = "Vietnamien";
"start_at_login_title" = "Lancer à l'ouverture de session";
"start_at_login_subtitle" = "Ouvre automatiquement CodexBar au démarrage de votre Mac.";
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/Resources/ja.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@
"language_french" = "フランス語";
"language_ukrainian" = "ウクライナ語";
"language_japanese" = "日本語";
"language_korean" = "韓国語";
"start_at_login_title" = "ログイン時に起動";
"start_at_login_subtitle" = "Mac の起動時に CodexBar を自動的に開きます。";
"show_cost_summary" = "コスト概要を表示";
Expand Down
1,042 changes: 1,042 additions & 0 deletions Sources/CodexBar/Resources/ko.lproj/Localizable.strings

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Sources/CodexBar/Resources/nl.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@
"language_french" = "Frans";
"language_ukrainian" = "Oekraïens";
"language_japanese" = "Japans";
"language_korean" = "Koreaans";
"language_swedish" = "Zweeds";
"language_vietnamese" = "Vietnamees";
"start_at_login_title" = "Begin bij Inloggen";
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/Resources/pt-BR.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@
"language_french" = "Francês";
"language_ukrainian" = "Ucraniano";
"language_japanese" = "Japonês";
"language_korean" = "Coreano";
"start_at_login_title" = "Iniciar ao fazer login";
"start_at_login_subtitle" = "Abre o CodexBar automaticamente ao iniciar o Mac.";
"show_cost_summary" = "Mostrar resumo de custos";
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/Resources/sv.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@
"language_french" = "Franska";
"language_ukrainian" = "Ukrainska";
"language_japanese" = "Japanska";
"language_korean" = "Koreanska";
"start_at_login_title" = "Starta vid inloggning";
"start_at_login_subtitle" = "Öppnar CodexBar automatiskt när du startar din Mac.";
"show_cost_summary" = "Visa kostnadssammanfattning";
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/Resources/uk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@
"language_dutch" = "Нідерландська";
"language_ukrainian" = "Українська";
"language_japanese" = "Японська";
"language_korean" = "Корейська";
"language_vietnamese" = "В'єтнамська";
"start_at_login_title" = "Почніть із входу";
"start_at_login_subtitle" = "Автоматично відкриває CodexBar під час запуску Mac.";
Expand Down
1 change: 1 addition & 0 deletions Sources/CodexBar/Resources/vi.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@
"language_dutch" = "Tiếng Hà Lan";
"language_ukrainian" = "Tiếng Ukraina";
"language_japanese" = "Tiếng Nhật";
"language_korean" = "Tiếng Hàn";
"start_at_login_title" = "Bắt đầu khi đăng nhập";
"start_at_login_subtitle" = "Tự động mở CodexBar khi bạn khởi động máy Mac.";
"show_cost_summary" = "Hiển thị tóm tắt chi phí";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@
"language_french" = "法语";
"language_ukrainian" = "乌克兰语";
"language_japanese" = "日语";
"language_korean" = "韩语";
"start_at_login_title" = "开机启动";
"start_at_login_subtitle" = "启动 Mac 时自动打开 CodexBar。";
"show_cost_summary" = "显示费用摘要";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@
"language_french" = "法語";
"language_ukrainian" = "烏克蘭語";
"language_japanese" = "日語";
"language_korean" = "韓語";
"start_at_login_title" = "登入時啟動";
"start_at_login_subtitle" = "登入 Mac 時自動開啟 CodexBar。";
"show_cost_summary" = "顯示費用摘要";
Expand Down
42 changes: 42 additions & 0 deletions Tests/CodexBarTests/LocalizationLanguageCatalogTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct LocalizationLanguageCatalogTests {
"language_ukrainian",
"language_vietnamese",
"language_japanese",
"language_korean",
]

@Test
Expand All @@ -25,6 +26,12 @@ struct LocalizationLanguageCatalogTests {
#expect(AppLanguage.ukrainian.rawValue == "uk")
}

@Test
func `app language catalog includes Korean`() {
#expect(AppLanguage.allCases.contains(.korean))
#expect(AppLanguage.korean.rawValue == "ko")
}

@Test
func `localized catalogs include every app language label`() throws {
#expect(self.languageKeys.count == AppLanguage.allCases.count)
Expand Down Expand Up @@ -70,6 +77,22 @@ struct LocalizationLanguageCatalogTests {
}
}

@Test
func `korean localization bundle includes representative native labels`() throws {
let root = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
.deletingLastPathComponent()
.deletingLastPathComponent()
let koURL = root.appendingPathComponent("Sources/CodexBar/Resources/ko.lproj/Localizable.strings")
let catalog = try #require(NSDictionary(contentsOf: koURL) as? [String: String])

#expect(catalog["language_korean"] == "한국어")
#expect(catalog["tab_general"] == "일반")
#expect(catalog["quota_warning_session"] == "세션")
#expect(catalog["quota_warning_warn_at"] == "경고 기준")
#expect(catalog["quit_app"] == "CodexBar 종료")
}

@Test
func `japanese usage chart accessibility text preserves argument meanings`() throws {
let root = URL(fileURLWithPath: #filePath)
Expand All @@ -88,4 +111,23 @@ struct LocalizationLanguageCatalogTests {
#expect(rendered.contains("7日間"))
#expect(rendered.contains("3サービス"))
}

@Test
func `korean usage chart accessibility text preserves argument meanings`() throws {
let root = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
.deletingLastPathComponent()
.deletingLastPathComponent()
let koURL = root.appendingPathComponent("Sources/CodexBar/Resources/ko.lproj/Localizable.strings")
let catalog = try #require(NSDictionary(contentsOf: koURL) as? [String: String])
let format = try #require(catalog["%d days of usage data across %d services"])

let rendered = String(
format: format,
locale: Locale(identifier: "ko_KR"),
arguments: [7, 3])

#expect(rendered.contains("7일간"))
#expect(rendered.contains("3개 서비스"))
}
}
15 changes: 12 additions & 3 deletions Tests/CodexBarTests/StatusMenuPersistentRefreshTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,21 @@ struct StatusMenuPersistentRefreshTests {

private func makeController(
settings: SettingsStore,
updater: UpdaterProviding = DisabledUpdaterController()) -> StatusItemController
updater: UpdaterProviding = DisabledUpdaterController(),
account: AccountInfo? = nil) -> StatusItemController
{
let fetcher = UsageFetcher()
let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)
if let account {
store.accountInfoCache[.codex] = UsageStore.AccountInfoCacheEntry(
account: account,
configRevision: settings.configRevision,
expiresAt: .distantFuture)
}
return StatusItemController(
store: store,
settings: settings,
account: fetcher.loadAccountInfo(),
account: account ?? fetcher.loadAccountInfo(),
updater: updater,
preferencesSelection: PreferencesSelection(),
statusBar: .system)
Expand Down Expand Up @@ -287,7 +294,9 @@ struct StatusMenuPersistentRefreshTests {
@Test
func `live subtitle preserves canonical model error filtering`() throws {
let settings = self.makeSettings()
let controller = self.makeController(settings: settings)
let controller = self.makeController(
settings: settings,
account: AccountInfo(email: "test@example.com", plan: "pro"))
controller.store.errors[.codex] = UsageError.noRateLimitsFound.errorDescription
let model = try #require(controller.menuCardModel(for: .codex))
let fallback = MenuCardLiveSubtitle(text: "Fallback", style: .error)
Expand Down