feat(editor): translate SimulatorCanvas header + remove dialog (Editor block 4)

The canvas header (the bar above the simulation area) and the
"Remove board?" confirmation dialog now read from i18n.

Translated:
- Status dot tooltip (Running / Stopped).
- Active board selector tooltip + "No board" placeholder + the
  hint that prompts the user to add a board.
- Undo / Redo buttons: aria-label, dynamic title with the action
  description and the empty-state fallback. Action descriptions
  themselves stay untranslated (they come from the editor history
  store as English literals — translating them would mean reaching
  into a different store; deferred).
- Serial Monitor and Oscilloscope toggles (button title + label).
- Zoom in / out / reset-view buttons.
- Component count tooltip + Add Component button.
- The error-banner Dismiss button.
- "Remove board" item in the right-click menu, with a localised
  "(N wires)" parenthetical via i18next pluralisation.
- The full removal confirmation dialog: title with board label
  interpolation, body copy with optional connected-wires sentence,
  Cancel + Remove buttons.

Pluralisation uses i18next's _one / _other (and _few / _many for
Russian) suffixes so wire counts read naturally per language.

Hand-translated for all 8 non-English locales. Untouched (deferred):
the property dialog, custom-chip dialog, sensor control panel, and
the various inline tooltips on board pins and wire endpoints —
those are denser and benefit from a separate pass.
This commit is contained in:
David Montero Crespo 2026-05-09 03:20:36 -03:00
parent fa4f3d6e80
commit ecc35f72cb
10 changed files with 419 additions and 38 deletions

View File

