Skip to content
Open
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ CodexSwitcher.app/
# Swift Package Manager
Package.resolved
Package.repos
.swiftpm/
2 changes: 1 addition & 1 deletion Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@
<key>SUPublicEDKey</key>
<string>7lqmueyvl3O06tlzS0bafDBXiWwB0fkyW0/xScLU/Fo=</string>
<key>SUEnableAutomaticChecks</key>
<true/>
<false/>
</dict>
</plist>
5 changes: 3 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import PackageDescription

let package = Package(
name: "CodexSwitcher",
platforms: [.macOS(.v26)],
defaultLocalization: "en",
platforms: [.macOS(.v15)],
dependencies: [
.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.6.0")
],
Expand All @@ -15,7 +16,7 @@ let package = Package(
],
path: "Sources/CodexSwitcher",
resources: [
.copy("Resources/codex.icns")
.process("Resources")
]
)
]
Expand Down
8 changes: 6 additions & 2 deletions Sources/CodexSwitcher/AddAccountInlineView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ struct AddAccountInlineView: View {
Circle()
.stroke(gw.opacity(0.06), lineWidth: 2)
.frame(width: 52 + pulsePhase * 20, height: 52 + pulsePhase * 20)
.opacity(1 - pulsePhase * 0.8)
.opacity(1 - Double(pulsePhase) * 0.8)

// Inner glass circle
Circle()
Expand Down Expand Up @@ -224,7 +224,11 @@ struct AddAccountInlineView: View {
.font(.system(size: 14, weight: .semibold))
.foregroundStyle(gw.opacity(0.9))

glassButton(Str.close, icon: "xmark") { store.closeAddAccountWindow() }
glassButton(Str.close, icon: "xmark") {
store.closeAddAccountWindow()
store.addingStep = .idle
store.isAddingAccount = false
}
}
}

Expand Down
11 changes: 6 additions & 5 deletions Sources/CodexSwitcher/AppStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,13 @@ final class AppStore: ObservableObject {
}
}

