mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-11 12:12:19 -10:00
* feat(web-shell): add Russian locale for the Goal card and Goals dialog The web-shell i18n shipped only en and zh-CN and collapsed every other browser locale (e.g. ru-RU) to en in normalizeLanguage, so a Russian user saw the Goal card and the Goals dialog in English around a Russian objective. Add a partial ru catalog for the goal.*/goals.* keys with Russian pluralisation (ход/хода/ходов, токен/токена/токенов, активная цель/активные цели/активных целей). getTranslator already falls back per key to EN (messages[key] ?? EN[key] ?? key), and the partial catalog relies on that for every other key — the behaviour this locale has been running with as an installer patch. - WEB_SHELL_LANGUAGES / LANGUAGE_LABELS: add 'ru' / 'Русский [ru]' to the settings picker - normalizeLanguage: accept ru / ru-ru / russian / русский - languageSettingToWebShellLanguage: accept the same spellings for the daemon's general.language setting - WebShellProps.language / WebShellTranscriptProps.language: accept the 'ru' and 'ru-ru' raw values - surfaces with their own WebShellLanguage-keyed copy tables (StandaloneAuth, RootErrorFallback, conversation search, desktop relay) reuse the EN copy for ru, same partial-catalog policy - re-measure the transcript entry size budget for the catalog (+4,649 to 1,325,015; ceiling raised to 1,328,000) Part of #13391 * fix(web-shell): goals.dropped count and ru_* language normalization goals.dropped now renders the session count with the correct Russian plural form (it previously dropped the ${count} interpolation and showed a bare phrase). The ru language normalizers match the ru prefix instead of a fixed spelling list, so region-qualified values (ru-RU, ru-UA, ru-KZ, …) and the underscore form resolve to the partial ru catalog instead of falling through to en. Add count and plural pins for goals.dropped and region-form pins for both normalizers. * chore: empty commit to retrigger CI and review * feat(core): Goal verifier verdict inherits the session output language The verifier's 'Last check:' reason is user-visible prose (#13391 part 2), so it should honor the session output-language preference. It was the only goal-related side query still passing skipOutputLanguagePreference: true; removing the flag makes runSideQuery append the preference. The decision enum stays schema-bound - only the reason prose localizes. Also re-measures the merged web-shell transcript entry (1,334,649 chars) and sets its size ceiling to 1_337_000 with the matching budget narrative. * fix(web-shell): drop stray conflict marker left by the main merge * fix(web-shell): accept /language ui ru and derive language listings from WEB_SHELL_LANGUAGES * fix(web-shell): render goals.dropped with the count and the dialog's session term * fix(web-shell): make the desktop relay copy table total over WebShellLanguage * fix(vscode-ide-companion): let the web shell resolve its own UI language * docs(web-shell): document ru in the language prop unions * test(web-shell): pin tokensOfBudget pluralising on the budget * revert(core): move the Goal verifier language change to companion PR #15806 The output-language change for the Goal verifier verdict is split out to its own PR (QwenLM/qwen-code#15806) so the core change gets dedicated review under the small-core-change rule. This PR is now web-shell only. * test(web-shell): pin the /language error and usage copy to the shipped set * fix(web-shell): restore the DesktopRelayControl header truncated in705a3b799The staged hunk for the relay copy-table change cut off the top of the file: the license header, all imports and the DesktopRelayPhase/ DesktopRelayBlocker types were missing from the committed file, leaving it uncompilable (tsc: 63 errors). Restore the header verbatim from the parent commit (c5ff652b5), re-indent the extracted EN_COPY table, and fold the ternary-free useCallback call to the single line prettier wants. The semantic change of705a3b799(en table extracted to EN_COPY, explicit ru: EN_COPY entry, ternary-free COPY[language][key] lookup) is preserved unchanged. * style(web-shell): prettier 3.6.1 formatting for the /language gate and README language row --------- Co-authored-by: Сергей Панченко <[email protected]>
120 lines
3.6 KiB
TypeScript
120 lines
3.6 KiB
TypeScript
import type { CSSProperties } from 'react';
|
|
import type { WebShellLanguage } from '../i18n';
|
|
|
|
interface RootErrorFallbackProps {
|
|
error: Error;
|
|
onRetry: () => void;
|
|
/** Selects the fallback copy. Defaults to English when omitted. */
|
|
language?: WebShellLanguage;
|
|
/**
|
|
* What the retry button does, so its label stays honest. Defaults to
|
|
* 'reset' (an in-place boundary retry); 'reload' is for callers whose
|
|
* retry navigates (e.g. the standalone root boundary reloads the page).
|
|
*/
|
|
retryMode?: 'reset' | 'reload';
|
|
}
|
|
|
|
interface FallbackCopy {
|
|
title: string;
|
|
body: string;
|
|
retry: string;
|
|
reload: string;
|
|
}
|
|
|
|
// This surface renders OUTSIDE the in-app I18nProvider (the boundary wraps the
|
|
// whole App, which owns that provider), so it cannot call useI18n. It carries
|
|
// its own minimal copy instead of pulling the full translation table.
|
|
const EN_COPY: FallbackCopy = {
|
|
title: 'Something went wrong',
|
|
body: 'An unexpected error occurred and this content could not be displayed.',
|
|
retry: 'Try again',
|
|
reload: 'Reload page',
|
|
};
|
|
|
|
const COPY: Record<WebShellLanguage, FallbackCopy> = {
|
|
en: EN_COPY,
|
|
'zh-CN': {
|
|
title: '出了点问题',
|
|
body: '发生意外错误,无法显示此内容。',
|
|
retry: '重试',
|
|
reload: '重新加载',
|
|
},
|
|
// Not translated yet — same partial-catalog policy as the ru table in
|
|
// i18n.tsx (#13391): ru reuses the EN copy.
|
|
ru: EN_COPY,
|
|
};
|
|
|
|
// The boundary wraps the whole App, so this surface renders when the themed
|
|
// App container (which defines --text-primary etc.) never mounted. It therefore
|
|
// cannot use app theme tokens. Inheriting the host's text color keeps it
|
|
// readable on both light and dark hosts; opacity carries the hierarchy and a
|
|
// neutral gray border reads acceptably on either background.
|
|
const containerStyle: CSSProperties = {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: '0.75rem',
|
|
padding: '2rem',
|
|
minHeight: '8rem',
|
|
height: '100%',
|
|
textAlign: 'center',
|
|
color: 'inherit',
|
|
};
|
|
|
|
const titleStyle: CSSProperties = {
|
|
margin: 0,
|
|
fontSize: '1rem',
|
|
fontWeight: 600,
|
|
};
|
|
|
|
const bodyStyle: CSSProperties = {
|
|
margin: 0,
|
|
fontSize: '0.85rem',
|
|
opacity: 0.7,
|
|
maxWidth: '32rem',
|
|
};
|
|
|
|
const buttonStyle: CSSProperties = {
|
|
appearance: 'none',
|
|
cursor: 'pointer',
|
|
padding: '0.4rem 1rem',
|
|
fontSize: '0.85rem',
|
|
borderRadius: '6px',
|
|
border: '1px solid rgba(128, 128, 128, 0.4)',
|
|
background: 'transparent',
|
|
color: 'inherit',
|
|
};
|
|
|
|
/**
|
|
* Last-resort surface for the top-level boundary. Self-contained (no provider,
|
|
* theme-token, or i18n dependency) so it survives even when the whole App tree
|
|
* fails to mount. Offers a retry instead of forcing a host-page reload, which
|
|
* would be hostile in embedded integrations.
|
|
*/
|
|
export function RootErrorFallback({
|
|
error,
|
|
onRetry,
|
|
language = 'en',
|
|
retryMode = 'reset',
|
|
}: RootErrorFallbackProps) {
|
|
const copy = COPY[language] ?? COPY.en;
|
|
return (
|
|
<div role="alert" style={containerStyle} data-web-shell-error>
|
|
<p style={titleStyle}>{copy.title}</p>
|
|
<p style={bodyStyle}>{copy.body}</p>
|
|
{/* Dev-only: end users (and embedded hosts) shouldn't see raw internal
|
|
error text — it stays in console.error for everyone. In the published
|
|
lib build DEV is statically false, so this never ships to consumers. */}
|
|
{import.meta.env.DEV && error.message && (
|
|
<p style={{ ...bodyStyle, fontFamily: 'monospace', opacity: 0.55 }}>
|
|
{error.message}
|
|
</p>
|
|
)}
|
|
<button type="button" style={buttonStyle} onClick={onRetry}>
|
|
{retryMode === 'reload' ? copy.reload : copy.retry}
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|