@ -2,6 +2,7 @@ import { useSimulatorStore, getEsp32Bridge } from '../../store/useSimulatorStore
import React, { useEffect, useState, useRef, useCallback } from 'react';
import { createPortal } from 'react-dom';
import { Undo2, Redo2 } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { ESP32_ADC_PIN_MAP } from '../velxio-components/Esp32Element';
import { ComponentPickerModal } from '../ComponentPickerModal';
import { ComponentPropertyDialog } from './ComponentPropertyDialog';
@ -87,6 +88,7 @@ interface SimulatorCanvasProps {
}
export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
const { t } = useTranslation();
const isTouchDevice = useIsCoarsePointer();
// Mirror to a ref so the long-lived touch handler effect (deps deliberately
// narrow to avoid rebinding listeners on every render) can read the latest
@ -1866,7 +1868,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
cursor: 'pointer',
}}
>
Dismiss
{t('editor.canvas.dismiss')}
</button>
</div>
)}
@ -1880,7 +1882,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
{/* Status LED */}
<span
className={`status-dot ${running ? 'running' : 'stopped'}`}
title={running ? 'Running' : 'Stopped'}
title={running ? t('editor.canvas.status.running') : t('editor.canvas.status.stopped')}
/>
{/* Active board selector (multi-board) — hidden when no boards */}
@ -1890,7 +1892,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
value={activeBoardId ?? ''}
onChange={(e) => useSimulatorStore.getState().setActiveBoardId(e.target.value)}
disabled={running}
title="Active board"
title={t('editor.canvas.activeBoard')}
>
{boards.map((b) => (
<option key={b.id} value={b.id}>
@ -1902,9 +1904,9 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
<span
className="board-selector"
style={{ opacity: 0.55, fontStyle: 'italic', cursor: 'default' }}
title="No board on canvas — add one with the Add button to compile and run code"
title={t('editor.canvas.noBoardHint')}
>
No board
{t('editor.canvas.noBoard')}
</span>
)}
@ -1918,10 +1920,10 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
className="canvas-icon-btn"
title={
historyIndex >= 0
? `Undo: ${history[historyIndex].description} (Ctrl+Z)`
: 'Nothing to undo'
? t('editor.canvas.undo.title', { description: history[historyIndex].description })
: t('editor.canvas.undo.empty')
}
aria-label="Undo"
aria-label={t('editor.canvas.undo.label')}
>
<Undo2 size={16} strokeWidth={2} aria-hidden="true" />
</button>
@ -1931,10 +1933,10 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
className="canvas-icon-btn"
title={
historyIndex < history.length - 1
? `Redo: ${history[historyIndex + 1].description} (Ctrl+Y)`
: 'Nothing to redo'
? t('editor.canvas.redo.title', { description: history[historyIndex + 1].description })
: t('editor.canvas.redo.empty')
}
aria-label="Redo"
aria-label={t('editor.canvas.redo.label')}
>
<Redo2 size={16} strokeWidth={2} aria-hidden="true" />
</button>
@ -1946,7 +1948,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
trackToggleSerialMonitor(!serialMonitorOpen);
}}
className={`canvas-serial-btn${serialMonitorOpen ? ' canvas-serial-btn-active' : ''}`}
title="Toggle Serial Monitor"
title={t('editor.canvas.toggleSerialMonitor')}
>
<svg
width="22"
@ -1961,7 +1963,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
<rect x="2" y="3" width="20" height="14" rx="2" />
<path d="M8 21h8M12 17v4" />
</svg>
Serial
{t('editor.canvas.serial')}
</button>
{/* ESP32-CAM webcam stream toggle */}
@ -2045,7 +2047,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
<button
onClick={toggleOscilloscope}
className={`canvas-serial-btn${oscilloscopeOpen ? ' canvas-serial-btn-active' : ''}`}
title="Toggle Oscilloscope / Logic Analyzer"
title={t('editor.canvas.toggleScope')}
>
<svg
width="22"
@ -2059,7 +2061,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
>
<polyline points="2 14 6 8 10 14 14 6 18 14 22 10" />
</svg>
Scope
{t('editor.canvas.scope')}
</button>
</div>
@ -2076,7 +2078,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
preventDefault: () => {},
} as any)
}
title="Zoom out"
title={t('editor.canvas.zoomOut')}
>
<svg
width="14"
@ -2093,7 +2095,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
<button
className="zoom-level"
onClick={handleResetView}
title="Reset view (click to reset)"
title={t('editor.canvas.resetView')}
>
{Math.round(zoom * 100)}%
</button>
@ -2107,7 +2109,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
preventDefault: () => {},
} as any)
}
title="Zoom in"
title={t('editor.canvas.zoomIn')}
>
<svg
width="14"
@ -2127,7 +2129,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
{/* Component count */}
<span
className="component-count"
title={`${components.length} component${components.length !== 1 ? 's' : ''}`}
title={t('editor.canvas.componentCount', { count: components.length })}
>
<svg
width="15"
@ -2149,7 +2151,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
<button
className="add-component-btn"
onClick={() => setShowComponentPicker(true)}
title="Add Component"
title={t('editor.canvas.addComponentTitle')}
disabled={running}
>
<svg
@ -2165,7 +2167,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
<line x1="12" y1="5" x2="12" y2="19" />
<line x1="5" y1="12" x2="19" y2="12" />
</svg>
Add
{t('editor.canvas.add')}
</button>
</div>
</div>
@ -2666,10 +2668,10 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
<polyline points="3 6 5 6 21 6" />
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
</svg>
Remove board
{t('editor.canvas.removeBoard')}
{connectedWires > 0 && (
<span style={{ color: '#888', fontSize: 11 }}>
({connectedWires} wire{connectedWires > 1 ? 's' : ''})
({t('editor.canvas.wireCount', { count: connectedWires })})
</span>
)}
</button>
@ -2682,7 +2684,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
{boardToRemove &&
(() => {
const board = boards.find((b) => b.id === boardToRemove);
const label = board ? BOARD_KIND_LABELS[board.boardKind] : 'Board';
const label = board ? BOARD_KIND_LABELS[board.boardKind] : t('editor.canvas.removeConfirm.boardFallback');
const connectedWires = wires.filter(
(w) => w.start.componentId === boardToRemove || w.end.componentId === boardToRemove,
).length;
@ -2709,20 +2711,12 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
}}
>
<h3 style={{ margin: '0 0 10px', color: '#e0e0e0', fontSize: 15 }}>
Remove {label}?
{t('editor.canvas.removeConfirm.title', { label })}
</h3>
<p style={{ margin: '0 0 16px', color: '#999', fontSize: 13, lineHeight: 1.5 }}>
This will remove the board from the workspace
{connectedWires > 0 && (
<>
{' '}
and{' '}
<strong style={{ color: '#e06c75' }}>
{connectedWires} connected wire{connectedWires > 1 ? 's' : ''}
</strong>
</>
)}
. This action cannot be undone.
{connectedWires > 0
? t('editor.canvas.removeConfirm.bodyWithWires', { count: connectedWires })
: t('editor.canvas.removeConfirm.body')}
</p>
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
<button
@ -2737,7 +2731,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
fontSize: 13,
}}
>
Cancel
{t('editor.canvas.removeConfirm.cancel')}
</button>
<button
onClick={() => {
@ -2754,7 +2748,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
fontSize: 13,
}}
>
Remove
{t('editor.canvas.removeConfirm.remove')}
</button>
</div>
</div>

