mirror of
https://github.com/philippemerle/KubeDiagrams.git
synced 2026-08-22 14:36:24 +00:00
[Chore] Translate French comments and UI strings to English
This commit is contained in:
@@ -23,13 +23,13 @@ def submit_feedback():
|
||||
if note or comment:
|
||||
try:
|
||||
with open(Config.FEEDBACK_FILE, "a", encoding="utf-8") as f:
|
||||
f.write(f"[{diagram_type.upper()}]\nNote: {note}\nCommentaire: {comment}\n\n")
|
||||
f.write(f"[{diagram_type.upper()}]\nNote: {note}\nComment: {comment}\n\n")
|
||||
|
||||
return ResponseBuilder.success(
|
||||
message="Feedback submitted successfully. Thank you!"
|
||||
)
|
||||
except Exception as e:
|
||||
# Log l'erreur dans le CSV
|
||||
# Log error to CSV
|
||||
csv_logger = logging.getLogger(Config.LOGGER_NAME)
|
||||
csv_logger.error(f"Error writing feedback: {e}")
|
||||
return ResponseBuilder.error(
|
||||
|
||||
@@ -25,11 +25,11 @@ def generate_from_helm(
|
||||
Returns:
|
||||
DiagramResult: Result of the generation
|
||||
"""
|
||||
# Extraction du nom de base
|
||||
# Extract base name for output file
|
||||
parsed = urlparse(chart_url)
|
||||
base_name = os.path.basename(parsed.path).replace(".tgz", "").replace(".tar.gz", "")
|
||||
|
||||
# Pour les URLs OCI
|
||||
# OCI URLs use the last path segment as chart name
|
||||
if chart_url.startswith('oci://'):
|
||||
base_name = chart_url.rstrip('/').split('/')[-1]
|
||||
|
||||
@@ -42,12 +42,11 @@ def generate_from_helm(
|
||||
if extra_args.strip():
|
||||
cmd.extend(parse_extra_args(extra_args))
|
||||
|
||||
# Execution
|
||||
# Run the command and capture output
|
||||
proc = subprocess.run(cmd, check=False, capture_output=True, text=True)
|
||||
stdout_output = proc.stdout or ""
|
||||
stderr_output = proc.stderr or ""
|
||||
|
||||
# First we verify if there was an error before file exist
|
||||
has_error = proc.returncode != 0 or has_fatal_error(stdout_output, stderr_output)
|
||||
|
||||
# Second we verify if there was an error in the stderr output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Utilitaires pour les services de génération de diagrammes."""
|
||||
"""Utilities for diagram generation services."""
|
||||
import base64
|
||||
import shlex
|
||||
|
||||
@@ -6,30 +6,30 @@ from constants import TEXT_FORMATS
|
||||
|
||||
def has_fatal_error(stdout_txt: str, stderr_txt: str) -> bool:
|
||||
"""
|
||||
Vérifie si la sortie contient une erreur fatale.
|
||||
|
||||
Check whether subprocess output contains a fatal error marker.
|
||||
|
||||
Args:
|
||||
stdout_txt: Sortie standard
|
||||
stderr_txt: Sortie d'erreur
|
||||
|
||||
stdout_txt: Standard output text
|
||||
stderr_txt: Standard error text
|
||||
|
||||
Returns:
|
||||
bool: True si erreur fatale détectée
|
||||
bool: True if a fatal error was detected
|
||||
"""
|
||||
return ("error:" in (stdout_txt or "").lower()) or ("error:" in (stderr_txt or "").lower())
|
||||
|
||||
|
||||
def parse_extra_args(extra_args: str) -> list[str]:
|
||||
"""
|
||||
Parse les arguments supplémentaires.
|
||||
|
||||
Parse a string of extra CLI arguments using shell-like tokenization.
|
||||
|
||||
Args:
|
||||
extra_args: Arguments supplémentaires en string
|
||||
|
||||
extra_args: Space-separated argument string (may include quoted tokens)
|
||||
|
||||
Returns:
|
||||
list[str]: Liste des arguments parsés
|
||||
|
||||
list[str]: Parsed argument tokens
|
||||
|
||||
Raises:
|
||||
ValueError: Si les arguments sont invalides
|
||||
ValueError: If the argument string has invalid shell syntax
|
||||
"""
|
||||
if not extra_args or not extra_args.strip():
|
||||
return []
|
||||
@@ -41,15 +41,18 @@ def parse_extra_args(extra_args: str) -> list[str]:
|
||||
|
||||
def encode_content(content: bytes, output_format: str) -> str:
|
||||
"""
|
||||
Encode le contenu en base64 ou UTF-8 selon le format.
|
||||
|
||||
Encode diagram content for JSON transport.
|
||||
|
||||
Text-based formats (SVG, DOT, DOT_JSON, DRAWIO) are decoded as UTF-8.
|
||||
Binary formats (PNG, JPG, PDF) are base64-encoded.
|
||||
|
||||
Args:
|
||||
content: Contenu à encoder
|
||||
output_format: Format de sortie
|
||||
|
||||
content: Raw file content
|
||||
output_format: Output format string (e.g. 'png', 'svg')
|
||||
|
||||
Returns:
|
||||
str: Contenu encodé
|
||||
str: Encoded content ready for the API response
|
||||
"""
|
||||
if output_format in TEXT_FORMATS:
|
||||
return content.decode("utf-8")
|
||||
return base64.b64encode(content).decode("utf-8")
|
||||
return base64.b64encode(content).decode("utf-8")
|
||||
@@ -75,7 +75,7 @@ class InputValidator:
|
||||
url: URL of the Helm chart
|
||||
|
||||
Returns:
|
||||
Tuple[bool, Optional[str]]: (est_valide, message_erreur)
|
||||
Tuple[bool, Optional[str]]: (is_valid, error_message)
|
||||
"""
|
||||
if not url or not url.strip():
|
||||
return False, "Chart URL cannot be empty."
|
||||
@@ -121,7 +121,7 @@ class InputValidator:
|
||||
Tuple[bool, Optional[str]]: (is_valid, error_message)
|
||||
"""
|
||||
if not args or not args.strip():
|
||||
return True, None # Les args vides sont valides
|
||||
return True, None
|
||||
|
||||
dangerous_chars = [';', '&', '|', '`', '$', '(', ')']
|
||||
for char in dangerous_chars:
|
||||
|
||||
@@ -21,7 +21,7 @@ function App() {
|
||||
setRestoredItem(null);
|
||||
};
|
||||
|
||||
// Mémoriser historyContext pour éviter de le recréer à chaque render
|
||||
// Memoize historyContext to avoid recreating it on every render
|
||||
const historyContext = useMemo(
|
||||
() => ({ addToHistory, getHistoryItem, restoredItem, clearRestoredItem }),
|
||||
[addToHistory, getHistoryItem, restoredItem]
|
||||
|
||||
@@ -120,9 +120,9 @@ const HistoryPanel = ({ history, onRestore, onRemove, onClear, isOpen, onToggle
|
||||
{history.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center h-full text-slate-500">
|
||||
<FileText className="w-16 h-16 mb-4 opacity-50" />
|
||||
<p className="text-center">Aucun diagramme dans l'historique</p>
|
||||
<p className="text-center">No diagrams in history</p>
|
||||
<p className="text-xs text-center mt-2">
|
||||
Les diagrammes générés apparaîtront ici
|
||||
Generated diagrams will appear here
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function PanZoomContainer({
|
||||
scrollPanSpeed = 3.0,
|
||||
buttonZoomFactor = 1.25,
|
||||
buttonZoomFactorFast = 1.5,
|
||||
// Souris
|
||||
// Mouse
|
||||
mouseZoomDampen = 0.018,
|
||||
clampDeltaY = 50,
|
||||
}) {
|
||||
@@ -22,17 +22,17 @@ export default function PanZoomContainer({
|
||||
const measureRef = useRef(null);
|
||||
const isInControls = (el) => !!(el && el.closest && el.closest('.kd-controls'));
|
||||
|
||||
// États affichés
|
||||
// Displayed state (reactive)
|
||||
const [scale, _setScale] = useState(1);
|
||||
const [tx, _setTx] = useState(0);
|
||||
const [ty, _setTy] = useState(0);
|
||||
|
||||
// Réfs calculs + animation
|
||||
// Refs for calculation and animation (non-reactive)
|
||||
const scaleRef = useRef(1);
|
||||
const txRef = useRef(0);
|
||||
const tyRef = useRef(0);
|
||||
|
||||
// Cibles animées
|
||||
// Animated targets
|
||||
const targetScaleRef = useRef(1);
|
||||
const targetTxRef = useRef(0);
|
||||
const targetTyRef = useRef(0);
|
||||
@@ -55,7 +55,7 @@ export default function PanZoomContainer({
|
||||
const [naturalSize, setNaturalSize] = useState({ w: 0, h: 0 });
|
||||
const [vpSize, setVpSize] = useState({ w: 0, h: 0 });
|
||||
|
||||
// Mesures
|
||||
// Measure natural content size and viewport size
|
||||
useLayoutEffect(() => {
|
||||
const el = measureRef.current;
|
||||
if (!el) return;
|
||||
@@ -83,7 +83,7 @@ export default function PanZoomContainer({
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Fit initial
|
||||
// Fit content into viewport on first render
|
||||
useEffect(() => {
|
||||
if (!naturalSize.w || !naturalSize.h || !vpSize.w || !vpSize.h) return;
|
||||
fit(true);
|
||||
@@ -91,7 +91,7 @@ export default function PanZoomContainer({
|
||||
|
||||
const clamp = (v, a, b) => Math.min(b, Math.max(a, v));
|
||||
|
||||
// Anime position/zoom vers la cible
|
||||
// Animate position/zoom toward targets with smoothing
|
||||
const animate = useCallback(() => {
|
||||
const s = scaleRef.current;
|
||||
const tx = txRef.current;
|
||||
@@ -167,7 +167,7 @@ export default function PanZoomContainer({
|
||||
dY *= vp.clientHeight;
|
||||
}
|
||||
|
||||
// --- ZOOM (pinch/ctrl) : adoucir la souris, garder le trackpad tel quel
|
||||
// --- ZOOM (pinch/ctrl): dampen mouse wheel, keep trackpad as-is
|
||||
if (e.ctrlKey) {
|
||||
const isTrackpad = isTrackpadWheel(e);
|
||||
const sens = isTrackpad ? pinchSensitivity : pinchSensitivity * mouseZoomDampen;
|
||||
@@ -178,7 +178,7 @@ export default function PanZoomContainer({
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Mode "zoom" à la molette : même adoucissement pour souris
|
||||
// --- Wheel zoom mode: same dampening as pinch/ctrl
|
||||
if (wheelMode === 'zoom') {
|
||||
const isTrackpad = isTrackpadWheel(e);
|
||||
const sens = isTrackpad ? wheelSensitivity : wheelSensitivity * mouseZoomDampen;
|
||||
@@ -189,7 +189,7 @@ export default function PanZoomContainer({
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Par défaut : PAN au wheel/trackpad (deux doigts)
|
||||
// --- Default: pan with wheel/trackpad (two-finger scroll)
|
||||
const k = scrollPanSpeed;
|
||||
targetTxRef.current = txRef.current - dX * k;
|
||||
targetTyRef.current = tyRef.current - dY * k;
|
||||
|
||||
@@ -9,8 +9,8 @@ const ProgressBar = ({ currentStep = 'idle', isVisible = false }) => {
|
||||
const steps = [
|
||||
{ id: 'parsing', label: 'Parsing' },
|
||||
{ id: 'validation', label: 'Validation' },
|
||||
{ id: 'generation', label: 'Génération' },
|
||||
{ id: 'rendering', label: 'Rendu' },
|
||||
{ id: 'generation', label: 'Generation' },
|
||||
{ id: 'rendering', label: 'Rendering' },
|
||||
];
|
||||
|
||||
const getStepStatus = (stepId) => {
|
||||
@@ -65,7 +65,7 @@ const ProgressBar = ({ currentStep = 'idle', isVisible = false }) => {
|
||||
>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-sm font-semibold text-slate-300">
|
||||
{currentStep === 'completed' ? '✓ Génération terminée' : 'Génération en cours...'}
|
||||
{currentStep === 'completed' ? '✓ Generation complete' : 'Generation in progress...'}
|
||||
</h3>
|
||||
{currentStep !== 'completed' && currentStep !== 'error' && (
|
||||
<Loader2 className="w-4 h-4 animate-spin text-blue-400" />
|
||||
|
||||
@@ -31,7 +31,7 @@ function Tabs({ historyContext }) {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-1 w-full">
|
||||
{/* Onglets */}
|
||||
{/* Tabs */}
|
||||
<div className="flex mb-4 space-x-4">
|
||||
{tabs.map((tab) => (
|
||||
<motion.button
|
||||
@@ -51,7 +51,7 @@ function Tabs({ historyContext }) {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Contenu des tabs */}
|
||||
{/* Tab content */}
|
||||
<div className="flex-1">
|
||||
{activeTab === 'manifest' && <ManifestTab historyContext={historyContext} />}
|
||||
{activeTab === 'helm' && <HelmTab historyContext={historyContext} />}
|
||||
|
||||
@@ -98,7 +98,7 @@ function HelmFileTab({ historyContext }) {
|
||||
historyContext.addToHistory(historyItem);
|
||||
lastHistoryIdRef.current = historyId;
|
||||
}
|
||||
}, [diagram, progressStep]); // Dépendances minimales pour éviter la boucle
|
||||
}, [diagram, progressStep]); // minimal deps — avoids a save loop
|
||||
|
||||
// Restore from history
|
||||
useEffect(() => {
|
||||
|
||||
@@ -93,7 +93,7 @@ function HelmTab({ historyContext }) {
|
||||
historyContext.addToHistory(historyItem);
|
||||
lastHistoryIdRef.current = historyId;
|
||||
}
|
||||
}, [diagram, progressStep]); // Dépendances minimales pour éviter la boucle
|
||||
}, [diagram, progressStep]); // minimal deps — avoids a save loop
|
||||
|
||||
// Restore from history
|
||||
useEffect(() => {
|
||||
@@ -133,7 +133,7 @@ function HelmTab({ historyContext }) {
|
||||
setChartUrl(v);
|
||||
setBackendError('');
|
||||
|
||||
// Valider seulement si l'URL n'est pas vide
|
||||
// Validate only if URL is not empty
|
||||
if (v && v.trim()) {
|
||||
setInputError(
|
||||
isValidChartUrl(v)
|
||||
|
||||
@@ -101,7 +101,7 @@ function ManifestTab({ historyContext }) {
|
||||
historyContext.addToHistory(historyItem);
|
||||
lastHistoryIdRef.current = historyId;
|
||||
}
|
||||
}, [diagram, progressStep]); // Dépendances minimales pour éviter la boucle
|
||||
}, [diagram, progressStep]); // minimal deps — avoids a save loop
|
||||
|
||||
// Restore from history
|
||||
useEffect(() => {
|
||||
|
||||
@@ -77,9 +77,9 @@ export const dismissToast = (toastId) => {
|
||||
*/
|
||||
export const showPromise = (promise, messages) => {
|
||||
return toast.promise(promise, {
|
||||
loading: messages.loading || 'Chargement...',
|
||||
success: messages.success || 'Terminé !',
|
||||
error: messages.error || 'Une erreur est survenue',
|
||||
loading: messages.loading || 'Loading...',
|
||||
success: messages.success || 'Done!',
|
||||
error: messages.error || 'An error occurred',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user