fix(settings): map unknown UI locales to en-US for commit prompts

Portuguese and other interface languages have no commit prompt catalog; sending pt-BR made settings fail to deserialize.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
leokun
2026-09-15 21:31:00 +08:00
co-authored by Cursor
parent c0d5f9a815
commit 127cf8b883
5 changed files with 56 additions and 12 deletions
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { api, pluginText, type CommitSettingsView } from "../../shared/api"; import { api, pluginText, type CommitSettingsView } from "../../shared/api";
import { commitPromptLocale } from "../../i18n/runtime";
import { useI18n } from "../../i18n/store"; import { useI18n } from "../../i18n/store";
import { useAppStore } from "../../shared/store/appStore"; import { useAppStore } from "../../shared/store/appStore";
import { Button } from "../../shared/ui/Button"; import { Button } from "../../shared/ui/Button";
@@ -33,11 +34,12 @@ export function CommitSettingsCard() {
void (async () => { void (async () => {
try { try {
let loaded = await api.commitSettings(locale); let loaded = await api.commitSettings(locale);
if (!loaded.prompt.trim() && loaded.prompt_locale !== locale) { const promptLocale = commitPromptLocale(locale);
if (!loaded.prompt.trim() && loaded.prompt_locale !== promptLocale) {
loaded = await api.setCommitSettings({ loaded = await api.setCommitSettings({
model_id: loaded.model_id, model_id: loaded.model_id,
prompt: "", prompt: "",
prompt_locale: locale, prompt_locale: promptLocale,
}); });
} }
if (active) { if (active) {
@@ -97,7 +99,7 @@ export function CommitSettingsCard() {
return api.setCommitSettings({ return api.setCommitSettings({
model_id: modelId, model_id: modelId,
prompt: normalizedPrompt, prompt: normalizedPrompt,
prompt_locale: locale, prompt_locale: commitPromptLocale(locale),
}); });
}, },
[view, locale], [view, locale],
+6
View File
@@ -3,6 +3,12 @@ import enUS from "./locales/en-US.json";
import ptBR from "./locales/pt-BR.json"; import ptBR from "./locales/pt-BR.json";
export type Locale = "zh-CN" | "en-US" | "pt-BR"; export type Locale = "zh-CN" | "en-US" | "pt-BR";
export type CommitPromptLocale = "zh-CN" | "en-US";
export function commitPromptLocale(locale: Locale): CommitPromptLocale {
return locale === "zh-CN" ? "zh-CN" : "en-US";
}
export type TranslationValue = string | number; export type TranslationValue = string | number;
export type TranslationParams = Readonly<Record<string, TranslationValue>>; export type TranslationParams = Readonly<Record<string, TranslationValue>>;
+2 -2
View File
@@ -1,5 +1,5 @@
import type { AdRuntime } from "../shell/ads/types"; import type { AdRuntime } from "../shell/ads/types";
import type { Locale } from "../i18n/runtime"; import type { CommitPromptLocale, Locale } from "../i18n/runtime";
export type ModelType = "openai" | "anthropic"; export type ModelType = "openai" | "anthropic";
@@ -154,7 +154,7 @@ export interface DesktopSettings {
export interface CommitSettings { export interface CommitSettings {
model_id: string; model_id: string;
prompt: string; prompt: string;
prompt_locale: Locale; prompt_locale: CommitPromptLocale;
} }
export interface CommitSettingsView extends CommitSettings { export interface CommitSettingsView extends CommitSettings {
+6 -5
View File
@@ -149,13 +149,11 @@ pub async fn update_pricing_settings(
} }
fn requested_commit_locale(headers: &HeaderMap) -> CommitPromptLocale { fn requested_commit_locale(headers: &HeaderMap) -> CommitPromptLocale {
match headers headers
.get(header::ACCEPT_LANGUAGE) .get(header::ACCEPT_LANGUAGE)
.and_then(|value| value.to_str().ok()) .and_then(|value| value.to_str().ok())
{ .map(CommitPromptLocale::from_interface_language)
Some(value) if value.eq_ignore_ascii_case("zh-CN") => CommitPromptLocale::ZhCn, .unwrap_or(CommitPromptLocale::EnUs)
_ => CommitPromptLocale::EnUs,
}
} }
#[cfg(test)] #[cfg(test)]
@@ -170,5 +168,8 @@ mod tests {
headers.insert(header::ACCEPT_LANGUAGE, "en-US".parse().unwrap()); headers.insert(header::ACCEPT_LANGUAGE, "en-US".parse().unwrap());
assert_eq!(requested_commit_locale(&headers), CommitPromptLocale::EnUs); assert_eq!(requested_commit_locale(&headers), CommitPromptLocale::EnUs);
headers.insert(header::ACCEPT_LANGUAGE, "pt-BR".parse().unwrap());
assert_eq!(requested_commit_locale(&headers), CommitPromptLocale::EnUs);
} }
} }
+37 -2
View File
@@ -1,5 +1,5 @@
//! Persists application settings. //! Persists application settings.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Deserializer, Serialize};
use crate::Result; use crate::Result;
@@ -105,7 +105,7 @@ impl TabSettings {
} }
} }
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, Serialize)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
pub enum CommitPromptLocale { pub enum CommitPromptLocale {
#[default] #[default]
#[serde(rename = "zh-CN")] #[serde(rename = "zh-CN")]
@@ -115,6 +115,14 @@ pub enum CommitPromptLocale {
} }
impl CommitPromptLocale { impl CommitPromptLocale {
pub fn from_interface_language(value: &str) -> Self {
if value.eq_ignore_ascii_case("zh-CN") {
Self::ZhCn
} else {
Self::EnUs
}
}
pub fn default_prompt(self) -> &'static str { pub fn default_prompt(self) -> &'static str {
match self { match self {
Self::ZhCn => DEFAULT_COMMIT_PROMPT_ZH_CN.trim(), Self::ZhCn => DEFAULT_COMMIT_PROMPT_ZH_CN.trim(),
@@ -123,6 +131,13 @@ impl CommitPromptLocale {
} }
} }
impl<'de> Deserialize<'de> for CommitPromptLocale {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = String::deserialize(deserializer)?;
Ok(Self::from_interface_language(&value))
}
}
/// User preferences for Git commit message generation. /// User preferences for Git commit message generation.
/// ///
/// Empty `model_id` means 直连: forward the original Cursor RPC unchanged. /// Empty `model_id` means 直连: forward the original Cursor RPC unchanged.
@@ -490,6 +505,26 @@ mod tests {
const LEGACY_PROXY_ROW: &str = const LEGACY_PROXY_ROW: &str =
r#"{"mode":"system","address":"","auth_enabled":false,"username":"","password":""}"#; r#"{"mode":"system","address":"","auth_enabled":false,"username":"","password":""}"#;
#[test]
fn commit_prompt_locale_maps_unknown_interface_languages_to_english() {
assert_eq!(
serde_json::from_str::<CommitPromptLocale>(r#""zh-CN""#).unwrap(),
CommitPromptLocale::ZhCn
);
assert_eq!(
serde_json::from_str::<CommitPromptLocale>(r#""en-US""#).unwrap(),
CommitPromptLocale::EnUs
);
assert_eq!(
serde_json::from_str::<CommitPromptLocale>(r#""pt-BR""#).unwrap(),
CommitPromptLocale::EnUs
);
assert_eq!(
serde_json::to_string(&CommitPromptLocale::EnUs).unwrap(),
r#""en-US""#
);
}
#[test] #[test]
fn default_commit_prompt_follows_its_saved_locale() { fn default_commit_prompt_follows_its_saved_locale() {
for (prompt_locale, expected) in [ for (prompt_locale, expected) in [