View File

@ -179,6 +179,49 @@
"copy": "Kopieren",
"privateWarning": "Dieses Projekt ist privat. Andere sehen beim Öffnen des Links einen 403-Fehler.",
"close": "Schließen"
},
"canvas": {
"status": {
"running": "Läuft",
"stopped": "Gestoppt"
},
"activeBoard": "Aktives Board",
"noBoard": "Kein Board",
"noBoardHint": "Kein Board auf der Leinwand — füge eines über den Hinzufügen-Button hinzu, um Code zu kompilieren und auszuführen",
"undo": {
"label": "Rückgängig",
"title": "Rückgängig: {{description}} (Strg+Z)",
"empty": "Nichts rückgängig zu machen"
},
"redo": {
"label": "Wiederherstellen",
"title": "Wiederherstellen: {{description}} (Strg+Y)",
"empty": "Nichts wiederherzustellen"
},
"toggleSerialMonitor": "Serial Monitor umschalten",
"serial": "Serial",
"toggleScope": "Oszilloskop / Logikanalysator umschalten",
"scope": "Scope",
"zoomIn": "Vergrößern",
"zoomOut": "Verkleinern",
"resetView": "Ansicht zurücksetzen (klicken zum Zurücksetzen)",
"componentCount_one": "{{count}} Bauteil",
"componentCount_other": "{{count}} Bauteile",
"addComponentTitle": "Bauteil hinzufügen",
"add": "Hinzufügen",
"dismiss": "Schließen",
"removeBoard": "Board entfernen",
"wireCount_one": "{{count}} Draht",
"wireCount_other": "{{count}} Drähte",
"removeConfirm": {
"boardFallback": "Board",
"title": "{{label}} entfernen?",
"body": "Das Board wird aus dem Arbeitsbereich entfernt. Dieser Vorgang kann nicht rückgängig gemacht werden.",
"bodyWithWires_one": "Das Board wird aus dem Arbeitsbereich entfernt zusammen mit {{count}} verbundenen Draht. Dieser Vorgang kann nicht rückgängig gemacht werden.",
"bodyWithWires_other": "Das Board wird aus dem Arbeitsbereich entfernt zusammen mit {{count}} verbundenen Drähten. Dieser Vorgang kann nicht rückgängig gemacht werden.",
"cancel": "Abbrechen",
"remove": "Entfernen"
}
}
}
}

View File

