Dictate in the user's language, not always en-US - #35
Merged
Conversation
The speech helper builds an SFSpeechRecognizer pinned to en-US, so dictation transcribes any other language into nonsense — a Portuguese speaker gets English words that sound vaguely similar. Walk the user's preferred languages and use the first one with an available recognizer, keeping en-US as the fallback. Recognizers the system has no model for report isAvailable == false, so this cannot end up worse than today. Co-Authored-By: Claude <noreply@anthropic.com>
milind-soni
approved these changes
Aug 14, 2026
milind-soni
left a comment
Owner
There was a problem hiding this comment.
Reviewed against current main: locale selection preserves en-US fallback. Swift typecheck, project typecheck, and all 159 tests pass in the combined tree.
guilimasp
added a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 18, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 19, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 20, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 21, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 22, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 23, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 24, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 25, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 26, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 27, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 28, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
github-actions Bot
pushed a commit
to guilimasp/OpenMausBot
that referenced
this pull request
Aug 29, 2026
Every patch had rotted across the ten-version jump the stalled sync hid, so a build would have skipped all seven and quietly shipped an app with none of this fork's behavior. Four are gone because upstream merged them (milind-soni#35 dictation language, milind-soni#36 the connected-apps prompt, milind-soni#37 the screen frame, milind-soni#67 composer drafts) or shipped its own version (milind-soni#68 became upstream's paste chips and file drop). Deleting a patch upstream now carries is the whole point of the mechanism. Three remain and are rewritten against the current code: - 020 box TTL: upstream landed boxErrorMessage and noEnv since, so the retry moves into a createBox helper instead of an inline second call. - 030 dictation: upstream took the language fallback but not the config override, so the patch is now just the {"dictation":{"locale"}} read in front of the preferences it already consults. - 060 pasted images: rewritten around upstream's new composer, whose attachment chips fold into the prompt text and so cannot carry an image. Images stay a separate strip and reach the driver as their own blocks. typecheck and the full suite pass with all three applied (103 files, 997 tests).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
speech-helper.swiftpins the recognizer toen-US, so dictation only works for English speakers — everyone else gets their speech transcribed into English words that sound roughly similar.It now walks
Locale.preferredLanguagesand uses the first locale with an available recognizer, falling back toen-US. A locale the system has no model for reportsisAvailable == false, so this can only match today's behavior or improve on it.One caveat: the system language is not always the language someone dictates in, so a per-user setting would cover that case properly.
pnpm typecheck && pnpm testpass, and the helper compiles withswiftc.