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
2 changes: 2 additions & 0 deletions Resources/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
<string></string>
<key>NSMicrophoneUsageDescription</key>
<string>A program running within cmux would like to use your microphone.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>A program running within cmux would like to use speech recognition.</string>
<key>NSCameraUsageDescription</key>
<string>A program running within cmux would like to use your camera.</string>
<key>NSBluetoothAlwaysUsageDescription</key>
Expand Down
113 changes: 113 additions & 0 deletions Resources/InfoPlist.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,119 @@
}
}
},
"NSSpeechRecognitionUsageDescription": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "A program running within cmux would like to use speech recognition."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "cmux 内で実行中のプログラムが音声認識の使用を求めています。"
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "在 cmux 中运行的程序想要使用语音识别。"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "在 cmux 中執行的程式想要使用語音辨識。"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "cmux 내에서 실행 중인 프로그램이 음성 인식을 사용하려고 합니다."
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "Ein in cmux ausgeführtes Programm möchte Spracherkennung verwenden."
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Un programa en ejecución dentro de cmux desea usar el reconocimiento de voz."
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Un programme s'exécutant dans cmux souhaite utiliser la reconnaissance vocale."
}
},
"it": {
"stringUnit": {
"state": "translated",
"value": "Un programma in esecuzione in cmux desidera utilizzare il riconoscimento vocale."
}
},
"da": {
"stringUnit": {
"state": "translated",
"value": "Et program, der kører i cmux, vil gerne bruge talegenkendelse."
}
},
"pl": {
"stringUnit": {
"state": "translated",
"value": "Program działający w cmux chciałby użyć rozpoznawania mowy."
}
},
"ru": {
"stringUnit": {
"state": "translated",
"value": "Программа, запущенная в cmux, хотела бы использовать распознавание речи."
}
},
"bs": {
"stringUnit": {
"state": "translated",
"value": "Program koji se izvršava unutar cmux želi koristiti prepoznavanje govora."
}
},
"ar": {
"stringUnit": {
"state": "translated",
"value": "يرغب برنامج يعمل داخل cmux في استخدام التعرف على الكلام."
}
},
"nb": {
"stringUnit": {
"state": "translated",
"value": "Et program som kjører i cmux ønsker å bruke talegjenkjenning."
}
},
"pt-BR": {
"stringUnit": {
"state": "translated",
"value": "Um programa em execução no cmux gostaria de usar reconhecimento de fala."
}
},
"th": {
"stringUnit": {
"state": "translated",
"value": "โปรแกรมที่ทำงานภายใน cmux ต้องการใช้การรู้จำเสียงพูด"
}
},
"tr": {
"stringUnit": {
"state": "translated",
"value": "cmux içinde çalışan bir program konuşma tanımayı kullanmak istiyor."
}
}
}
},
Comment on lines +152 to +264
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing translations for all supported locales

The new NSSpeechRecognitionUsageDescription entry is only provided in English, but the catalog already carries translations for NSMicrophoneUsageDescription (the most structurally similar key) in ja, zh-Hans, zh-Hant, ko, de, es, fr, it, da, pl, ru, bs, ar, nb, pt-BR, th, and tr. Users running cmux on a device set to any of those locales will see the raw key or the English fallback instead of a translated permission prompt — which on macOS Ventura+ typically surfaces as an untranslated string in the system privacy dialog. The same applies to NSBluetoothAlwaysUsageDescription (ja) and NSCameraUsageDescription (ja), so at minimum a Japanese translation is required to match the baseline set by every other key in this file.

Rule Used: Flag production user-facing text that is not fully... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +152 to +264
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing translations for all supported locales

The new NSSpeechRecognitionUsageDescription entry is only translated into English, but the catalog already carries 17 other locales for comparable keys like NSMicrophoneUsageDescription: ja, zh-Hans, zh-Hant, ko, de, es, fr, it, da, pl, ru, bs, ar, nb, pt-BR, th, tr. Users running cmux under any of those system languages will be shown the English fallback instead of a localized permission prompt. Per the full-internationalization rule, every locale already present in the touched catalog must be covered by a new entry.

Rule Used: Flag production user-facing text that is not fully... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +152 to +264
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Add full locale coverage for the new usage-description key.

NSSpeechRecognitionUsageDescription is added with only en, but this catalog/project already supports multiple locales. Please add translated entries for each existing locale in this catalog (not placeholders or copied English fallbacks).