@ -179,6 +179,49 @@
"copy": "Copy",
"privateWarning": "This project is private. Others will see a 403 error when opening this link.",
"close": "Close"
},
"canvas": {
"status": {
"running": "Running",
"stopped": "Stopped"
},
"activeBoard": "Active board",
"noBoard": "No board",
"noBoardHint": "No board on canvas — add one with the Add button to compile and run code",
"undo": {
"label": "Undo",
"title": "Undo: {{description}} (Ctrl+Z)",
"empty": "Nothing to undo"
},
"redo": {
"label": "Redo",
"title": "Redo: {{description}} (Ctrl+Y)",
"empty": "Nothing to redo"
},
"toggleSerialMonitor": "Toggle Serial Monitor",
"serial": "Serial",
"toggleScope": "Toggle Oscilloscope / Logic Analyzer",
"scope": "Scope",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetView": "Reset view (click to reset)",
"componentCount_one": "{{count}} component",
"componentCount_other": "{{count}} components",
"addComponentTitle": "Add Component",
"add": "Add",
"dismiss": "Dismiss",
"removeBoard": "Remove board",
"wireCount_one": "{{count}} wire",
"wireCount_other": "{{count}} wires",
"removeConfirm": {
"boardFallback": "Board",
"title": "Remove {{label}}?",
"body": "This will remove the board from the workspace. This action cannot be undone.",
"bodyWithWires_one": "This will remove the board from the workspace and {{count}} connected wire. This action cannot be undone.",
"bodyWithWires_other": "This will remove the board from the workspace and {{count}} connected wires. This action cannot be undone.",
"cancel": "Cancel",
"remove": "Remove"
}
}
}
}

View File

@ -179,6 +179,49 @@
"copy": "Copiar",
"privateWarning": "Este proyecto es privado. Otros verán un error 403 al abrir este enlace.",
"close": "Cerrar"
},
"canvas": {
"status": {
"running": "En ejecución",
"stopped": "Detenido"
},
"activeBoard": "Placa activa",
"noBoard": "Sin placa",
"noBoardHint": "No hay placa en el lienzo — añade una con el botón Añadir para compilar y ejecutar código",
"undo": {
"label": "Deshacer",
"title": "Deshacer: {{description}} (Ctrl+Z)",
"empty": "Nada que deshacer"
},
"redo": {
"label": "Rehacer",
"title": "Rehacer: {{description}} (Ctrl+Y)",
"empty": "Nada que rehacer"
},
"toggleSerialMonitor": "Mostrar/ocultar monitor serie",
"serial": "Serie",
"toggleScope": "Mostrar/ocultar osciloscopio / analizador lógico",
"scope": "Osciloscopio",
"zoomIn": "Acercar",
"zoomOut": "Alejar",
"resetView": "Restablecer vista (clic para restablecer)",
"componentCount_one": "{{count}} componente",
"componentCount_other": "{{count}} componentes",
"addComponentTitle": "Añadir componente",
"add": "Añadir",
"dismiss": "Descartar",
"removeBoard": "Quitar placa",
"wireCount_one": "{{count}} cable",
"wireCount_other": "{{count}} cables",
"removeConfirm": {
"boardFallback": "Placa",
"title": "¿Quitar {{label}}?",
"body": "Esto eliminará la placa del espacio de trabajo. Esta acción no se puede deshacer.",
"bodyWithWires_one": "Esto eliminará la placa del espacio de trabajo y {{count}} cable conectado. Esta acción no se puede deshacer.",
"bodyWithWires_other": "Esto eliminará la placa del espacio de trabajo y {{count}} cables conectados. Esta acción no se puede deshacer.",
"cancel": "Cancelar",
"remove": "Quitar"
}
}
}
}

View File

