feat: add Claude Code changelog link to version notifiers (#820)

* feat: add Claude Code changelog link to version notifiers

Add link to Claude Code Changelog in both:
- Claude Code CLI status badge popover
- App Update Notification dialog

The link opens https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md
in external browser, allowing users to check what's new in Claude Code.

Also converts AppUpdateNotification to use i18n translations.

Fixes #817

* refactor: improve AppUpdateNotification code quality

- Extract CLAUDE_CODE_CHANGELOG_URL to named constant
- Remove unused "common" namespace from useTranslation hook

---------

Co-authored-by: StillKnotKnown <stillknotknown@users.noreply.github.com>
This commit is contained in:
StillKnotKnown
2026-01-08 16:01:16 +02:00
committed by GitHub
parent cbb1cb8154
commit ada91fb195
6 changed files with 230 additions and 141 deletions
@@ -1,24 +1,20 @@
import { useState, useEffect, useMemo } from 'react';
import {
Download,
RefreshCw,
CheckCircle2,
AlertCircle
} from 'lucide-react';
import { Button } from './ui/button';
import { Progress } from './ui/progress';
import { useState, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Download, RefreshCw, CheckCircle2, AlertCircle, ExternalLink } from "lucide-react";
import { Button } from "./ui/button";
import { Progress } from "./ui/progress";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle
} from './ui/dialog';
import type {
AppUpdateAvailableEvent,
AppUpdateProgress
} from '../../shared/types';
DialogTitle,
} from "./ui/dialog";
import type { AppUpdateAvailableEvent, AppUpdateProgress } from "../../shared/types";
const CLAUDE_CODE_CHANGELOG_URL =
"https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md";
/**
* Simple markdown renderer for release notes
@@ -28,23 +24,32 @@ function ReleaseNotesRenderer({ markdown }: { markdown: string }) {
const html = useMemo(() => {
const result = markdown
// Escape HTML
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
// Headers (### Header -> <h3>)
.replace(/^### (.+)$/gm, '<h4 class="text-sm font-semibold text-foreground mt-3 mb-1.5 first:mt-0">$1</h4>')
.replace(/^## (.+)$/gm, '<h3 class="text-sm font-semibold text-foreground mt-3 mb-1.5 first:mt-0">$1</h3>')
.replace(
/^### (.+)$/gm,
'<h4 class="text-sm font-semibold text-foreground mt-3 mb-1.5 first:mt-0">$1</h4>'
)
.replace(
/^## (.+)$/gm,
'<h3 class="text-sm font-semibold text-foreground mt-3 mb-1.5 first:mt-0">$1</h3>'
)
// Bold (**text** -> <strong>)
.replace(/\*\*([^*]+)\*\*/g, '<strong class="text-foreground font-medium">$1</strong>')
// Inline code (`code` -> <code>)
.replace(/`([^`]+)`/g, '<code class="px-1 py-0.5 bg-muted rounded text-xs">$1</code>')
// List items (- item -> <li>)
.replace(/^- (.+)$/gm, '<li class="ml-4 text-muted-foreground before:content-[\'•\'] before:mr-2 before:text-muted-foreground/60">$1</li>')
.replace(
/^- (.+)$/gm,
"<li class=\"ml-4 text-muted-foreground before:content-['•'] before:mr-2 before:text-muted-foreground/60\">$1</li>"
)
// Wrap consecutive list items
.replace(/(<li[^>]*>.*?<\/li>\n?)+/g, '<ul class="space-y-1 my-2">$&</ul>')
// Line breaks for remaining lines
.replace(/\n\n/g, '<div class="h-2"></div>')
.replace(/\n/g, '<br/>');
.replace(/\n/g, "<br/>");
return result;
}, [markdown]);
@@ -62,6 +67,7 @@ function ReleaseNotesRenderer({ markdown }: { markdown: string }) {
* Shows when a new app version is available and handles download/install workflow
*/
export function AppUpdateNotification() {
const { t } = useTranslation(["dialogs"]);
const [isOpen, setIsOpen] = useState(false);
const [updateInfo, setUpdateInfo] = useState<AppUpdateAvailableEvent | null>(null);
const [downloadProgress, setDownloadProgress] = useState<AppUpdateProgress | null>(null);
@@ -109,12 +115,14 @@ export function AppUpdateNotification() {
try {
const result = await window.electronAPI.downloadAppUpdate();
if (!result.success) {
setDownloadError(result.error || 'Failed to download update');
setDownloadError(
result.error || t("dialogs:appUpdate.downloadError", "Failed to download update")
);
setIsDownloading(false);
}
} catch (error) {
console.error('Failed to download app update:', error);
setDownloadError('Failed to download update');
console.error("Failed to download app update:", error);
setDownloadError(t("dialogs:appUpdate.downloadError", "Failed to download update"));
setIsDownloading(false);
}
};
@@ -137,10 +145,13 @@ export function AppUpdateNotification() {
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Download className="h-5 w-5" />
App Update Available
{t("dialogs:appUpdate.title", "App Update Available")}
</DialogTitle>
<DialogDescription>
A new version of Auto Claude is ready to download
{t(
"dialogs:appUpdate.description",
"A new version of Auto Claude is ready to download"
)}
</DialogDescription>
</DialogHeader>
@@ -150,14 +161,13 @@ export function AppUpdateNotification() {
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider mb-1">
New Version
</p>
<p className="text-base font-medium text-foreground">
{updateInfo.version}
{t("dialogs:appUpdate.newVersion", "New Version")}
</p>
<p className="text-base font-medium text-foreground">{updateInfo.version}</p>
{updateInfo.releaseDate && (
<p className="text-xs text-muted-foreground mt-1">
Released {new Date(updateInfo.releaseDate).toLocaleDateString()}
{t("dialogs:appUpdate.released", "Released")}{" "}
{new Date(updateInfo.releaseDate).toLocaleDateString()}
</p>
)}
</div>
@@ -178,18 +188,36 @@ export function AppUpdateNotification() {
</div>
)}
{/* Claude Code Changelog Link */}
<Button
variant="link"
size="sm"
className="w-full text-xs text-muted-foreground gap-1"
onClick={() => window.electronAPI?.openExternal?.(CLAUDE_CODE_CHANGELOG_URL)}
aria-label={t(
"dialogs:appUpdate.claudeCodeChangelogAriaLabel",
"View Claude Code Changelog (opens in new window)"
)}
>
{t("dialogs:appUpdate.claudeCodeChangelog", "View Claude Code Changelog")}
<ExternalLink className="h-3 w-3" aria-hidden="true" />
</Button>
{/* Download Progress */}
{isDownloading && downloadProgress && (
<div className="space-y-2">
<div className="flex items-center justify-between text-sm">
<span className="text-muted-foreground">Downloading...</span>
<span className="text-muted-foreground">
{t("dialogs:appUpdate.downloading", "Downloading...")}
</span>
<span className="text-foreground font-medium">
{Math.round(downloadProgress.percent)}%
</span>
</div>
<Progress value={downloadProgress.percent} className="h-2" />
<p className="text-xs text-muted-foreground text-right">
{(downloadProgress.transferred / 1024 / 1024).toFixed(2)} MB / {(downloadProgress.total / 1024 / 1024).toFixed(2)} MB
{(downloadProgress.transferred / 1024 / 1024).toFixed(2)} MB /{" "}
{(downloadProgress.total / 1024 / 1024).toFixed(2)} MB
</p>
</div>
)}
@@ -206,39 +234,39 @@ export function AppUpdateNotification() {
{isDownloaded && (
<div className="flex items-center gap-3 text-sm text-success bg-success/10 border border-success/30 rounded-lg p-3">
<CheckCircle2 className="h-5 w-5 shrink-0" />
<span>Update downloaded successfully! Click Install to restart and apply the update.</span>
<span>
{t(
"dialogs:appUpdate.updateDownloaded",
"Update downloaded successfully! Click Install to restart and apply the update."
)}
</span>
</div>
)}
</div>
<DialogFooter className="flex flex-col sm:flex-row gap-3">
<Button
variant="outline"
onClick={handleDismiss}
disabled={isDownloading}
>
{isDownloaded ? 'Install Later' : 'Remind Me Later'}
<Button variant="outline" onClick={handleDismiss} disabled={isDownloading}>
{isDownloaded
? t("dialogs:appUpdate.installLater", "Install Later")
: t("dialogs:appUpdate.remindMeLater", "Remind Me Later")}
</Button>
{isDownloaded ? (
<Button onClick={handleInstall}>
<RefreshCw className="mr-2 h-4 w-4" />
Install and Restart
{t("dialogs:appUpdate.installAndRestart", "Install and Restart")}
</Button>
) : (
<Button
onClick={handleDownload}
disabled={isDownloading}
>
<Button onClick={handleDownload} disabled={isDownloading}>
{isDownloading ? (
<>
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
Downloading...
{t("dialogs:appUpdate.downloading", "Downloading...")}
</>
) : (
<>
<Download className="mr-2 h-4 w-4" />
Download Update
{t("dialogs:appUpdate.downloadUpdate", "Download Update")}
</>
)}
</Button>
@@ -1,17 +1,18 @@
import { useState, useEffect, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Terminal, Check, AlertTriangle, X, Loader2, Download, RefreshCw, ExternalLink } from 'lucide-react';
import { Button } from './ui/button';
import { useState, useEffect, useCallback } from "react";
import { useTranslation } from "react-i18next";
import {
Popover,
PopoverContent,
PopoverTrigger
} from './ui/popover';
import {
Tooltip,
TooltipContent,
TooltipTrigger
} from './ui/tooltip';
Terminal,
Check,
AlertTriangle,
X,
Loader2,
Download,
RefreshCw,
ExternalLink,
} from "lucide-react";
import { Button } from "./ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
import {
AlertDialog,
AlertDialogAction,
@@ -20,16 +21,16 @@ import {
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle
} from './ui/alert-dialog';
import { cn } from '../lib/utils';
import type { ClaudeCodeVersionInfo } from '../../shared/types/cli';
AlertDialogTitle,
} from "./ui/alert-dialog";
import { cn } from "../lib/utils";
import type { ClaudeCodeVersionInfo } from "../../shared/types/cli";
interface ClaudeCodeStatusBadgeProps {
className?: string;
}
type StatusType = 'loading' | 'installed' | 'outdated' | 'not-found' | 'error';
type StatusType = "loading" | "installed" | "outdated" | "not-found" | "error";
// Check every 24 hours
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
@@ -39,8 +40,8 @@ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
* Shows installation status and provides quick access to install/update.
*/
export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps) {
const { t } = useTranslation(['common', 'navigation']);
const [status, setStatus] = useState<StatusType>('loading');
const { t } = useTranslation(["common", "navigation"]);
const [status, setStatus] = useState<StatusType>("loading");
const [versionInfo, setVersionInfo] = useState<ClaudeCodeVersionInfo | null>(null);
const [isInstalling, setIsInstalling] = useState(false);
const [lastChecked, setLastChecked] = useState<Date | null>(null);
@@ -51,7 +52,7 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
const checkVersion = useCallback(async () => {
try {
if (!window.electronAPI?.checkClaudeCodeVersion) {
setStatus('error');
setStatus("error");
return;
}
@@ -62,18 +63,18 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
setLastChecked(new Date());
if (!result.data.installed) {
setStatus('not-found');
setStatus("not-found");
} else if (result.data.isOutdated) {
setStatus('outdated');
setStatus("outdated");
} else {
setStatus('installed');
setStatus("installed");
}
} else {
setStatus('error');
setStatus("error");
}
} catch (err) {
console.error('Failed to check Claude Code version:', err);
setStatus('error');
console.error("Failed to check Claude Code version:", err);
setStatus("error");
}
}, []);
@@ -107,7 +108,7 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
}, 5000);
}
} catch (err) {
console.error('Failed to install Claude Code:', err);
console.error("Failed to install Claude Code:", err);
} finally {
setIsInstalling(false);
}
@@ -115,7 +116,7 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
// Handle install/update button click
const handleInstall = () => {
if (status === 'outdated') {
if (status === "outdated") {
// Show warning for updates since it will close running Claude sessions
setShowUpdateWarning(true);
} else {
@@ -127,30 +128,30 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
// Get status indicator color
const getStatusColor = () => {
switch (status) {
case 'installed':
return 'bg-green-500';
case 'outdated':
return 'bg-yellow-500';
case 'not-found':
case 'error':
return 'bg-destructive';
case "installed":
return "bg-green-500";
case "outdated":
return "bg-yellow-500";
case "not-found":
case "error":
return "bg-destructive";
default:
return 'bg-muted-foreground';
return "bg-muted-foreground";
}
};
// Get status icon
const getStatusIcon = () => {
switch (status) {
case 'loading':
case "loading":
return <Loader2 className="h-3 w-3 animate-spin" />;
case 'installed':
case "installed":
return <Check className="h-3 w-3" />;
case 'outdated':
case "outdated":
return <AlertTriangle className="h-3 w-3" />;
case 'not-found':
case "not-found":
return <X className="h-3 w-3" />;
case 'error':
case "error":
return <AlertTriangle className="h-3 w-3" />;
}
};
@@ -158,16 +159,16 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
// Get tooltip text
const getTooltipText = () => {
switch (status) {
case 'loading':
return t('navigation:claudeCode.checking', 'Checking Claude Code...');
case 'installed':
return t('navigation:claudeCode.upToDate', 'Claude Code is up to date');
case 'outdated':
return t('navigation:claudeCode.updateAvailable', 'Claude Code update available');
case 'not-found':
return t('navigation:claudeCode.notInstalled', 'Claude Code not installed');
case 'error':
return t('navigation:claudeCode.error', 'Error checking Claude Code');
case "loading":
return t("navigation:claudeCode.checking", "Checking Claude Code...");
case "installed":
return t("navigation:claudeCode.upToDate", "Claude Code is up to date");
case "outdated":
return t("navigation:claudeCode.updateAvailable", "Claude Code update available");
case "not-found":
return t("navigation:claudeCode.notInstalled", "Claude Code not installed");
case "error":
return t("navigation:claudeCode.error", "Error checking Claude Code");
}
};
@@ -180,36 +181,36 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
variant="ghost"
size="sm"
className={cn(
'w-full justify-start gap-2 text-xs',
status === 'not-found' || status === 'error' ? 'text-destructive' : '',
status === 'outdated' ? 'text-yellow-600 dark:text-yellow-500' : '',
"w-full justify-start gap-2 text-xs",
status === "not-found" || status === "error" ? "text-destructive" : "",
status === "outdated" ? "text-yellow-600 dark:text-yellow-500" : "",
className
)}
>
<div className="relative">
<Terminal className="h-4 w-4" />
<span className={cn(
'absolute -bottom-0.5 -right-0.5 h-2 w-2 rounded-full',
getStatusColor()
)} />
<span
className={cn(
"absolute -bottom-0.5 -right-0.5 h-2 w-2 rounded-full",
getStatusColor()
)}
/>
</div>
<span className="truncate">Claude Code</span>
{status === 'outdated' && (
{status === "outdated" && (
<span className="ml-auto text-[10px] bg-yellow-500/20 text-yellow-600 dark:text-yellow-400 px-1.5 py-0.5 rounded">
{t('common:update', 'Update')}
{t("common:update", "Update")}
</span>
)}
{status === 'not-found' && (
{status === "not-found" && (
<span className="ml-auto text-[10px] bg-destructive/20 text-destructive px-1.5 py-0.5 rounded">
{t('common:install', 'Install')}
{t("common:install", "Install")}
</span>
)}
</Button>
</PopoverTrigger>
</TooltipTrigger>
<TooltipContent side="right">
{getTooltipText()}
</TooltipContent>
<TooltipContent side="right">{getTooltipText()}</TooltipContent>
</Tooltip>
<PopoverContent side="right" align="end" className="w-72">
@@ -223,33 +224,37 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
<h4 className="text-sm font-medium">Claude Code CLI</h4>
<p className="text-xs text-muted-foreground flex items-center gap-1">
{getStatusIcon()}
{status === 'installed' && t('navigation:claudeCode.installed', 'Installed')}
{status === 'outdated' && t('navigation:claudeCode.outdated', 'Update available')}
{status === 'not-found' && t('navigation:claudeCode.missing', 'Not installed')}
{status === 'loading' && t('navigation:claudeCode.checking', 'Checking...')}
{status === 'error' && t('navigation:claudeCode.error', 'Error')}
{status === "installed" && t("navigation:claudeCode.installed", "Installed")}
{status === "outdated" && t("navigation:claudeCode.outdated", "Update available")}
{status === "not-found" && t("navigation:claudeCode.missing", "Not installed")}
{status === "loading" && t("navigation:claudeCode.checking", "Checking...")}
{status === "error" && t("navigation:claudeCode.error", "Error")}
</p>
</div>
</div>
{/* Version info */}
{versionInfo && status !== 'loading' && (
{versionInfo && status !== "loading" && (
<div className="text-xs space-y-1 p-2 bg-muted rounded-md">
{versionInfo.installed && (
<div className="flex justify-between">
<span className="text-muted-foreground">{t('navigation:claudeCode.current', 'Current')}:</span>
<span className="text-muted-foreground">
{t("navigation:claudeCode.current", "Current")}:
</span>
<span className="font-mono">{versionInfo.installed}</span>
</div>
)}
{versionInfo.latest && versionInfo.latest !== 'unknown' && (
{versionInfo.latest && versionInfo.latest !== "unknown" && (
<div className="flex justify-between">
<span className="text-muted-foreground">{t('navigation:claudeCode.latest', 'Latest')}:</span>
<span className="text-muted-foreground">
{t("navigation:claudeCode.latest", "Latest")}:
</span>
<span className="font-mono">{versionInfo.latest}</span>
</div>
)}
{lastChecked && (
<div className="flex justify-between text-muted-foreground">
<span>{t('navigation:claudeCode.lastChecked', 'Last checked')}:</span>
<span>{t("navigation:claudeCode.lastChecked", "Last checked")}:</span>
<span>{lastChecked.toLocaleTimeString()}</span>
</div>
)}
@@ -258,7 +263,7 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
{/* Actions */}
<div className="flex gap-2">
{(status === 'not-found' || status === 'outdated') && (
{(status === "not-found" || status === "outdated") && (
<Button
size="sm"
className="flex-1 gap-1"
@@ -270,10 +275,9 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
) : (
<Download className="h-3 w-3" />
)}
{status === 'outdated'
? t('common:update', 'Update')
: t('common:install', 'Install')
}
{status === "outdated"
? t("common:update", "Update")
: t("common:install", "Install")}
</Button>
)}
<Button
@@ -281,10 +285,10 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
size="sm"
className="gap-1"
onClick={() => checkVersion()}
disabled={status === 'loading'}
disabled={status === "loading"}
>
<RefreshCw className={cn('h-3 w-3', status === 'loading' && 'animate-spin')} />
{t('common:refresh', 'Refresh')}
<RefreshCw className={cn("h-3 w-3", status === "loading" && "animate-spin")} />
{t("common:refresh", "Refresh")}
</Button>
</div>
@@ -293,10 +297,32 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
variant="link"
size="sm"
className="w-full text-xs text-muted-foreground gap-1"
onClick={() => window.electronAPI?.openExternal?.('https://claude.ai/code')}
aria-label={t('navigation:claudeCode.learnMoreAriaLabel', 'Learn more about Claude Code (opens in new window)')}
onClick={() => window.electronAPI?.openExternal?.("https://claude.ai/code")}
aria-label={t(
"navigation:claudeCode.learnMoreAriaLabel",
"Learn more about Claude Code (opens in new window)"
)}
>
{t('navigation:claudeCode.learnMore', 'Learn more about Claude Code')}
{t("navigation:claudeCode.learnMore", "Learn more about Claude Code")}
<ExternalLink className="h-3 w-3" aria-hidden="true" />
</Button>
{/* Changelog link */}
<Button
variant="link"
size="sm"
className="w-full text-xs text-muted-foreground gap-1"
onClick={() =>
window.electronAPI?.openExternal?.(
"https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md"
)
}
aria-label={t(
"navigation:claudeCode.viewChangelogAriaLabel",
"View Claude Code Changelog (opens in new window)"
)}
>
{t("navigation:claudeCode.viewChangelog", "View Claude Code Changelog")}
<ExternalLink className="h-3 w-3" aria-hidden="true" />
</Button>
</div>
@@ -307,18 +333,19 @@ export function ClaudeCodeStatusBadge({ className }: ClaudeCodeStatusBadgeProps)
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t('navigation:claudeCode.updateWarningTitle', 'Update Claude Code?')}
{t("navigation:claudeCode.updateWarningTitle", "Update Claude Code?")}
</AlertDialogTitle>
<AlertDialogDescription>
{t('navigation:claudeCode.updateWarningDescription', 'Updating will close all running Claude Code sessions. Any unsaved work in those sessions may be lost. Make sure to save your work before proceeding.')}
{t(
"navigation:claudeCode.updateWarningDescription",
"Updating will close all running Claude Code sessions. Any unsaved work in those sessions may be lost. Make sure to save your work before proceeding."
)}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>
{t('common:cancel', 'Cancel')}
</AlertDialogCancel>
<AlertDialogCancel>{t("common:cancel", "Cancel")}</AlertDialogCancel>
<AlertDialogAction onClick={performInstall}>
{t('navigation:claudeCode.updateAnyway', 'Update Anyway')}
{t("navigation:claudeCode.updateAnyway", "Update Anyway")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
@@ -133,5 +133,20 @@
"cancel": "Cancel",
"remove": "Remove",
"error": "Failed to remove project"
},
"appUpdate": {
"title": "App Update Available",
"description": "A new version of Auto Claude is ready to download",
"newVersion": "New Version",
"released": "Released",
"downloading": "Downloading...",
"downloadUpdate": "Download Update",
"installAndRestart": "Install and Restart",
"installLater": "Install Later",
"remindMeLater": "Remind Me Later",
"updateDownloaded": "Update downloaded successfully! Click Install to restart and apply the update.",
"downloadError": "Failed to download update",
"claudeCodeChangelog": "View Claude Code Changelog",
"claudeCodeChangelogAriaLabel": "View Claude Code Changelog (opens in new window)"
}
}
@@ -44,6 +44,8 @@
"lastChecked": "Last checked",
"learnMore": "Learn more about Claude Code",
"learnMoreAriaLabel": "Learn more about Claude Code (opens in new window)",
"viewChangelog": "View Claude Code Changelog",
"viewChangelogAriaLabel": "View Claude Code Changelog (opens in new window)",
"updateWarningTitle": "Update Claude Code?",
"updateWarningDescription": "Updating will close all running Claude Code sessions. Any unsaved work in those sessions may be lost. Make sure to save your work before proceeding.",
"updateAnyway": "Update Anyway"
@@ -133,5 +133,20 @@
"cancel": "Annuler",
"remove": "Retirer",
"error": "Échec de la suppression du projet"
},
"appUpdate": {
"title": "Mise à jour de l'application disponible",
"description": "Une nouvelle version d'Auto Claude est prête à être téléchargée",
"newVersion": "Nouvelle version",
"released": "Publiée",
"downloading": "Téléchargement...",
"downloadUpdate": "Télécharger la mise à jour",
"installAndRestart": "Installer et redémarrer",
"installLater": "Installer plus tard",
"remindMeLater": "Me rappeler plus tard",
"updateDownloaded": "Mise à jour téléchargée avec succès ! Cliquez sur Installer pour redémarrer et appliquer la mise à jour.",
"downloadError": "Échec du téléchargement de la mise à jour",
"claudeCodeChangelog": "Voir le journal des modifications Claude Code",
"claudeCodeChangelogAriaLabel": "Voir le journal des modifications Claude Code (s'ouvre dans une nouvelle fenêtre)"
}
}
@@ -44,6 +44,8 @@
"lastChecked": "Dernière vérification",
"learnMore": "En savoir plus sur Claude Code",
"learnMoreAriaLabel": "En savoir plus sur Claude Code (s'ouvre dans une nouvelle fenêtre)",
"viewChangelog": "Voir le journal des modifications Claude Code",
"viewChangelogAriaLabel": "Voir le journal des modifications Claude Code (s'ouvre dans une nouvelle fenêtre)",
"updateWarningTitle": "Mettre à jour Claude Code ?",
"updateWarningDescription": "La mise à jour fermera toutes les sessions Claude Code en cours. Tout travail non sauvegardé dans ces sessions pourrait être perdu. Assurez-vous de sauvegarder votre travail avant de continuer.",
"updateAnyway": "Mettre à jour quand même"