As per coding guidelines, “Every new Swift localization key must be backed by a matching Resources/*.xcstrings entry with translated values for every locale already supported by that catalog.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Resources/InfoPlist.xcstrings` around lines 152 - 162, The new InfoPlist
localization key NSSpeechRecognitionUsageDescription currently only has an
English entry; add fully translated entries for every locale already present in
this localization catalog (i.e., mirror the set of locale keys used elsewhere in
this Resources/*.xcstrings catalog), not placeholders or en fallbacks. For each
locale add the same structure as the "en" block (include "stringUnit" with
"state": "translated" and a proper translated "value") so the key is present and
marked translated for all supported locales; ensure the key name
NSSpeechRecognitionUsageDescription matches exactly across entries.

"New $(PRODUCT_NAME) Workspace Here": {
"extractionState": "manual",
"localizations": {
Expand Down
4 changes: 4 additions & 0 deletions cmux.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@
F4100000A1B2C3D4E5F60718 /* PortScannerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4100001A1B2C3D4E5F60718 /* PortScannerTests.swift */; };
A5001270 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = A5001271 /* PostHog */; };
A5001521 /* PostHogAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001520 /* PostHogAnalytics.swift */; };
C0DEF5000000000000000001 /* PrivacyUsageDescriptionBundleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DEF5000000000000000002 /* PrivacyUsageDescriptionBundleTests.swift */; };
C47110010000000000000001 /* ProcessPipeReadCrashRegressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47110010000000000000002 /* ProcessPipeReadCrashRegressionTests.swift */; };
C47110020000000000000001 /* ProcessPipeReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47110020000000000000002 /* ProcessPipeReader.swift */; };
C47110020000000000000003 /* ProcessPipeReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47110020000000000000002 /* ProcessPipeReader.swift */; };
Expand Down Expand Up @@ -977,6 +978,7 @@
A5001541 /* PortScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortScanner.swift; sourceTree = "<group>"; };
F4100001A1B2C3D4E5F60718 /* PortScannerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortScannerTests.swift; sourceTree = "<group>"; };
A5001520 /* PostHogAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogAnalytics.swift; sourceTree = "<group>"; };
C0DEF5000000000000000002 /* PrivacyUsageDescriptionBundleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyUsageDescriptionBundleTests.swift; sourceTree = "<group>"; };
C47110010000000000000002 /* ProcessPipeReadCrashRegressionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessPipeReadCrashRegressionTests.swift; sourceTree = "<group>"; };
C47110020000000000000002 /* ProcessPipeReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessPipeReader.swift; sourceTree = "<group>"; };
A5C0DE0000000000000000B9 /* ProjectBuildSettingsTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Panels/ProjectBuildSettingsTabView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1815,6 +1817,7 @@
D7AB00000000000000000012 /* WorkspaceAdjacentPaneMoveTests.swift */,
D7AB3605C10DEF0000000002 /* WorkspaceCloseTabsContextMenuTests.swift */,
D0B10009A1B2C3D4E5F60001 /* PortalTabDragRoutingTests.swift */,
C0DEF5000000000000000002 /* PrivacyUsageDescriptionBundleTests.swift */,
71F8ED91A4B55D34BE6A0668 /* WorkspaceUnitTests.swift */,
71F8ED92A4B55D34BE6A0668 /* WorkspaceSplitStartupCommandTests.swift */,
EF7347934AB1BD387686EE43 /* WorkspaceActionDispatcherTests.swift */,
Expand Down Expand Up @@ -2699,6 +2702,7 @@
B3575000000000000000000A /* PiVaultAgentPersistenceTests.swift in Sources */,
D0B10008A1B2C3D4E5F60001 /* PortalTabDragRoutingTests.swift in Sources */,
F4100000A1B2C3D4E5F60718 /* PortScannerTests.swift in Sources */,
C0DEF5000000000000000001 /* PrivacyUsageDescriptionBundleTests.swift in Sources */,
C47110010000000000000001 /* ProcessPipeReadCrashRegressionTests.swift in Sources */,
F5410004A1B2C3D4E5F60718 /* RestorableAgentHookProviderHermesTests.swift in Sources */,
F5410000A1B2C3D4E5F60718 /* RestorableAgentHookProviderResumeTests.swift in Sources */,
Expand Down
21 changes: 21 additions & 0 deletions cmuxTests/PrivacyUsageDescriptionBundleTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation
import Testing

#if canImport(cmux_DEV)
@testable import cmux_DEV
#elseif canImport(cmux)
@testable import cmux
#endif

@MainActor
@Suite
struct PrivacyUsageDescriptionBundleTests {
@Test
func appBundleDeclaresSpeechRecognitionUsageDescription() throws {
let usageDescription = try #require(Bundle(for: AppDelegate.self)
.infoDictionary?["NSSpeechRecognitionUsageDescription"] as? String
)

#expect(usageDescription == "A program running within cmux would like to use speech recognition.")
}
}