@ -179,6 +179,49 @@
"copy": "Copier",
"privateWarning": "Ce projet est privé. Les autres verront une erreur 403 en ouvrant ce lien.",
"close": "Fermer"
},
"canvas": {
"status": {
"running": "En cours",
"stopped": "Arrêté"
},
"activeBoard": "Carte active",
"noBoard": "Aucune carte",
"noBoardHint": "Aucune carte sur le canevas — ajoutez-en une avec le bouton Ajouter pour compiler et exécuter du code",
"undo": {
"label": "Annuler",
"title": "Annuler : {{description}} (Ctrl+Z)",
"empty": "Rien à annuler"
},
"redo": {
"label": "Rétablir",
"title": "Rétablir : {{description}} (Ctrl+Y)",
"empty": "Rien à rétablir"
},
"toggleSerialMonitor": "Afficher/masquer le moniteur série",
"serial": "Série",
"toggleScope": "Afficher/masquer l'oscilloscope / analyseur logique",
"scope": "Oscilloscope",
"zoomIn": "Zoom avant",
"zoomOut": "Zoom arrière",
"resetView": "Réinitialiser la vue (cliquez pour réinitialiser)",
"componentCount_one": "{{count}} composant",
"componentCount_other": "{{count}} composants",
"addComponentTitle": "Ajouter un composant",
"add": "Ajouter",
"dismiss": "Fermer",
"removeBoard": "Retirer la carte",
"wireCount_one": "{{count}} fil",
"wireCount_other": "{{count}} fils",
"removeConfirm": {
"boardFallback": "Carte",
"title": "Retirer {{label}} ?",
"body": "La carte sera retirée de l'espace de travail. Cette action est irréversible.",
"bodyWithWires_one": "La carte sera retirée de l'espace de travail ainsi que {{count}} fil connecté. Cette action est irréversible.",
"bodyWithWires_other": "La carte sera retirée de l'espace de travail ainsi que {{count}} fils connectés. Cette action est irréversible.",
"cancel": "Annuler",
"remove": "Retirer"
}
}
}
}

View File

@ -179,6 +179,49 @@
"copy": "Copia",
"privateWarning": "Questo progetto è privato. Gli altri vedranno un errore 403 aprendo il link.",
"close": "Chiudi"
},
"canvas": {
"status": {
"running": "In esecuzione",
"stopped": "Fermato"
},
"activeBoard": "Scheda attiva",
"noBoard": "Nessuna scheda",
"noBoardHint": "Nessuna scheda sul canvas — aggiungine una con il pulsante Aggiungi per compilare ed eseguire codice",
"undo": {
"label": "Annulla",
"title": "Annulla: {{description}} (Ctrl+Z)",
"empty": "Niente da annullare"
},
"redo": {
"label": "Ripeti",
"title": "Ripeti: {{description}} (Ctrl+Y)",
"empty": "Niente da ripetere"
},
"toggleSerialMonitor": "Mostra/nascondi monitor seriale",
"serial": "Seriale",
"toggleScope": "Mostra/nascondi oscilloscopio / analizzatore logico",
"scope": "Oscilloscopio",
"zoomIn": "Zoom avanti",
"zoomOut": "Zoom indietro",
"resetView": "Ripristina vista (clic per ripristinare)",
"componentCount_one": "{{count}} componente",
"componentCount_other": "{{count}} componenti",
"addComponentTitle": "Aggiungi componente",
"add": "Aggiungi",
"dismiss": "Ignora",
"removeBoard": "Rimuovi scheda",
"wireCount_one": "{{count}} filo",
"wireCount_other": "{{count}} fili",
"removeConfirm": {
"boardFallback": "Scheda",
"title": "Rimuovere {{label}}?",
"body": "La scheda verrà rimossa dal workspace. Questa azione non può essere annullata.",
"bodyWithWires_one": "La scheda verrà rimossa dal workspace insieme a {{count}} filo collegato. Questa azione non può essere annullata.",
"bodyWithWires_other": "La scheda verrà rimossa dal workspace insieme a {{count}} fili collegati. Questa azione non può essere annullata.",
"cancel": "Annulla",
"remove": "Rimuovi"
}
}
}
}

View File

