feat(about): 关于页自动检查更新开关 + 自定义检查间隔

This commit is contained in:
Syngnat
2026-07-22 15:40:46 +08:00
committed by GitHub
12 changed files with 302 additions and 6 deletions

View File

@@ -231,6 +231,14 @@ describe('settings center layout', () => {
expect(appSource).toContain("gridTemplateColumns: 'minmax(0, 1fr) minmax(220px, 260px)'");
expect(appSource).toContain('className="gonavi-about-update-channel"');
expect(appSource).toContain('<Segmented');
expect(appSource).toContain("t('app.about.field.auto_check_updates')");
expect(appSource).toContain("t('app.about.field.auto_check_interval')");
expect(appSource).toContain("t('app.about.version_update.auto_check_hint')");
expect(appSource).toContain("t('app.about.version_update.auto_check_disabled_hint')");
expect(appSource).toContain('className="gonavi-about-auto-check-interval"');
expect(appSource).toContain('checked={autoCheckForUpdates}');
expect(appSource).toContain('setAutoCheckForUpdates(checked)');
expect(appSource).toContain('setAutoCheckForUpdatesIntervalMinutes(Number(value))');
expect(appSource).toContain('maxWidth: 360');
expect(appSource).toContain("alignItems: 'start'");
expect(appSource).toContain("overflowWrap: 'anywhere'");

View File

@@ -40,6 +40,7 @@ import CustomThemeStyleHost, {
type CustomThemeAntTokenSnapshot,
} from './components/theme/CustomThemeStyleHost';
import {
AUTO_CHECK_FOR_UPDATES_INTERVAL_OPTIONS,
DEFAULT_APPEARANCE,
MAX_V2_SIDEBAR_RAIL_SCALE,
MIN_V2_SIDEBAR_RAIL_SCALE,
@@ -713,6 +714,10 @@ function App() {
const setFontSize = useStore(state => state.setFontSize);
const startupFullscreen = useStore(state => state.startupFullscreen);
const setStartupFullscreen = useStore(state => state.setStartupFullscreen);
const autoCheckForUpdates = useStore(state => state.autoCheckForUpdates);
const setAutoCheckForUpdates = useStore(state => state.setAutoCheckForUpdates);
const autoCheckForUpdatesIntervalMinutes = useStore(state => state.autoCheckForUpdatesIntervalMinutes);
const setAutoCheckForUpdatesIntervalMinutes = useStore(state => state.setAutoCheckForUpdatesIntervalMinutes);
const globalProxy = useStore(state => state.globalProxy);
const replaceConnections = useStore(state => state.replaceConnections);
const replaceGlobalProxy = useStore(state => state.replaceGlobalProxy);
@@ -5026,8 +5031,8 @@ function App() {
</div>
<div style={{ borderTop: `1px solid ${dividerColor}`, padding: '18px 24px' }}>
<div style={{ display: 'grid', gap: 8 }}>
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(220px, 260px)', alignItems: 'center', gap: 16 }}>
<div style={{ fontSize: 15, fontWeight: 600, color: overlayTheme.titleText }}>{t('app.about.field.update_channel')}</div>
<div style={{ display: 'grid', gridTemplateColumns: '100px 1fr', alignItems: 'center', gap: 16 }}>
<div style={{ fontSize: 15, fontWeight: 600, color: overlayTheme.titleText, whiteSpace: 'nowrap' }}>{t('app.about.field.update_channel')}</div>
<Segmented
className="gonavi-about-update-channel"
value={updateChannel}
@@ -5044,11 +5049,41 @@ function App() {
|| updateDownloadProgress.status === 'start'
|| updateDownloadProgress.status === 'downloading'
}
block
style={{ width: '100%' }}
style={{ flexShrink: 0 }}
/>
</div>
<div style={{ ...utilityMutedTextStyle, lineHeight: 1.55, maxWidth: 360 }}>{t('app.about.version_update.channel_hint')}</div>
<div style={{ display: 'grid', gridTemplateColumns: '100px 1fr', alignItems: 'center', gap: 16, marginTop: 10 }}>
<div style={{ fontSize: 15, fontWeight: 600, color: overlayTheme.titleText, whiteSpace: 'nowrap' }}>{t('app.about.field.auto_check_updates')}</div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Switch
checked={autoCheckForUpdates}
onChange={(checked) => setAutoCheckForUpdates(checked)}
/>
</div>
</div>
{autoCheckForUpdates ? (
<>
<div style={{ display: 'grid', gridTemplateColumns: '100px 1fr', alignItems: 'center', gap: 16, marginTop: 10 }}>
<div style={{ fontSize: 15, fontWeight: 600, color: overlayTheme.titleText, whiteSpace: 'nowrap' }}>{t('app.about.field.auto_check_interval')}</div>
<Select
className="gonavi-about-auto-check-interval"
value={autoCheckForUpdatesIntervalMinutes}
options={AUTO_CHECK_FOR_UPDATES_INTERVAL_OPTIONS.map((minutes) => ({
value: minutes,
label: minutes >= 60 && minutes % 60 === 0
? t('app.about.auto_check_interval.hours', { hours: minutes / 60 })
: t('app.about.auto_check_interval.minutes', { minutes }),
}))}
onChange={(value) => setAutoCheckForUpdatesIntervalMinutes(Number(value))}
style={{ width: '100%' }}
/>
</div>
<div style={{ ...utilityMutedTextStyle, lineHeight: 1.55, maxWidth: 360 }}>{t('app.about.version_update.auto_check_hint')}</div>
</>
) : (
<div style={{ ...utilityMutedTextStyle, lineHeight: 1.55, maxWidth: 360 }}>{t('app.about.version_update.auto_check_disabled_hint')}</div>
)}
</div>
<div style={{ height: 1, background: dividerColor, margin: '18px 0' }} />
<div style={{ display: 'grid', gap: 13 }}>

View File

@@ -14,12 +14,28 @@ const messageApi = vi.hoisted(() => ({
error: vi.fn(),
}));
const storeApi = vi.hoisted(() => ({
autoCheckForUpdates: true,
autoCheckForUpdatesIntervalMinutes: 30,
}));
vi.mock('../../wailsjs/runtime', () => runtimeApi);
vi.mock('antd', () => ({
message: messageApi,
}));
vi.mock('../store', () => ({
useStore: (selector: (state: {
autoCheckForUpdates: boolean;
autoCheckForUpdatesIntervalMinutes: number;
}) => unknown) =>
selector({
autoCheckForUpdates: storeApi.autoCheckForUpdates,
autoCheckForUpdatesIntervalMinutes: storeApi.autoCheckForUpdatesIntervalMinutes,
}),
}));
type BackendAppMock = {
CheckForUpdates: ReturnType<typeof vi.fn>;
CheckForUpdatesSilently: ReturnType<typeof vi.fn>;
@@ -72,6 +88,8 @@ describe('useAppUpdateManager', () => {
backendApp = createBackendAppMock();
hook = null;
renderer = null;
storeApi.autoCheckForUpdates = true;
storeApi.autoCheckForUpdatesIntervalMinutes = 30;
runtimeApi.EventsOn.mockClear();
messageApi.info.mockReset();
messageApi.success.mockReset();
@@ -104,6 +122,82 @@ describe('useAppUpdateManager', () => {
expect(resolveUpdateInstallAction({ packageType: 'msi', autoRelaunch: false })).toBe('launch-installer');
});
it('schedules silent update checks when auto-check is enabled', async () => {
backendApp.CheckForUpdatesSilently.mockResolvedValue({
success: true,
data: {
hasUpdate: false,
currentVersion: '0.8.1',
latestVersion: '0.8.1',
},
});
renderHook();
await act(async () => {
await vi.advanceTimersByTimeAsync(2000);
});
expect(backendApp.CheckForUpdatesSilently).toHaveBeenCalledTimes(1);
await act(async () => {
await vi.advanceTimersByTimeAsync(30 * 60 * 1000);
});
expect(backendApp.CheckForUpdatesSilently).toHaveBeenCalledTimes(2);
});
it('uses the configured auto-check interval for subsequent silent checks', async () => {
storeApi.autoCheckForUpdatesIntervalMinutes = 15;
backendApp.CheckForUpdatesSilently.mockResolvedValue({
success: true,
data: {
hasUpdate: false,
currentVersion: '0.8.1',
latestVersion: '0.8.1',
},
});
renderHook();
await act(async () => {
await vi.advanceTimersByTimeAsync(2000);
});
expect(backendApp.CheckForUpdatesSilently).toHaveBeenCalledTimes(1);
await act(async () => {
await vi.advanceTimersByTimeAsync(14 * 60 * 1000);
});
expect(backendApp.CheckForUpdatesSilently).toHaveBeenCalledTimes(1);
await act(async () => {
await vi.advanceTimersByTimeAsync(60 * 1000);
});
expect(backendApp.CheckForUpdatesSilently).toHaveBeenCalledTimes(2);
});
it('does not schedule silent update checks when auto-check is disabled', async () => {
storeApi.autoCheckForUpdates = false;
backendApp.CheckForUpdatesSilently.mockResolvedValue({
success: true,
data: {
hasUpdate: false,
currentVersion: '0.8.1',
latestVersion: '0.8.1',
},
});
renderHook();
await act(async () => {
await vi.advanceTimersByTimeAsync(2000);
await vi.advanceTimersByTimeAsync(30 * 60 * 1000);
});
expect(backendApp.CheckForUpdatesSilently).not.toHaveBeenCalled();
expect(backendApp.CheckForUpdates).not.toHaveBeenCalled();
});
it('merges complete MSI download metadata returned by the backend', async () => {
backendApp.CheckForUpdates.mockResolvedValue({
success: true,

View File

@@ -1,6 +1,7 @@
import { useCallback, useEffect, useRef, useState, type MutableRefObject } from 'react';
import { message } from 'antd';
import { EventsOn } from '../../wailsjs/runtime';
import { useStore } from '../store';
import { resolveAboutDisplayVersion } from '../utils/appVersionDisplay';
type Translator = (key: string, params?: Record<string, any>) => string;
@@ -181,6 +182,10 @@ export const useAppUpdateManager = ({
t,
updateCenterBridgeRef,
}: UseAppUpdateManagerOptions) => {
const autoCheckForUpdates = useStore((state) => state.autoCheckForUpdates);
const autoCheckForUpdatesIntervalMinutes = useStore(
(state) => state.autoCheckForUpdatesIntervalMinutes,
);
const updateCheckInFlightRef = useRef(false);
const updateDownloadInFlightRef = useRef(false);
const updateUserDismissedRef = useRef(false);
@@ -686,17 +691,21 @@ export const useAppUpdateManager = ({
}, [loadUpdateChannel]);
useEffect(() => {
if (!autoCheckForUpdates) {
return;
}
const intervalMs = Math.max(1, autoCheckForUpdatesIntervalMinutes) * 60 * 1000;
const startupTimer = window.setTimeout(() => {
void checkForUpdates(true);
}, 2000);
const interval = window.setInterval(() => {
void checkForUpdates(true);
}, 30 * 60 * 1000);
}, intervalMs);
return () => {
window.clearTimeout(startupTimer);
window.clearInterval(interval);
};
}, [checkForUpdates]);
}, [autoCheckForUpdates, autoCheckForUpdatesIntervalMinutes, checkForUpdates]);
useEffect(() => {
let offDownloadProgress: any = null;

View File

@@ -2950,6 +2950,57 @@ describe('store appearance persistence', () => {
expect(reloaded.useStore.getState().startupFullscreen).toBe(true);
});
it('defaults auto-check for updates to true and persists explicit disable', async () => {
const { useStore } = await importStore();
expect(useStore.getState().autoCheckForUpdates).toBe(true);
useStore.getState().setAutoCheckForUpdates(false);
const persisted = JSON.parse(storage.getItem('lite-db-storage') || '{}');
expect(persisted.state.autoCheckForUpdates).toBe(false);
vi.resetModules();
const reloaded = await importStore();
expect(reloaded.useStore.getState().autoCheckForUpdates).toBe(false);
storage.setItem('lite-db-storage', JSON.stringify({
state: {},
version: 17,
}));
vi.resetModules();
const hydrated = await importStore();
expect(hydrated.useStore.getState().autoCheckForUpdates).toBe(true);
});
it('defaults auto-check interval to 30 minutes and sanitizes invalid values', async () => {
const { useStore } = await importStore();
expect(useStore.getState().autoCheckForUpdatesIntervalMinutes).toBe(30);
useStore.getState().setAutoCheckForUpdatesIntervalMinutes(60);
const persisted = JSON.parse(storage.getItem('lite-db-storage') || '{}');
expect(persisted.state.autoCheckForUpdatesIntervalMinutes).toBe(60);
vi.resetModules();
const reloaded = await importStore();
expect(reloaded.useStore.getState().autoCheckForUpdatesIntervalMinutes).toBe(60);
reloaded.useStore.getState().setAutoCheckForUpdatesIntervalMinutes(7);
expect(reloaded.useStore.getState().autoCheckForUpdatesIntervalMinutes).toBe(30);
storage.setItem('lite-db-storage', JSON.stringify({
state: {
autoCheckForUpdatesIntervalMinutes: 99,
},
version: 17,
}));
vi.resetModules();
const hydrated = await importStore();
expect(hydrated.useStore.getState().autoCheckForUpdatesIntervalMinutes).toBe(30);
});
it('persists window state and bounds immediately so Windows reopen keeps maximise or size memory', async () => {
const { useStore } = await importStore();

View File

@@ -192,6 +192,15 @@ const DEFAULT_FONT_SIZE = 14;
const MIN_FONT_SIZE = 12;
const MAX_FONT_SIZE = 20;
const DEFAULT_STARTUP_FULLSCREEN = false;
const DEFAULT_AUTO_CHECK_FOR_UPDATES = true;
/** 自动检查更新间隔(分钟);与关于页 Select 选项保持一致 */
export const AUTO_CHECK_FOR_UPDATES_INTERVAL_OPTIONS = [
15, 30, 60, 120, 360, 720, 1440,
] as const;
const DEFAULT_AUTO_CHECK_FOR_UPDATES_INTERVAL_MINUTES = 30;
const AUTO_CHECK_FOR_UPDATES_INTERVAL_OPTIONS_SET = new Set<number>(
AUTO_CHECK_FOR_UPDATES_INTERVAL_OPTIONS,
);
const LEGACY_DEFAULT_OPACITY = 0.95;
const OPACITY_EPSILON = 1e-6;
const MAX_SIDEBAR_PERSISTED_FILTER_LENGTH = 120;
@@ -1742,6 +1751,10 @@ interface AppState {
uiScale: number;
fontSize: number;
startupFullscreen: boolean;
/** 启动后与定时静默检查更新;默认开启 */
autoCheckForUpdates: boolean;
/** 自动检查更新间隔(分钟),默认 30 */
autoCheckForUpdatesIntervalMinutes: number;
globalProxy: GlobalProxyConfig;
sqlFormatOptions: { keywordCase: "upper" | "lower" };
queryOptions: QueryOptions;
@@ -1909,6 +1922,8 @@ interface AppState {
setUiScale: (scale: number) => void;
setFontSize: (size: number) => void;
setStartupFullscreen: (enabled: boolean) => void;
setAutoCheckForUpdates: (enabled: boolean) => void;
setAutoCheckForUpdatesIntervalMinutes: (minutes: number) => void;
setGlobalProxy: (proxy: Partial<GlobalProxyConfig>) => void;
replaceGlobalProxy: (proxy: Partial<GlobalProxyConfig>) => void;
setSqlFormatOptions: (options: { keywordCase: "upper" | "lower" }) => void;
@@ -2935,6 +2950,23 @@ const sanitizeStartupFullscreen = (value: unknown): boolean => {
return value === true;
};
const sanitizeAutoCheckForUpdates = (value: unknown): boolean => {
return typeof value === "boolean" ? value : DEFAULT_AUTO_CHECK_FOR_UPDATES;
};
const sanitizeAutoCheckForUpdatesIntervalMinutes = (
value: unknown,
): number => {
const minutes = Math.round(Number(value));
if (
Number.isFinite(minutes) &&
AUTO_CHECK_FOR_UPDATES_INTERVAL_OPTIONS_SET.has(minutes)
) {
return minutes;
}
return DEFAULT_AUTO_CHECK_FOR_UPDATES_INTERVAL_MINUTES;
};
const sanitizeUiScale = (value: unknown): number => {
return normalizeFloatInRange(
value,
@@ -3273,6 +3305,8 @@ const PERSISTED_STATE_DEPENDENCY_KEYS = [
"uiScale",
"fontSize",
"startupFullscreen",
"autoCheckForUpdates",
"autoCheckForUpdatesIntervalMinutes",
"aiChatOpenMode",
"aiChatDetachedBoundsMemory",
"globalProxy",
@@ -3325,6 +3359,9 @@ const buildPersistedStateProjection = (
uiScale: state.uiScale,
fontSize: state.fontSize,
startupFullscreen: state.startupFullscreen,
autoCheckForUpdates: state.autoCheckForUpdates,
autoCheckForUpdatesIntervalMinutes:
state.autoCheckForUpdatesIntervalMinutes,
aiChatOpenMode: sanitizeAIChatOpenMode(state.aiChatOpenMode),
aiChatDetachedBoundsMemory: sanitizeAIChatDetachedBoundsMemory(
state.aiChatDetachedBoundsMemory,
@@ -3423,6 +3460,9 @@ export const useStore = create<AppState>()(
uiScale: DEFAULT_UI_SCALE,
fontSize: DEFAULT_FONT_SIZE,
startupFullscreen: DEFAULT_STARTUP_FULLSCREEN,
autoCheckForUpdates: DEFAULT_AUTO_CHECK_FOR_UPDATES,
autoCheckForUpdatesIntervalMinutes:
DEFAULT_AUTO_CHECK_FOR_UPDATES_INTERVAL_MINUTES,
globalProxy: { ...DEFAULT_GLOBAL_PROXY },
sqlFormatOptions: { keywordCase: "upper" },
queryOptions: {
@@ -4917,6 +4957,15 @@ export const useStore = create<AppState>()(
set({ startupFullscreen: nextValue });
writePersistedStatePatch({ startupFullscreen: nextValue });
},
setAutoCheckForUpdates: (enabled) => {
set({ autoCheckForUpdates: sanitizeAutoCheckForUpdates(enabled) });
},
setAutoCheckForUpdatesIntervalMinutes: (minutes) => {
set({
autoCheckForUpdatesIntervalMinutes:
sanitizeAutoCheckForUpdatesIntervalMinutes(minutes),
});
},
setGlobalProxy: (proxy) =>
set((state) => ({
globalProxy: sanitizeGlobalProxy({ ...state.globalProxy, ...proxy }),
@@ -5657,6 +5706,13 @@ export const useStore = create<AppState>()(
nextState.startupFullscreen = sanitizeStartupFullscreen(
state.startupFullscreen,
);
nextState.autoCheckForUpdates = sanitizeAutoCheckForUpdates(
state.autoCheckForUpdates,
);
nextState.autoCheckForUpdatesIntervalMinutes =
sanitizeAutoCheckForUpdatesIntervalMinutes(
state.autoCheckForUpdatesIntervalMinutes,
);
nextState.globalProxy = sanitizeGlobalProxy(state.globalProxy);
nextState.sqlFormatOptions = sanitizeSqlFormatOptions(
state.sqlFormatOptions,
@@ -5779,6 +5835,13 @@ export const useStore = create<AppState>()(
uiScale: sanitizeUiScale(state.uiScale),
fontSize: sanitizeFontSize(state.fontSize),
startupFullscreen: sanitizeStartupFullscreen(state.startupFullscreen),
autoCheckForUpdates: sanitizeAutoCheckForUpdates(
state.autoCheckForUpdates,
),
autoCheckForUpdatesIntervalMinutes:
sanitizeAutoCheckForUpdatesIntervalMinutes(
state.autoCheckForUpdatesIntervalMinutes,
),
globalProxy: sanitizeGlobalProxy(state.globalProxy),
tableSortPreference: sanitizeTableSortPreference(
state.tableSortPreference,

View File

@@ -2448,6 +2448,10 @@
"app.about.download_progress.title_with_version": "Update {{version}} herunterladen",
"app.about.field.author": "Autor",
"app.about.field.community": "Community",
"app.about.auto_check_interval.hours": "{{hours}} Stunden",
"app.about.auto_check_interval.minutes": "{{minutes}} Minuten",
"app.about.field.auto_check_interval": "Prüfintervall",
"app.about.field.auto_check_updates": "Automatisch nach Updates suchen",
"app.about.field.update_channel": "Update-Kanal",
"app.about.field.update_status": "Updatestatus",
"app.about.field.version": "Version",
@@ -2489,6 +2493,8 @@
"app.about.version.release_notes": "Updateinhalt",
"app.about.version.release_time": "Veröffentlichungszeit",
"app.about.version_update.available": "Update verfügbar",
"app.about.version_update.auto_check_disabled_hint": "Wenn deaktiviert, werden Updates nur über die manuelle Aktion „Nach Updates suchen“ geprüft",
"app.about.version_update.auto_check_hint": "Beim Start und im gewählten Intervall im Hintergrund nach Updates suchen",
"app.about.version_update.channel_hint": "Neueste Entwicklungsversion erhalten",
"app.about.version_update.title": "Version und Updates",
"app.about.version_update.up_to_date": "Auf dem neuesten Stand",

View File

@@ -2448,6 +2448,10 @@
"app.about.download_progress.title_with_version": "Download Update {{version}}",
"app.about.field.author": "Author",
"app.about.field.community": "Community",
"app.about.auto_check_interval.hours": "{{hours}} hours",
"app.about.auto_check_interval.minutes": "{{minutes}} minutes",
"app.about.field.auto_check_interval": "Check interval",
"app.about.field.auto_check_updates": "Automatically check for updates",
"app.about.field.update_channel": "Update Channel",
"app.about.field.update_status": "Update Status",
"app.about.field.version": "Version",
@@ -2489,6 +2493,8 @@
"app.about.version.release_notes": "Update Notes",
"app.about.version.release_time": "Release Time",
"app.about.version_update.available": "Update available",
"app.about.version_update.auto_check_disabled_hint": "When off, updates are only checked via the manual Check for Updates action",
"app.about.version_update.auto_check_hint": "Check for updates in the background on startup and at the selected interval",
"app.about.version_update.channel_hint": "Receive the latest development build",
"app.about.version_update.title": "Version and Updates",
"app.about.version_update.up_to_date": "Up to date",

View File

@@ -2448,6 +2448,10 @@
"app.about.download_progress.title_with_version": "更新をダウンロード {{version}}",
"app.about.field.author": "作者",
"app.about.field.community": "コミュニティ",
"app.about.auto_check_interval.hours": "{{hours}} 時間",
"app.about.auto_check_interval.minutes": "{{minutes}} 分",
"app.about.field.auto_check_interval": "チェック間隔",
"app.about.field.auto_check_updates": "更新を自動チェック",
"app.about.field.update_channel": "更新チャネル",
"app.about.field.update_status": "更新状況",
"app.about.field.version": "バージョン",
@@ -2489,6 +2493,8 @@
"app.about.version.release_notes": "更新内容",
"app.about.version.release_time": "公開日時",
"app.about.version_update.available": "利用可能な更新があります",
"app.about.version_update.auto_check_disabled_hint": "オフの場合、手動の「更新を確認」でのみ新バージョンを取得します",
"app.about.version_update.auto_check_hint": "起動後にバックグラウンドで確認し、設定した間隔で繰り返し確認します",
"app.about.version_update.channel_hint": "最新の開発版を受け取る",
"app.about.version_update.title": "バージョンと更新",
"app.about.version_update.up_to_date": "最新です",

View File

@@ -2448,6 +2448,10 @@
"app.about.download_progress.title_with_version": "Скачать обновление {{version}}",
"app.about.field.author": "Автор",
"app.about.field.community": "Сообщество",
"app.about.auto_check_interval.hours": "{{hours}} ч.",
"app.about.auto_check_interval.minutes": "{{minutes}} мин.",
"app.about.field.auto_check_interval": "Интервал проверки",
"app.about.field.auto_check_updates": "Автоматически проверять обновления",
"app.about.field.update_channel": "Канал обновления",
"app.about.field.update_status": "Статус обновления",
"app.about.field.version": "Версия",
@@ -2489,6 +2493,8 @@
"app.about.version.release_notes": "Содержание обновления",
"app.about.version.release_time": "Время выпуска",
"app.about.version_update.available": "Доступно обновление",
"app.about.version_update.auto_check_disabled_hint": "При отключении обновления проверяются только вручную через «Проверить обновления»",
"app.about.version_update.auto_check_hint": "Проверять обновления в фоне при запуске и с выбранным интервалом",
"app.about.version_update.channel_hint": "Получать последнюю dev-версию",
"app.about.version_update.title": "Версия и обновления",
"app.about.version_update.up_to_date": "Актуально",

View File

@@ -2448,6 +2448,10 @@
"app.about.download_progress.title_with_version": "下载更新 {{version}}",
"app.about.field.author": "作者",
"app.about.field.community": "技术圈",
"app.about.auto_check_interval.hours": "{{hours}} 小时",
"app.about.auto_check_interval.minutes": "{{minutes}} 分钟",
"app.about.field.auto_check_interval": "检查间隔",
"app.about.field.auto_check_updates": "自动检查更新",
"app.about.field.update_channel": "更新通道",
"app.about.field.update_status": "更新状态",
"app.about.field.version": "版本",
@@ -2489,6 +2493,8 @@
"app.about.version.release_notes": "更新内容",
"app.about.version.release_time": "发布时间",
"app.about.version_update.available": "有可用更新",
"app.about.version_update.auto_check_disabled_hint": "关闭后仅可通过手动「检查更新」获取新版本",
"app.about.version_update.auto_check_hint": "启动后在后台检查,并按设定间隔重复检查",
"app.about.version_update.channel_hint": "接收最新的开发版本",
"app.about.version_update.title": "版本与更新",
"app.about.version_update.up_to_date": "当前已是最新",

View File

@@ -2448,6 +2448,10 @@
"app.about.download_progress.title_with_version": "下載更新 {{version}}",
"app.about.field.author": "作者",
"app.about.field.community": "技術圈",
"app.about.auto_check_interval.hours": "{{hours}} 小時",
"app.about.auto_check_interval.minutes": "{{minutes}} 分鐘",
"app.about.field.auto_check_interval": "檢查間隔",
"app.about.field.auto_check_updates": "自動檢查更新",
"app.about.field.update_channel": "更新通道",
"app.about.field.update_status": "更新状态",
"app.about.field.version": "版本",
@@ -2489,6 +2493,8 @@
"app.about.version.release_notes": "更新內容",
"app.about.version.release_time": "發布時間",
"app.about.version_update.available": "有可用更新",
"app.about.version_update.auto_check_disabled_hint": "關閉後僅可透過手動「檢查更新」取得新版本",
"app.about.version_update.auto_check_hint": "啟動後在背景檢查,並依設定間隔重複檢查",
"app.about.version_update.channel_hint": "接收最新的開發版本",
"app.about.version_update.title": "版本與更新",
"app.about.version_update.up_to_date": "目前已是最新",