From a2f8ba72a2eb257a0063a889c1e3a426f99e5a0c Mon Sep 17 00:00:00 2001 From: lucaronin Date: Sun, 26 Apr 2026 19:37:21 +0200 Subject: [PATCH] fix: complete app localization pass --- src/App.test.tsx | 26 + src/App.tsx | 20 +- src/components/AddPropertyForm.tsx | 29 +- src/components/AppLoadingSkeleton.tsx | 8 +- src/components/ArchivedNoteBanner.tsx | 10 +- src/components/BreadcrumbBar.tsx | 205 +++++--- src/components/CommandPalette.tsx | 6 +- src/components/ConflictNoteBanner.tsx | 14 +- .../DynamicPropertiesPanel.test.tsx | 13 + src/components/DynamicPropertiesPanel.tsx | 17 +- src/components/Editor.css | 11 +- src/components/Editor.tsx | 16 +- src/components/EditorRightPanel.tsx | 4 + src/components/FolderTree.tsx | 15 +- src/components/Inspector.tsx | 21 +- src/components/PulseView.tsx | 85 +-- src/components/RawEditorView.tsx | 6 +- src/components/Sidebar.tsx | 260 +++++++--- src/components/SidebarParts.tsx | 27 +- src/components/SortDropdown.tsx | 47 +- src/components/StatusBar.tsx | 135 +++-- src/components/TypeSelector.tsx | 34 +- src/components/UpdateBanner.test.tsx | 17 +- src/components/UpdateBanner.tsx | 34 +- .../editor-content/EditorContentLayout.tsx | 27 +- .../editor-content/useEditorContentModel.ts | 2 + .../folder-tree/FolderContextMenu.tsx | 7 +- src/components/folder-tree/FolderItemRow.tsx | 13 +- src/components/folder-tree/FolderTreeRow.tsx | 14 +- src/components/inspector/InspectorChrome.tsx | 26 +- src/components/inspector/NoteInfoPanel.tsx | 21 +- .../inspector/RelationshipsPanel.tsx | 62 ++- src/components/note-list/FilterPills.tsx | 14 +- src/components/note-list/InboxFilterPills.tsx | 16 +- .../note-list/ListPropertiesPopover.tsx | 25 +- .../note-list/NoteListChangesMenu.tsx | 128 +++-- .../note-list/NoteListChangesMenuView.tsx | 105 ++++ src/components/note-list/NoteListHeader.tsx | 22 +- src/components/note-list/NoteListLayout.tsx | 1 + src/components/note-list/NoteListViews.tsx | 2 +- .../note-list/RelationshipGroupSection.tsx | 6 +- src/components/note-list/useNoteListModel.tsx | 5 +- src/components/sidebar/FavoritesSection.tsx | 5 +- src/components/sidebar/SidebarSections.tsx | 35 +- src/components/sidebar/SidebarTopNav.tsx | 9 +- src/components/sidebar/SidebarViewItem.tsx | 9 +- src/components/status-bar/AiAgentsBadge.tsx | 43 +- src/components/status-bar/StatusBarBadges.tsx | 482 ++++++++++++------ .../status-bar/StatusBarSections.tsx | 78 +-- src/components/status-bar/VaultMenu.tsx | 36 +- src/hooks/commands/localizeCommands.ts | 157 ++++++ src/hooks/useCodeMirror.ts | 8 +- src/hooks/useCommandRegistry.ts | 182 ++++--- src/lib/i18n.test.ts | 9 +- src/lib/i18n.ts | 201 +------- src/lib/locales/en.ts | 385 ++++++++++++++ src/lib/locales/zh-Hans.ts | 387 ++++++++++++++ 57 files changed, 2566 insertions(+), 1016 deletions(-) create mode 100644 src/components/note-list/NoteListChangesMenuView.tsx create mode 100644 src/hooks/commands/localizeCommands.ts create mode 100644 src/lib/locales/en.ts create mode 100644 src/lib/locales/zh-Hans.ts diff --git a/src/App.test.tsx b/src/App.test.tsx index 81e0d472..d3769f15 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -1015,6 +1015,32 @@ describe('App', () => { }) }) + it('clears the Git setup dialog when switching to a Git-enabled vault', async () => { + mockCommandResults.load_vault_list = { + vaults: [ + { label: 'Missing Git', path: '/work' }, + { label: 'Git Vault', path: '/vault-2' }, + ], + active_vault: '/work', + hidden_defaults: [], + } + mockCommandResults.is_git_repo = ({ vaultPath }: { vaultPath?: string } = {}) => vaultPath === '/vault-2' + + render() + + expect(await screen.findByText('Enable Git for this vault?')).toBeInTheDocument() + + fireEvent.click(screen.getByTestId('status-vault-trigger')) + fireEvent.click(screen.getByTestId('vault-menu-item-Git Vault')) + + await waitFor(() => { + expect(screen.getByTestId('status-vault-trigger')).toHaveTextContent('Git Vault') + }) + await waitFor(() => { + expect(screen.queryByText('Enable Git for this vault?')).not.toBeInTheDocument() + }) + }) + it('Cmd+1 hides sidebar and note list (editor-only mode)', async () => { render() await waitFor(() => { diff --git a/src/App.tsx b/src/App.tsx index 85a453ef..fe2fbf8e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -341,9 +341,15 @@ function App() { setShowGitSetupDialog(true) }, [gitRepoState, noteWindowParams, resolvedPath]) + useEffect(() => { + if (gitRepoState === 'missing') return + setShowGitSetupDialog(false) + }, [gitRepoState]) + const openGitSetupDialog = useCallback(() => { + if (gitRepoState !== 'missing') return setShowGitSetupDialog(true) - }, []) + }, [gitRepoState]) const dismissGitSetupDialog = useCallback(() => { dismissedGitSetupPathRef.current = resolvedPath @@ -893,6 +899,7 @@ function App() { }) const suggestedCommitMessage = useMemo(() => generateCommitMessage(vault.modifiedFiles), [vault.modifiedFiles]) const isGitVault = gitRepoState !== 'missing' + const shouldShowGitSetupDialog = !noteWindowParams && gitRepoState === 'missing' && showGitSetupDialog const modifiedFilesSignature = useMemo( () => vault.modifiedFiles.map((file) => `${file.relativePath}:${file.status}`).sort().join('|'), [vault.modifiedFiles], @@ -1505,7 +1512,7 @@ function App() { {sidebarVisible && ( <>
- +
@@ -1514,7 +1521,7 @@ function App() { <>
{effectiveSelection.kind === 'filter' && effectiveSelection.filter === 'pulse' ? ( - handleSetViewMode('all')} /> + handleSetViewMode('all')} locale={appLocale} /> ) : ( )} @@ -1579,13 +1586,14 @@ function App() { onKeepMine={conflictFlow.handleKeepMine} onKeepTheirs={conflictFlow.handleKeepTheirs} flushPendingRawContentRef={flushPendingRawContentRef} + locale={appLocale} />
- + - handleSetSelection({ kind: 'filter', filter: 'changes' })} onClickPulse={() => handleSetSelection({ kind: 'filter', filter: 'pulse' })} onCommitPush={handleCommitPush} onInitializeGit={openGitSetupDialog} isOffline={networkStatus.isOffline} isGitVault={isGitVault} syncStatus={autoSync.syncStatus} lastSyncTime={autoSync.lastSyncTime} conflictCount={autoSync.conflictFiles.length} remoteStatus={autoSync.remoteStatus} onTriggerSync={autoSync.triggerSync} onPullAndPush={autoSync.pullAndPush} onOpenConflictResolver={conflictFlow.handleOpenConflictResolver} zoomLevel={zoom.zoomLevel} themeMode={documentThemeMode} onZoomReset={zoom.zoomReset} onToggleThemeMode={settingsLoaded ? handleToggleThemeMode : undefined} buildNumber={buildNumber} onCheckForUpdates={handleCheckForUpdates} onRemoveVault={vaultSwitcher.removeVault} mcpStatus={mcpStatus} onInstallMcp={openMcpSetupDialog} aiAgentsStatus={aiAgentsStatus} vaultAiGuidanceStatus={vaultAiGuidanceStatus} defaultAiAgent={aiAgentPreferences.defaultAiAgent} onSetDefaultAiAgent={aiAgentPreferences.setDefaultAiAgent} onRestoreVaultAiGuidance={() => { void restoreVaultAiGuidance() }} /> - + handleSetSelection({ kind: 'filter', filter: 'changes' })} onClickPulse={() => handleSetSelection({ kind: 'filter', filter: 'pulse' })} onCommitPush={handleCommitPush} onInitializeGit={openGitSetupDialog} isOffline={networkStatus.isOffline} isGitVault={isGitVault} syncStatus={autoSync.syncStatus} lastSyncTime={autoSync.lastSyncTime} conflictCount={autoSync.conflictFiles.length} remoteStatus={autoSync.remoteStatus} onTriggerSync={autoSync.triggerSync} onPullAndPush={autoSync.pullAndPush} onOpenConflictResolver={conflictFlow.handleOpenConflictResolver} zoomLevel={zoom.zoomLevel} themeMode={documentThemeMode} onZoomReset={zoom.zoomReset} onToggleThemeMode={settingsLoaded ? handleToggleThemeMode : undefined} buildNumber={buildNumber} onCheckForUpdates={handleCheckForUpdates} onRemoveVault={vaultSwitcher.removeVault} mcpStatus={mcpStatus} onInstallMcp={openMcpSetupDialog} aiAgentsStatus={aiAgentsStatus} vaultAiGuidanceStatus={vaultAiGuidanceStatus} defaultAiAgent={aiAgentPreferences.defaultAiAgent} onSetDefaultAiAgent={aiAgentPreferences.setDefaultAiAgent} onRestoreVaultAiGuidance={() => { void restoreVaultAiGuidance() }} locale={appLocale} /> + setToastMessage(null)} /> diff --git a/src/components/AddPropertyForm.tsx b/src/components/AddPropertyForm.tsx index 6d2f38bb..4aa87982 100644 --- a/src/components/AddPropertyForm.tsx +++ b/src/components/AddPropertyForm.tsx @@ -12,6 +12,7 @@ import { } from '../utils/propertyTypes' import { StatusPill, StatusDropdown } from './StatusDropdown' import { DISPLAY_MODE_OPTIONS, DISPLAY_MODE_ICONS } from '../utils/propertyTypes' +import { translate, type AppLocale } from '../lib/i18n' function parseDateValue(value: string): Date | undefined { const iso = toISODate(value) @@ -39,7 +40,7 @@ function canSubmitProperty({ key, value, displayMode }: { key: string; value: st return displayMode !== 'number' || isValidNumberValue(value) } -function AddBooleanInput({ value, onChange }: { value: string; onChange: (v: string) => void }) { +function AddBooleanInput({ value, locale, onChange }: { value: string; locale: AppLocale; onChange: (v: string) => void }) { const boolVal = value.toLowerCase() === 'true' return ( ) } -function AddDateInput({ value, onChange }: { value: string; onChange: (v: string) => void }) { +function AddDateInput({ value, locale, onChange }: { value: string; locale: AppLocale; onChange: (v: string) => void }) { const selectedDate = value ? parseDateValue(value) : undefined const formatted = value ? formatDateValue(value) : '' return ( @@ -64,7 +65,7 @@ function AddDateInput({ value, onChange }: { value: string; onChange: (v: string > - {formatted || 'Pick a date\u2026'} + {formatted || translate(locale, 'inspector.properties.pickDate')} @@ -122,15 +123,16 @@ function AddNumberInput({ value, onChange, onKeyDown }: { ) } -function AddPropertyValueInput({ displayMode, value, onChange, onKeyDown, vaultStatuses }: { +function AddPropertyValueInput({ displayMode, value, onChange, onKeyDown, vaultStatuses, locale }: { displayMode: PropertyDisplayMode; value: string; onChange: (v: string) => void onKeyDown: (e: React.KeyboardEvent) => void; vaultStatuses: string[] + locale: AppLocale }) { switch (displayMode) { case 'number': return - case 'boolean': return - case 'date': return + case 'boolean': return + case 'date': return case 'status': return case 'tags': return ( ) default: return ( - onChange(e.target.value)} onKeyDown={onKeyDown} /> ) } } -export function AddPropertyForm({ onAdd, onCancel, vaultStatuses }: { +export function AddPropertyForm({ onAdd, onCancel, vaultStatuses, locale = 'en' }: { onAdd: (key: string, value: string, displayMode: PropertyDisplayMode) => void; onCancel: () => void vaultStatuses: string[] + locale?: AppLocale }) { const [newKey, setNewKey] = useState('') const [newValue, setNewValue] = useState('') @@ -169,7 +172,7 @@ export function AddPropertyForm({ onAdd, onCancel, vaultStatuses }: {
setNewKey(e.target.value)} onKeyDown={handleKeyDown} autoFocus /> - + -
diff --git a/src/components/AppLoadingSkeleton.tsx b/src/components/AppLoadingSkeleton.tsx index f24af454..386f6761 100644 --- a/src/components/AppLoadingSkeleton.tsx +++ b/src/components/AppLoadingSkeleton.tsx @@ -125,13 +125,7 @@ function SidebarGroupSkeleton({ rows, count }: { rows: string[]; count?: string function SidebarSkeleton() { return (