@ -179,6 +179,46 @@
"copy": "コピー",
"privateWarning": "このプロジェクトは非公開です。他の人がこのリンクを開くと 403 エラーが表示されます。",
"close": "閉じる"
},
"canvas": {
"status": {
"running": "実行中",
"stopped": "停止中"
},
"activeBoard": "アクティブなボード",
"noBoard": "ボードなし",
"noBoardHint": "キャンバスにボードがありません — 「追加」ボタンでボードを追加するとコードのコンパイルと実行ができます",
"undo": {
"label": "元に戻す",
"title": "元に戻す:{{description}} (Ctrl+Z)",
"empty": "元に戻す操作はありません"
},
"redo": {
"label": "やり直し",
"title": "やり直し:{{description}} (Ctrl+Y)",
"empty": "やり直す操作はありません"
},
"toggleSerialMonitor": "シリアルモニタの切り替え",
"serial": "シリアル",
"toggleScope": "オシロスコープ / ロジックアナライザの切り替え",
"scope": "スコープ",
"zoomIn": "ズームイン",
"zoomOut": "ズームアウト",
"resetView": "ビューをリセット(クリックでリセット)",
"componentCount_other": "{{count}} 個のコンポーネント",
"addComponentTitle": "コンポーネントを追加",
"add": "追加",
"dismiss": "閉じる",
"removeBoard": "ボードを削除",
"wireCount_other": "{{count}} 本の配線",
"removeConfirm": {
"boardFallback": "ボード",
"title": "{{label}} を削除しますか?",
"body": "このボードをワークスペースから削除します。この操作は取り消せません。",
"bodyWithWires_other": "このボードと接続された {{count}} 本の配線をワークスペースから削除します。この操作は取り消せません。",
"cancel": "キャンセル",
"remove": "削除"
}
}
}
}

View File

@ -179,6 +179,49 @@
"copy": "Copiar",
"privateWarning": "Este projeto é privado. Outros verão um erro 403 ao abrir este link.",
"close": "Fechar"
},
"canvas": {
"status": {
"running": "Em execução",
"stopped": "Parado"
},
"activeBoard": "Placa ativa",
"noBoard": "Sem placa",
"noBoardHint": "Sem placa na tela — adicione uma com o botão Adicionar para compilar e executar código",
"undo": {
"label": "Desfazer",
"title": "Desfazer: {{description}} (Ctrl+Z)",
"empty": "Nada para desfazer"
},
"redo": {
"label": "Refazer",
"title": "Refazer: {{description}} (Ctrl+Y)",
"empty": "Nada para refazer"
},
"toggleSerialMonitor": "Alternar monitor serial",
"serial": "Serial",
"toggleScope": "Alternar osciloscópio / analisador lógico",
"scope": "Osciloscópio",
"zoomIn": "Aumentar zoom",
"zoomOut": "Reduzir zoom",
"resetView": "Redefinir visualização (clique para redefinir)",
"componentCount_one": "{{count}} componente",
"componentCount_other": "{{count}} componentes",
"addComponentTitle": "Adicionar componente",
"add": "Adicionar",
"dismiss": "Dispensar",
"removeBoard": "Remover placa",
"wireCount_one": "{{count}} fio",
"wireCount_other": "{{count}} fios",
"removeConfirm": {
"boardFallback": "Placa",
"title": "Remover {{label}}?",
"body": "Isso vai remover a placa do espaço de trabalho. Esta ação não pode ser desfeita.",
"bodyWithWires_one": "Isso vai remover a placa do espaço de trabalho e {{count}} fio conectado. Esta ação não pode ser desfeita.",
"bodyWithWires_other": "Isso vai remover a placa do espaço de trabalho e {{count}} fios conectados. Esta ação não pode ser desfeita.",
"cancel": "Cancelar",
"remove": "Remover"
}
}
}
}

View File