// Consecutive failure gating
// Consecutive failure gating — back off for up to 3 skipped polls, then retry
if successCount == 0 && !credPairs.isEmpty {
consecutiveFetchFailures += 1
if consecutiveFetchFailures >= 3 {
// Back off: skip next few polls
if consecutiveFetchFailures < 6 && consecutiveFetchFailures >= 3 {
return
} else if consecutiveFetchFailures >= 6 {
consecutiveFetchFailures = 0
}
} else {
consecutiveFetchFailures = 0
Expand Down Expand Up @@ -605,7 +606,7 @@ final class AppStore: ObservableObject {
stopAuthWatcher()
closeAddAccountWindow()
notifyProfileChanged()
sendNotification(title: "Hesap eklendi", body: profile.displayName)
sendNotification(title: L("Hesap eklendi", "Account added"), body: profile.displayName)
Task { await fetchAllRateLimits() }
}

Expand Down Expand Up @@ -733,7 +734,7 @@ final class AppStore: ObservableObject {
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let tokens = dict["tokens"] as? [String: Any],
let access = tokens["access_token"] as? String else { return }
pendingProfileEmail = profileManager.extractEmail(from: access) ?? "bilinmeyen"
pendingProfileEmail = profileManager.extractEmail(from: access) ?? "unknown"
addingStep = .confirmProfile
} else {
// External modification detected — verify
Expand Down
13 changes: 8 additions & 5 deletions Sources/CodexSwitcher/L10n.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import Foundation

/// Basit TR/EN yerelleştirme.
/// Önce UserDefaults'taki "appLanguage" key'ine bakar ("tr" / "en" / "system").
/// "system" veya ayarlanmamışsa sistem dilini kullanır.
/// TR/EN localisation. English is the base language; Turkish ships as a standard
/// tr.lproj/Localizable.strings bundle loaded via NSLocalizedString.
/// UserDefaults key "appLanguage": "en" (default) | "tr" | "system"
func L(_ tr: String, _ en: String) -> String {
let stored = UserDefaults.standard.string(forKey: "appLanguage") ?? "system"
let stored = UserDefaults.standard.string(forKey: "appLanguage") ?? "en"
let code: String
if stored == "system" {
code = Locale.current.language.languageCode?.identifier ?? "en"
} else {
code = stored
}
return code == "tr" ? tr : en
guard code == "tr" else { return en }
// Look up Turkish from the bundle; fall back to the hardcoded tr string
// for interpolated strings that can't be static .strings keys.
return NSLocalizedString(en, tableName: "Localizable", bundle: .module, value: tr, comment: "")
}

enum Str {
Expand Down
10 changes: 5 additions & 5 deletions Sources/CodexSwitcher/ProfileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ final class ProfileManager: @unchecked Sendable {
return nil
}

let email = extractEmail(from: accessToken) ?? "bilinmeyen@hesap.com"
let email = extractEmail(from: accessToken) ?? "unknown@account.com"
let profile = Profile(
id: UUID(),
alias: alias,
Expand Down Expand Up @@ -270,13 +270,13 @@ enum SwitcherError: LocalizedError {
var errorDescription: String? {
switch self {
case .missingAuthFile(let email):
return "Auth dosyası bulunamadı: \(email)"
return L("Auth dosyası bulunamadı: \(email)", "Auth file not found: \(email)")
case .noProfilesAvailable:
return "Henüz hesap eklenmedi."
return L("Henüz hesap eklenmedi.", "No accounts added yet.")
case .allProfilesExhausted:
return "Tüm hesapların limiti doldu!"
return L("Tüm hesapların limiti doldu!", "All accounts have reached their limit!")
case .activationFailed(let email):
return "Aktivasyon başarısız: \(email)"
return L("Aktivasyon başarısız: \(email)", "Activation failed: \(email)")
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/CodexSwitcher/RateLimitFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ struct RateLimitInfo: Sendable {
var weeklyResetLabel: String {
guard let date = weeklyResetAt else { return "" }
let fmt = DateFormatter()
fmt.locale = Locale(identifier: "tr_TR")
fmt.locale = Locale(identifier: "en_US_POSIX")
fmt.dateFormat = "d MMM"
return fmt.string(from: date)
}

var fiveHourResetLabel: String {
guard let date = fiveHourResetAt else { return "" }
let fmt = DateFormatter()
fmt.locale = Locale(identifier: "tr_TR")
fmt.locale = Locale(identifier: "en_US_POSIX")
fmt.dateFormat = "HH:mm"
return fmt.string(from: date)
}
Expand Down
70 changes: 70 additions & 0 deletions Sources/CodexSwitcher/Resources/tr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* Str enum */
"Add Account" = "Hesap Ekle";
"Switch Now" = "Şimdi Geç";
"Quit" = "Çıkış";
"Weekly" = "Haftalık";
"5 Hour" = "5 Saat";
"Active Account" = "Aktif Hesap";
"No accounts added" = "Hesap eklenmedi";
"Rename" = "Yeniden Adlandır";
"Delete" = "Sil";
"Cancel" = "İptal";
"Save" = "Kaydet";
"Close" = "Kapat";
"Start" = "Başlat";
"Add New Account" = "Yeni Hesap Ekle";
"Waiting for Login" = "Login Bekleniyor";
"Account Detected!" = "Hesap Algılandı!";
"Account Added!" = "Hesap Eklendi!";
"Alias (optional)" = "Takma ad (opsiyonel)";
"e.g. Work account" = "örn: İş hesabım";
"Terminal will run `codex login`.\nSign in with your new account in the browser." = "Terminal'de `codex login` çalıştırılacak.\nBrowser'da yeni hesabınla giriş yap.";
"Sign in with your account in the browser.\nWill continue automatically when done." = "Browser'da hesabınla giriş yap.\nTamamlanınca otomatik devam eder.";
"Rename Account" = "Hesap Adını Değiştir";
"All accounts exhausted!" = "Tüm hesaplar doldu!";
"Switch anyway" = "Yine de geç";
"last" = "son";
"usage unknown" = "kullanım bilinmiyor";
"Resets" = "Sıfırlanma";
"History" = "Geçmiş";
"No switches yet" = "Henüz geçiş yok";
"Dark" = "Koyu";
"Light" = "Açık";
"Hide" = "Gizle";
"Show" = "Göster";
"Auto" = "Oto";
"Back" = "Geri";
"Limit reset" = "Limit sıfırlandı";
"is ready to use again" = "kullanıma hazır";

/* MenuContentView */
"Manual override" = "Manuel override";
"Chart" = "Grafik";
"Re-login" = "Girişi Yenile";
"Update" = "Güncelle";
"Reset" = "Sıfırla";
"No usage data in the last 7 days" = "Son 7 günde kullanım verisi yok";
"Last 7 days — Token usage" = "Son 7 gün — Token kullanımı";

/* AppStore */
"Account" = "Hesap";
"Limit warning" = "Limit uyarısı";
"Manual switch" = "Manuel geçiş";
"Will resume when limits reset." = "Limitler sıfırlanınca devam eder.";
"Manual selection" = "Manuel seçim";
"Switch failed" = "Geçiş başarısız";
"Account verification failed. Please try again." = "Hesap doğrulanamadı. Lütfen tekrar deneyin.";
"Account switched" = "Hesap değiştirildi";
"Account Switched" = "Hesap değiştirildi";
"Codex is restarting. New account is now active." = "Codex yeniden başlatılıyor. Yeni hesap aktif.";
"Limit reached" = "Limit doldu";
"Account added" = "Hesap eklendi";
"Re-login successful" = "Giriş yenilendi";
"Wrong account" = "Hatalı hesap";
"A different account was detected. Please try again." = "Farklı bir hesaba giriş yapıldı. Tekrar deneyin.";
"Auth issue" = "Auth sorunu";
"Auth file corrupted. You may need to re-login to your accounts." = "Auth dosyası bozuldu. Hesapları yeniden giriş yapmanız gerekebilir.";

/* ProfileManager errors */
"No accounts added yet." = "Henüz hesap eklenmedi.";
"All accounts have reached their limit!" = "Tüm hesapların limiti doldu!";
7 changes: 7 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ cp "${BUILD_DIR}/${APP_NAME}" "${APP_BUNDLE}/Contents/MacOS/"
cp "Info.plist" "${APP_BUNDLE}/Contents/"
cp "Sources/CodexSwitcher/Resources/AppIcon.icns" "${APP_BUNDLE}/Contents/Resources/"

# Bundle Sparkle framework
mkdir -p "${APP_BUNDLE}/Contents/Frameworks"
cp -R "${BUILD_DIR}/Sparkle.framework" "${APP_BUNDLE}/Contents/Frameworks/"

# Fix rpath so the binary finds Sparkle in Contents/Frameworks
install_name_tool -add_rpath "@executable_path/../Frameworks" "${APP_BUNDLE}/Contents/MacOS/${APP_NAME}" 2>/dev/null || true

# Ad-hoc sign — Gatekeeper "damaged" hatasını önler
codesign --force --deep --sign - "${APP_BUNDLE}"

Expand Down