|
|
@@ -504,6 +504,7 @@ final class ViewController: NSViewController {
|
|
|
private weak var schedulePageCardsScrollView: NSScrollView?
|
|
|
private weak var settingsPagePrimaryLanguagePopup: NSPopUpButton?
|
|
|
private weak var settingsPageSecondaryLanguagePopup: NSPopUpButton?
|
|
|
+ private weak var settingsPageAppLanguagePopup: NSPopUpButton?
|
|
|
|
|
|
// MARK: - Calendar page (custom month UI)
|
|
|
private var calendarPageMonthAnchor: Date = Calendar.current.startOfDay(for: Date())
|
|
|
@@ -4532,7 +4533,10 @@ private extension ViewController {
|
|
|
let darkModeRow = makeSettingsDarkModeRow()
|
|
|
stack.addArrangedSubview(darkModeRow)
|
|
|
darkModeRow.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
|
- stack.setCustomSpacing(24, after: darkModeRow)
|
|
|
+ let appLanguageRow = makeSettingsAppLanguageRow()
|
|
|
+ stack.addArrangedSubview(appLanguageRow)
|
|
|
+ appLanguageRow.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
|
+ stack.setCustomSpacing(24, after: appLanguageRow)
|
|
|
|
|
|
let aiCompanionTitle = textLabel("AI Companion".localized, font: typography.joinWithURLTitle, color: palette.textPrimary)
|
|
|
stack.addArrangedSubview(aiCompanionTitle)
|
|
|
@@ -4648,6 +4652,44 @@ private extension ViewController {
|
|
|
return row
|
|
|
}
|
|
|
|
|
|
+ private func makeSettingsAppLanguageRow() -> NSView {
|
|
|
+ let row = roundedContainer(cornerRadius: 10, color: palette.inputBackground)
|
|
|
+ row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ row.heightAnchor.constraint(equalToConstant: 72).isActive = true
|
|
|
+ styleSurface(row, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
|
+
|
|
|
+ let titleLabel = textLabel("Language".localized, font: NSFont.systemFont(ofSize: 13, weight: .semibold), color: palette.textPrimary)
|
|
|
+ let popup = NSPopUpButton(frame: .zero, pullsDown: false)
|
|
|
+ popup.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ popup.target = self
|
|
|
+ popup.action = #selector(settingsPageAppLanguageChanged(_:))
|
|
|
+
|
|
|
+ popup.addItem(withTitle: "System Language".localized)
|
|
|
+ popup.lastItem?.representedObject = ""
|
|
|
+ for code in AppLanguageManager.availableLanguageCodes {
|
|
|
+ popup.addItem(withTitle: AppLanguageManager.displayName(for: code))
|
|
|
+ popup.lastItem?.representedObject = code
|
|
|
+ }
|
|
|
+
|
|
|
+ let selectedCode = AppLanguageManager.selectedLanguageCode ?? ""
|
|
|
+ selectSettingsPageLanguage(identifier: selectedCode, in: popup)
|
|
|
+ settingsPageAppLanguagePopup = popup
|
|
|
+
|
|
|
+ row.addSubview(titleLabel)
|
|
|
+ row.addSubview(popup)
|
|
|
+ NSLayoutConstraint.activate([
|
|
|
+ titleLabel.leadingAnchor.constraint(equalTo: row.leadingAnchor, constant: 14),
|
|
|
+ titleLabel.topAnchor.constraint(equalTo: row.topAnchor, constant: 10),
|
|
|
+ titleLabel.trailingAnchor.constraint(equalTo: row.trailingAnchor, constant: -14),
|
|
|
+
|
|
|
+ popup.leadingAnchor.constraint(equalTo: row.leadingAnchor, constant: 14),
|
|
|
+ popup.trailingAnchor.constraint(equalTo: row.trailingAnchor, constant: -14),
|
|
|
+ popup.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8),
|
|
|
+ popup.heightAnchor.constraint(equalToConstant: 28)
|
|
|
+ ])
|
|
|
+ return row
|
|
|
+ }
|
|
|
+
|
|
|
private func makeSettingsSpeechLanguageRow(title: String, isPrimary: Bool) -> NSView {
|
|
|
let row = roundedContainer(cornerRadius: 10, color: palette.inputBackground)
|
|
|
row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -4710,6 +4752,13 @@ private extension ViewController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ @objc private func settingsPageAppLanguageChanged(_ sender: NSPopUpButton) {
|
|
|
+ let raw = sender.selectedItem?.representedObject as? String ?? ""
|
|
|
+ let code: String? = raw.isEmpty ? nil : raw
|
|
|
+ AppLanguageManager.setLanguage(code: code)
|
|
|
+ reloadTheme()
|
|
|
+ }
|
|
|
+
|
|
|
@objc private func settingsPageSpeechLanguageChanged(_ sender: NSPopUpButton) {
|
|
|
guard let primary = settingsPagePrimaryLanguagePopup?.selectedItem?.representedObject as? String,
|
|
|
primary.isEmpty == false else { return }
|