@ -179,6 +179,55 @@
"copy": "Копировать",
"privateWarning": "Этот проект приватный. Другие увидят ошибку 403 при открытии ссылки.",
"close": "Закрыть"
},
"canvas": {
"status": {
"running": "Работает",
"stopped": "Остановлено"
},
"activeBoard": "Активная плата",
"noBoard": "Нет платы",
"noBoardHint": "На холсте нет платы — добавьте её кнопкой «Добавить», чтобы скомпилировать и запустить код",
"undo": {
"label": "Отменить",
"title": "Отменить: {{description}} (Ctrl+Z)",
"empty": "Нечего отменять"
},
"redo": {
"label": "Повторить",
"title": "Повторить: {{description}} (Ctrl+Y)",
"empty": "Нечего повторять"
},
"toggleSerialMonitor": "Показать/скрыть Serial Monitor",
"serial": "Serial",
"toggleScope": "Показать/скрыть осциллограф / логический анализатор",
"scope": "Осциллограф",
"zoomIn": "Приблизить",
"zoomOut": "Отдалить",
"resetView": "Сбросить вид (нажмите для сброса)",
"componentCount_one": "{{count}} компонент",
"componentCount_few": "{{count}} компонента",
"componentCount_many": "{{count}} компонентов",
"componentCount_other": "{{count}} компонентов",
"addComponentTitle": "Добавить компонент",
"add": "Добавить",
"dismiss": "Закрыть",
"removeBoard": "Удалить плату",
"wireCount_one": "{{count}} провод",
"wireCount_few": "{{count}} провода",
"wireCount_many": "{{count}} проводов",
"wireCount_other": "{{count}} проводов",
"removeConfirm": {
"boardFallback": "Плата",
"title": "Удалить {{label}}?",
"body": "Плата будет удалена из рабочей области. Это действие нельзя отменить.",
"bodyWithWires_one": "Плата будет удалена из рабочей области вместе с {{count}} подключённым проводом. Это действие нельзя отменить.",
"bodyWithWires_few": "Плата будет удалена из рабочей области вместе с {{count}} подключёнными проводами. Это действие нельзя отменить.",
"bodyWithWires_many": "Плата будет удалена из рабочей области вместе с {{count}} подключёнными проводами. Это действие нельзя отменить.",
"bodyWithWires_other": "Плата будет удалена из рабочей области вместе с {{count}} подключёнными проводами. Это действие нельзя отменить.",
"cancel": "Отмена",
"remove": "Удалить"
}
}
}
}

View File

@ -179,6 +179,46 @@
"copy": "复制",
"privateWarning": "此项目为私有。他人打开此链接将看到 403 错误。",
"close": "关闭"
},
"canvas": {
"status": {
"running": "运行中",
"stopped": "已停止"
},
"activeBoard": "当前开发板",
"noBoard": "无开发板",
"noBoardHint": "画布上没有开发板 —— 点击「添加」按钮以编译并运行代码",
"undo": {
"label": "撤销",
"title": "撤销:{{description}} (Ctrl+Z)",
"empty": "无可撤销"
},
"redo": {
"label": "重做",
"title": "重做:{{description}} (Ctrl+Y)",
"empty": "无可重做"
},
"toggleSerialMonitor": "切换串口监视器",
"serial": "串口",
"toggleScope": "切换示波器 / 逻辑分析仪",
"scope": "示波器",
"zoomIn": "放大",
"zoomOut": "缩小",
"resetView": "重置视图(点击重置)",
"componentCount_other": "{{count}} 个元件",
"addComponentTitle": "添加元件",
"add": "添加",
"dismiss": "关闭",
"removeBoard": "移除开发板",
"wireCount_other": "{{count}} 根连线",
"removeConfirm": {
"boardFallback": "开发板",
"title": "移除 {{label}}",
"body": "这将从工作区中移除该开发板。此操作无法撤销。",
"bodyWithWires_other": "这将从工作区中移除该开发板以及 {{count}} 根连接的线缆。此操作无法撤销。",
"cancel": "取消",
"remove": "移除"
}
}
}
}