diff --git a/.vscode/launch.json b/.vscode/launch.json index c3f37149e..5c336c081 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,10 +8,6 @@ "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", "windows": { "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd", - "env": { - "NODE_ENV": "development", - "NODE_PRESERVE_SYMLINKS": "1" - } }, "cwd": "${workspaceFolder}/electron-app", "protocol": "inspector", diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index 9a46e057e..9856fc401 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -1,6 +1,6 @@ { "name": "arduino-ide-extension", - "version": "2.0.0-rc1", + "version": "2.0.0-rc2", "description": "An extension for Theia building the Arduino IDE", "license": "AGPL-3.0-or-later", "scripts": { @@ -19,12 +19,11 @@ "test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\"" }, "dependencies": { - "arduino-serial-plotter-webapp": "0.0.15", "@grpc/grpc-js": "^1.3.7", "@theia/application-package": "1.19.0", "@theia/core": "1.19.0", "@theia/editor": "1.19.0", - "@theia/editor-preview": "1.19.0", + "@theia/editor-preview": "1.19.0", "@theia/filesystem": "1.19.0", "@theia/git": "1.19.0", "@theia/keymaps": "1.19.0", @@ -53,10 +52,10 @@ "@types/ps-tree": "^1.1.0", "@types/react-select": "^3.0.0", "@types/react-tabs": "^2.3.2", - "@types/sinon": "^7.5.2", "@types/temp": "^0.8.34", "@types/which": "^1.3.1", "ajv": "^6.5.3", + "arduino-serial-plotter-webapp": "0.0.15", "async-mutex": "^0.3.0", "atob": "^2.1.2", "auth0-js": "^9.14.0", @@ -97,6 +96,8 @@ "@types/chai-string": "^1.4.2", "@types/mocha": "^5.2.7", "@types/react-window": "^1.8.5", + "@types/sinon": "^10.0.6", + "@types/sinon-chai": "^3.2.6", "chai": "^4.2.0", "chai-string": "^1.5.0", "decompress": "^4.2.0", @@ -109,7 +110,8 @@ "moment": "^2.24.0", "protoc": "^1.0.4", "shelljs": "^0.8.3", - "sinon": "^9.0.1", + "sinon": "^12.0.1", + "sinon-chai": "^3.7.0", "typemoq": "^2.1.0", "uuid": "^3.2.1", "yargs": "^11.1.0" @@ -149,7 +151,7 @@ ], "arduino": { "cli": { - "version": "0.20.1" + "version": "0.20.2" }, "fwuploader": { "version": "2.0.0" diff --git a/arduino-ide-extension/scripts/download-ls.js b/arduino-ide-extension/scripts/download-ls.js index 6e53f9dcc..2b65023b2 100755 --- a/arduino-ide-extension/scripts/download-ls.js +++ b/arduino-ide-extension/scripts/download-ls.js @@ -4,7 +4,7 @@ // - https://downloads.arduino.cc/arduino-language-server/clangd/clangd_${VERSION}_${SUFFIX} (() => { - const DEFAULT_ALS_VERSION = '0.5.0-rc2'; + const DEFAULT_ALS_VERSION = '0.5.0'; const DEFAULT_CLANGD_VERSION = 'snapshot_20210124'; const path = require('path'); diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 3cfc49fd8..b72ba4e29 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -7,6 +7,7 @@ import { SketchesService, ExecutableService, Sketch, + LibraryService, } from '../common/protocol'; import { Mutex } from 'async-mutex'; import { @@ -69,7 +70,7 @@ import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-c import { SaveAsSketch } from './contributions/save-as-sketch'; import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-widget-contribution'; -const INIT_AVR_PACKAGES = 'initializedAvrPackages'; +const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages'; @injectable() export class ArduinoFrontendContribution @@ -89,6 +90,9 @@ export class ArduinoFrontendContribution @inject(BoardsService) protected readonly boardsService: BoardsService; + @inject(LibraryService) + protected readonly libraryService: LibraryService; + @inject(BoardsServiceProvider) protected readonly boardsServiceClientImpl: BoardsServiceProvider; @@ -161,15 +165,26 @@ export class ArduinoFrontendContribution @postConstruct() protected async init(): Promise { - const notFirstStartup = await this.localStorageService.getData( - INIT_AVR_PACKAGES - ); - if (!notFirstStartup) { - await this.localStorageService.setData(INIT_AVR_PACKAGES, true); + const isFirstStartup = !(await this.localStorageService.getData( + INIT_LIBS_AND_PACKAGES + )); + if (isFirstStartup) { + await this.localStorageService.setData(INIT_LIBS_AND_PACKAGES, true); const avrPackage = await this.boardsService.getBoardPackage({ id: 'arduino:avr', }); - avrPackage && (await this.boardsService.install({ item: avrPackage })); + const builtInLibrary = ( + await this.libraryService.search({ + query: 'Arduino_BuiltIn', + }) + )[0]; + + !!avrPackage && (await this.boardsService.install({ item: avrPackage })); + !!builtInLibrary && + (await this.libraryService.install({ + item: builtInLibrary, + installDependencies: true, + })); } if (!window.navigator.onLine) { // tslint:disable-next-line:max-line-length @@ -359,7 +374,7 @@ export class ArduinoFrontendContribution 'arduino.languageserver.start', { lsPath, - cliDaemonAddr: `localhost:${config.daemon.port}`, + cliDaemonAddr: `localhost:${config.daemon.port}`, // TODO: verify if this port is coming from the BE clangdPath, log: currentSketchPath ? currentSketchPath : log, cliDaemonInstance: '1', diff --git a/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts b/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts index 1ee0dea56..75aaef8fa 100644 --- a/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts +++ b/arduino-ide-extension/src/browser/contributions/burn-bootloader.ts @@ -48,7 +48,6 @@ export class BurnBootloader extends SketchContribution { } async burnBootloader(): Promise { - await this.serialConnection.disconnect(); try { const { boardsConfig } = this.boardsServiceClientImpl; const port = boardsConfig.selectedPort; @@ -79,11 +78,15 @@ export class BurnBootloader extends SketchContribution { } ); } catch (e) { - this.messageService.error(e.toString()); - } finally { - if (this.serialConnection.isSerialOpen()) { - await this.serialConnection.connect(); + let errorMessage = ""; + if (typeof e === "string") { + errorMessage = e; + } else { + errorMessage = e.toString(); } + this.messageService.error(errorMessage); + } finally { + await this.serialConnection.reconnectAfterUpload(); } } } diff --git a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts index b18861b0d..df196cb7f 100644 --- a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts @@ -63,7 +63,9 @@ export class UploadSketch extends SketchContribution { if (!fqbn) { return ''; } - const address = boardsConfig.selectedBoard?.port?.address; + const address = + boardsConfig.selectedBoard?.port?.address || + boardsConfig.selectedPort?.address; if (!address) { return ''; } @@ -210,7 +212,7 @@ export class UploadSketch extends SketchContribution { if (!sketch) { return; } - await this.serialConnection.disconnect(); + try { const { boardsConfig } = this.boardsServiceClientImpl; const [fqbn, { selectedProgrammer }, verify, verbose, sourceOverride] = @@ -277,32 +279,18 @@ export class UploadSketch extends SketchContribution { { timeout: 3000 } ); } catch (e) { - this.messageService.error(e.toString()); + let errorMessage = ''; + if (typeof e === 'string') { + errorMessage = e; + } else { + errorMessage = e.toString(); + } + this.messageService.error(errorMessage); } finally { this.uploadInProgress = false; this.onDidChangeEmitter.fire(); - if ( - this.serialConnection.isSerialOpen() && - this.serialConnection.serialConfig - ) { - const { board, port } = this.serialConnection.serialConfig; - try { - await this.boardsServiceClientImpl.waitUntilAvailable( - Object.assign(board, { port }), - 10_000 - ); - await this.serialConnection.connect(); - } catch (waitError) { - this.messageService.error( - nls.localize( - 'arduino/sketch/couldNotConnectToSerial', - 'Could not reconnect to serial port. {0}', - waitError.toString() - ) - ); - } - } + setTimeout(() => this.serialConnection.reconnectAfterUpload(), 5000); } } } diff --git a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts index 2440f1a6b..898953ae8 100644 --- a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts @@ -127,7 +127,13 @@ export class VerifySketch extends SketchContribution { { timeout: 3000 } ); } catch (e) { - this.messageService.error(e.toString()); + let errorMessage = ""; + if (typeof e === "string") { + errorMessage = e; + } else { + errorMessage = e.toString(); + } + this.messageService.error(errorMessage); } finally { this.verifyInProgress = false; this.onDidChangeEmitter.fire(); diff --git a/arduino-ide-extension/src/browser/serial/monitor/monitor-view-contribution.tsx b/arduino-ide-extension/src/browser/serial/monitor/monitor-view-contribution.tsx index 9d4facff3..970819c97 100644 --- a/arduino-ide-extension/src/browser/serial/monitor/monitor-view-contribution.tsx +++ b/arduino-ide-extension/src/browser/serial/monitor/monitor-view-contribution.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { injectable, inject } from 'inversify'; -import { AbstractViewContribution } from '@theia/core/lib/browser'; +import { AbstractViewContribution, codicon } from '@theia/core/lib/browser'; import { MonitorWidget } from './monitor-widget'; import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core'; import { @@ -32,7 +32,7 @@ export namespace SerialMonitor { { id: 'serial-monitor-clear-output', label: 'Clear Output', - iconClass: 'clear-all', + iconClass: codicon('clear-all'), }, 'vscode/output.contribution/clearOutput.label' ); diff --git a/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx b/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx index b1ae21953..f5e68df54 100644 --- a/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx +++ b/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx @@ -12,7 +12,7 @@ import { import { SerialConfig } from '../../../common/protocol/serial-service'; import { ArduinoSelect } from '../../widgets/arduino-select'; import { SerialModel } from '../serial-model'; -import { Serial, SerialConnectionManager } from '../serial-connection-manager'; +import { SerialConnectionManager } from '../serial-connection-manager'; import { SerialMonitorSendInput } from './serial-monitor-send-input'; import { SerialMonitorOutput } from './serial-monitor-send-output'; import { BoardsServiceProvider } from '../../boards/boards-service-provider'; @@ -57,9 +57,7 @@ export class MonitorWidget extends ReactWidget { this.scrollOptions = undefined; this.toDispose.push(this.clearOutputEmitter); this.toDispose.push( - Disposable.create(() => - this.serialConnection.closeSerial(Serial.Type.Monitor) - ) + Disposable.create(() => this.serialConnection.closeWStoBE()) ); } @@ -83,7 +81,7 @@ export class MonitorWidget extends ReactWidget { protected onAfterAttach(msg: Message): void { super.onAfterAttach(msg); - this.serialConnection.openSerial(Serial.Type.Monitor); + this.serialConnection.openWSToBE(); } onCloseRequest(msg: Message): void { @@ -171,7 +169,7 @@ export class MonitorWidget extends ReactWidget {
diff --git a/arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx b/arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx index 6c06c2f32..5b730c17a 100644 --- a/arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx +++ b/arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx @@ -1,18 +1,20 @@ import * as React from 'react'; import { Key, KeyCode } from '@theia/core/lib/browser/keys'; import { Board, Port } from '../../../common/protocol/boards-service'; -import { SerialConfig } from '../../../common/protocol/serial-service'; import { isOSX } from '@theia/core/lib/common/os'; -import { nls } from '@theia/core/lib/common'; +import { DisposableCollection, nls } from '@theia/core/lib/common'; +import { SerialConnectionManager } from '../serial-connection-manager'; +import { SerialPlotter } from '../plotter/protocol'; export namespace SerialMonitorSendInput { export interface Props { - readonly serialConfig?: SerialConfig; + readonly serialConnection: SerialConnectionManager; readonly onSend: (text: string) => void; readonly resolveFocus: (element: HTMLElement | undefined) => void; } export interface State { text: string; + connected: boolean; } } @@ -20,20 +22,45 @@ export class SerialMonitorSendInput extends React.Component< SerialMonitorSendInput.Props, SerialMonitorSendInput.State > { + protected toDisposeBeforeUnmount = new DisposableCollection(); + constructor(props: Readonly) { super(props); - this.state = { text: '' }; + this.state = { text: '', connected: false }; this.onChange = this.onChange.bind(this); this.onSend = this.onSend.bind(this); this.onKeyDown = this.onKeyDown.bind(this); } + componentDidMount(): void { + this.props.serialConnection.isBESerialConnected().then((connected) => { + this.setState({ connected }); + }); + + this.toDisposeBeforeUnmount.pushAll([ + this.props.serialConnection.onRead(({ messages }) => { + if ( + messages.command === + SerialPlotter.Protocol.Command.MIDDLEWARE_CONFIG_CHANGED && + 'connected' in messages.data + ) { + this.setState({ connected: messages.data.connected }); + } + }), + ]); + } + + componentWillUnmount(): void { + // TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout? + this.toDisposeBeforeUnmount.dispose(); + } + render(): React.ReactNode { return ( { if (!!this.window) { this.window = null; - await this.serialConnection.closeSerial(Serial.Type.Plotter); } }); - return super.onStart(app); } @@ -77,17 +75,15 @@ export class PlotterFrontendContribution extends Contribution { this.window.focus(); return; } - const status = await this.serialConnection.openSerial(Serial.Type.Plotter); const wsPort = this.serialConnection.getWsPort(); - if (Status.isOK(status) && wsPort) { + if (wsPort) { this.open(wsPort); } else { - this.serialConnection.closeSerial(Serial.Type.Plotter); this.messageService.error(`Couldn't open serial plotter`); } } - protected open(wsPort: number): void { + protected async open(wsPort: number): Promise { const initConfig: Partial = { baudrates: SerialConfig.BaudRates.map((b) => b), currentBaudrate: this.model.baudRate, @@ -95,7 +91,7 @@ export class PlotterFrontendContribution extends Contribution { darkTheme: this.themeService.getCurrentTheme().type === 'dark', wsPort, interpolate: this.model.interpolate, - connected: this.serialConnection.connected, + connected: await this.serialConnection.isBESerialConnected(), serialPort: this.boardsServiceProvider.boardsConfig.selectedPort?.address, }; const urlWithParams = queryString.stringifyUrl( diff --git a/arduino-ide-extension/src/browser/serial/serial-connection-manager.ts b/arduino-ide-extension/src/browser/serial/serial-connection-manager.ts index aad197ac5..5c29cf566 100644 --- a/arduino-ide-extension/src/browser/serial/serial-connection-manager.ts +++ b/arduino-ide-extension/src/browser/serial/serial-connection-manager.ts @@ -1,5 +1,4 @@ import { injectable, inject } from 'inversify'; -import { deepClone } from '@theia/core/lib/common/objects'; import { Emitter, Event } from '@theia/core/lib/common/event'; import { MessageService } from '@theia/core/lib/common/message-service'; import { @@ -23,8 +22,6 @@ import { nls } from '@theia/core/lib/common/nls'; @injectable() export class SerialConnectionManager { - protected _state: Serial.State = []; - protected _connected = false; protected config: Partial = { board: undefined, port: undefined, @@ -62,7 +59,9 @@ export class SerialConnectionManager { protected readonly boardsServiceProvider: BoardsServiceProvider, @inject(MessageService) protected messageService: MessageService, @inject(ThemeService) protected readonly themeService: ThemeService, - @inject(CoreService) protected readonly core: CoreService + @inject(CoreService) protected readonly core: CoreService, + @inject(BoardsServiceProvider) + protected readonly boardsServiceClientImpl: BoardsServiceProvider ) { this.serialServiceClient.onWebSocketChanged( this.handleWebSocketChanged.bind(this) @@ -89,8 +88,11 @@ export class SerialConnectionManager { ); // Handles the `baudRate` changes by reconnecting if required. - this.serialModel.onChange(({ property }) => { - if (property === 'baudRate' && this.connected) { + this.serialModel.onChange(async ({ property }) => { + if ( + property === 'baudRate' && + (await this.serialService.isSerialPortOpen()) + ) { const { boardsConfig } = this.boardsServiceProvider; this.handleBoardConfigChange(boardsConfig); } @@ -114,8 +116,8 @@ export class SerialConnectionManager { } /** - * Set the config passing only the properties that has changed. If some has changed and the serial is open, - * we try to reconnect + * Updated the config in the BE passing only the properties that has changed. + * BE will create a new connection if needed. * * @param newConfig the porperties of the config that has changed */ @@ -127,17 +129,16 @@ export class SerialConnectionManager { this.config = { ...this.config, [key]: newConfig[key] }; } }); - if ( - configHasChanged && - this.isSerialOpen() && - !(await this.core.isUploading()) - ) { + + if (configHasChanged) { this.serialService.updateWsConfigParam({ currentBaudrate: this.config.baudRate, serialPort: this.config.port?.address, }); - await this.disconnect(); - await this.connect(); + + if (isSerialConfig(this.config)) { + this.serialService.setSerialConfig(this.config); + } } } @@ -149,134 +150,56 @@ export class SerialConnectionManager { return this.wsPort; } - isWebSocketConnected(): boolean { - return !!this.webSocket?.url; - } - protected handleWebSocketChanged(wsPort: number): void { this.wsPort = wsPort; } - /** - * When the serial is open and the frontend is connected to the serial, we create the websocket here - */ - protected createWsConnection(): boolean { - if (this.wsPort) { - try { - this.webSocket = new WebSocket(`ws://localhost:${this.wsPort}`); - this.webSocket.onmessage = (res) => { - const messages = JSON.parse(res.data); - this.onReadEmitter.fire({ messages }); - }; - return true; - } catch { - return false; - } - } - return false; - } - - /** - * Sets the types of connections needed by the client. - * - * @param newState The array containing the list of desired connections. - * If the previuos state was empty and 'newState' is not, it tries to reconnect to the serial service - * If the provios state was NOT empty and now it is, it disconnects to the serial service - * @returns The status of the operation - */ - protected async setState(newState: Serial.State): Promise { - const oldState = deepClone(this._state); - let status = Status.OK; - - if (this.isSerialOpen(oldState) && !this.isSerialOpen(newState)) { - status = await this.disconnect(); - } else if (!this.isSerialOpen(oldState) && this.isSerialOpen(newState)) { - if (await this.core.isUploading()) { - this.messageService.error(`Cannot open serial port when uploading`); - return Status.NOT_CONNECTED; - } - status = await this.connect(); - } - this._state = newState; - return status; - } - - protected get state(): Serial.State { - return this._state; - } - - isSerialOpen(state?: Serial.State): boolean { - return (state ? state : this._state).length > 0; - } - get serialConfig(): SerialConfig | undefined { return isSerialConfig(this.config) ? (this.config as SerialConfig) : undefined; } - get connected(): boolean { - return this._connected; + async isBESerialConnected(): Promise { + return await this.serialService.isSerialPortOpen(); } - set connected(c: boolean) { - this._connected = c; - this.serialService.updateWsConfigParam({ connected: c }); - this.onConnectionChangedEmitter.fire(this._connected); - } - /** - * Called when a client opens the serial from the GUI - * - * @param type could be either 'Monitor' or 'Plotter'. If it's 'Monitor' we also connect to the websocket and - * listen to the message events - * @returns the status of the operation - */ - async openSerial(type: Serial.Type): Promise { + openWSToBE(): void { if (!isSerialConfig(this.config)) { this.messageService.error( `Please select a board and a port to open the serial connection.` ); - return Status.NOT_CONNECTED; } - if (this.state.includes(type)) return Status.OK; - const newState = deepClone(this.state); - newState.push(type); - const status = await this.setState(newState); - if (Status.isOK(status) && type === Serial.Type.Monitor) - this.createWsConnection(); - return status; + + if (!this.webSocket && this.wsPort) { + try { + this.webSocket = new WebSocket(`ws://localhost:${this.wsPort}`); + this.webSocket.onmessage = (res) => { + const messages = JSON.parse(res.data); + this.onReadEmitter.fire({ messages }); + }; + } catch { + this.messageService.error(`Unable to connect to websocket`); + } + } } - /** - * Called when a client closes the serial from the GUI - * - * @param type could be either 'Monitor' or 'Plotter'. If it's 'Monitor' we close the websocket connection - * @returns the status of the operation - */ - async closeSerial(type: Serial.Type): Promise { - const index = this.state.indexOf(type); - let status = Status.OK; - if (index >= 0) { - const newState = deepClone(this.state); - newState.splice(index, 1); - status = await this.setState(newState); - if ( - Status.isOK(status) && - type === Serial.Type.Monitor && - this.webSocket - ) { + closeWStoBE(): void { + if (this.webSocket) { + try { this.webSocket.close(); this.webSocket = undefined; + } catch { + this.messageService.error(`Unable to close websocket`); } } - return status; } /** * Handles error on the SerialServiceClient and try to reconnect, eventually */ - handleError(error: SerialError): void { - if (!this.connected) return; + async handleError(error: SerialError): Promise { + if (!(await this.serialService.isSerialPortOpen())) return; const { code, config } = error; const { board, port } = config; const options = { timeout: 3000 }; @@ -329,9 +252,8 @@ export class SerialConnectionManager { break; } } - this.connected = false; - if (this.isSerialOpen()) { + if ((await this.serialService.clientsAttached()) > 0) { if (this.serialErrors.length >= 10) { this.messageService.warn( nls.localize( @@ -354,7 +276,7 @@ export class SerialConnectionManager { this.messageService.warn( nls.localize( 'arduino/serial/reconnect', - 'Reconnecting {0} to {1} in {2] seconds...', + 'Reconnecting {0} to {1} in {2} seconds...', Board.toString(board, { useFqbn: false, }), @@ -363,59 +285,31 @@ export class SerialConnectionManager { ) ); this.reconnectTimeout = window.setTimeout( - () => this.connect(), + () => this.reconnectAfterUpload(), timeout ); } } } - async connect(): Promise { - if (this.connected) return Status.ALREADY_CONNECTED; - if (!isSerialConfig(this.config)) return Status.NOT_CONNECTED; - - console.info( - `>>> Creating serial connection for ${Board.toString( - this.config.board - )} on port ${Port.toString(this.config.port)}...` - ); - const connectStatus = await this.serialService.connect(this.config); - if (Status.isOK(connectStatus)) { - this.connected = true; - console.info( - `<<< Serial connection created for ${Board.toString(this.config.board, { - useFqbn: false, - })} on port ${Port.toString(this.config.port)}.` - ); - } - - return Status.isOK(connectStatus); - } - - async disconnect(): Promise { - if (!this.connected) { - return Status.OK; - } - - console.log('>>> Disposing existing serial connection...'); - const status = await this.serialService.disconnect(); - if (Status.isOK(status)) { - this.connected = false; - console.log( - `<<< Disposed serial connection. Was: ${Serial.Config.toString( - this.config - )}` - ); - this.wsPort = undefined; - } else { - console.warn( - `<<< Could not dispose serial connection. Activate connection: ${Serial.Config.toString( - this.config - )}` + async reconnectAfterUpload(): Promise { + try { + if (isSerialConfig(this.config)) { + await this.boardsServiceClientImpl.waitUntilAvailable( + Object.assign(this.config.board, { port: this.config.port }), + 10_000 + ); + this.serialService.connectSerialIfRequired(); + } + } catch (waitError) { + this.messageService.error( + nls.localize( + 'arduino/sketch/couldNotConnectToSerial', + 'Could not reconnect to serial port. {0}', + waitError.toString() + ) ); } - - return status; } /** @@ -424,7 +318,7 @@ export class SerialConnectionManager { * It is a NOOP if connected. */ async send(data: string): Promise { - if (!this.connected) { + if (!(await this.serialService.isSerialPortOpen())) { return Status.NOT_CONNECTED; } return new Promise((resolve) => { @@ -438,7 +332,7 @@ export class SerialConnectionManager { return this.onConnectionChangedEmitter.event; } - get onRead(): Event<{ messages: string[] }> { + get onRead(): Event<{ messages: any }> { return this.onReadEmitter.event; } @@ -453,18 +347,6 @@ export class SerialConnectionManager { } export namespace Serial { - export enum Type { - Monitor = 'Monitor', - Plotter = 'Plotter', - } - - /** - * The state represents which types of connections are needed by the client, and it should match whether the Serial Monitor - * or the Serial Plotter are open or not in the GUI. It's an array cause it's possible to have both, none or only one of - * them open - */ - export type State = Serial.Type[]; - export namespace Config { export function toString(config: Partial): string { if (!isSerialConfig(config)) return ''; diff --git a/arduino-ide-extension/src/browser/theia/messages/notification-center-component.tsx b/arduino-ide-extension/src/browser/theia/messages/notification-center-component.tsx index fa929276f..390662bcb 100644 --- a/arduino-ide-extension/src/browser/theia/messages/notification-center-component.tsx +++ b/arduino-ide-extension/src/browser/theia/messages/notification-center-component.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { NotificationComponent } from './notification-component'; import { NotificationCenterComponent as TheiaNotificationCenterComponent } from '@theia/messages/lib/browser/notification-center-component'; import { nls } from '@theia/core/lib/common'; +import { codicon } from '@theia/core/lib/browser'; const PerfectScrollbar = require('react-perfect-scrollbar'); @@ -28,7 +29,7 @@ export class NotificationCenterComponent extends TheiaNotificationCenterComponen
  • {expandable && (
  • ; + getPort(): Promise; } diff --git a/arduino-ide-extension/src/common/protocol/serial-service.ts b/arduino-ide-extension/src/common/protocol/serial-service.ts index 0aa2793fa..0e77bb9cc 100644 --- a/arduino-ide-extension/src/common/protocol/serial-service.ts +++ b/arduino-ide-extension/src/common/protocol/serial-service.ts @@ -18,15 +18,22 @@ export namespace Status { export const ALREADY_CONNECTED: ErrorStatus = { message: 'Already connected.', }; + export const CONFIG_MISSING: ErrorStatus = { + message: 'Serial Config missing.', + }; } export const SerialServicePath = '/services/serial'; export const SerialService = Symbol('SerialService'); export interface SerialService extends JsonRpcServer { - connect(config: SerialConfig): Promise; - disconnect(): Promise; + clientsAttached(): Promise; + setSerialConfig(config: SerialConfig): Promise; sendMessageToSerial(message: string): Promise; updateWsConfigParam(config: Partial): Promise; + isSerialPortOpen(): Promise; + connectSerialIfRequired(): Promise; + disconnect(reason?: SerialError): Promise; + uploadInProgress: boolean; } export interface SerialConfig { diff --git a/arduino-ide-extension/src/electron-main/theia/electron-main-application.ts b/arduino-ide-extension/src/electron-main/theia/electron-main-application.ts index d2c602924..e625a8288 100644 --- a/arduino-ide-extension/src/electron-main/theia/electron-main-application.ts +++ b/arduino-ide-extension/src/electron-main/theia/electron-main-application.ts @@ -108,7 +108,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication { electronWindow.webContents.on( 'new-window', - (event, url, frameName, disposition, options, additionalFeatures) => { + (event, url, frameName, disposition, options) => { if (frameName === 'serialPlotter') { event.preventDefault(); Object.assign(options, { diff --git a/arduino-ide-extension/src/node/arduino-daemon-impl.ts b/arduino-ide-extension/src/node/arduino-daemon-impl.ts index efa8a33c2..e8ff9f27b 100644 --- a/arduino-ide-extension/src/node/arduino-daemon-impl.ts +++ b/arduino-ide-extension/src/node/arduino-daemon-impl.ts @@ -42,6 +42,7 @@ export class ArduinoDaemonImpl protected _running = false; protected _ready = new Deferred(); protected _execPath: string | undefined; + protected _port: string; // Backend application lifecycle. @@ -55,12 +56,17 @@ export class ArduinoDaemonImpl return Promise.resolve(this._running); } + async getPort(): Promise { + return Promise.resolve(this._port); + } + async startDaemon(): Promise { try { this.toDispose.dispose(); // This will `kill` the previously started daemon process, if any. const cliPath = await this.getExecPath(); this.onData(`Starting daemon from ${cliPath}...`); - const daemon = await this.spawnDaemonProcess(); + const { daemon, port } = await this.spawnDaemonProcess(); + this._port = port; // Watchdog process for terminating the daemon process when the backend app terminates. spawn( process.execPath, @@ -148,6 +154,10 @@ export class ArduinoDaemonImpl const cliConfigPath = join(FileUri.fsPath(configDirUri), CLI_CONFIG); return [ 'daemon', + '--format', + 'jsonmini', + '--port', + '0', '--config-file', `"${cliConfigPath}"`, '-v', @@ -156,12 +166,15 @@ export class ArduinoDaemonImpl ]; } - protected async spawnDaemonProcess(): Promise { + protected async spawnDaemonProcess(): Promise<{ + daemon: ChildProcess; + port: string; + }> { const [cliPath, args] = await Promise.all([ this.getExecPath(), this.getSpawnArgs(), ]); - const ready = new Deferred(); + const ready = new Deferred<{ daemon: ChildProcess; port: string }>(); const options = { shell: true }; const daemon = spawn(`"${cliPath}"`, args, options); @@ -171,20 +184,37 @@ export class ArduinoDaemonImpl daemon.stdout.on('data', (data) => { const message = data.toString(); + + let port = ''; + let address = ''; + message + .split('\n') + .filter((line: string) => line.length) + .forEach((line: string) => { + try { + const parsedLine = JSON.parse(line); + if ('Port' in parsedLine) { + port = parsedLine.Port; + } + if ('IP' in parsedLine) { + address = parsedLine.IP; + } + } catch (err) { + // ignore + } + }); + this.onData(message); if (!grpcServerIsReady) { const error = DaemonError.parse(message); if (error) { ready.reject(error); + return; } - for (const expected of [ - 'Daemon is listening on TCP port', - 'Daemon is now listening on 127.0.0.1', - ]) { - if (message.includes(expected)) { - grpcServerIsReady = true; - ready.resolve(daemon); - } + + if (port.length && address.length) { + grpcServerIsReady = true; + ready.resolve({ daemon, port }); } } }); diff --git a/arduino-ide-extension/src/node/board-discovery.ts b/arduino-ide-extension/src/node/board-discovery.ts index 8a639e07e..e612979e1 100644 --- a/arduino-ide-extension/src/node/board-discovery.ts +++ b/arduino-ide-extension/src/node/board-discovery.ts @@ -60,11 +60,29 @@ export class BoardDiscovery extends CoreClientAware { this.startBoardListWatch(coreClient); } + stopBoardListWatch(coreClient: CoreClientProvider.Client): Promise { + return new Promise((resolve, reject) => { + if (!this.boardWatchDuplex) { + return resolve(); + } + + const { instance } = coreClient; + const req = new BoardListWatchRequest(); + req.setInstance(instance); + try { + this.boardWatchDuplex.write(req.setInterrupt(true), resolve); + } catch (e) { + this.discoveryLogger.error(e); + resolve(); + } + }); + } + startBoardListWatch(coreClient: CoreClientProvider.Client): void { if (this.watching) { // We want to avoid starting the board list watch process multiple // times to meet unforseen consequences - return + return; } this.watching = true; const { client, instance } = coreClient; @@ -73,9 +91,19 @@ export class BoardDiscovery extends CoreClientAware { this.boardWatchDuplex = client.boardListWatch(); this.boardWatchDuplex.on('end', () => { this.watching = false; - console.info('board watch ended') - }) + console.info('board watch ended'); + }); + this.boardWatchDuplex.on('close', () => { + this.watching = false; + console.info('board watch ended'); + }); this.boardWatchDuplex.on('data', (resp: BoardListWatchResponse) => { + if (resp.getEventType() === 'quit') { + this.watching = false; + console.info('board watch ended'); + return; + } + const detectedPort = resp.getPort(); if (detectedPort) { let eventType: 'add' | 'remove' | 'unknown' = 'unknown'; @@ -96,7 +124,7 @@ export class BoardDiscovery extends CoreClientAware { const address = (detectedPort as any).getPort().getAddress(); const protocol = (detectedPort as any).getPort().getProtocol(); - const label = (detectedPort as any).getPort().getLabel();; + const label = (detectedPort as any).getPort().getLabel(); const port = { address, protocol, label }; const boards: Board[] = []; for (const item of detectedPort.getMatchingBoardsList()) { @@ -111,7 +139,9 @@ export class BoardDiscovery extends CoreClientAware { if (newState[port.address]) { const [, knownBoards] = newState[port.address]; console.warn( - `Port '${port.address}' was already available. Known boards before override: ${JSON.stringify( + `Port '${ + port.address + }' was already available. Known boards before override: ${JSON.stringify( knownBoards )}` ); diff --git a/arduino-ide-extension/src/node/boards-service-impl.ts b/arduino-ide-extension/src/node/boards-service-impl.ts index acb4a7315..d7c5da331 100644 --- a/arduino-ide-extension/src/node/boards-service-impl.ts +++ b/arduino-ide-extension/src/node/boards-service-impl.ts @@ -45,7 +45,8 @@ import { InstallWithProgress } from './grpc-installable'; @injectable() export class BoardsServiceImpl extends CoreClientAware - implements BoardsService { + implements BoardsService +{ @inject(ILogger) protected logger: ILogger; @@ -247,7 +248,10 @@ export class BoardsServiceImpl return boards; } - async getBoardUserFields(options: { fqbn: string, protocol: string }): Promise { + async getBoardUserFields(options: { + fqbn: string; + protocol: string; + }): Promise { await this.coreClientProvider.initialized; const coreClient = await this.coreClient(); const { client, instance } = coreClient; @@ -257,25 +261,23 @@ export class BoardsServiceImpl supportedUserFieldsReq.setFqbn(options.fqbn); supportedUserFieldsReq.setProtocol(options.protocol); - const supportedUserFieldsResp = await new Promise( - (resolve, reject) => { + const supportedUserFieldsResp = + await new Promise((resolve, reject) => { client.supportedUserFields(supportedUserFieldsReq, (err, resp) => { - (!!err ? reject : resolve)(!!err ? err : resp) - }) - } - ); - return supportedUserFieldsResp.getUserFieldsList().map(e => { + (!!err ? reject : resolve)(!!err ? err : resp); + }); + }); + return supportedUserFieldsResp.getUserFieldsList().map((e) => { return { toolId: e.getToolId(), name: e.getName(), label: e.getLabel(), secret: e.getSecret(), - value: "", + value: '', }; }); } - async search(options: { query?: string }): Promise { await this.coreClientProvider.initialized; const coreClient = await this.coreClient(); @@ -408,6 +410,10 @@ export class BoardsServiceImpl req.setVersion(version); console.info('>>> Starting boards package installation...', item); + + // stop the board discovery + await this.boardDiscovery.stopBoardListWatch(coreClient); + const resp = client.platformInstall(req); resp.on( 'data', @@ -418,7 +424,7 @@ export class BoardsServiceImpl ); await new Promise((resolve, reject) => { resp.on('end', () => { - this.boardDiscovery.startBoardListWatch(coreClient) + this.boardDiscovery.startBoardListWatch(coreClient); resolve(); }); resp.on('error', (error) => { @@ -456,6 +462,10 @@ export class BoardsServiceImpl req.setPlatformPackage(platform); console.info('>>> Starting boards package uninstallation...', item); + + // stop the board discovery + await this.boardDiscovery.stopBoardListWatch(coreClient); + const resp = client.platformUninstall(req); resp.on( 'data', @@ -466,7 +476,7 @@ export class BoardsServiceImpl ); await new Promise((resolve, reject) => { resp.on('end', () => { - this.boardDiscovery.startBoardListWatch(coreClient) + this.boardDiscovery.startBoardListWatch(coreClient); resolve(); }); resp.on('error', reject); diff --git a/arduino-ide-extension/src/node/config-service-impl.ts b/arduino-ide-extension/src/node/config-service-impl.ts index fd215f9e0..66bf58375 100644 --- a/arduino-ide-extension/src/node/config-service-impl.ts +++ b/arduino-ide-extension/src/node/config-service-impl.ts @@ -75,9 +75,11 @@ export class ConfigServiceImpl async getConfiguration(): Promise { await this.ready.promise; - return this.config; + await this.daemon.ready; + return { ...this.config, daemon: { port: await this.daemon.getPort() } }; } + // Used by frontend to update the config. async setConfiguration(config: Config): Promise { await this.ready.promise; if (Config.sameAs(this.config, config)) { @@ -108,7 +110,9 @@ export class ConfigServiceImpl copyDefaultCliConfig.locale = locale || 'en'; const proxy = Network.stringify(network); copyDefaultCliConfig.network = { proxy }; - const { port } = copyDefaultCliConfig.daemon; + + // always use the port of the daemon + const port = await this.daemon.getPort(); await this.updateDaemon(port, copyDefaultCliConfig); await this.writeDaemonState(port); diff --git a/arduino-ide-extension/src/node/core-client-provider.ts b/arduino-ide-extension/src/node/core-client-provider.ts index aac884879..fc792a8bd 100644 --- a/arduino-ide-extension/src/node/core-client-provider.ts +++ b/arduino-ide-extension/src/node/core-client-provider.ts @@ -48,9 +48,9 @@ export class CoreClientProvider extends GrpcClientProvider(); } - protected async reconcileClient( - port: string | number | undefined - ): Promise { + protected async reconcileClient(): Promise { + const port = await this.daemon.getPort(); + if (port && port === this._port) { // No need to create a new gRPC client, but we have to update the indexes. if (this._client && !(this._client instanceof Error)) { @@ -58,7 +58,7 @@ export class CoreClientProvider extends GrpcClientProvider { this.daemon.ready.then(async () => { - const cliConfig = this.configService.cliConfiguration; // First create the client and the instance synchronously // and notify client is ready. // TODO: Creation failure should probably be handled here - await this.reconcileClient( - cliConfig ? cliConfig.daemon.port : undefined - ).then(() => { + await this.reconcileClient().then(() => { this._created.resolve(); }); diff --git a/arduino-ide-extension/src/node/core-service-impl.ts b/arduino-ide-extension/src/node/core-service-impl.ts index 59535662b..f0319f511 100644 --- a/arduino-ide-extension/src/node/core-service-impl.ts +++ b/arduino-ide-extension/src/node/core-service-impl.ts @@ -23,6 +23,8 @@ import { NotificationServiceServer } from '../common/protocol'; import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb'; import { firstToUpperCase, firstToLowerCase } from '../common/utils'; import { Port } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb'; +import { nls } from '@theia/core'; +import { SerialService } from './../common/protocol/serial-service'; @injectable() export class CoreServiceImpl extends CoreClientAware implements CoreService { @@ -32,6 +34,9 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { @inject(NotificationServiceServer) protected readonly notificationService: NotificationServiceServer; + @inject(SerialService) + protected readonly serialService: SerialService; + protected uploading = false; async compile( @@ -85,11 +90,16 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { chunk: '\n--------------------------\nCompilation complete.\n', }); } catch (e) { + const errorMessage = nls.localize( + 'arduino/compile/error', + 'Compilation error: {0}', + e.details + ); this.responseService.appendToOutput({ - chunk: `Compilation error: ${e.details}\n`, + chunk: `${errorMessage}}\n`, severity: 'error', }); - throw e; + throw new Error(errorMessage); } } @@ -126,8 +136,13 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { ) => ClientReadableStream, task = 'upload' ): Promise { - this.uploading = true; await this.compile(Object.assign(options, { exportBinaries: false })); + + this.uploading = true; + this.serialService.uploadInProgress = true; + + await this.serialService.disconnect(); + const { sketchUri, fqbn, port, programmer } = options; const sketchPath = FileUri.fsPath(sketchUri); @@ -154,7 +169,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { req.setVerbose(options.verbose); req.setVerify(options.verify); - options.userFields.forEach(e => { + options.userFields.forEach((e) => { req.getUserFieldsMap().set(e.name, e.value); }); @@ -180,17 +195,28 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { ' complete.\n', }); } catch (e) { + const errorMessage = nls.localize( + 'arduino/upload/error', + '{0} error: {1}', + firstToUpperCase(task), + e.details + ); this.responseService.appendToOutput({ - chunk: `${firstToUpperCase(task)} error: ${e.details}\n`, + chunk: `${errorMessage}\n`, severity: 'error', }); - throw e; + throw new Error(errorMessage); } finally { this.uploading = false; + this.serialService.uploadInProgress = false; } } async burnBootloader(options: CoreService.Bootloader.Options): Promise { + this.uploading = true; + this.serialService.uploadInProgress = true; + await this.serialService.disconnect(); + await this.coreClientProvider.initialized; const coreClient = await this.coreClient(); const { client, instance } = coreClient; @@ -227,11 +253,19 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { result.on('end', () => resolve()); }); } catch (e) { + const errorMessage = nls.localize( + 'arduino/burnBootloader/error', + 'Error while burning the bootloader: {0}', + e.details + ); this.responseService.appendToOutput({ - chunk: `Error while burning the bootloader: ${e.details}\n`, + chunk: `${errorMessage}\n`, severity: 'error', }); - throw e; + throw new Error(errorMessage); + } finally { + this.uploading = false; + this.serialService.uploadInProgress = false; } } diff --git a/arduino-ide-extension/src/node/grpc-client-provider.ts b/arduino-ide-extension/src/node/grpc-client-provider.ts index d87ac026d..c4e5655c7 100644 --- a/arduino-ide-extension/src/node/grpc-client-provider.ts +++ b/arduino-ide-extension/src/node/grpc-client-provider.ts @@ -21,8 +21,7 @@ export abstract class GrpcClientProvider { @postConstruct() protected init(): void { const updateClient = () => { - const cliConfig = this.configService.cliConfiguration; - this.reconcileClient(cliConfig ? cliConfig.daemon.port : undefined); + this.reconcileClient(); }; this.configService.onConfigChange(updateClient); this.daemon.ready.then(updateClient); @@ -44,9 +43,9 @@ export abstract class GrpcClientProvider { } } - protected async reconcileClient( - port: string | number | undefined - ): Promise { + protected async reconcileClient(): Promise { + const port = await this.daemon.getPort(); + if (this._port === port) { return; // Nothing to do. } diff --git a/arduino-ide-extension/src/node/library-service-server-impl.ts b/arduino-ide-extension/src/node/library-service-server-impl.ts index 7d24dc834..e1f1b48dc 100644 --- a/arduino-ide-extension/src/node/library-service-server-impl.ts +++ b/arduino-ide-extension/src/node/library-service-server-impl.ts @@ -30,7 +30,8 @@ import { InstallWithProgress } from './grpc-installable'; @injectable() export class LibraryServiceImpl extends CoreClientAware - implements LibraryService { + implements LibraryService +{ @inject(ILogger) protected logger: ILogger; @@ -267,11 +268,13 @@ export class LibraryServiceImpl req.setInstance(instance); req.setName(item.name); req.setVersion(version); - if (options.installDependencies === false) { - req.setNoDeps(true); - } + req.setNoDeps(!options.installDependencies); console.info('>>> Starting library package installation...', item); + + // stop the board discovery + await this.boardDiscovery.stopBoardListWatch(coreClient); + const resp = client.libraryInstall(req); resp.on( 'data', @@ -282,13 +285,14 @@ export class LibraryServiceImpl ); await new Promise((resolve, reject) => { resp.on('end', () => { - this.boardDiscovery.startBoardListWatch(coreClient) + this.boardDiscovery.startBoardListWatch(coreClient); resolve(); }); resp.on('error', (error) => { this.responseService.appendToOutput({ - chunk: `Failed to install library: ${item.name}${version ? `:${version}` : '' - }.\n`, + chunk: `Failed to install library: ${item.name}${ + version ? `:${version}` : '' + }.\n`, }); this.responseService.appendToOutput({ chunk: error.toString(), @@ -322,6 +326,10 @@ export class LibraryServiceImpl if (typeof overwrite === 'boolean') { req.setOverwrite(overwrite); } + + // stop the board discovery + await this.boardDiscovery.stopBoardListWatch(coreClient); + const resp = client.zipLibraryInstall(req); resp.on( 'data', @@ -332,7 +340,7 @@ export class LibraryServiceImpl ); await new Promise((resolve, reject) => { resp.on('end', () => { - this.boardDiscovery.startBoardListWatch(coreClient) + this.boardDiscovery.startBoardListWatch(coreClient); resolve(); }); resp.on('error', reject); @@ -354,6 +362,10 @@ export class LibraryServiceImpl req.setVersion(item.installedVersion!); console.info('>>> Starting library package uninstallation...', item); + + // stop the board discovery + await this.boardDiscovery.stopBoardListWatch(coreClient); + const resp = client.libraryUninstall(req); resp.on( 'data', @@ -364,7 +376,7 @@ export class LibraryServiceImpl ); await new Promise((resolve, reject) => { resp.on('end', () => { - this.boardDiscovery.startBoardListWatch(coreClient) + this.boardDiscovery.startBoardListWatch(coreClient); resolve(); }); resp.on('error', reject); diff --git a/arduino-ide-extension/src/node/serial/serial-service-impl.ts b/arduino-ide-extension/src/node/serial/serial-service-impl.ts index c60bca36a..7b288ac10 100644 --- a/arduino-ide-extension/src/node/serial/serial-service-impl.ts +++ b/arduino-ide-extension/src/node/serial/serial-service-impl.ts @@ -63,27 +63,61 @@ namespace ErrorWithCode { @injectable() export class SerialServiceImpl implements SerialService { - @named(SerialServiceName) - @inject(ILogger) - protected readonly logger: ILogger; + protected theiaFEClient?: SerialServiceClient; + protected serialConfig?: SerialConfig; - @inject(MonitorClientProvider) - protected readonly serialClientProvider: MonitorClientProvider; - - @inject(WebSocketService) - protected readonly webSocketService: WebSocketService; - - protected client?: SerialServiceClient; protected serialConnection?: { duplex: ClientDuplexStream; config: SerialConfig; }; protected messages: string[] = []; protected onMessageReceived: Disposable | null; + protected onWSClientsNumberChanged: Disposable | null; + protected flushMessagesInterval: NodeJS.Timeout | null; + uploadInProgress = false; + + constructor( + @inject(ILogger) + @named(SerialServiceName) + protected readonly logger: ILogger, + + @inject(MonitorClientProvider) + protected readonly serialClientProvider: MonitorClientProvider, + + @inject(WebSocketService) + protected readonly webSocketService: WebSocketService + ) {} + + async isSerialPortOpen(): Promise { + return !!this.serialConnection; + } + setClient(client: SerialServiceClient | undefined): void { - this.client = client; + this.theiaFEClient = client; + + this.theiaFEClient?.notifyWebSocketChanged( + this.webSocketService.getAddress().port + ); + + // listen for the number of websocket clients and create or dispose the serial connection + this.onWSClientsNumberChanged = + this.webSocketService.onClientsNumberChanged(async () => { + await this.connectSerialIfRequired(); + }); + } + + public async clientsAttached(): Promise { + return this.webSocketService.getConnectedClientsNumber.bind( + this.webSocketService + )(); + } + + public async connectSerialIfRequired(): Promise { + if (this.uploadInProgress) return; + const clients = await this.clientsAttached(); + clients > 0 ? await this.connect() : await this.disconnect(); } dispose(): void { @@ -92,7 +126,13 @@ export class SerialServiceImpl implements SerialService { this.disconnect(); } this.logger.info('<<< Disposed serial service.'); - this.client = undefined; + this.theiaFEClient = undefined; + } + + async setSerialConfig(config: SerialConfig): Promise { + this.serialConfig = config; + await this.disconnect(); + await this.connectSerialIfRequired(); } async updateWsConfigParam( @@ -105,12 +145,17 @@ export class SerialServiceImpl implements SerialService { this.webSocketService.sendMessage(JSON.stringify(msg)); } - async connect(config: SerialConfig): Promise { + private async connect(): Promise { + if (!this.serialConfig) { + return Status.CONFIG_MISSING; + } + this.logger.info( `>>> Creating serial connection for ${Board.toString( - config.board - )} on port ${Port.toString(config.port)}...` + this.serialConfig.board + )} on port ${Port.toString(this.serialConfig.port)}...` ); + if (this.serialConnection) { return Status.ALREADY_CONNECTED; } @@ -122,27 +167,29 @@ export class SerialServiceImpl implements SerialService { return { message: client.message }; } const duplex = client.streamingOpen(); - this.serialConnection = { duplex, config }; + this.serialConnection = { duplex, config: this.serialConfig }; + + const serialConfig = this.serialConfig; duplex.on( 'error', ((error: Error) => { - const serialError = ErrorWithCode.toSerialError(error, config); - this.disconnect(serialError).then(() => { - if (this.client) { - this.client.notifyError(serialError); - } - if (serialError.code === undefined) { - // Log the original, unexpected error. - this.logger.error(error); - } - }); + const serialError = ErrorWithCode.toSerialError(error, serialConfig); + if (serialError.code !== SerialError.ErrorCodes.CLIENT_CANCEL) { + this.disconnect(serialError).then(() => { + if (this.theiaFEClient) { + this.theiaFEClient.notifyError(serialError); + } + }); + } + if (serialError.code === undefined) { + // Log the original, unexpected error. + this.logger.error(error); + } }).bind(this) ); - this.client?.notifyWebSocketChanged( - this.webSocketService.getAddress().port - ); + this.updateWsConfigParam({ connected: !!this.serialConnection }); const flushMessagesToFrontend = () => { if (this.messages.length) { @@ -162,17 +209,17 @@ export class SerialServiceImpl implements SerialService { break; case SerialPlotter.Protocol.Command.PLOTTER_SET_BAUDRATE: - this.client?.notifyBaudRateChanged( + this.theiaFEClient?.notifyBaudRateChanged( parseInt(message.data, 10) as SerialConfig.BaudRate ); break; case SerialPlotter.Protocol.Command.PLOTTER_SET_LINE_ENDING: - this.client?.notifyLineEndingChanged(message.data); + this.theiaFEClient?.notifyLineEndingChanged(message.data); break; case SerialPlotter.Protocol.Command.PLOTTER_SET_INTERPOLATE: - this.client?.notifyInterpolateChanged(message.data); + this.theiaFEClient?.notifyInterpolateChanged(message.data); break; default: @@ -185,27 +232,6 @@ export class SerialServiceImpl implements SerialService { // empty the queue every 32ms (~30fps) this.flushMessagesInterval = setInterval(flushMessagesToFrontend, 32); - // converts 'ab\nc\nd' => [ab\n,c\n,d] - const stringToArray = (string: string, separator = '\n') => { - const retArray: string[] = []; - - let prevChar = separator; - - for (let i = 0; i < string.length; i++) { - const currChar = string[i]; - - if (prevChar === separator) { - retArray.push(currChar); - } else { - const lastWord = retArray[retArray.length - 1]; - retArray[retArray.length - 1] = lastWord + currChar; - } - - prevChar = currChar; - } - return retArray; - }; - duplex.on( 'data', ((resp: StreamingOpenResponse) => { @@ -219,69 +245,105 @@ export class SerialServiceImpl implements SerialService { }).bind(this) ); - const { type, port } = config; + const { type, port } = this.serialConfig; const req = new StreamingOpenRequest(); const monitorConfig = new GrpcMonitorConfig(); monitorConfig.setType(this.mapType(type)); monitorConfig.setTarget(port.address); - if (config.baudRate !== undefined) { + if (this.serialConfig.baudRate !== undefined) { monitorConfig.setAdditionalConfig( - Struct.fromJavaScript({ BaudRate: config.baudRate }) + Struct.fromJavaScript({ BaudRate: this.serialConfig.baudRate }) ); } req.setConfig(monitorConfig); - return new Promise((resolve) => { - if (this.serialConnection) { - this.serialConnection.duplex.write(req, () => { + if (!this.serialConnection) { + return await this.disconnect(); + } + + const writeTimeout = new Promise((resolve) => { + setTimeout(async () => { + resolve(Status.NOT_CONNECTED); + }, 1000); + }); + + const writePromise = (serialConnection: any) => { + return new Promise((resolve) => { + serialConnection.duplex.write(req, () => { + const boardName = this.serialConfig?.board + ? Board.toString(this.serialConfig.board, { + useFqbn: false, + }) + : 'unknown board'; + + const portName = this.serialConfig?.port + ? Port.toString(this.serialConfig.port) + : 'unknown port'; this.logger.info( - `<<< Serial connection created for ${Board.toString(config.board, { - useFqbn: false, - })} on port ${Port.toString(config.port)}.` + `<<< Serial connection created for ${boardName} on port ${portName}.` ); resolve(Status.OK); }); - return; - } - this.disconnect().then(() => resolve(Status.NOT_CONNECTED)); - }); + }); + }; + + const status = await Promise.race([ + writeTimeout, + writePromise(this.serialConnection), + ]); + + if (status === Status.NOT_CONNECTED) { + this.disconnect(); + } + + return status; } - async disconnect(reason?: SerialError): Promise { - try { - if (this.onMessageReceived) { - this.onMessageReceived.dispose(); - this.onMessageReceived = null; - } - if (this.flushMessagesInterval) { - clearInterval(this.flushMessagesInterval); - this.flushMessagesInterval = null; - } + public async disconnect(reason?: SerialError): Promise { + return new Promise((resolve) => { + try { + if (this.onMessageReceived) { + this.onMessageReceived.dispose(); + this.onMessageReceived = null; + } + if (this.flushMessagesInterval) { + clearInterval(this.flushMessagesInterval); + this.flushMessagesInterval = null; + } - if ( - !this.serialConnection && - reason && - reason.code === SerialError.ErrorCodes.CLIENT_CANCEL - ) { - return Status.OK; - } - this.logger.info('>>> Disposing serial connection...'); - if (!this.serialConnection) { - this.logger.warn('<<< Not connected. Nothing to dispose.'); - return Status.NOT_CONNECTED; + if ( + !this.serialConnection && + reason && + reason.code === SerialError.ErrorCodes.CLIENT_CANCEL + ) { + resolve(Status.OK); + return; + } + this.logger.info('>>> Disposing serial connection...'); + if (!this.serialConnection) { + this.logger.warn('<<< Not connected. Nothing to dispose.'); + resolve(Status.NOT_CONNECTED); + return; + } + const { duplex, config } = this.serialConnection; + + this.logger.info( + `<<< Disposed serial connection for ${Board.toString(config.board, { + useFqbn: false, + })} on port ${Port.toString(config.port)}.` + ); + + duplex.cancel(); + } finally { + this.serialConnection = undefined; + this.updateWsConfigParam({ connected: !!this.serialConnection }); + this.messages.length = 0; + + setTimeout(() => { + resolve(Status.OK); + }, 200); } - const { duplex, config } = this.serialConnection; - duplex.cancel(); - this.logger.info( - `<<< Disposed serial connection for ${Board.toString(config.board, { - useFqbn: false, - })} on port ${Port.toString(config.port)}.` - ); - this.serialConnection = undefined; - return Status.OK; - } finally { - this.messages.length = 0; - } + }); } async sendMessageToSerial(message: string): Promise { @@ -312,3 +374,24 @@ export class SerialServiceImpl implements SerialService { } } } + +// converts 'ab\nc\nd' => [ab\n,c\n,d] +function stringToArray(string: string, separator = '\n') { + const retArray: string[] = []; + + let prevChar = separator; + + for (let i = 0; i < string.length; i++) { + const currChar = string[i]; + + if (prevChar === separator) { + retArray.push(currChar); + } else { + const lastWord = retArray[retArray.length - 1]; + retArray[retArray.length - 1] = lastWord + currChar; + } + + prevChar = currChar; + } + return retArray; +} diff --git a/arduino-ide-extension/src/node/web-socket/web-socket-service-impl.ts b/arduino-ide-extension/src/node/web-socket/web-socket-service-impl.ts index 0f05759aa..869c2cf8d 100644 --- a/arduino-ide-extension/src/node/web-socket/web-socket-service-impl.ts +++ b/arduino-ide-extension/src/node/web-socket/web-socket-service-impl.ts @@ -11,6 +11,9 @@ export default class WebSocketServiceImpl implements WebSocketService { protected readonly onMessage = new Emitter(); public readonly onMessageReceived = this.onMessage.event; + protected readonly onConnectedClients = new Emitter(); + public readonly onClientsNumberChanged = this.onConnectedClients.event; + constructor() { this.wsClients = []; this.server = new WebSocket.Server({ port: 0 }); @@ -21,8 +24,11 @@ export default class WebSocketServiceImpl implements WebSocketService { private addClient(ws: WebSocket): void { this.wsClients.push(ws); + this.onConnectedClients.fire(this.wsClients.length); + ws.onclose = () => { this.wsClients.splice(this.wsClients.indexOf(ws), 1); + this.onConnectedClients.fire(this.wsClients.length); }; ws.onmessage = (res) => { @@ -30,6 +36,10 @@ export default class WebSocketServiceImpl implements WebSocketService { }; } + getConnectedClientsNumber(): number { + return this.wsClients.length; + } + getAddress(): WebSocket.AddressInfo { return this.server.address() as WebSocket.AddressInfo; } diff --git a/arduino-ide-extension/src/node/web-socket/web-socket-service.ts b/arduino-ide-extension/src/node/web-socket/web-socket-service.ts index 5d612560d..c793a07c4 100644 --- a/arduino-ide-extension/src/node/web-socket/web-socket-service.ts +++ b/arduino-ide-extension/src/node/web-socket/web-socket-service.ts @@ -6,4 +6,6 @@ export interface WebSocketService { getAddress(): WebSocket.AddressInfo; sendMessage(message: string): void; onMessageReceived: Event; + onClientsNumberChanged: Event; + getConnectedClientsNumber(): number; } diff --git a/arduino-ide-extension/src/test/browser/serial-connection-manager.test.ts b/arduino-ide-extension/src/test/browser/serial-connection-manager.test.ts deleted file mode 100644 index 6d9a96730..000000000 --- a/arduino-ide-extension/src/test/browser/serial-connection-manager.test.ts +++ /dev/null @@ -1,375 +0,0 @@ -import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; -const disableJSDOM = enableJSDOM(); - -import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; -import { ApplicationProps } from '@theia/application-package/lib/application-props'; -FrontendApplicationConfigProvider.set({ - ...ApplicationProps.DEFAULT.frontend.config, -}); - -import { MessageService } from '@theia/core'; -import { BoardsServiceProvider } from '../../browser/boards/boards-service-provider'; -import { - BoardsService, - CoreService, - SerialService, - SerialServiceClient, - Status, -} from '../../common/protocol'; -import { IMock, It, Mock, Times } from 'typemoq'; -import { - Serial, - SerialConnectionManager, -} from '../../browser/serial/serial-connection-manager'; -import { ThemeService } from '@theia/core/lib/browser/theming'; -import { SerialModel } from '../../browser/serial/serial-model'; -import { - aBoardConfig, - anotherBoardConfig, - anotherPort, - aPort, -} from './fixtures/boards'; -import { BoardsConfig } from '../../browser/boards/boards-config'; -import { - anotherSerialConfig, - aSerialConfig, - WebSocketMock, -} from './fixtures/serial'; -import { expect } from 'chai'; -import { tick } from '../utils'; - -disableJSDOM(); - -global.WebSocket = WebSocketMock as any; - -describe.only('SerialConnectionManager', () => { - let subject: SerialConnectionManager; - - let serialModel: IMock; - let serialService: IMock; - let serialServiceClient: IMock; - let boardsService: IMock; - let boardsServiceProvider: IMock; - let messageService: IMock; - let themeService: IMock; - let core: IMock; - - let handleBoardConfigChange: ( - boardsConfig: BoardsConfig.Config - ) => Promise; - let handleWebSocketChanged: (wsPort: number) => void; - const wsPort = 1234; - - beforeEach(() => { - serialModel = Mock.ofType(); - serialService = Mock.ofType(); - serialServiceClient = Mock.ofType(); - boardsService = Mock.ofType(); - boardsServiceProvider = Mock.ofType(); - messageService = Mock.ofType(); - themeService = Mock.ofType(); - core = Mock.ofType(); - - boardsServiceProvider - .setup((b) => b.boardsConfig) - .returns(() => aBoardConfig); - - boardsServiceProvider - .setup((b) => b.onBoardsConfigChanged(It.isAny())) - .returns((h) => { - handleBoardConfigChange = h; - return { dispose: () => {} }; - }); - - boardsServiceProvider - .setup((b) => b.canUploadTo(It.isAny(), It.isValue({ silent: false }))) - .returns(() => true); - - boardsService - .setup((b) => b.getAvailablePorts()) - .returns(() => Promise.resolve([aPort, anotherPort])); - - serialModel - .setup((m) => m.baudRate) - .returns(() => aSerialConfig.baudRate || 9600); - - serialServiceClient - .setup((m) => m.onWebSocketChanged(It.isAny())) - .returns((h) => { - handleWebSocketChanged = h; - return { dispose: () => {} }; - }); - - serialService - .setup((m) => m.disconnect()) - .returns(() => Promise.resolve(Status.OK)); - - core.setup((u) => u.isUploading()).returns(() => Promise.resolve(false)); - - subject = new SerialConnectionManager( - serialModel.object, - serialService.object, - serialServiceClient.object, - boardsService.object, - boardsServiceProvider.object, - messageService.object, - themeService.object, - core.object - ); - }); - - context('when no serial config is set', () => { - context('and the serial is NOT open', () => { - context('and it tries to open the serial plotter', () => { - it('should not try to connect and show an error', async () => { - await subject.openSerial(Serial.Type.Plotter); - messageService.verify((m) => m.error(It.isAnyString()), Times.once()); - serialService.verify((m) => m.disconnect(), Times.never()); - serialService.verify((m) => m.connect(It.isAny()), Times.never()); - }); - }); - context('and a serial config is set', () => { - it('should not try to reconnect', async () => { - await handleBoardConfigChange(aBoardConfig); - serialService.verify((m) => m.disconnect(), Times.never()); - serialService.verify((m) => m.connect(It.isAny()), Times.never()); - expect(subject.getConfig()).to.deep.equal(aSerialConfig); - }); - }); - }); - }); - context('when a serial config is set', () => { - beforeEach(() => { - subject.setConfig(aSerialConfig); - }); - context('and the serial is NOT open', () => { - context('and it tries to disconnect', () => { - it('should do nothing', async () => { - const status = await subject.disconnect(); - expect(status).to.be.ok; - expect(subject.connected).to.be.false; - }); - }); - context('and the config changes', () => { - beforeEach(() => { - subject.setConfig(anotherSerialConfig); - }); - it('should not try to reconnect', async () => { - await tick(); - messageService.verify( - (m) => m.error(It.isAnyString()), - Times.never() - ); - serialService.verify((m) => m.disconnect(), Times.never()); - serialService.verify((m) => m.connect(It.isAny()), Times.never()); - }); - }); - context( - 'and the connection to the serial succeeds with the config', - () => { - beforeEach(() => { - serialService - .setup((m) => m.connect(It.isValue(aSerialConfig))) - .returns(() => { - handleWebSocketChanged(wsPort); - return Promise.resolve(Status.OK); - }); - }); - context('and it tries to open the serial plotter', () => { - let status: Status; - beforeEach(async () => { - status = await subject.openSerial(Serial.Type.Plotter); - }); - it('should successfully connect to the serial', async () => { - messageService.verify( - (m) => m.error(It.isAnyString()), - Times.never() - ); - serialService.verify((m) => m.disconnect(), Times.never()); - serialService.verify((m) => m.connect(It.isAny()), Times.once()); - expect(status).to.be.ok; - expect(subject.connected).to.be.true; - expect(subject.getWsPort()).to.equal(wsPort); - expect(subject.isSerialOpen()).to.be.true; - expect(subject.isWebSocketConnected()).to.be.false; - }); - context('and it tries to open the serial monitor', () => { - let status: Status; - beforeEach(async () => { - status = await subject.openSerial(Serial.Type.Monitor); - }); - it('should open it using the same serial connection', () => { - messageService.verify( - (m) => m.error(It.isAnyString()), - Times.never() - ); - serialService.verify((m) => m.disconnect(), Times.never()); - serialService.verify( - (m) => m.connect(It.isAny()), - Times.once() - ); - expect(status).to.be.ok; - expect(subject.connected).to.be.true; - expect(subject.isSerialOpen()).to.be.true; - }); - it('should create a websocket connection', () => { - expect(subject.getWsPort()).to.equal(wsPort); - expect(subject.isWebSocketConnected()).to.be.true; - }); - context('and then it closes the serial plotter', () => { - beforeEach(async () => { - status = await subject.closeSerial(Serial.Type.Plotter); - }); - it('should close the plotter without disconnecting from the serial', () => { - messageService.verify( - (m) => m.error(It.isAnyString()), - Times.never() - ); - serialService.verify((m) => m.disconnect(), Times.never()); - serialService.verify( - (m) => m.connect(It.isAny()), - Times.once() - ); - expect(status).to.be.ok; - expect(subject.connected).to.be.true; - expect(subject.isSerialOpen()).to.be.true; - expect(subject.getWsPort()).to.equal(wsPort); - }); - it('should not close the websocket connection', () => { - expect(subject.isWebSocketConnected()).to.be.true; - }); - }); - context('and then it closes the serial monitor', () => { - beforeEach(async () => { - status = await subject.closeSerial(Serial.Type.Monitor); - }); - it('should close the monitor without disconnecting from the serial', () => { - messageService.verify( - (m) => m.error(It.isAnyString()), - Times.never() - ); - serialService.verify((m) => m.disconnect(), Times.never()); - serialService.verify( - (m) => m.connect(It.isAny()), - Times.once() - ); - expect(status).to.be.ok; - expect(subject.connected).to.be.true; - expect(subject.getWsPort()).to.equal(wsPort); - expect(subject.isSerialOpen()).to.be.true; - }); - it('should close the websocket connection', () => { - expect(subject.isWebSocketConnected()).to.be.false; - }); - }); - }); - context('and then it closes the serial plotter', () => { - beforeEach(async () => { - status = await subject.closeSerial(Serial.Type.Plotter); - }); - it('should successfully disconnect from the serial', () => { - messageService.verify( - (m) => m.error(It.isAnyString()), - Times.never() - ); - serialService.verify((m) => m.disconnect(), Times.once()); - serialService.verify( - (m) => m.connect(It.isAny()), - Times.once() - ); - expect(status).to.be.ok; - expect(subject.connected).to.be.false; - expect(subject.getWsPort()).to.be.undefined; - expect(subject.isSerialOpen()).to.be.false; - expect(subject.isWebSocketConnected()).to.be.false; - }); - }); - context('and the config changes', () => { - beforeEach(() => { - subject.setConfig(anotherSerialConfig); - }); - it('should try to reconnect', async () => { - await tick(); - messageService.verify( - (m) => m.error(It.isAnyString()), - Times.never() - ); - serialService.verify((m) => m.disconnect(), Times.once()); - serialService.verify( - (m) => m.connect(It.isAny()), - Times.exactly(2) - ); - }); - }); - }); - } - ); - context( - 'and the connection to the serial does NOT succeed with the config', - () => { - beforeEach(() => { - serialService - .setup((m) => m.connect(It.isValue(aSerialConfig))) - .returns(() => { - return Promise.resolve(Status.NOT_CONNECTED); - }); - serialService - .setup((m) => m.connect(It.isValue(anotherSerialConfig))) - .returns(() => { - handleWebSocketChanged(wsPort); - return Promise.resolve(Status.OK); - }); - }); - context('and it tries to open the serial plotter', () => { - let status: Status; - beforeEach(async () => { - status = await subject.openSerial(Serial.Type.Plotter); - }); - - it('should fail to connect to the serial', async () => { - messageService.verify( - (m) => m.error(It.isAnyString()), - Times.never() - ); - serialService.verify((m) => m.disconnect(), Times.never()); - serialService.verify( - (m) => m.connect(It.isValue(aSerialConfig)), - Times.once() - ); - expect(status).to.be.false; - expect(subject.connected).to.be.false; - expect(subject.getWsPort()).to.be.undefined; - expect(subject.isSerialOpen()).to.be.true; - }); - - context( - 'and the board config changes with an acceptable one', - () => { - beforeEach(async () => { - await handleBoardConfigChange(anotherBoardConfig); - }); - - it('should successfully connect to the serial', async () => { - await tick(); - messageService.verify( - (m) => m.error(It.isAnyString()), - Times.never() - ); - serialService.verify((m) => m.disconnect(), Times.never()); - serialService.verify( - (m) => m.connect(It.isValue(anotherSerialConfig)), - Times.once() - ); - expect(subject.connected).to.be.true; - expect(subject.getWsPort()).to.equal(wsPort); - expect(subject.isSerialOpen()).to.be.true; - expect(subject.isWebSocketConnected()).to.be.false; - }); - } - ); - }); - } - ); - }); - }); -}); diff --git a/arduino-ide-extension/src/test/node/arduino-daemon-impl.test.ts b/arduino-ide-extension/src/test/node/arduino-daemon-impl.test.ts index 9d3aed9af..0fd6cf4d9 100644 --- a/arduino-ide-extension/src/test/node/arduino-daemon-impl.test.ts +++ b/arduino-ide-extension/src/test/node/arduino-daemon-impl.test.ts @@ -2,21 +2,17 @@ import * as fs from 'fs'; // import * as net from 'net'; import * as path from 'path'; import * as temp from 'temp'; -import { fail } from 'assert'; import { expect } from 'chai'; import { ChildProcess } from 'child_process'; import { safeLoad, safeDump } from 'js-yaml'; -import { DaemonError, ArduinoDaemonImpl } from '../../node/arduino-daemon-impl'; +import { ArduinoDaemonImpl } from '../../node/arduino-daemon-impl'; import { spawnCommand } from '../../node/exec-util'; import { CLI_CONFIG } from '../../node/cli-config'; const track = temp.track(); class SilentArduinoDaemonImpl extends ArduinoDaemonImpl { - constructor( - private port: string | number, - private logFormat: 'text' | 'json' - ) { + constructor(private logFormat: 'text' | 'json') { super(); } @@ -24,7 +20,7 @@ class SilentArduinoDaemonImpl extends ArduinoDaemonImpl { // NOOP } - async spawnDaemonProcess(): Promise { + async spawnDaemonProcess(): Promise<{ daemon: ChildProcess; port: string }> { return super.spawnDaemonProcess(); } @@ -32,6 +28,10 @@ class SilentArduinoDaemonImpl extends ArduinoDaemonImpl { const cliConfigPath = await this.initCliConfig(); return [ 'daemon', + '--format', + 'jsonmini', + '--port', + '0', '--config-file', cliConfigPath, '-v', @@ -53,7 +53,7 @@ class SilentArduinoDaemonImpl extends ArduinoDaemonImpl { encoding: 'utf8', }); const cliConfig = safeLoad(content) as any; - cliConfig.daemon.port = String(this.port); + // cliConfig.daemon.port = String(this.port); const modifiedContent = safeDump(cliConfig); fs.writeFileSync(path.join(destDir, CLI_CONFIG), modifiedContent, { encoding: 'utf8', @@ -113,43 +113,23 @@ describe('arduino-daemon-impl', () => { // } // }); - it('should parse an error - unknown address [json]', async () => { - try { - await new SilentArduinoDaemonImpl('foo', 'json').spawnDaemonProcess(); - fail('Expected a failure.'); - } catch (e) { - expect(e).to.be.instanceOf(DaemonError); - expect(e.code).to.be.equal(DaemonError.UNKNOWN_ADDRESS); - } - }); + it('should parse the port address when the log format is json', async () => { + const { daemon, port } = await new SilentArduinoDaemonImpl( + 'json' + ).spawnDaemonProcess(); - it('should parse an error - unknown address [text]', async () => { - try { - await new SilentArduinoDaemonImpl('foo', 'text').spawnDaemonProcess(); - fail('Expected a failure.'); - } catch (e) { - expect(e).to.be.instanceOf(DaemonError); - expect(e.code).to.be.equal(DaemonError.UNKNOWN_ADDRESS); - } + expect(port).not.to.be.undefined; + expect(port).not.to.be.equal('0'); + daemon.kill(); }); - it('should parse an error - invalid port [json]', async () => { - try { - await new SilentArduinoDaemonImpl(-1, 'json').spawnDaemonProcess(); - fail('Expected a failure.'); - } catch (e) { - expect(e).to.be.instanceOf(DaemonError); - expect(e.code).to.be.equal(DaemonError.INVALID_PORT); - } - }); + it('should parse the port address when the log format is text', async () => { + const { daemon, port } = await new SilentArduinoDaemonImpl( + 'text' + ).spawnDaemonProcess(); - it('should parse an error - invalid port [text]', async () => { - try { - await new SilentArduinoDaemonImpl(-1, 'text').spawnDaemonProcess(); - fail('Expected a failure.'); - } catch (e) { - expect(e).to.be.instanceOf(DaemonError); - expect(e.code).to.be.equal(DaemonError.INVALID_PORT); - } + expect(port).not.to.be.undefined; + expect(port).not.to.be.equal('0'); + daemon.kill(); }); }); diff --git a/arduino-ide-extension/src/test/node/serial-service-impl.test.ts b/arduino-ide-extension/src/test/node/serial-service-impl.test.ts new file mode 100644 index 000000000..a4ddcbe43 --- /dev/null +++ b/arduino-ide-extension/src/test/node/serial-service-impl.test.ts @@ -0,0 +1,167 @@ +import { SerialServiceImpl } from './../../node/serial/serial-service-impl'; +import { IMock, It, Mock } from 'typemoq'; +import { createSandbox } from 'sinon'; +import * as sinonChai from 'sinon-chai'; +import { expect, use } from 'chai'; +use(sinonChai); + +import { ILogger } from '@theia/core/lib/common/logger'; +import { MonitorClientProvider } from '../../node/serial/monitor-client-provider'; +import { WebSocketService } from '../../node/web-socket/web-socket-service'; +import { MonitorServiceClient } from '../../node/cli-protocol/cc/arduino/cli/monitor/v1/monitor_grpc_pb'; +import { Status } from '../../common/protocol'; + +describe('SerialServiceImpl', () => { + let subject: SerialServiceImpl; + + let logger: IMock; + let serialClientProvider: IMock; + let webSocketService: IMock; + + beforeEach(() => { + logger = Mock.ofType(); + logger.setup((b) => b.info(It.isAnyString())); + logger.setup((b) => b.warn(It.isAnyString())); + logger.setup((b) => b.error(It.isAnyString())); + + serialClientProvider = Mock.ofType(); + webSocketService = Mock.ofType(); + + subject = new SerialServiceImpl( + logger.object, + serialClientProvider.object, + webSocketService.object + ); + }); + + context('when a serial connection is requested', () => { + const sandbox = createSandbox(); + beforeEach(() => { + subject.uploadInProgress = false; + sandbox.spy(subject, 'disconnect'); + sandbox.spy(subject, 'updateWsConfigParam'); + }); + + afterEach(function () { + sandbox.restore(); + }); + + context('and an upload is in progress', () => { + beforeEach(async () => { + subject.uploadInProgress = true; + }); + + it('should not change the connection status', async () => { + await subject.connectSerialIfRequired(); + expect(subject.disconnect).to.have.callCount(0); + }); + }); + + context('and there is no upload in progress', () => { + beforeEach(async () => { + subject.uploadInProgress = false; + }); + + context('and there are 0 attached ws clients', () => { + it('should disconnect', async () => { + await subject.connectSerialIfRequired(); + expect(subject.disconnect).to.have.been.calledOnce; + }); + }); + + context('and there are > 0 attached ws clients', () => { + beforeEach(() => { + webSocketService + .setup((b) => b.getConnectedClientsNumber()) + .returns(() => 1); + }); + + it('should not call the disconenct', async () => { + await subject.connectSerialIfRequired(); + expect(subject.disconnect).to.have.callCount(0); + }); + }); + }); + }); + + context('when a disconnection is requested', () => { + const sandbox = createSandbox(); + beforeEach(() => {}); + + afterEach(function () { + sandbox.restore(); + }); + + context('and a serialConnection is not set', () => { + it('should return a NOT_CONNECTED status', async () => { + const status = await subject.disconnect(); + expect(status).to.be.equal(Status.NOT_CONNECTED); + }); + }); + + context('and a serialConnection is set', async () => { + beforeEach(async () => { + sandbox.spy(subject, 'updateWsConfigParam'); + await subject.disconnect(); + }); + + it('should dispose the serialConnection', async () => { + const serialConnectionOpen = await subject.isSerialPortOpen(); + expect(serialConnectionOpen).to.be.false; + }); + + it('should call updateWsConfigParam with disconnected status', async () => { + expect(subject.updateWsConfigParam).to.be.calledWith({ + connected: false, + }); + }); + }); + }); + + context('when a new config is passed in', () => { + const sandbox = createSandbox(); + beforeEach(async () => { + subject.uploadInProgress = false; + webSocketService + .setup((b) => b.getConnectedClientsNumber()) + .returns(() => 1); + + serialClientProvider + .setup((b) => b.client()) + .returns(async () => { + return { + streamingOpen: () => { + return { + on: (str: string, cb: any) => {}, + write: (chunk: any, cb: any) => { + cb(); + }, + cancel: () => {}, + }; + }, + } as MonitorServiceClient; + }); + + sandbox.spy(subject, 'disconnect'); + + await subject.setSerialConfig({ + board: { name: 'test' }, + port: { address: 'test', protocol: 'test' }, + }); + }); + + afterEach(function () { + sandbox.restore(); + subject.dispose(); + }); + + it('should disconnect from previous connection', async () => { + expect(subject.disconnect).to.be.called; + }); + + it('should create the serialConnection', async () => { + const serialConnectionOpen = await subject.isSerialPortOpen(); + expect(serialConnectionOpen).to.be.true; + }); + }); +}); diff --git a/browser-app/package.json b/browser-app/package.json index f69566968..294be261f 100644 --- a/browser-app/package.json +++ b/browser-app/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "browser-app", - "version": "2.0.0-rc1", + "version": "2.0.0-rc2", "license": "AGPL-3.0-or-later", "dependencies": { "@theia/core": "1.19.0", @@ -20,7 +20,7 @@ "@theia/process": "1.19.0", "@theia/terminal": "1.19.0", "@theia/workspace": "1.19.0", - "arduino-ide-extension": "2.0.0-rc1" + "arduino-ide-extension": "2.0.0-rc2" }, "devDependencies": { "@theia/cli": "1.19.0" diff --git a/electron-app/package.json b/electron-app/package.json index dfdf58270..36677b83d 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "electron-app", - "version": "2.0.0-rc1", + "version": "2.0.0-rc2", "license": "AGPL-3.0-or-later", "main": "src-gen/frontend/electron-main.js", "dependencies": { @@ -22,7 +22,7 @@ "@theia/process": "1.19.0", "@theia/terminal": "1.19.0", "@theia/workspace": "1.19.0", - "arduino-ide-extension": "2.0.0-rc1" + "arduino-ide-extension": "2.0.0-rc2" }, "devDependencies": { "@theia/cli": "1.19.0" diff --git a/i18n/ar.json b/i18n/ar.json new file mode 100644 index 000000000..68a285407 --- /dev/null +++ b/i18n/ar.json @@ -0,0 +1,334 @@ +{ + "arduino": { + "common": { + "offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.", + "noBoardSelected": "No board selected", + "selectedOn": "on {0}", + "notConnected": "[not connected]", + "serialMonitor": "مراقب المنفذ التسلسلي \"سيريال بورت\"\n ", + "oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", + "later": "Later", + "selectBoard": "Select Board", + "unknown": "Unknown", + "processing": "Processing", + "saveChangesToSketch": "Do you want to save changes to this sketch before closing?", + "loseChanges": "If you don't save, your changes will be lost." + }, + "menu": { + "sketch": "Sketch", + "tools": "ادوات" + }, + "debug": { + "optimizeForDebugging": "Optimize for Debugging", + "debugWithMessage": "Debug - {0}", + "noPlatformInstalledFor": "Platform is not installed for '{0}'", + "debuggingNotSupported": "Debugging is not supported by '{0}'" + }, + "preferences": { + "language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", + "compile.verbose": "True for verbose compile output. False by default", + "compile.warnings": "Tells gcc which warning level to use. It's 'None' by default", + "upload.verbose": "True for verbose upload output. False by default.", + "window.autoScale": "True if the user interface automatically scales with the font size.", + "window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.", + "ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.", + "board.certificates": "List of certificates that can be uploaded to boards", + "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.", + "cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.", + "cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.", + "cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.", + "cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.", + "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", + "auth.clientID": "The OAuth2 client ID.", + "auth.domain": "The OAuth2 domain.", + "auth.audience": "The OAuth2 audience.", + "auth.registerUri": "The URI used to register a new user.", + "network": "شبكة", + "sketchbook.location": "Sketchbook location", + "browse": "استعرض", + "files.inside.sketches": "Show files inside Sketches", + "editorFontSize": "Editor font size", + "interfaceScale": "Interface scale", + "showVerbose": "Show verbose output during", + "compilerWarnings": "Compiler warnings", + "automatic": "تلقائي", + "compile": "compile", + "upload": "upload", + "verifyAfterUpload": "Verify code after upload", + "checkForUpdates": "افحص التحديثات عند التشغيل", + "editorQuickSuggestions": "Editor Quick Suggestions", + "additionalManagerURLs": "تدبير عناوين اللوحات الإضافية", + "noProxy": "No proxy", + "manualProxy": "Manual proxy configuration", + "newSketchbookLocation": "Select new sketchbook location", + "choose": "Choose", + "enterAdditionalURLs": "Enter additional URLs, one for each row", + "unofficialBoardSupport": "Click for a list of unofficial board support URLs", + "invalid.sketchbook.location": "Invalid sketchbook location: {0}", + "invalid.editorFontSize": "Invalid editor font size. It must be a positive integer.", + "invalid.theme": "Invalid theme." + }, + "cloud": { + "signIn": "SIGN IN", + "signOut": "Sign Out", + "chooseSketchVisibility": "Choose visibility of your Sketch:", + "privateVisibility": "Private. Only you can view the Sketch.", + "publicVisibility": "Public. Anyone with the link can view the Sketch.", + "link": "Link:", + "embed": "Embed:", + "cloudSketchbook": "Cloud Sketchbook", + "shareSketch": "Share Sketch", + "showHideRemoveSketchbook": "Show/Hide Remote Sketchbook", + "pullSketch": "Pull Sketch", + "openInCloudEditor": "Open in Cloud Editor", + "options": "Options...", + "share": "Share...", + "remote": "Remote", + "emptySketchbook": "Your Sketchbook is empty", + "visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches.", + "signInToCloud": "Sign in to Arduino Cloud", + "syncEditSketches": "Sync and edit your Arduino Cloud Sketches", + "learnMore": "Learn more", + "continue": "Continue", + "pushSketch": "Push Sketch", + "pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.", + "pull": "Pull", + "pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?", + "donePulling": "Done pulling ‘{0}’.", + "notYetPulled": "Cannot push to Cloud. It is not yet pulled.", + "push": "Push", + "pullFirst": "You have to pull first to be able to push to the Cloud.", + "donePushing": "Done pushing ‘{0}’.", + "connected": "Connected", + "offline": "Offline", + "profilePicture": "Profile picture" + }, + "board": { + "installManually": "Install Manually", + "installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?", + "configDialogTitle": "Select Other Board & Port", + "configDialog1": "Select both a Board and a Port if you want to upload a sketch.", + "configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.", + "pleasePickBoard": "Please pick a board connected to the port you have selected.", + "showAllAvailablePorts": "Shows all available ports when enabled", + "programmer": "Programmer", + "succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}", + "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}", + "couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?", + "reselectLater": "Reselect later", + "noneSelected": "No boards selected.", + "noPortsSelected": "No ports selected for board: '{0}'.", + "noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?", + "openBoardsConfig": "Select other board and port…", + "boardListItem": "{0} at {1}", + "selectBoardForInfo": "Please select a board to obtain board info.", + "platformMissing": "The platform for the selected '{0}' board is not installed.", + "selectPortForInfo": "Please select a port to obtain board info.", + "boardInfo": "معلومات اللوحة", + "board": "Board{0}", + "port": "Port{0}", + "getBoardInfo": "Get Board Info", + "inSketchbook": " (in Sketchbook)" + }, + "boardsManager": "مدير اللوحة", + "about": { + "label": "About {0}", + "detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}" + }, + "contributions": { + "addFile": "اضف ملف...", + "replaceTitle": "Replace", + "fileAdded": "One file added to the sketch." + }, + "replaceMsg": "Replace the existing version of {0}?", + "library": { + "addZip": "اضف مكتبة .ZIP ...", + "zipLibrary": "Library", + "overwriteExistingLibrary": "Do you want to overwrite the existing library?", + "successfullyInstalledZipLibrary": "Successfully installed library from {0} archive", + "namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?", + "libraryAlreadyExists": "A library already exists. Do you want to overwrite it?", + "include": "Include Library", + "manageLibraries": "Manage Libraries...", + "arduinoLibraries": "Arduino libraries", + "contributedLibraries": "Contributed libraries", + "title": "Library Manager", + "needsOneDependency": "The library {0}:{1} needs another dependency currently not installed:", + "needsMultipleDependencies": "The library {0}:{1} needs some other dependencies currently not installed:", + "installOneMissingDependency": "Would you like to install the missing dependency?", + "installMissingDependencies": "Would you like to install all the missing dependencies?", + "dependenciesForLibrary": "Dependencies for library {0}:{1}", + "installAll": "Install all", + "installOnly": "Install {0} only", + "installedSuccessfully": "Successfully installed library {0}:{1}", + "uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}" + }, + "selectZip": "Select a zip file containing the library you'd like to add", + "sketch": { + "archiveSketch": "ارشفة الشيفرة البرمجية", + "saveSketchAs": "Save sketch folder as...", + "createdArchive": "Created archive '{0}'.", + "new": "New", + "openRecent": "Open Recent", + "showFolder": "Show Sketch Folder", + "sketch": "Sketch", + "moving": "Moving", + "movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?", + "cantOpen": "المجلد المسمى \"{0}\" موجود مسبقا. لا يمكن فتح الشيفرة البرمجية", + "saveFolderAs": "Save sketch folder as...", + "sketchbook": "Sketchbook", + "upload": "رفع", + "uploadUsingProgrammer": "Upload Using Programmer", + "userFieldsNotFoundError": "Can't find user fields for connected board", + "doneUploading": "Done uploading.", + "configureAndUpload": "Configure And Upload", + "verifyOrCompile": "Verify/Compile", + "exportBinary": "Export Compiled Binary", + "verify": "Verify", + "doneCompiling": "Done compiling.", + "couldNotConnectToSerial": "Could not reconnect to serial port. {0}", + "openSketchInNewWindow": "Open Sketch in New Window", + "openFolder": "Open Folder", + "titleLocalSketchbook": "Local Sketchbook", + "titleSketchbook": "Sketchbook", + "close": "Are you sure you want to close the sketch?" + }, + "bootloader": { + "burnBootloader": "ثبت محمل برنامج الإقلاع", + "doneBurningBootloader": "Done burning bootloader." + }, + "editor": { + "copyForForum": "Copy for Forum (Markdown)", + "commentUncomment": "ملاحظة/ الغاء الملاحظة", + "increaseIndent": "Increase Indent", + "decreaseIndent": "Decrease Indent", + "increaseFontSize": "Increase Font Size", + "decreaseFontSize": "تصغير حجم الخط", + "autoFormat": "تنسيق تلقائي" + }, + "examples": { + "menu": "Examples", + "couldNotInitializeExamples": "Could not initialize built-in examples.", + "builtInExamples": "الأمثلة المدمجة", + "customLibrary": "Examples from Custom Libraries", + "for": "Examples for {0}", + "forAny": "Examples for any board" + }, + "help": { + "search": "Search on Arduino.cc", + "keyword": "Type a keyword", + "gettingStarted": "Getting Started", + "environment": "Environment", + "troubleshooting": "Troubleshooting", + "reference": "Reference", + "findInReference": "Find in Reference", + "faq": "Frequently Asked Questions", + "visit": "Visit Arduino.cc" + }, + "certificate": { + "uploadRootCertificates": "Upload SSL Root Certificates", + "openContext": "Open context", + "remove": "حذف", + "upload": "رفع", + "addURL": "Add URL to fetch SSL certificate", + "enterURL": "Enter URL", + "selectCertificateToUpload": "1. Select certificate to upload", + "addNew": "Add New", + "selectDestinationBoardToUpload": "2. Select destination board and upload certificate", + "uploadingCertificates": "Uploading certificates.", + "certificatesUploaded": "Certificates uploaded.", + "uploadFailed": "Upload failed. Please try again.", + "selectBoard": "Select a board...", + "boardAtPort": "{0} at {1}", + "noSupportedBoardConnected": "No supported board connected" + }, + "firmware": { + "updater": "WiFi101 / WiFiNINA Firmware Updater", + "selectBoard": "Select Board", + "checkUpdates": "Check Updates", + "selectVersion": "Select firmware version", + "install": "تنصيب", + "overwriteSketch": "Installation will overwrite the Sketch on the board.", + "installingFirmware": "Installing firmware.", + "successfullyInstalled": "Firmware succesfully installed.", + "failedInstall": "Installation failed. Please try again." + }, + "dialog": { + "dontAskAgain": "Don't ask again" + }, + "userFields": { + "cancel": "Cancel", + "upload": "رفع" + }, + "serial": { + "toggleTimestamp": "Toggle Timestamp", + "autoscroll": "Autoscroll", + "timestamp": "Timestamp", + "noLineEndings": "نهاية السطر غير موجودة", + "newLine": "سطر جديد", + "carriageReturn": "اعادة الحمل", + "newLineCarriageReturn": "كلاهما NL & CR", + "notConnected": "Not connected. Select a board and a port to connect automatically.", + "message": "Message ({0} + Enter to send message to '{1}' on '{2}'", + "connectionBusy": "Connection failed. Serial port is busy: {0}", + "disconnected": "Disconnected {0} from {1}.", + "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", + "failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.", + "reconnect": "Reconnecting {0} to {1} in {2} seconds..." + }, + "component": { + "uninstall": "Uninstall", + "uninstallMsg": "Do you want to uninstall {0}?", + "by": "by", + "version": "النسخة {0}", + "moreInfo": "More info", + "install": "تنصيب", + "filterSearch": "ترشيح بحثك..." + }, + "electron": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "unsavedChanges": "Any unsaved changes will not be saved." + }, + "compile": { + "error": "Compilation error: {0}" + }, + "upload": { + "error": "{0} error: {1}" + }, + "burnBootloader": { + "error": "Error while burning the bootloader: {0}" + } + }, + "theia": { + "core": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "offline": "Offline", + "daemonOffline": "CLI Daemon Offline", + "cannotConnectBackend": "Cannot connect to the backend.", + "cannotConnectDaemon": "Cannot connect to the CLI daemon." + }, + "debug": { + "start": "Start...", + "typeNotSupported": "The debug session type \"{0}\" is not supported.", + "startError": "There was an error starting the debug session, check the logs for more details." + }, + "editor": { + "unsavedTitle": "Unsaved – {0}" + }, + "messages": { + "expand": "Expand", + "collapse": "Collapse" + }, + "workspace": { + "fileNewName": "Name for new file", + "invalidFilename": "Invalid filename.", + "invalidExtension": "\".{0}\" امتداد غير صالح", + "newFileName": "New name for file", + "deleteCurrentSketch": "Do you want to delete the current sketch?", + "sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected." + } + }, + "cloud": { + "GoToCloud": "GO TO CLOUD" + } +} diff --git a/i18n/de.json b/i18n/de.json new file mode 100644 index 000000000..d5dbe2deb --- /dev/null +++ b/i18n/de.json @@ -0,0 +1,334 @@ +{ + "arduino": { + "common": { + "offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.", + "noBoardSelected": "No board selected", + "selectedOn": "on {0}", + "notConnected": "[not connected]", + "serialMonitor": "Serieller Monitor", + "oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", + "later": "Later", + "selectBoard": "Select Board", + "unknown": "Unknown", + "processing": "Processing", + "saveChangesToSketch": "Do you want to save changes to this sketch before closing?", + "loseChanges": "If you don't save, your changes will be lost." + }, + "menu": { + "sketch": "Sketch", + "tools": "Werkzeuge" + }, + "debug": { + "optimizeForDebugging": "Optimize for Debugging", + "debugWithMessage": "Debug - {0}", + "noPlatformInstalledFor": "Platform is not installed for '{0}'", + "debuggingNotSupported": "Debugging is not supported by '{0}'" + }, + "preferences": { + "language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", + "compile.verbose": "True for verbose compile output. False by default", + "compile.warnings": "Tells gcc which warning level to use. It's 'None' by default", + "upload.verbose": "True for verbose upload output. False by default.", + "window.autoScale": "True if the user interface automatically scales with the font size.", + "window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.", + "ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.", + "board.certificates": "List of certificates that can be uploaded to boards", + "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.", + "cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.", + "cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.", + "cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.", + "cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.", + "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", + "auth.clientID": "The OAuth2 client ID.", + "auth.domain": "The OAuth2 domain.", + "auth.audience": "The OAuth2 audience.", + "auth.registerUri": "The URI used to register a new user.", + "network": "Netzwerk", + "sketchbook.location": "Sketchbook location", + "browse": "Durchsuchen", + "files.inside.sketches": "Show files inside Sketches", + "editorFontSize": "Editor font size", + "interfaceScale": "Interface scale", + "showVerbose": "Show verbose output during", + "compilerWarnings": "Compiler warnings", + "automatic": "Automatisch", + "compile": "compile", + "upload": "upload", + "verifyAfterUpload": "Verify code after upload", + "checkForUpdates": "Beim Start nach Updates suchen", + "editorQuickSuggestions": "Editor Quick Suggestions", + "additionalManagerURLs": "Zusätzliche Boardverwalter-URLs", + "noProxy": "No proxy", + "manualProxy": "Manual proxy configuration", + "newSketchbookLocation": "Select new sketchbook location", + "choose": "Choose", + "enterAdditionalURLs": "Enter additional URLs, one for each row", + "unofficialBoardSupport": "Click for a list of unofficial board support URLs", + "invalid.sketchbook.location": "Invalid sketchbook location: {0}", + "invalid.editorFontSize": "Invalid editor font size. It must be a positive integer.", + "invalid.theme": "Invalid theme." + }, + "cloud": { + "signIn": "SIGN IN", + "signOut": "Sign Out", + "chooseSketchVisibility": "Choose visibility of your Sketch:", + "privateVisibility": "Private. Only you can view the Sketch.", + "publicVisibility": "Public. Anyone with the link can view the Sketch.", + "link": "Link:", + "embed": "Embed:", + "cloudSketchbook": "Cloud Sketchbook", + "shareSketch": "Share Sketch", + "showHideRemoveSketchbook": "Show/Hide Remote Sketchbook", + "pullSketch": "Pull Sketch", + "openInCloudEditor": "Open in Cloud Editor", + "options": "Options...", + "share": "Share...", + "remote": "Remote", + "emptySketchbook": "Your Sketchbook is empty", + "visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches.", + "signInToCloud": "Sign in to Arduino Cloud", + "syncEditSketches": "Sync and edit your Arduino Cloud Sketches", + "learnMore": "Learn more", + "continue": "Continue", + "pushSketch": "Push Sketch", + "pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.", + "pull": "Pull", + "pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?", + "donePulling": "Done pulling ‘{0}’.", + "notYetPulled": "Cannot push to Cloud. It is not yet pulled.", + "push": "Push", + "pullFirst": "You have to pull first to be able to push to the Cloud.", + "donePushing": "Done pushing ‘{0}’.", + "connected": "Connected", + "offline": "Offline", + "profilePicture": "Profile picture" + }, + "board": { + "installManually": "Install Manually", + "installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?", + "configDialogTitle": "Select Other Board & Port", + "configDialog1": "Select both a Board and a Port if you want to upload a sketch.", + "configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.", + "pleasePickBoard": "Please pick a board connected to the port you have selected.", + "showAllAvailablePorts": "Shows all available ports when enabled", + "programmer": "Programmer", + "succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}", + "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}", + "couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?", + "reselectLater": "Reselect later", + "noneSelected": "No boards selected.", + "noPortsSelected": "No ports selected for board: '{0}'.", + "noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?", + "openBoardsConfig": "Select other board and port…", + "boardListItem": "{0} at {1}", + "selectBoardForInfo": "Please select a board to obtain board info.", + "platformMissing": "The platform for the selected '{0}' board is not installed.", + "selectPortForInfo": "Please select a port to obtain board info.", + "boardInfo": "Boardinformationen", + "board": "Board{0}", + "port": "Port{0}", + "getBoardInfo": "Get Board Info", + "inSketchbook": " (in Sketchbook)" + }, + "boardsManager": "Boardverwalter", + "about": { + "label": "About {0}", + "detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}" + }, + "contributions": { + "addFile": "Datei hinzufügen...", + "replaceTitle": "Replace", + "fileAdded": "One file added to the sketch." + }, + "replaceMsg": "Replace the existing version of {0}?", + "library": { + "addZip": ".ZIP-Bibliothek hinzufügen...", + "zipLibrary": "Library", + "overwriteExistingLibrary": "Do you want to overwrite the existing library?", + "successfullyInstalledZipLibrary": "Successfully installed library from {0} archive", + "namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?", + "libraryAlreadyExists": "A library already exists. Do you want to overwrite it?", + "include": "Include Library", + "manageLibraries": "Manage Libraries...", + "arduinoLibraries": "Arduino libraries", + "contributedLibraries": "Contributed libraries", + "title": "Library Manager", + "needsOneDependency": "The library {0}:{1} needs another dependency currently not installed:", + "needsMultipleDependencies": "The library {0}:{1} needs some other dependencies currently not installed:", + "installOneMissingDependency": "Would you like to install the missing dependency?", + "installMissingDependencies": "Would you like to install all the missing dependencies?", + "dependenciesForLibrary": "Dependencies for library {0}:{1}", + "installAll": "Install all", + "installOnly": "Install {0} only", + "installedSuccessfully": "Successfully installed library {0}:{1}", + "uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}" + }, + "selectZip": "Select a zip file containing the library you'd like to add", + "sketch": { + "archiveSketch": "Sketch archivieren", + "saveSketchAs": "Save sketch folder as...", + "createdArchive": "Created archive '{0}'.", + "new": "New", + "openRecent": "Open Recent", + "showFolder": "Show Sketch Folder", + "sketch": "Sketch", + "moving": "Moving", + "movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?", + "cantOpen": "Ein Ordner mit dem Namen \"{0}\" ist bereits vorhanden. Der Sketch kann nicht geöffnet werden.", + "saveFolderAs": "Save sketch folder as...", + "sketchbook": "Sketchbook", + "upload": "Hochladen", + "uploadUsingProgrammer": "Upload Using Programmer", + "userFieldsNotFoundError": "Can't find user fields for connected board", + "doneUploading": "Done uploading.", + "configureAndUpload": "Configure And Upload", + "verifyOrCompile": "Verify/Compile", + "exportBinary": "Export Compiled Binary", + "verify": "Verify", + "doneCompiling": "Done compiling.", + "couldNotConnectToSerial": "Could not reconnect to serial port. {0}", + "openSketchInNewWindow": "Open Sketch in New Window", + "openFolder": "Open Folder", + "titleLocalSketchbook": "Local Sketchbook", + "titleSketchbook": "Sketchbook", + "close": "Are you sure you want to close the sketch?" + }, + "bootloader": { + "burnBootloader": "Bootloader brennen", + "doneBurningBootloader": "Done burning bootloader." + }, + "editor": { + "copyForForum": "Für Forum kopieren (Markdown)", + "commentUncomment": "Kommentieren/Kommentar aufheben", + "increaseIndent": "Increase Indent", + "decreaseIndent": "Decrease Indent", + "increaseFontSize": "Increase Font Size", + "decreaseFontSize": "Schriftgröße verringern", + "autoFormat": "Automatische Formatierung" + }, + "examples": { + "menu": "Examples", + "couldNotInitializeExamples": "Could not initialize built-in examples.", + "builtInExamples": "Mitgelieferte Beispiele", + "customLibrary": "Examples from Custom Libraries", + "for": "Examples for {0}", + "forAny": "Examples for any board" + }, + "help": { + "search": "Search on Arduino.cc", + "keyword": "Type a keyword", + "gettingStarted": "Getting Started", + "environment": "Environment", + "troubleshooting": "Troubleshooting", + "reference": "Reference", + "findInReference": "Find in Reference", + "faq": "Frequently Asked Questions", + "visit": "Visit Arduino.cc" + }, + "certificate": { + "uploadRootCertificates": "Upload SSL Root Certificates", + "openContext": "Open context", + "remove": "Remove", + "upload": "Hochladen", + "addURL": "Add URL to fetch SSL certificate", + "enterURL": "Enter URL", + "selectCertificateToUpload": "1. Select certificate to upload", + "addNew": "Add New", + "selectDestinationBoardToUpload": "2. Select destination board and upload certificate", + "uploadingCertificates": "Uploading certificates.", + "certificatesUploaded": "Certificates uploaded.", + "uploadFailed": "Upload failed. Please try again.", + "selectBoard": "Select a board...", + "boardAtPort": "{0} at {1}", + "noSupportedBoardConnected": "No supported board connected" + }, + "firmware": { + "updater": "WiFi101 / WiFiNINA Firmware Updater", + "selectBoard": "Select Board", + "checkUpdates": "Check Updates", + "selectVersion": "Select firmware version", + "install": "Installieren", + "overwriteSketch": "Installation will overwrite the Sketch on the board.", + "installingFirmware": "Installing firmware.", + "successfullyInstalled": "Firmware succesfully installed.", + "failedInstall": "Installation failed. Please try again." + }, + "dialog": { + "dontAskAgain": "Don't ask again" + }, + "userFields": { + "cancel": "Cancel", + "upload": "Hochladen" + }, + "serial": { + "toggleTimestamp": "Toggle Timestamp", + "autoscroll": "Autoscroll", + "timestamp": "Timestamp", + "noLineEndings": "Kein Zeilenende", + "newLine": "Neue Zeile", + "carriageReturn": "Zeilenumbruch (CR)", + "newLineCarriageReturn": "Sowohl NL als auch CR", + "notConnected": "Not connected. Select a board and a port to connect automatically.", + "message": "Message ({0} + Enter to send message to '{1}' on '{2}'", + "connectionBusy": "Connection failed. Serial port is busy: {0}", + "disconnected": "Disconnected {0} from {1}.", + "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", + "failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.", + "reconnect": "Reconnecting {0} to {1} in {2} seconds..." + }, + "component": { + "uninstall": "Uninstall", + "uninstallMsg": "Do you want to uninstall {0}?", + "by": "by", + "version": "Version {0}", + "moreInfo": "More info", + "install": "Installieren", + "filterSearch": "Grenzen Sie Ihre Suche ein..." + }, + "electron": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "unsavedChanges": "Any unsaved changes will not be saved." + }, + "compile": { + "error": "Compilation error: {0}" + }, + "upload": { + "error": "{0} error: {1}" + }, + "burnBootloader": { + "error": "Error while burning the bootloader: {0}" + } + }, + "theia": { + "core": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "offline": "Offline", + "daemonOffline": "CLI Daemon Offline", + "cannotConnectBackend": "Cannot connect to the backend.", + "cannotConnectDaemon": "Cannot connect to the CLI daemon." + }, + "debug": { + "start": "Start...", + "typeNotSupported": "The debug session type \"{0}\" is not supported.", + "startError": "There was an error starting the debug session, check the logs for more details." + }, + "editor": { + "unsavedTitle": "Unsaved – {0}" + }, + "messages": { + "expand": "Expand", + "collapse": "Collapse" + }, + "workspace": { + "fileNewName": "Name for new file", + "invalidFilename": "Invalid filename.", + "invalidExtension": "\".{0}\" ist keine gültige Dateierweiterung.", + "newFileName": "New name for file", + "deleteCurrentSketch": "Do you want to delete the current sketch?", + "sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected." + } + }, + "cloud": { + "GoToCloud": "GO TO CLOUD" + } +} diff --git a/i18n/en.json b/i18n/en.json index d1ea8c596..459770646 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -181,12 +181,12 @@ "uploadUsingProgrammer": "Upload Using Programmer", "userFieldsNotFoundError": "Can't find user fields for connected board", "doneUploading": "Done uploading.", - "couldNotConnectToSerial": "Could not reconnect to serial port. {0}", "configureAndUpload": "Configure And Upload", "verifyOrCompile": "Verify/Compile", "exportBinary": "Export Compiled Binary", "verify": "Verify", "doneCompiling": "Done compiling.", + "couldNotConnectToSerial": "Could not reconnect to serial port. {0}", "openSketchInNewWindow": "Open Sketch in New Window", "openFolder": "Open Folder", "titleLocalSketchbook": "Local Sketchbook", @@ -274,7 +274,7 @@ "disconnected": "Disconnected {0} from {1}.", "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", "failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.", - "reconnect": "Reconnecting {0} to {1} in {2] seconds..." + "reconnect": "Reconnecting {0} to {1} in {2} seconds..." }, "component": { "uninstall": "Uninstall", @@ -288,6 +288,15 @@ "electron": { "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", "unsavedChanges": "Any unsaved changes will not be saved." + }, + "compile": { + "error": "Compilation error: {0}" + }, + "upload": { + "error": "{0} error: {1}" + }, + "burnBootloader": { + "error": "Error while burning the bootloader: {0}" } }, "theia": { diff --git a/i18n/es.json b/i18n/es.json new file mode 100644 index 000000000..5342f673d --- /dev/null +++ b/i18n/es.json @@ -0,0 +1,334 @@ +{ + "arduino": { + "common": { + "offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.", + "noBoardSelected": "No board selected", + "selectedOn": "on {0}", + "notConnected": "[not connected]", + "serialMonitor": "Monitor Serie", + "oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", + "later": "Later", + "selectBoard": "Select Board", + "unknown": "Unknown", + "processing": "Processing", + "saveChangesToSketch": "Do you want to save changes to this sketch before closing?", + "loseChanges": "If you don't save, your changes will be lost." + }, + "menu": { + "sketch": "Programa", + "tools": "Herramientas" + }, + "debug": { + "optimizeForDebugging": "Optimize for Debugging", + "debugWithMessage": "Debug - {0}", + "noPlatformInstalledFor": "Platform is not installed for '{0}'", + "debuggingNotSupported": "Debugging is not supported by '{0}'" + }, + "preferences": { + "language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", + "compile.verbose": "True for verbose compile output. False by default", + "compile.warnings": "Tells gcc which warning level to use. It's 'None' by default", + "upload.verbose": "True for verbose upload output. False by default.", + "window.autoScale": "True if the user interface automatically scales with the font size.", + "window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.", + "ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.", + "board.certificates": "List of certificates that can be uploaded to boards", + "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.", + "cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.", + "cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.", + "cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.", + "cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.", + "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", + "auth.clientID": "The OAuth2 client ID.", + "auth.domain": "The OAuth2 domain.", + "auth.audience": "The OAuth2 audience.", + "auth.registerUri": "The URI used to register a new user.", + "network": "Red", + "sketchbook.location": "Sketchbook location", + "browse": "Explorar", + "files.inside.sketches": "Show files inside Sketches", + "editorFontSize": "Editor font size", + "interfaceScale": "Interface scale", + "showVerbose": "Show verbose output during", + "compilerWarnings": "Compiler warnings", + "automatic": "Automático", + "compile": "compile", + "upload": "upload", + "verifyAfterUpload": "Verify code after upload", + "checkForUpdates": "Comprobar actualizaciones al iniciar", + "editorQuickSuggestions": "Editor Quick Suggestions", + "additionalManagerURLs": "Gestor de URLs Adicionales de Tarjetas", + "noProxy": "No proxy", + "manualProxy": "Manual proxy configuration", + "newSketchbookLocation": "Select new sketchbook location", + "choose": "Choose", + "enterAdditionalURLs": "Enter additional URLs, one for each row", + "unofficialBoardSupport": "Click for a list of unofficial board support URLs", + "invalid.sketchbook.location": "Invalid sketchbook location: {0}", + "invalid.editorFontSize": "Invalid editor font size. It must be a positive integer.", + "invalid.theme": "Invalid theme." + }, + "cloud": { + "signIn": "SIGN IN", + "signOut": "Sign Out", + "chooseSketchVisibility": "Choose visibility of your Sketch:", + "privateVisibility": "Private. Only you can view the Sketch.", + "publicVisibility": "Public. Anyone with the link can view the Sketch.", + "link": "Link:", + "embed": "Embed:", + "cloudSketchbook": "Cloud Sketchbook", + "shareSketch": "Share Sketch", + "showHideRemoveSketchbook": "Show/Hide Remote Sketchbook", + "pullSketch": "Pull Sketch", + "openInCloudEditor": "Open in Cloud Editor", + "options": "Options...", + "share": "Share...", + "remote": "Remote", + "emptySketchbook": "Your Sketchbook is empty", + "visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches.", + "signInToCloud": "Sign in to Arduino Cloud", + "syncEditSketches": "Sync and edit your Arduino Cloud Sketches", + "learnMore": "Learn more", + "continue": "Continue", + "pushSketch": "Push Sketch", + "pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.", + "pull": "Pull", + "pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?", + "donePulling": "Done pulling ‘{0}’.", + "notYetPulled": "Cannot push to Cloud. It is not yet pulled.", + "push": "Push", + "pullFirst": "You have to pull first to be able to push to the Cloud.", + "donePushing": "Done pushing ‘{0}’.", + "connected": "Connected", + "offline": "Offline", + "profilePicture": "Profile picture" + }, + "board": { + "installManually": "Install Manually", + "installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?", + "configDialogTitle": "Select Other Board & Port", + "configDialog1": "Select both a Board and a Port if you want to upload a sketch.", + "configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.", + "pleasePickBoard": "Please pick a board connected to the port you have selected.", + "showAllAvailablePorts": "Shows all available ports when enabled", + "programmer": "Programmer", + "succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}", + "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}", + "couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?", + "reselectLater": "Reselect later", + "noneSelected": "No boards selected.", + "noPortsSelected": "No ports selected for board: '{0}'.", + "noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?", + "openBoardsConfig": "Select other board and port…", + "boardListItem": "{0} at {1}", + "selectBoardForInfo": "Please select a board to obtain board info.", + "platformMissing": "The platform for the selected '{0}' board is not installed.", + "selectPortForInfo": "Please select a port to obtain board info.", + "boardInfo": "Obtén información de la placa", + "board": "Board{0}", + "port": "Port{0}", + "getBoardInfo": "Get Board Info", + "inSketchbook": " (in Sketchbook)" + }, + "boardsManager": "Gestor de tarjetas", + "about": { + "label": "About {0}", + "detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}" + }, + "contributions": { + "addFile": "Añadir fichero...", + "replaceTitle": "Replace", + "fileAdded": "One file added to the sketch." + }, + "replaceMsg": "Replace the existing version of {0}?", + "library": { + "addZip": "Añadir biblioteca .ZIP...", + "zipLibrary": "Library", + "overwriteExistingLibrary": "Do you want to overwrite the existing library?", + "successfullyInstalledZipLibrary": "Successfully installed library from {0} archive", + "namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?", + "libraryAlreadyExists": "A library already exists. Do you want to overwrite it?", + "include": "Include Library", + "manageLibraries": "Manage Libraries...", + "arduinoLibraries": "Arduino libraries", + "contributedLibraries": "Contributed libraries", + "title": "Library Manager", + "needsOneDependency": "The library {0}:{1} needs another dependency currently not installed:", + "needsMultipleDependencies": "The library {0}:{1} needs some other dependencies currently not installed:", + "installOneMissingDependency": "Would you like to install the missing dependency?", + "installMissingDependencies": "Would you like to install all the missing dependencies?", + "dependenciesForLibrary": "Dependencies for library {0}:{1}", + "installAll": "Install all", + "installOnly": "Install {0} only", + "installedSuccessfully": "Successfully installed library {0}:{1}", + "uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}" + }, + "selectZip": "Select a zip file containing the library you'd like to add", + "sketch": { + "archiveSketch": "Archivo de programa", + "saveSketchAs": "Save sketch folder as...", + "createdArchive": "Created archive '{0}'.", + "new": "New", + "openRecent": "Open Recent", + "showFolder": "Show Sketch Folder", + "sketch": "Sketch", + "moving": "Moving", + "movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?", + "cantOpen": "Ya existe una carpeta con el nombre \"{0}\". No se puede abrir.", + "saveFolderAs": "Save sketch folder as...", + "sketchbook": "Sketchbook", + "upload": "Upload", + "uploadUsingProgrammer": "Upload Using Programmer", + "userFieldsNotFoundError": "Can't find user fields for connected board", + "doneUploading": "Done uploading.", + "configureAndUpload": "Configure And Upload", + "verifyOrCompile": "Verify/Compile", + "exportBinary": "Export Compiled Binary", + "verify": "Verify", + "doneCompiling": "Done compiling.", + "couldNotConnectToSerial": "Could not reconnect to serial port. {0}", + "openSketchInNewWindow": "Open Sketch in New Window", + "openFolder": "Open Folder", + "titleLocalSketchbook": "Local Sketchbook", + "titleSketchbook": "Sketchbook", + "close": "Are you sure you want to close the sketch?" + }, + "bootloader": { + "burnBootloader": "Quemar Bootloader", + "doneBurningBootloader": "Done burning bootloader." + }, + "editor": { + "copyForForum": "Copiar al Foro (Markdown)", + "commentUncomment": "Comentar / Descomentar", + "increaseIndent": "Increase Indent", + "decreaseIndent": "Decrease Indent", + "increaseFontSize": "Increase Font Size", + "decreaseFontSize": "Reducir Tamaño de Fuente", + "autoFormat": "Auto Formato" + }, + "examples": { + "menu": "Examples", + "couldNotInitializeExamples": "Could not initialize built-in examples.", + "builtInExamples": "Ejemplos Construidos ", + "customLibrary": "Examples from Custom Libraries", + "for": "Examples for {0}", + "forAny": "Examples for any board" + }, + "help": { + "search": "Search on Arduino.cc", + "keyword": "Type a keyword", + "gettingStarted": "Getting Started", + "environment": "Environment", + "troubleshooting": "Troubleshooting", + "reference": "Reference", + "findInReference": "Find in Reference", + "faq": "Frequently Asked Questions", + "visit": "Visit Arduino.cc" + }, + "certificate": { + "uploadRootCertificates": "Upload SSL Root Certificates", + "openContext": "Open context", + "remove": "Remove", + "upload": "Subir", + "addURL": "Add URL to fetch SSL certificate", + "enterURL": "Enter URL", + "selectCertificateToUpload": "1. Select certificate to upload", + "addNew": "Add New", + "selectDestinationBoardToUpload": "2. Select destination board and upload certificate", + "uploadingCertificates": "Uploading certificates.", + "certificatesUploaded": "Certificates uploaded.", + "uploadFailed": "Upload failed. Please try again.", + "selectBoard": "Select a board...", + "boardAtPort": "{0} at {1}", + "noSupportedBoardConnected": "No supported board connected" + }, + "firmware": { + "updater": "WiFi101 / WiFiNINA Firmware Updater", + "selectBoard": "Select Board", + "checkUpdates": "Check Updates", + "selectVersion": "Select firmware version", + "install": "Instalar", + "overwriteSketch": "Installation will overwrite the Sketch on the board.", + "installingFirmware": "Installing firmware.", + "successfullyInstalled": "Firmware succesfully installed.", + "failedInstall": "Installation failed. Please try again." + }, + "dialog": { + "dontAskAgain": "Don't ask again" + }, + "userFields": { + "cancel": "Cancel", + "upload": "Subir" + }, + "serial": { + "toggleTimestamp": "Toggle Timestamp", + "autoscroll": "Autoscroll", + "timestamp": "Timestamp", + "noLineEndings": "Sin ajuste de línea", + "newLine": "Nueva línea", + "carriageReturn": "Retorno de carro", + "newLineCarriageReturn": "Ambos NL & CR", + "notConnected": "Not connected. Select a board and a port to connect automatically.", + "message": "Message ({0} + Enter to send message to '{1}' on '{2}'", + "connectionBusy": "Connection failed. Serial port is busy: {0}", + "disconnected": "Disconnected {0} from {1}.", + "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", + "failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.", + "reconnect": "Reconnecting {0} to {1} in {2} seconds..." + }, + "component": { + "uninstall": "Uninstall", + "uninstallMsg": "Do you want to uninstall {0}?", + "by": "by", + "version": "Versión {0}", + "moreInfo": "More info", + "install": "Instalar", + "filterSearch": "Filtre su búsqueda..." + }, + "electron": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "unsavedChanges": "Any unsaved changes will not be saved." + }, + "compile": { + "error": "Compilation error: {0}" + }, + "upload": { + "error": "{0} error: {1}" + }, + "burnBootloader": { + "error": "Error while burning the bootloader: {0}" + } + }, + "theia": { + "core": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "offline": "Offline", + "daemonOffline": "CLI Daemon Offline", + "cannotConnectBackend": "Cannot connect to the backend.", + "cannotConnectDaemon": "Cannot connect to the CLI daemon." + }, + "debug": { + "start": "Start...", + "typeNotSupported": "The debug session type \"{0}\" is not supported.", + "startError": "There was an error starting the debug session, check the logs for more details." + }, + "editor": { + "unsavedTitle": "Unsaved – {0}" + }, + "messages": { + "expand": "Expand", + "collapse": "Collapse" + }, + "workspace": { + "fileNewName": "Name for new file", + "invalidFilename": "Invalid filename.", + "invalidExtension": "\".{0}\" no es una extensión válida", + "newFileName": "New name for file", + "deleteCurrentSketch": "Do you want to delete the current sketch?", + "sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected." + } + }, + "cloud": { + "GoToCloud": "GO TO CLOUD" + } +} diff --git a/i18n/fr.json b/i18n/fr.json new file mode 100644 index 000000000..108f9768d --- /dev/null +++ b/i18n/fr.json @@ -0,0 +1,334 @@ +{ + "arduino": { + "common": { + "offlineIndicator": "Il semblerais que vous êtes hors-ligne. Sans connexion internet, l'Arduino CLI sera incapable de télécharger les ressources nécessaire et de communiquer avec le cloud. Merci de vous reconnectez et de redémarrer l'application.", + "noBoardSelected": "Aucune carte n'a été sélectionné ", + "selectedOn": "sur [0]", + "notConnected": "[not connected]", + "serialMonitor": "Moniteur série", + "oldFormat": "Le \"[0]\" continue d'utiliser l'ancien format `.pde`. Souhaitez-vous utiliser le nouveau format `.ino`?", + "later": "Plus tard", + "selectBoard": "Selectionner une carte", + "unknown": "Inconnu", + "processing": "Traitement", + "saveChangesToSketch": "Souhaitez-vous enregistrer les modifications apportées au croquis avant la fermeture ?", + "loseChanges": "Si vous n'enregistrez pas, les modifications apportées seront perdues." + }, + "menu": { + "sketch": "Croquis", + "tools": "Outils" + }, + "debug": { + "optimizeForDebugging": "Optimisé pour le déboggage.", + "debugWithMessage": "Débogage - [0]", + "noPlatformInstalledFor": "la plateforme n'est pas installé pour '{0}'", + "debuggingNotSupported": "Le débogage n'est pas supporté pour '{0}'" + }, + "preferences": { + "language.log": "Vrai si le serveur de langage Arduino devrait générer des fichiers logs dans le dossier du croquis. Si non faux. La valeur par défaut est faux.", + "compile.verbose": "Vrai pour une sortie de compilation verbeuse. Faux par défaut.", + "compile.warnings": "Dis à GCC quel niveau d'avertissement à utiliser. Valeur par défaut : 'None'.", + "upload.verbose": "Vrai si le téléchargement en mode verbose. Faux par défaut.", + "window.autoScale": "Vrai si l'interface utilisateur s'ajuste avec la taille de la police.", + "window.zoomLevel": "Ajuste le zoom de la fenêtre. La taille originale est 0 et chaque augmentation (par exemple 1) ou diminution (par exemple -1) représentent un zoom de plus ou moins 20 %. Vous pouvez également entrer des décimales pour ajuster plus finement le zoom. ", + "ide.autoUpdate": "Vrai pour activer les mise à jour automatiques. L'IDE va vérifier automatiquement et de manière périodique les mise à jour.", + "board.certificates": "Liste des certificats pouvant être téléversé vers les cartes.", + "sketchbook.showAllFiles": "Vrai pour montrer tous les fichiers croquis à l'intérieur du croquis. La valeur par défaut est faux.", + "cloud.enabled": "Vrai si les fonctions de synchronisation de croquis est activé. Par défaut, la valeur est vrai.", + "cloud.pull.warn": "Vrai si les utilisateurs devrait être averti avant de pull un croquis sur le cloud. Par défaut, la valeur est vrai.", + "cloud.push.warn": "Vrai, si les utilisateurs devrait être averti avant de push un croquis sur le cloud. Par défaut, la valeur est vrai.", + "cloud.pushpublic.warn": "Vrai si les utilisateurs devrait être avertit avant de publier un croquis public sur le cloud. Vrai par défaut.", + "cloud.sketchSyncEnpoint": "L'endpoint est utilisé pour pousser et tirer des croquis à partir du backend. Par défault, il pointe vers l'Arduino Cloud API.", + "auth.clientID": "L'identifiant client OAuth2.", + "auth.domain": "Le domaine 0Auth2.", + "auth.audience": "L'audience OAuth2.", + "auth.registerUri": "L'URI utilisé pour créer un nouvel utilisateur.", + "network": "Réseau", + "sketchbook.location": "Localisation du croquis.", + "browse": "Parcourir", + "files.inside.sketches": "Afficher les fichiers à l’intérieur de Sketches.", + "editorFontSize": "Taille de police de l'éditeur", + "interfaceScale": "Échelle de l'interface", + "showVerbose": "Afficher la sortie de débogage verbeuse pendant", + "compilerWarnings": "Avertissements du compilateur ", + "automatic": "Automatique", + "compile": "compiler", + "upload": "téléverser", + "verifyAfterUpload": "Vérifier le code après le téléversement", + "checkForUpdates": "Vérifier les mises à jour au démarrage", + "editorQuickSuggestions": "Suggestion rapide pour l'éditeur", + "additionalManagerURLs": "URL de gestionnaire de cartes supplémentaires", + "noProxy": "Aucun proxy", + "manualProxy": "Configuration manuelle du proxy", + "newSketchbookLocation": "Sélectionner la localisation du nouveau croquis.", + "choose": "Choisir", + "enterAdditionalURLs": "Entrer des URLs additionnelles, une par colonne", + "unofficialBoardSupport": "Cliquer pour la liste non-officielle des URLs des support de cartes", + "invalid.sketchbook.location": "Localisation invalide du croquis : {0}", + "invalid.editorFontSize": "Police de l'éditeur invalide. La taille doit être un entier positif.", + "invalid.theme": "Thème invalide." + }, + "cloud": { + "signIn": "Se connecter", + "signOut": "Deconnexion", + "chooseSketchVisibility": "Choisissez la visibilité du croquis :", + "privateVisibility": "Privé. Seulement vous pouvez voir le croquis.", + "publicVisibility": "Publique. Tout les personnes disposant du lien pourront consulter le croquis.", + "link": "Lien :", + "embed": "Embarqué : ", + "cloudSketchbook": "Carnet de Croquis Cloud", + "shareSketch": "Partager le croquis", + "showHideRemoveSketchbook": "Montrer / Cacher le carnet de croquis distant.", + "pullSketch": "Pull le croquis", + "openInCloudEditor": "Ouvrir le croquis dans l'éditeur du cloud", + "options": "Options ...", + "share": "Partager...", + "remote": "A distance", + "emptySketchbook": "Votre carnet de croquis est vide", + "visitArduinoCloud": "Visitez Arduino Cloud pour créer des croquis sut le cloud.", + "signInToCloud": "Se connecter à Arduino Cloud", + "syncEditSketches": "Synchroniser et éditer vos croquis Arduino Cloud.", + "learnMore": "En savoir plus", + "continue": "Continuer", + "pushSketch": "Push le croquis", + "pushSketchMsg": "Ceci est un croquis public. Avant de le publier, assurez-vous que les informations sensibles sont bien définies dans le fichier arduino_secrets.h. Vous pouvez rendre priver le croquis à partir du panneau de Partage.", + "pull": "Tirer", + "pullSketchMsg": "Pousser le croquis à partir du Cloud va écraser la version locale. Êtes-vous sûr de vouloir continuer ?", + "donePulling": "Done pulling ‘{0}’.", + "notYetPulled": "Impossible de push sur le Cloud. Rien n'a été pull jusque la.", + "push": "Push", + "pullFirst": "Vous devez tout d'abord pull vos modifications avant de pouvoir de pouvoir push à partir du Cloud.", + "donePushing": "Done pushing ‘{0}’.", + "connected": "Connecté", + "offline": "Hors-ligne", + "profilePicture": "Photo de profil" + }, + "board": { + "installManually": "Installer manuellement.", + "installNow": "Le \"{0} {1}\" core doit être installé pour la carte sélectionnée \"{2}\". Souhaitez vous l'installer maintenant ?", + "configDialogTitle": "Sélectionner une autre carte et un autre port", + "configDialog1": "Sélectionnez une carte et un port si vous souhaitez téléverser un croquis.", + "configDialog2": "Si vous sélectionnez seulement une carte, vous pourrez seulement capable de compliquer votre croquis, mais pas de le téléverser.", + "pleasePickBoard": "Merci de sélectionner une carte connecté au port que vous avez sélectionné.", + "showAllAvailablePorts": "Affiche les ports disponibles quand activer.", + "programmer": "Programmeur", + "succesfullyInstalledPlatform": "Plateforme installé avec succès {0}:{1}", + "succesfullyUninstalledPlatform": "Plateforme désinstallée avec succès {0}:{1}", + "couldNotFindPreviouslySelected": "Impossible de trouver la carte précédente sélectionnée \"{0}\" dans les plateformes installées \"{1}\". Merci de re-sélectionner manuellement la carte que vous souhaitez utiliser. Souhaitez vous la re-sélectionner maintenant ?", + "reselectLater": "Re-sélectionner plus tard", + "noneSelected": "Aucune cartes sélectionnée.", + "noPortsSelected": "Aucun porte sélectionné pour la carte : '{0}'", + "noFQBN": "Le FQBN n'est pas disponible pour la carte sélectionnée \"{0}\". Avez installé le cœur correspondant ?", + "openBoardsConfig": "Sélectionner une autre carte et un autre port ...", + "boardListItem": "{0} à {1}", + "selectBoardForInfo": "Please select a board to obtain board info.", + "platformMissing": "La plateforme pour la carte '{0}' sélectionnée n'est pas installée.", + "selectPortForInfo": "Please select a port to obtain board info.", + "boardInfo": "Information de la carte", + "board": "Board{0}", + "port": "Port{0}", + "getBoardInfo": "Get Board Info", + "inSketchbook": " (in Sketchbook)" + }, + "boardsManager": "Gestionnaire de carte", + "about": { + "label": "About {0}", + "detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}" + }, + "contributions": { + "addFile": "Ajouter un fichier", + "replaceTitle": "Replace", + "fileAdded": "One file added to the sketch." + }, + "replaceMsg": "Replace the existing version of {0}?", + "library": { + "addZip": "Ajouter la bibliothèque .ZIP...", + "zipLibrary": "Library", + "overwriteExistingLibrary": "Souhaitez-vous écraser la librairie existante ?", + "successfullyInstalledZipLibrary": "Successfully installed library from {0} archive", + "namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?", + "libraryAlreadyExists": "Cette librairie existe déjà. Souhaitez-vous l'écraser ?", + "include": "Include Library", + "manageLibraries": "Manage Libraries...", + "arduinoLibraries": "Arduino libraries", + "contributedLibraries": "Contributed libraries", + "title": "Library Manager", + "needsOneDependency": "The library {0}:{1} needs another dependency currently not installed:", + "needsMultipleDependencies": "La librairie {0}:{1}à besoin de d'autres dépendantes qui ne sont actuellement pas installé:", + "installOneMissingDependency": "Souhaitez-vous installer la dépendance manquante?", + "installMissingDependencies": "Souhaitez vous installer toutes les dépendances manquantes ?", + "dependenciesForLibrary": "Dépendances pour la libraire {0}:{1}", + "installAll": "Tout installer", + "installOnly": "Installer {0} seulement", + "installedSuccessfully": "Librairie installée avec succès {0}:{1}", + "uninstalledSuccessfully": "Librairie désinstallée avec succès {0}:{1}" + }, + "selectZip": "Sélectionnez un fichier zip contenant la bibliothèque que vous souhaitez ajouter", + "sketch": { + "archiveSketch": "Archiver le croquis", + "saveSketchAs": "Sauvegarder un dossier de croquis comme ...", + "createdArchive": "Archive créer '{0}'", + "new": "Nouveau", + "openRecent": "Ouvrir les récents", + "showFolder": "Ouvrir le dossier de croquis", + "sketch": "Croquis", + "moving": "Déplacement", + "movingMsg": "Le fichier \"{0}\" à besoin d'être à l'intérieur d'un dossier de croquis appelé \"{1}\".\nCréer ce dossier, déplacer le fichier et continuer ?", + "cantOpen": "Un dossier nommé « {0} » existe déjà. Impossible d''ouvrir le croquis.", + "saveFolderAs": "Sauvegarder le dossier de croquis en tant que ...", + "sketchbook": "Carnet de croquis", + "upload": "Téléverser", + "uploadUsingProgrammer": "Téléverser en utilisant un programmateur", + "userFieldsNotFoundError": "Impossible de trouver un champ utilisateur pour les cartes connectés", + "doneUploading": "Téléversement fait.", + "configureAndUpload": "Configurer et téléverser", + "verifyOrCompile": "Vérifier / Compiler", + "exportBinary": "Exporter les binaires compilés", + "verify": "Vérifier", + "doneCompiling": "Compilation terminée.", + "couldNotConnectToSerial": "Impossible de se reconnecter au port série. {0}", + "openSketchInNewWindow": "Ouvrir le croquis dans une nouvelle fenêtre", + "openFolder": "Ouvrir le dossier", + "titleLocalSketchbook": "Localiser le carnet de croquis", + "titleSketchbook": "Carnet de croquis", + "close": "Êtes-vous sûr de vouloir fermer ce croquis ?" + }, + "bootloader": { + "burnBootloader": "Graver la séquence d'initialisation", + "doneBurningBootloader": "Le bootloader à été gravé." + }, + "editor": { + "copyForForum": "Copier pour le forum (Markdown)", + "commentUncomment": "Commenter/Décommenter", + "increaseIndent": "Augmenter le retrait", + "decreaseIndent": "Diminuer le retrait", + "increaseFontSize": "Augmenter la taille de la police", + "decreaseFontSize": "Réduire la taille de la police", + "autoFormat": "Formatage automatique" + }, + "examples": { + "menu": "Exemples", + "couldNotInitializeExamples": "Impossible d'initialiser les exemples inclus.", + "builtInExamples": "Exemples inclus", + "customLibrary": "Exemples des librairies personnalisées", + "for": "Exemples pour {0}", + "forAny": "Exemples pour n'importe quelle carte" + }, + "help": { + "search": "Rechercher sur Arduino.cc", + "keyword": "Tapez un mot clé", + "gettingStarted": "Commencer", + "environment": "Environnement", + "troubleshooting": "Dépannage", + "reference": "Référence", + "findInReference": "Trouver dans Référence", + "faq": "Questions fréquemment posées", + "visit": "Visitez Arduino.cc" + }, + "certificate": { + "uploadRootCertificates": "Téléverser les certificats racine SSL", + "openContext": "Open context", + "remove": "Supprimer ", + "upload": "Téléverser", + "addURL": "Add URL to fetch SSL certificate", + "enterURL": "Enter URL", + "selectCertificateToUpload": "1. Select certificate to upload", + "addNew": "Add New", + "selectDestinationBoardToUpload": "2. Select destination board and upload certificate", + "uploadingCertificates": "Uploading certificates.", + "certificatesUploaded": "Certificates uploaded.", + "uploadFailed": "Upload failed. Please try again.", + "selectBoard": "Select a board...", + "boardAtPort": "{0} at {1}", + "noSupportedBoardConnected": "No supported board connected" + }, + "firmware": { + "updater": "WiFi101 / WiFiNINA Firmware Updater", + "selectBoard": "Selectionner une carte", + "checkUpdates": "Check Updates", + "selectVersion": "Select firmware version", + "install": "Installer", + "overwriteSketch": "Installation will overwrite the Sketch on the board.", + "installingFirmware": "Installing firmware.", + "successfullyInstalled": "Firmware succesfully installed.", + "failedInstall": "Installation failed. Please try again." + }, + "dialog": { + "dontAskAgain": "Don't ask again" + }, + "userFields": { + "cancel": "Cancel", + "upload": "Téléverser" + }, + "serial": { + "toggleTimestamp": "Toggle Timestamp", + "autoscroll": "Défilement automatique", + "timestamp": "Timestamp", + "noLineEndings": "Pas de fin de ligne", + "newLine": "Nouvelle ligne", + "carriageReturn": "Retour chariot", + "newLineCarriageReturn": "Les deux, NL et CR", + "notConnected": "No connecté. Sélectionnez une carte ainsi qu'un port à connecter automatiquement.", + "message": "Message ({0} + Enter to send message to '{1}' on '{2}'", + "connectionBusy": "Connection failed. Serial port is busy: {0}", + "disconnected": "{0} déconnecté de {1}.", + "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", + "failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.", + "reconnect": "Reconnecting {0} to {1} in {2} seconds..." + }, + "component": { + "uninstall": "Uninstall", + "uninstallMsg": "Do you want to uninstall {0}?", + "by": "by", + "version": "Version {0}", + "moreInfo": "More info", + "install": "Installer", + "filterSearch": "Filtrez votre recherche" + }, + "electron": { + "couldNotSave": "Impossible d'enregistrer le croquis. Merci de faire une copie de votre travail non-enregistré dans votre éditeur de texte favoris, et redémarrez votre IDE.", + "unsavedChanges": "Any unsaved changes will not be saved." + }, + "compile": { + "error": "Compilation error: {0}" + }, + "upload": { + "error": "{0} error: {1}" + }, + "burnBootloader": { + "error": "Error while burning the bootloader: {0}" + } + }, + "theia": { + "core": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "offline": "Offline", + "daemonOffline": "CLI Daemon Offline", + "cannotConnectBackend": "Cannot connect to the backend.", + "cannotConnectDaemon": "Cannot connect to the CLI daemon." + }, + "debug": { + "start": "Start...", + "typeNotSupported": "The debug session type \"{0}\" is not supported.", + "startError": "Une erreur est survenue lors du démarrage du débogage, consultez les logs pour plus de détails." + }, + "editor": { + "unsavedTitle": "Unsaved – {0}" + }, + "messages": { + "expand": "Expand", + "collapse": "Collapse" + }, + "workspace": { + "fileNewName": "Name for new file", + "invalidFilename": "Invalid filename.", + "invalidExtension": "« .{0} » n''est pas une extension valide.", + "newFileName": "New name for file", + "deleteCurrentSketch": "Do you want to delete the current sketch?", + "sketchDirectoryError": "Une erreur est survenue lors de la création du dossier du croquis. Consultez les logs pour plus de détails. L'application ne fonctionnera probablement pas comme attendu." + } + }, + "cloud": { + "GoToCloud": "GO TO CLOUD" + } +} diff --git a/i18n/it.json b/i18n/it.json new file mode 100644 index 000000000..7acfa2b93 --- /dev/null +++ b/i18n/it.json @@ -0,0 +1,334 @@ +{ + "arduino": { + "common": { + "offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.", + "noBoardSelected": "No board selected", + "selectedOn": "on {0}", + "notConnected": "[not connected]", + "serialMonitor": "Monitor seriale", + "oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", + "later": "Later", + "selectBoard": "Select Board", + "unknown": "Unknown", + "processing": "Processing", + "saveChangesToSketch": "Do you want to save changes to this sketch before closing?", + "loseChanges": "If you don't save, your changes will be lost." + }, + "menu": { + "sketch": "Sketch", + "tools": "Strumenti" + }, + "debug": { + "optimizeForDebugging": "Optimize for Debugging", + "debugWithMessage": "Debug - {0}", + "noPlatformInstalledFor": "Platform is not installed for '{0}'", + "debuggingNotSupported": "Debugging is not supported by '{0}'" + }, + "preferences": { + "language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", + "compile.verbose": "True for verbose compile output. False by default", + "compile.warnings": "Tells gcc which warning level to use. It's 'None' by default", + "upload.verbose": "True for verbose upload output. False by default.", + "window.autoScale": "True if the user interface automatically scales with the font size.", + "window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.", + "ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.", + "board.certificates": "List of certificates that can be uploaded to boards", + "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.", + "cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.", + "cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.", + "cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.", + "cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.", + "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", + "auth.clientID": "The OAuth2 client ID.", + "auth.domain": "The OAuth2 domain.", + "auth.audience": "The OAuth2 audience.", + "auth.registerUri": "The URI used to register a new user.", + "network": "Rete", + "sketchbook.location": "Sketchbook location", + "browse": "Sfoglia", + "files.inside.sketches": "Show files inside Sketches", + "editorFontSize": "Editor font size", + "interfaceScale": "Interface scale", + "showVerbose": "Show verbose output during", + "compilerWarnings": "Compiler warnings", + "automatic": "Automatico", + "compile": "compile", + "upload": "upload", + "verifyAfterUpload": "Verify code after upload", + "checkForUpdates": "Controlla aggiornamenti all'avvio", + "editorQuickSuggestions": "Editor Quick Suggestions", + "additionalManagerURLs": "URL aggiuntive per il Gestore schede", + "noProxy": "No proxy", + "manualProxy": "Manual proxy configuration", + "newSketchbookLocation": "Select new sketchbook location", + "choose": "Choose", + "enterAdditionalURLs": "Enter additional URLs, one for each row", + "unofficialBoardSupport": "Click for a list of unofficial board support URLs", + "invalid.sketchbook.location": "Invalid sketchbook location: {0}", + "invalid.editorFontSize": "Invalid editor font size. It must be a positive integer.", + "invalid.theme": "Invalid theme." + }, + "cloud": { + "signIn": "SIGN IN", + "signOut": "Sign Out", + "chooseSketchVisibility": "Choose visibility of your Sketch:", + "privateVisibility": "Private. Only you can view the Sketch.", + "publicVisibility": "Public. Anyone with the link can view the Sketch.", + "link": "Link:", + "embed": "Embed:", + "cloudSketchbook": "Cloud Sketchbook", + "shareSketch": "Share Sketch", + "showHideRemoveSketchbook": "Show/Hide Remote Sketchbook", + "pullSketch": "Pull Sketch", + "openInCloudEditor": "Open in Cloud Editor", + "options": "Options...", + "share": "Share...", + "remote": "Remote", + "emptySketchbook": "Your Sketchbook is empty", + "visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches.", + "signInToCloud": "Sign in to Arduino Cloud", + "syncEditSketches": "Sync and edit your Arduino Cloud Sketches", + "learnMore": "Learn more", + "continue": "Continua", + "pushSketch": "Push Sketch", + "pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.", + "pull": "Pull", + "pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?", + "donePulling": "Done pulling ‘{0}’.", + "notYetPulled": "Cannot push to Cloud. It is not yet pulled.", + "push": "Push", + "pullFirst": "You have to pull first to be able to push to the Cloud.", + "donePushing": "Done pushing ‘{0}’.", + "connected": "Connected", + "offline": "Offline", + "profilePicture": "Profile picture" + }, + "board": { + "installManually": "Install Manually", + "installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?", + "configDialogTitle": "Select Other Board & Port", + "configDialog1": "Select both a Board and a Port if you want to upload a sketch.", + "configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.", + "pleasePickBoard": "Please pick a board connected to the port you have selected.", + "showAllAvailablePorts": "Shows all available ports when enabled", + "programmer": "Programmer", + "succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}", + "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}", + "couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?", + "reselectLater": "Reselect later", + "noneSelected": "No boards selected.", + "noPortsSelected": "No ports selected for board: '{0}'.", + "noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?", + "openBoardsConfig": "Select other board and port…", + "boardListItem": "{0} at {1}", + "selectBoardForInfo": "Please select a board to obtain board info.", + "platformMissing": "The platform for the selected '{0}' board is not installed.", + "selectPortForInfo": "Please select a port to obtain board info.", + "boardInfo": "Informazioni sulla scheda", + "board": "Board{0}", + "port": "Port{0}", + "getBoardInfo": "Get Board Info", + "inSketchbook": " (in Sketchbook)" + }, + "boardsManager": "Gestore schede", + "about": { + "label": "About {0}", + "detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}" + }, + "contributions": { + "addFile": "Aggiungi file...", + "replaceTitle": "Replace", + "fileAdded": "One file added to the sketch." + }, + "replaceMsg": "Replace the existing version of {0}?", + "library": { + "addZip": "Aggiungi libreria da file .ZIP...", + "zipLibrary": "Library", + "overwriteExistingLibrary": "Do you want to overwrite the existing library?", + "successfullyInstalledZipLibrary": "Successfully installed library from {0} archive", + "namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?", + "libraryAlreadyExists": "A library already exists. Do you want to overwrite it?", + "include": "Include Library", + "manageLibraries": "Manage Libraries...", + "arduinoLibraries": "Arduino libraries", + "contributedLibraries": "Contributed libraries", + "title": "Library Manager", + "needsOneDependency": "The library {0}:{1} needs another dependency currently not installed:", + "needsMultipleDependencies": "The library {0}:{1} needs some other dependencies currently not installed:", + "installOneMissingDependency": "Would you like to install the missing dependency?", + "installMissingDependencies": "Would you like to install all the missing dependencies?", + "dependenciesForLibrary": "Dependencies for library {0}:{1}", + "installAll": "Install all", + "installOnly": "Install {0} only", + "installedSuccessfully": "Successfully installed library {0}:{1}", + "uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}" + }, + "selectZip": "Select a zip file containing the library you'd like to add", + "sketch": { + "archiveSketch": "Archivia sketch", + "saveSketchAs": "Save sketch folder as...", + "createdArchive": "Created archive '{0}'.", + "new": "New", + "openRecent": "Open Recent", + "showFolder": "Show Sketch Folder", + "sketch": "Sketch", + "moving": "Moving", + "movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?", + "cantOpen": "Una cartella di nome \"{0}\" esiste già. Impossibile aprire lo sketch.", + "saveFolderAs": "Save sketch folder as...", + "sketchbook": "Sketchbook", + "upload": "Upload", + "uploadUsingProgrammer": "Upload Using Programmer", + "userFieldsNotFoundError": "Can't find user fields for connected board", + "doneUploading": "Done uploading.", + "configureAndUpload": "Configure And Upload", + "verifyOrCompile": "Verify/Compile", + "exportBinary": "Export Compiled Binary", + "verify": "Verify", + "doneCompiling": "Done compiling.", + "couldNotConnectToSerial": "Could not reconnect to serial port. {0}", + "openSketchInNewWindow": "Open Sketch in New Window", + "openFolder": "Open Folder", + "titleLocalSketchbook": "Local Sketchbook", + "titleSketchbook": "Sketchbook", + "close": "Are you sure you want to close the sketch?" + }, + "bootloader": { + "burnBootloader": "Scrivi il bootloader", + "doneBurningBootloader": "Done burning bootloader." + }, + "editor": { + "copyForForum": "Copia per il forum (Markdown)", + "commentUncomment": "Commenta / togli commento", + "increaseIndent": "Increase Indent", + "decreaseIndent": "Decrease Indent", + "increaseFontSize": "Increase Font Size", + "decreaseFontSize": "Riduci la dimensione del testo", + "autoFormat": "Formattazione automatica" + }, + "examples": { + "menu": "Examples", + "couldNotInitializeExamples": "Could not initialize built-in examples.", + "builtInExamples": "Esempi integrati", + "customLibrary": "Examples from Custom Libraries", + "for": "Examples for {0}", + "forAny": "Examples for any board" + }, + "help": { + "search": "Search on Arduino.cc", + "keyword": "Type a keyword", + "gettingStarted": "Getting Started", + "environment": "Environment", + "troubleshooting": "Troubleshooting", + "reference": "Reference", + "findInReference": "Find in Reference", + "faq": "Frequently Asked Questions", + "visit": "Visit Arduino.cc" + }, + "certificate": { + "uploadRootCertificates": "Upload SSL Root Certificates", + "openContext": "Open context", + "remove": "Remove", + "upload": "Carica", + "addURL": "Add URL to fetch SSL certificate", + "enterURL": "Enter URL", + "selectCertificateToUpload": "1. Select certificate to upload", + "addNew": "Add New", + "selectDestinationBoardToUpload": "2. Select destination board and upload certificate", + "uploadingCertificates": "Uploading certificates.", + "certificatesUploaded": "Certificates uploaded.", + "uploadFailed": "Upload failed. Please try again.", + "selectBoard": "Select a board...", + "boardAtPort": "{0} at {1}", + "noSupportedBoardConnected": "No supported board connected" + }, + "firmware": { + "updater": "WiFi101 / WiFiNINA Firmware Updater", + "selectBoard": "Select Board", + "checkUpdates": "Check Updates", + "selectVersion": "Select firmware version", + "install": "Installa", + "overwriteSketch": "Installation will overwrite the Sketch on the board.", + "installingFirmware": "Installing firmware.", + "successfullyInstalled": "Firmware succesfully installed.", + "failedInstall": "Installation failed. Please try again." + }, + "dialog": { + "dontAskAgain": "Don't ask again" + }, + "userFields": { + "cancel": "Cancel", + "upload": "Carica" + }, + "serial": { + "toggleTimestamp": "Toggle Timestamp", + "autoscroll": "Scorrimento automatico", + "timestamp": "Timestamp", + "noLineEndings": "Nessun fine riga", + "newLine": "A capo (NL)", + "carriageReturn": "Ritorno carrello (CR)", + "newLineCarriageReturn": "Entrambi NL & CR", + "notConnected": "Not connected. Select a board and a port to connect automatically.", + "message": "Message ({0} + Enter to send message to '{1}' on '{2}'", + "connectionBusy": "Connection failed. Serial port is busy: {0}", + "disconnected": "Disconnected {0} from {1}.", + "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", + "failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.", + "reconnect": "Reconnecting {0} to {1} in {2} seconds..." + }, + "component": { + "uninstall": "Uninstall", + "uninstallMsg": "Do you want to uninstall {0}?", + "by": "by", + "version": "Versione {0}", + "moreInfo": "More info", + "install": "Installa", + "filterSearch": "Filtra la tua ricerca..." + }, + "electron": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "unsavedChanges": "Any unsaved changes will not be saved." + }, + "compile": { + "error": "Compilation error: {0}" + }, + "upload": { + "error": "{0} error: {1}" + }, + "burnBootloader": { + "error": "Error while burning the bootloader: {0}" + } + }, + "theia": { + "core": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "offline": "Offline", + "daemonOffline": "CLI Daemon Offline", + "cannotConnectBackend": "Cannot connect to the backend.", + "cannotConnectDaemon": "Cannot connect to the CLI daemon." + }, + "debug": { + "start": "Start...", + "typeNotSupported": "The debug session type \"{0}\" is not supported.", + "startError": "There was an error starting the debug session, check the logs for more details." + }, + "editor": { + "unsavedTitle": "Unsaved – {0}" + }, + "messages": { + "expand": "Expand", + "collapse": "Collapse" + }, + "workspace": { + "fileNewName": "Name for new file", + "invalidFilename": "Invalid filename.", + "invalidExtension": "\".{0}\" non è un'estensione valida", + "newFileName": "New name for file", + "deleteCurrentSketch": "Do you want to delete the current sketch?", + "sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected." + } + }, + "cloud": { + "GoToCloud": "GO TO CLOUD" + } +} diff --git a/i18n/ja.json b/i18n/ja.json new file mode 100644 index 000000000..d712fa7b1 --- /dev/null +++ b/i18n/ja.json @@ -0,0 +1,334 @@ +{ + "arduino": { + "common": { + "offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.", + "noBoardSelected": "No board selected", + "selectedOn": "on {0}", + "notConnected": "[not connected]", + "serialMonitor": "シリアルモニタ", + "oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", + "later": "Later", + "selectBoard": "Select Board", + "unknown": "Unknown", + "processing": "Processing", + "saveChangesToSketch": "Do you want to save changes to this sketch before closing?", + "loseChanges": "If you don't save, your changes will be lost." + }, + "menu": { + "sketch": "スケッチ ", + "tools": "ツール" + }, + "debug": { + "optimizeForDebugging": "Optimize for Debugging", + "debugWithMessage": "Debug - {0}", + "noPlatformInstalledFor": "Platform is not installed for '{0}'", + "debuggingNotSupported": "Debugging is not supported by '{0}'" + }, + "preferences": { + "language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", + "compile.verbose": "True for verbose compile output. False by default", + "compile.warnings": "Tells gcc which warning level to use. It's 'None' by default", + "upload.verbose": "True for verbose upload output. False by default.", + "window.autoScale": "True if the user interface automatically scales with the font size.", + "window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.", + "ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.", + "board.certificates": "List of certificates that can be uploaded to boards", + "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.", + "cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.", + "cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.", + "cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.", + "cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.", + "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", + "auth.clientID": "The OAuth2 client ID.", + "auth.domain": "The OAuth2 domain.", + "auth.audience": "The OAuth2 audience.", + "auth.registerUri": "The URI used to register a new user.", + "network": "ネットワーク", + "sketchbook.location": "Sketchbook location", + "browse": "参照", + "files.inside.sketches": "Show files inside Sketches", + "editorFontSize": "Editor font size", + "interfaceScale": "Interface scale", + "showVerbose": "Show verbose output during", + "compilerWarnings": "Compiler warnings", + "automatic": "自動", + "compile": "compile", + "upload": "upload", + "verifyAfterUpload": "Verify code after upload", + "checkForUpdates": "起動時に最新バージョンの有無をチェックする", + "editorQuickSuggestions": "Editor Quick Suggestions", + "additionalManagerURLs": "追加のボードマネージャのURL", + "noProxy": "No proxy", + "manualProxy": "Manual proxy configuration", + "newSketchbookLocation": "Select new sketchbook location", + "choose": "Choose", + "enterAdditionalURLs": "Enter additional URLs, one for each row", + "unofficialBoardSupport": "Click for a list of unofficial board support URLs", + "invalid.sketchbook.location": "Invalid sketchbook location: {0}", + "invalid.editorFontSize": "Invalid editor font size. It must be a positive integer.", + "invalid.theme": "Invalid theme." + }, + "cloud": { + "signIn": "SIGN IN", + "signOut": "Sign Out", + "chooseSketchVisibility": "Choose visibility of your Sketch:", + "privateVisibility": "Private. Only you can view the Sketch.", + "publicVisibility": "Public. Anyone with the link can view the Sketch.", + "link": "Link:", + "embed": "Embed:", + "cloudSketchbook": "Cloud Sketchbook", + "shareSketch": "Share Sketch", + "showHideRemoveSketchbook": "Show/Hide Remote Sketchbook", + "pullSketch": "Pull Sketch", + "openInCloudEditor": "Open in Cloud Editor", + "options": "Options...", + "share": "Share...", + "remote": "Remote", + "emptySketchbook": "Your Sketchbook is empty", + "visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches.", + "signInToCloud": "Sign in to Arduino Cloud", + "syncEditSketches": "Sync and edit your Arduino Cloud Sketches", + "learnMore": "Learn more", + "continue": "Continue", + "pushSketch": "Push Sketch", + "pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.", + "pull": "Pull", + "pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?", + "donePulling": "Done pulling ‘{0}’.", + "notYetPulled": "Cannot push to Cloud. It is not yet pulled.", + "push": "Push", + "pullFirst": "You have to pull first to be able to push to the Cloud.", + "donePushing": "Done pushing ‘{0}’.", + "connected": "Connected", + "offline": "Offline", + "profilePicture": "Profile picture" + }, + "board": { + "installManually": "Install Manually", + "installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?", + "configDialogTitle": "Select Other Board & Port", + "configDialog1": "Select both a Board and a Port if you want to upload a sketch.", + "configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.", + "pleasePickBoard": "Please pick a board connected to the port you have selected.", + "showAllAvailablePorts": "Shows all available ports when enabled", + "programmer": "Programmer", + "succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}", + "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}", + "couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?", + "reselectLater": "Reselect later", + "noneSelected": "No boards selected.", + "noPortsSelected": "No ports selected for board: '{0}'.", + "noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?", + "openBoardsConfig": "Select other board and port…", + "boardListItem": "{0} at {1}", + "selectBoardForInfo": "Please select a board to obtain board info.", + "platformMissing": "The platform for the selected '{0}' board is not installed.", + "selectPortForInfo": "Please select a port to obtain board info.", + "boardInfo": "ボード情報", + "board": "Board{0}", + "port": "Port{0}", + "getBoardInfo": "Get Board Info", + "inSketchbook": " (in Sketchbook)" + }, + "boardsManager": "ボードマネージャ", + "about": { + "label": "About {0}", + "detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}" + }, + "contributions": { + "addFile": "ファイルを追加...", + "replaceTitle": "Replace", + "fileAdded": "One file added to the sketch." + }, + "replaceMsg": "Replace the existing version of {0}?", + "library": { + "addZip": ".ZIP形式のライブラリをインストール...", + "zipLibrary": "Library", + "overwriteExistingLibrary": "Do you want to overwrite the existing library?", + "successfullyInstalledZipLibrary": "Successfully installed library from {0} archive", + "namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?", + "libraryAlreadyExists": "A library already exists. Do you want to overwrite it?", + "include": "Include Library", + "manageLibraries": "Manage Libraries...", + "arduinoLibraries": "Arduino libraries", + "contributedLibraries": "Contributed libraries", + "title": "Library Manager", + "needsOneDependency": "The library {0}:{1} needs another dependency currently not installed:", + "needsMultipleDependencies": "The library {0}:{1} needs some other dependencies currently not installed:", + "installOneMissingDependency": "Would you like to install the missing dependency?", + "installMissingDependencies": "Would you like to install all the missing dependencies?", + "dependenciesForLibrary": "Dependencies for library {0}:{1}", + "installAll": "Install all", + "installOnly": "Install {0} only", + "installedSuccessfully": "Successfully installed library {0}:{1}", + "uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}" + }, + "selectZip": "Select a zip file containing the library you'd like to add", + "sketch": { + "archiveSketch": "スケッチをアーカイブする", + "saveSketchAs": "Save sketch folder as...", + "createdArchive": "Created archive '{0}'.", + "new": "New", + "openRecent": "Open Recent", + "showFolder": "Show Sketch Folder", + "sketch": "Sketch", + "moving": "Moving", + "movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?", + "cantOpen": "「{0}」というフォルダはすでに存在します。スケッチを開けません。", + "saveFolderAs": "Save sketch folder as...", + "sketchbook": "Sketchbook", + "upload": "Upload", + "uploadUsingProgrammer": "Upload Using Programmer", + "userFieldsNotFoundError": "Can't find user fields for connected board", + "doneUploading": "Done uploading.", + "configureAndUpload": "Configure And Upload", + "verifyOrCompile": "Verify/Compile", + "exportBinary": "Export Compiled Binary", + "verify": "Verify", + "doneCompiling": "Done compiling.", + "couldNotConnectToSerial": "Could not reconnect to serial port. {0}", + "openSketchInNewWindow": "Open Sketch in New Window", + "openFolder": "Open Folder", + "titleLocalSketchbook": "Local Sketchbook", + "titleSketchbook": "Sketchbook", + "close": "Are you sure you want to close the sketch?" + }, + "bootloader": { + "burnBootloader": "ブートローダを書き込む", + "doneBurningBootloader": "Done burning bootloader." + }, + "editor": { + "copyForForum": "Copy for Forum (Markdown)", + "commentUncomment": "コメント化・復帰", + "increaseIndent": "Increase Indent", + "decreaseIndent": "Decrease Indent", + "increaseFontSize": "Increase Font Size", + "decreaseFontSize": "フォントサイズを小さく", + "autoFormat": "自動整形" + }, + "examples": { + "menu": "Examples", + "couldNotInitializeExamples": "Could not initialize built-in examples.", + "builtInExamples": "内蔵のスケッチ例", + "customLibrary": "Examples from Custom Libraries", + "for": "Examples for {0}", + "forAny": "Examples for any board" + }, + "help": { + "search": "Search on Arduino.cc", + "keyword": "Type a keyword", + "gettingStarted": "Getting Started", + "environment": "Environment", + "troubleshooting": "Troubleshooting", + "reference": "Reference", + "findInReference": "Find in Reference", + "faq": "Frequently Asked Questions", + "visit": "Visit Arduino.cc" + }, + "certificate": { + "uploadRootCertificates": "Upload SSL Root Certificates", + "openContext": "Open context", + "remove": "Remove", + "upload": "マイコンボードに書き込む", + "addURL": "Add URL to fetch SSL certificate", + "enterURL": "Enter URL", + "selectCertificateToUpload": "1. Select certificate to upload", + "addNew": "Add New", + "selectDestinationBoardToUpload": "2. Select destination board and upload certificate", + "uploadingCertificates": "Uploading certificates.", + "certificatesUploaded": "Certificates uploaded.", + "uploadFailed": "Upload failed. Please try again.", + "selectBoard": "Select a board...", + "boardAtPort": "{0} at {1}", + "noSupportedBoardConnected": "No supported board connected" + }, + "firmware": { + "updater": "WiFi101 / WiFiNINA Firmware Updater", + "selectBoard": "Select Board", + "checkUpdates": "Check Updates", + "selectVersion": "Select firmware version", + "install": "インストール", + "overwriteSketch": "Installation will overwrite the Sketch on the board.", + "installingFirmware": "Installing firmware.", + "successfullyInstalled": "Firmware succesfully installed.", + "failedInstall": "Installation failed. Please try again." + }, + "dialog": { + "dontAskAgain": "Don't ask again" + }, + "userFields": { + "cancel": "Cancel", + "upload": "マイコンボードに書き込む" + }, + "serial": { + "toggleTimestamp": "Toggle Timestamp", + "autoscroll": "自動スクロール", + "timestamp": "Timestamp", + "noLineEndings": "改行なし", + "newLine": "LFのみ", + "carriageReturn": "CRのみ", + "newLineCarriageReturn": "CRおよびLF", + "notConnected": "Not connected. Select a board and a port to connect automatically.", + "message": "Message ({0} + Enter to send message to '{1}' on '{2}'", + "connectionBusy": "Connection failed. Serial port is busy: {0}", + "disconnected": "Disconnected {0} from {1}.", + "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", + "failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.", + "reconnect": "Reconnecting {0} to {1} in {2} seconds..." + }, + "component": { + "uninstall": "Uninstall", + "uninstallMsg": "Do you want to uninstall {0}?", + "by": "by", + "version": "バージョン{0}", + "moreInfo": "More info", + "install": "インストール", + "filterSearch": "検索をフィルタ…" + }, + "electron": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "unsavedChanges": "Any unsaved changes will not be saved." + }, + "compile": { + "error": "Compilation error: {0}" + }, + "upload": { + "error": "{0} error: {1}" + }, + "burnBootloader": { + "error": "Error while burning the bootloader: {0}" + } + }, + "theia": { + "core": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "offline": "Offline", + "daemonOffline": "CLI Daemon Offline", + "cannotConnectBackend": "Cannot connect to the backend.", + "cannotConnectDaemon": "Cannot connect to the CLI daemon." + }, + "debug": { + "start": "Start...", + "typeNotSupported": "The debug session type \"{0}\" is not supported.", + "startError": "There was an error starting the debug session, check the logs for more details." + }, + "editor": { + "unsavedTitle": "Unsaved – {0}" + }, + "messages": { + "expand": "Expand", + "collapse": "Collapse" + }, + "workspace": { + "fileNewName": "Name for new file", + "invalidFilename": "Invalid filename.", + "invalidExtension": "拡張子「.{0}」は、使えません。", + "newFileName": "New name for file", + "deleteCurrentSketch": "Do you want to delete the current sketch?", + "sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected." + } + }, + "cloud": { + "GoToCloud": "GO TO CLOUD" + } +} diff --git a/i18n/pt.json b/i18n/pt.json new file mode 100644 index 000000000..549bf2692 --- /dev/null +++ b/i18n/pt.json @@ -0,0 +1,334 @@ +{ + "arduino": { + "common": { + "offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.", + "noBoardSelected": "No board selected", + "selectedOn": "on {0}", + "notConnected": "[not connected]", + "serialMonitor": "Monitor Série", + "oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", + "later": "Later", + "selectBoard": "Select Board", + "unknown": "Unknown", + "processing": "Processing", + "saveChangesToSketch": "Do you want to save changes to this sketch before closing?", + "loseChanges": "If you don't save, your changes will be lost." + }, + "menu": { + "sketch": "Rascunho", + "tools": "Ferramentas" + }, + "debug": { + "optimizeForDebugging": "Optimize for Debugging", + "debugWithMessage": "Debug - {0}", + "noPlatformInstalledFor": "Platform is not installed for '{0}'", + "debuggingNotSupported": "Debugging is not supported by '{0}'" + }, + "preferences": { + "language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", + "compile.verbose": "True for verbose compile output. False by default", + "compile.warnings": "Tells gcc which warning level to use. It's 'None' by default", + "upload.verbose": "True for verbose upload output. False by default.", + "window.autoScale": "True if the user interface automatically scales with the font size.", + "window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.", + "ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.", + "board.certificates": "List of certificates that can be uploaded to boards", + "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.", + "cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.", + "cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.", + "cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.", + "cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.", + "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", + "auth.clientID": "The OAuth2 client ID.", + "auth.domain": "The OAuth2 domain.", + "auth.audience": "The OAuth2 audience.", + "auth.registerUri": "The URI used to register a new user.", + "network": "Rede", + "sketchbook.location": "Sketchbook location", + "browse": "Procurar", + "files.inside.sketches": "Show files inside Sketches", + "editorFontSize": "Editor font size", + "interfaceScale": "Interface scale", + "showVerbose": "Show verbose output during", + "compilerWarnings": "Compiler warnings", + "automatic": "Automático", + "compile": "compile", + "upload": "upload", + "verifyAfterUpload": "Verify code after upload", + "checkForUpdates": "Procurar por atualizações ao iniciar", + "editorQuickSuggestions": "Editor Quick Suggestions", + "additionalManagerURLs": "URLs Adicionais para Gerenciadores de Placas", + "noProxy": "No proxy", + "manualProxy": "Manual proxy configuration", + "newSketchbookLocation": "Select new sketchbook location", + "choose": "Choose", + "enterAdditionalURLs": "Enter additional URLs, one for each row", + "unofficialBoardSupport": "Click for a list of unofficial board support URLs", + "invalid.sketchbook.location": "Invalid sketchbook location: {0}", + "invalid.editorFontSize": "Invalid editor font size. It must be a positive integer.", + "invalid.theme": "Invalid theme." + }, + "cloud": { + "signIn": "SIGN IN", + "signOut": "Sign Out", + "chooseSketchVisibility": "Choose visibility of your Sketch:", + "privateVisibility": "Private. Only you can view the Sketch.", + "publicVisibility": "Public. Anyone with the link can view the Sketch.", + "link": "Link:", + "embed": "Embed:", + "cloudSketchbook": "Cloud Sketchbook", + "shareSketch": "Share Sketch", + "showHideRemoveSketchbook": "Show/Hide Remote Sketchbook", + "pullSketch": "Pull Sketch", + "openInCloudEditor": "Open in Cloud Editor", + "options": "Options...", + "share": "Share...", + "remote": "Remote", + "emptySketchbook": "Your Sketchbook is empty", + "visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches.", + "signInToCloud": "Sign in to Arduino Cloud", + "syncEditSketches": "Sync and edit your Arduino Cloud Sketches", + "learnMore": "Learn more", + "continue": "Continue", + "pushSketch": "Push Sketch", + "pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.", + "pull": "Pull", + "pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?", + "donePulling": "Done pulling ‘{0}’.", + "notYetPulled": "Cannot push to Cloud. It is not yet pulled.", + "push": "Push", + "pullFirst": "You have to pull first to be able to push to the Cloud.", + "donePushing": "Done pushing ‘{0}’.", + "connected": "Connected", + "offline": "Offline", + "profilePicture": "Profile picture" + }, + "board": { + "installManually": "Install Manually", + "installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?", + "configDialogTitle": "Select Other Board & Port", + "configDialog1": "Select both a Board and a Port if you want to upload a sketch.", + "configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.", + "pleasePickBoard": "Please pick a board connected to the port you have selected.", + "showAllAvailablePorts": "Shows all available ports when enabled", + "programmer": "Programmer", + "succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}", + "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}", + "couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?", + "reselectLater": "Reselect later", + "noneSelected": "No boards selected.", + "noPortsSelected": "No ports selected for board: '{0}'.", + "noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?", + "openBoardsConfig": "Select other board and port…", + "boardListItem": "{0} at {1}", + "selectBoardForInfo": "Please select a board to obtain board info.", + "platformMissing": "The platform for the selected '{0}' board is not installed.", + "selectPortForInfo": "Please select a port to obtain board info.", + "boardInfo": "Informações da Placa", + "board": "Board{0}", + "port": "Port{0}", + "getBoardInfo": "Get Board Info", + "inSketchbook": " (in Sketchbook)" + }, + "boardsManager": "Gerenciador de Placas", + "about": { + "label": "About {0}", + "detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}" + }, + "contributions": { + "addFile": "Adicionar Ficheiro...", + "replaceTitle": "Replace", + "fileAdded": "One file added to the sketch." + }, + "replaceMsg": "Replace the existing version of {0}?", + "library": { + "addZip": "Adicionar biblioteca .ZIP...", + "zipLibrary": "Library", + "overwriteExistingLibrary": "Do you want to overwrite the existing library?", + "successfullyInstalledZipLibrary": "Successfully installed library from {0} archive", + "namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?", + "libraryAlreadyExists": "A library already exists. Do you want to overwrite it?", + "include": "Include Library", + "manageLibraries": "Manage Libraries...", + "arduinoLibraries": "Arduino libraries", + "contributedLibraries": "Contributed libraries", + "title": "Library Manager", + "needsOneDependency": "The library {0}:{1} needs another dependency currently not installed:", + "needsMultipleDependencies": "The library {0}:{1} needs some other dependencies currently not installed:", + "installOneMissingDependency": "Would you like to install the missing dependency?", + "installMissingDependencies": "Would you like to install all the missing dependencies?", + "dependenciesForLibrary": "Dependencies for library {0}:{1}", + "installAll": "Install all", + "installOnly": "Install {0} only", + "installedSuccessfully": "Successfully installed library {0}:{1}", + "uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}" + }, + "selectZip": "Select a zip file containing the library you'd like to add", + "sketch": { + "archiveSketch": "Arquivar Sketch", + "saveSketchAs": "Save sketch folder as...", + "createdArchive": "Created archive '{0}'.", + "new": "New", + "openRecent": "Open Recent", + "showFolder": "Show Sketch Folder", + "sketch": "Sketch", + "moving": "Moving", + "movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?", + "cantOpen": "Directoria chamada \"{0}\" já existe. Não é possível abrir o rascunho.", + "saveFolderAs": "Save sketch folder as...", + "sketchbook": "Sketchbook", + "upload": "Upload", + "uploadUsingProgrammer": "Upload Using Programmer", + "userFieldsNotFoundError": "Can't find user fields for connected board", + "doneUploading": "Done uploading.", + "configureAndUpload": "Configure And Upload", + "verifyOrCompile": "Verify/Compile", + "exportBinary": "Export Compiled Binary", + "verify": "Verify", + "doneCompiling": "Done compiling.", + "couldNotConnectToSerial": "Could not reconnect to serial port. {0}", + "openSketchInNewWindow": "Open Sketch in New Window", + "openFolder": "Open Folder", + "titleLocalSketchbook": "Local Sketchbook", + "titleSketchbook": "Sketchbook", + "close": "Are you sure you want to close the sketch?" + }, + "bootloader": { + "burnBootloader": "Gravar bootloader", + "doneBurningBootloader": "Done burning bootloader." + }, + "editor": { + "copyForForum": "Copiar para o Fórum (Markdown)", + "commentUncomment": "Comentar/Eliminar Comentário", + "increaseIndent": "Increase Indent", + "decreaseIndent": "Decrease Indent", + "increaseFontSize": "Increase Font Size", + "decreaseFontSize": "Diminuir tamanho da fonte", + "autoFormat": "Autoformatação" + }, + "examples": { + "menu": "Examples", + "couldNotInitializeExamples": "Could not initialize built-in examples.", + "builtInExamples": "Exemplos Incluídos", + "customLibrary": "Examples from Custom Libraries", + "for": "Examples for {0}", + "forAny": "Examples for any board" + }, + "help": { + "search": "Search on Arduino.cc", + "keyword": "Type a keyword", + "gettingStarted": "Getting Started", + "environment": "Environment", + "troubleshooting": "Troubleshooting", + "reference": "Reference", + "findInReference": "Find in Reference", + "faq": "Frequently Asked Questions", + "visit": "Visit Arduino.cc" + }, + "certificate": { + "uploadRootCertificates": "Upload SSL Root Certificates", + "openContext": "Open context", + "remove": "Remove", + "upload": "Carregar", + "addURL": "Add URL to fetch SSL certificate", + "enterURL": "Enter URL", + "selectCertificateToUpload": "1. Select certificate to upload", + "addNew": "Add New", + "selectDestinationBoardToUpload": "2. Select destination board and upload certificate", + "uploadingCertificates": "Uploading certificates.", + "certificatesUploaded": "Certificates uploaded.", + "uploadFailed": "Upload failed. Please try again.", + "selectBoard": "Select a board...", + "boardAtPort": "{0} at {1}", + "noSupportedBoardConnected": "No supported board connected" + }, + "firmware": { + "updater": "WiFi101 / WiFiNINA Firmware Updater", + "selectBoard": "Select Board", + "checkUpdates": "Check Updates", + "selectVersion": "Select firmware version", + "install": "Instalar", + "overwriteSketch": "Installation will overwrite the Sketch on the board.", + "installingFirmware": "Installing firmware.", + "successfullyInstalled": "Firmware succesfully installed.", + "failedInstall": "Installation failed. Please try again." + }, + "dialog": { + "dontAskAgain": "Don't ask again" + }, + "userFields": { + "cancel": "Cancel", + "upload": "Carregar" + }, + "serial": { + "toggleTimestamp": "Toggle Timestamp", + "autoscroll": "Avanço automático de linha", + "timestamp": "Timestamp", + "noLineEndings": "Sem final de linha", + "newLine": "Nova linha", + "carriageReturn": "Retorno de linha", + "newLineCarriageReturn": "Nova linha e retorno de linha", + "notConnected": "Not connected. Select a board and a port to connect automatically.", + "message": "Message ({0} + Enter to send message to '{1}' on '{2}'", + "connectionBusy": "Connection failed. Serial port is busy: {0}", + "disconnected": "Disconnected {0} from {1}.", + "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", + "failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.", + "reconnect": "Reconnecting {0} to {1} in {2} seconds..." + }, + "component": { + "uninstall": "Uninstall", + "uninstallMsg": "Do you want to uninstall {0}?", + "by": "by", + "version": "Versão {0}", + "moreInfo": "More info", + "install": "Instalar", + "filterSearch": "Filtrar a sua pesquisa…" + }, + "electron": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "unsavedChanges": "Any unsaved changes will not be saved." + }, + "compile": { + "error": "Compilation error: {0}" + }, + "upload": { + "error": "{0} error: {1}" + }, + "burnBootloader": { + "error": "Error while burning the bootloader: {0}" + } + }, + "theia": { + "core": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "offline": "Offline", + "daemonOffline": "CLI Daemon Offline", + "cannotConnectBackend": "Cannot connect to the backend.", + "cannotConnectDaemon": "Cannot connect to the CLI daemon." + }, + "debug": { + "start": "Start...", + "typeNotSupported": "The debug session type \"{0}\" is not supported.", + "startError": "There was an error starting the debug session, check the logs for more details." + }, + "editor": { + "unsavedTitle": "Unsaved – {0}" + }, + "messages": { + "expand": "Expand", + "collapse": "Collapse" + }, + "workspace": { + "fileNewName": "Name for new file", + "invalidFilename": "Invalid filename.", + "invalidExtension": "\".{0}\" não é uma extensão válida.", + "newFileName": "New name for file", + "deleteCurrentSketch": "Do you want to delete the current sketch?", + "sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected." + } + }, + "cloud": { + "GoToCloud": "GO TO CLOUD" + } +} diff --git a/i18n/ru.json b/i18n/ru.json new file mode 100644 index 000000000..78b6edfd0 --- /dev/null +++ b/i18n/ru.json @@ -0,0 +1,334 @@ +{ + "arduino": { + "common": { + "offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.", + "noBoardSelected": "No board selected", + "selectedOn": "on {0}", + "notConnected": "[not connected]", + "serialMonitor": "Монитор порта", + "oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", + "later": "Later", + "selectBoard": "Select Board", + "unknown": "Unknown", + "processing": "Processing", + "saveChangesToSketch": "Do you want to save changes to this sketch before closing?", + "loseChanges": "If you don't save, your changes will be lost." + }, + "menu": { + "sketch": "Скетч", + "tools": "Инструменты" + }, + "debug": { + "optimizeForDebugging": "Optimize for Debugging", + "debugWithMessage": "Debug - {0}", + "noPlatformInstalledFor": "Platform is not installed for '{0}'", + "debuggingNotSupported": "Debugging is not supported by '{0}'" + }, + "preferences": { + "language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", + "compile.verbose": "True for verbose compile output. False by default", + "compile.warnings": "Tells gcc which warning level to use. It's 'None' by default", + "upload.verbose": "True for verbose upload output. False by default.", + "window.autoScale": "True if the user interface automatically scales with the font size.", + "window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.", + "ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.", + "board.certificates": "List of certificates that can be uploaded to boards", + "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.", + "cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.", + "cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.", + "cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.", + "cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.", + "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", + "auth.clientID": "The OAuth2 client ID.", + "auth.domain": "The OAuth2 domain.", + "auth.audience": "The OAuth2 audience.", + "auth.registerUri": "The URI used to register a new user.", + "network": "Сеть", + "sketchbook.location": "Sketchbook location", + "browse": "Обзор", + "files.inside.sketches": "Show files inside Sketches", + "editorFontSize": "Editor font size", + "interfaceScale": "Interface scale", + "showVerbose": "Show verbose output during", + "compilerWarnings": "Compiler warnings", + "automatic": "Автоматика", + "compile": "compile", + "upload": "upload", + "verifyAfterUpload": "Verify code after upload", + "checkForUpdates": "Проверять обновления при запуске", + "editorQuickSuggestions": "Editor Quick Suggestions", + "additionalManagerURLs": "Дополнительные ссылки для Менеджера плат", + "noProxy": "No proxy", + "manualProxy": "Manual proxy configuration", + "newSketchbookLocation": "Select new sketchbook location", + "choose": "Choose", + "enterAdditionalURLs": "Enter additional URLs, one for each row", + "unofficialBoardSupport": "Click for a list of unofficial board support URLs", + "invalid.sketchbook.location": "Invalid sketchbook location: {0}", + "invalid.editorFontSize": "Invalid editor font size. It must be a positive integer.", + "invalid.theme": "Invalid theme." + }, + "cloud": { + "signIn": "SIGN IN", + "signOut": "Sign Out", + "chooseSketchVisibility": "Choose visibility of your Sketch:", + "privateVisibility": "Private. Only you can view the Sketch.", + "publicVisibility": "Public. Anyone with the link can view the Sketch.", + "link": "Link:", + "embed": "Embed:", + "cloudSketchbook": "Cloud Sketchbook", + "shareSketch": "Share Sketch", + "showHideRemoveSketchbook": "Show/Hide Remote Sketchbook", + "pullSketch": "Pull Sketch", + "openInCloudEditor": "Open in Cloud Editor", + "options": "Options...", + "share": "Share...", + "remote": "Remote", + "emptySketchbook": "Your Sketchbook is empty", + "visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches.", + "signInToCloud": "Sign in to Arduino Cloud", + "syncEditSketches": "Sync and edit your Arduino Cloud Sketches", + "learnMore": "Learn more", + "continue": "Continue", + "pushSketch": "Push Sketch", + "pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.", + "pull": "Pull", + "pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?", + "donePulling": "Done pulling ‘{0}’.", + "notYetPulled": "Cannot push to Cloud. It is not yet pulled.", + "push": "Push", + "pullFirst": "You have to pull first to be able to push to the Cloud.", + "donePushing": "Done pushing ‘{0}’.", + "connected": "Connected", + "offline": "Offline", + "profilePicture": "Profile picture" + }, + "board": { + "installManually": "Install Manually", + "installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?", + "configDialogTitle": "Select Other Board & Port", + "configDialog1": "Select both a Board and a Port if you want to upload a sketch.", + "configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.", + "pleasePickBoard": "Please pick a board connected to the port you have selected.", + "showAllAvailablePorts": "Shows all available ports when enabled", + "programmer": "Programmer", + "succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}", + "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}", + "couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?", + "reselectLater": "Reselect later", + "noneSelected": "No boards selected.", + "noPortsSelected": "No ports selected for board: '{0}'.", + "noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?", + "openBoardsConfig": "Select other board and port…", + "boardListItem": "{0} at {1}", + "selectBoardForInfo": "Please select a board to obtain board info.", + "platformMissing": "The platform for the selected '{0}' board is not installed.", + "selectPortForInfo": "Please select a port to obtain board info.", + "boardInfo": "Информация о плате", + "board": "Board{0}", + "port": "Port{0}", + "getBoardInfo": "Get Board Info", + "inSketchbook": " (in Sketchbook)" + }, + "boardsManager": "Менеджер плат", + "about": { + "label": "About {0}", + "detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}" + }, + "contributions": { + "addFile": "Добавить файл...", + "replaceTitle": "Replace", + "fileAdded": "One file added to the sketch." + }, + "replaceMsg": "Replace the existing version of {0}?", + "library": { + "addZip": "Добавить .ZIP библиотеку...", + "zipLibrary": "Library", + "overwriteExistingLibrary": "Do you want to overwrite the existing library?", + "successfullyInstalledZipLibrary": "Successfully installed library from {0} archive", + "namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?", + "libraryAlreadyExists": "A library already exists. Do you want to overwrite it?", + "include": "Include Library", + "manageLibraries": "Manage Libraries...", + "arduinoLibraries": "Arduino libraries", + "contributedLibraries": "Contributed libraries", + "title": "Library Manager", + "needsOneDependency": "The library {0}:{1} needs another dependency currently not installed:", + "needsMultipleDependencies": "The library {0}:{1} needs some other dependencies currently not installed:", + "installOneMissingDependency": "Would you like to install the missing dependency?", + "installMissingDependencies": "Would you like to install all the missing dependencies?", + "dependenciesForLibrary": "Dependencies for library {0}:{1}", + "installAll": "Install all", + "installOnly": "Install {0} only", + "installedSuccessfully": "Successfully installed library {0}:{1}", + "uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}" + }, + "selectZip": "Select a zip file containing the library you'd like to add", + "sketch": { + "archiveSketch": "Архивировать скетч", + "saveSketchAs": "Save sketch folder as...", + "createdArchive": "Created archive '{0}'.", + "new": "New", + "openRecent": "Open Recent", + "showFolder": "Show Sketch Folder", + "sketch": "Sketch", + "moving": "Moving", + "movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?", + "cantOpen": "Папка \"{0}\" уже существует. Невозможно открыть скетч.", + "saveFolderAs": "Save sketch folder as...", + "sketchbook": "Sketchbook", + "upload": "Upload", + "uploadUsingProgrammer": "Upload Using Programmer", + "userFieldsNotFoundError": "Can't find user fields for connected board", + "doneUploading": "Done uploading.", + "configureAndUpload": "Configure And Upload", + "verifyOrCompile": "Verify/Compile", + "exportBinary": "Export Compiled Binary", + "verify": "Verify", + "doneCompiling": "Done compiling.", + "couldNotConnectToSerial": "Could not reconnect to serial port. {0}", + "openSketchInNewWindow": "Open Sketch in New Window", + "openFolder": "Open Folder", + "titleLocalSketchbook": "Local Sketchbook", + "titleSketchbook": "Sketchbook", + "close": "Are you sure you want to close the sketch?" + }, + "bootloader": { + "burnBootloader": "Записать Загрузчик", + "doneBurningBootloader": "Done burning bootloader." + }, + "editor": { + "copyForForum": "Копировать для форума (Markdown)", + "commentUncomment": "Добавить/Удалить комментарий", + "increaseIndent": "Increase Indent", + "decreaseIndent": "Decrease Indent", + "increaseFontSize": "Increase Font Size", + "decreaseFontSize": "Уменьшить размер шрифта", + "autoFormat": "АвтоФорматирование" + }, + "examples": { + "menu": "Examples", + "couldNotInitializeExamples": "Could not initialize built-in examples.", + "builtInExamples": "Встроенные Примеры", + "customLibrary": "Examples from Custom Libraries", + "for": "Examples for {0}", + "forAny": "Examples for any board" + }, + "help": { + "search": "Search on Arduino.cc", + "keyword": "Type a keyword", + "gettingStarted": "Getting Started", + "environment": "Environment", + "troubleshooting": "Troubleshooting", + "reference": "Reference", + "findInReference": "Find in Reference", + "faq": "Frequently Asked Questions", + "visit": "Visit Arduino.cc" + }, + "certificate": { + "uploadRootCertificates": "Upload SSL Root Certificates", + "openContext": "Open context", + "remove": "Remove", + "upload": "Загрузка", + "addURL": "Add URL to fetch SSL certificate", + "enterURL": "Enter URL", + "selectCertificateToUpload": "1. Select certificate to upload", + "addNew": "Add New", + "selectDestinationBoardToUpload": "2. Select destination board and upload certificate", + "uploadingCertificates": "Uploading certificates.", + "certificatesUploaded": "Certificates uploaded.", + "uploadFailed": "Upload failed. Please try again.", + "selectBoard": "Select a board...", + "boardAtPort": "{0} at {1}", + "noSupportedBoardConnected": "No supported board connected" + }, + "firmware": { + "updater": "WiFi101 / WiFiNINA Firmware Updater", + "selectBoard": "Select Board", + "checkUpdates": "Check Updates", + "selectVersion": "Select firmware version", + "install": "Установка", + "overwriteSketch": "Installation will overwrite the Sketch on the board.", + "installingFirmware": "Installing firmware.", + "successfullyInstalled": "Firmware succesfully installed.", + "failedInstall": "Installation failed. Please try again." + }, + "dialog": { + "dontAskAgain": "Don't ask again" + }, + "userFields": { + "cancel": "Cancel", + "upload": "Загрузка" + }, + "serial": { + "toggleTimestamp": "Toggle Timestamp", + "autoscroll": "Автопрокрутка", + "timestamp": "Timestamp", + "noLineEndings": "Нет конца строки", + "newLine": "Новая строка", + "carriageReturn": "CR Возврат каретки", + "newLineCarriageReturn": "NL & CR", + "notConnected": "Not connected. Select a board and a port to connect automatically.", + "message": "Message ({0} + Enter to send message to '{1}' on '{2}'", + "connectionBusy": "Connection failed. Serial port is busy: {0}", + "disconnected": "Disconnected {0} from {1}.", + "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", + "failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.", + "reconnect": "Reconnecting {0} to {1} in {2} seconds..." + }, + "component": { + "uninstall": "Uninstall", + "uninstallMsg": "Do you want to uninstall {0}?", + "by": "by", + "version": "Версия {0}", + "moreInfo": "More info", + "install": "Установка", + "filterSearch": "Отфильтровать результаты поиска..." + }, + "electron": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "unsavedChanges": "Any unsaved changes will not be saved." + }, + "compile": { + "error": "Compilation error: {0}" + }, + "upload": { + "error": "{0} error: {1}" + }, + "burnBootloader": { + "error": "Error while burning the bootloader: {0}" + } + }, + "theia": { + "core": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "offline": "Offline", + "daemonOffline": "CLI Daemon Offline", + "cannotConnectBackend": "Cannot connect to the backend.", + "cannotConnectDaemon": "Cannot connect to the CLI daemon." + }, + "debug": { + "start": "Start...", + "typeNotSupported": "The debug session type \"{0}\" is not supported.", + "startError": "There was an error starting the debug session, check the logs for more details." + }, + "editor": { + "unsavedTitle": "Unsaved – {0}" + }, + "messages": { + "expand": "Expand", + "collapse": "Collapse" + }, + "workspace": { + "fileNewName": "Name for new file", + "invalidFilename": "Invalid filename.", + "invalidExtension": "Файлы с расширением \".{0}\" не поддерживаются.", + "newFileName": "New name for file", + "deleteCurrentSketch": "Do you want to delete the current sketch?", + "sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected." + } + }, + "cloud": { + "GoToCloud": "GO TO CLOUD" + } +} diff --git a/i18n/zh.json b/i18n/zh.json new file mode 100644 index 000000000..c788ff236 --- /dev/null +++ b/i18n/zh.json @@ -0,0 +1,334 @@ +{ + "arduino": { + "common": { + "offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.", + "noBoardSelected": "No board selected", + "selectedOn": "on {0}", + "notConnected": "[not connected]", + "serialMonitor": "串口监视器", + "oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", + "later": "Later", + "selectBoard": "Select Board", + "unknown": "Unknown", + "processing": "Processing", + "saveChangesToSketch": "Do you want to save changes to this sketch before closing?", + "loseChanges": "If you don't save, your changes will be lost." + }, + "menu": { + "sketch": "项目", + "tools": "工具" + }, + "debug": { + "optimizeForDebugging": "Optimize for Debugging", + "debugWithMessage": "Debug - {0}", + "noPlatformInstalledFor": "Platform is not installed for '{0}'", + "debuggingNotSupported": "Debugging is not supported by '{0}'" + }, + "preferences": { + "language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.", + "compile.verbose": "True for verbose compile output. False by default", + "compile.warnings": "Tells gcc which warning level to use. It's 'None' by default", + "upload.verbose": "True for verbose upload output. False by default.", + "window.autoScale": "True if the user interface automatically scales with the font size.", + "window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.", + "ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.", + "board.certificates": "List of certificates that can be uploaded to boards", + "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.", + "cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.", + "cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.", + "cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.", + "cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.", + "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", + "auth.clientID": "The OAuth2 client ID.", + "auth.domain": "The OAuth2 domain.", + "auth.audience": "The OAuth2 audience.", + "auth.registerUri": "The URI used to register a new user.", + "network": "网络", + "sketchbook.location": "Sketchbook location", + "browse": "浏览", + "files.inside.sketches": "Show files inside Sketches", + "editorFontSize": "Editor font size", + "interfaceScale": "Interface scale", + "showVerbose": "Show verbose output during", + "compilerWarnings": "Compiler warnings", + "automatic": " 自动调整", + "compile": "compile", + "upload": "upload", + "verifyAfterUpload": "Verify code after upload", + "checkForUpdates": "启动时检查更新", + "editorQuickSuggestions": "Editor Quick Suggestions", + "additionalManagerURLs": "附加开发板管理器网址", + "noProxy": "No proxy", + "manualProxy": "Manual proxy configuration", + "newSketchbookLocation": "Select new sketchbook location", + "choose": "Choose", + "enterAdditionalURLs": "Enter additional URLs, one for each row", + "unofficialBoardSupport": "Click for a list of unofficial board support URLs", + "invalid.sketchbook.location": "Invalid sketchbook location: {0}", + "invalid.editorFontSize": "Invalid editor font size. It must be a positive integer.", + "invalid.theme": "Invalid theme." + }, + "cloud": { + "signIn": "SIGN IN", + "signOut": "Sign Out", + "chooseSketchVisibility": "Choose visibility of your Sketch:", + "privateVisibility": "Private. Only you can view the Sketch.", + "publicVisibility": "Public. Anyone with the link can view the Sketch.", + "link": "Link:", + "embed": "Embed:", + "cloudSketchbook": "Cloud Sketchbook", + "shareSketch": "Share Sketch", + "showHideRemoveSketchbook": "Show/Hide Remote Sketchbook", + "pullSketch": "Pull Sketch", + "openInCloudEditor": "Open in Cloud Editor", + "options": "Options...", + "share": "Share...", + "remote": "Remote", + "emptySketchbook": "Your Sketchbook is empty", + "visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches.", + "signInToCloud": "Sign in to Arduino Cloud", + "syncEditSketches": "Sync and edit your Arduino Cloud Sketches", + "learnMore": "Learn more", + "continue": "Continue", + "pushSketch": "Push Sketch", + "pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.", + "pull": "Pull", + "pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?", + "donePulling": "Done pulling ‘{0}’.", + "notYetPulled": "Cannot push to Cloud. It is not yet pulled.", + "push": "Push", + "pullFirst": "You have to pull first to be able to push to the Cloud.", + "donePushing": "Done pushing ‘{0}’.", + "connected": "Connected", + "offline": "Offline", + "profilePicture": "Profile picture" + }, + "board": { + "installManually": "Install Manually", + "installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?", + "configDialogTitle": "Select Other Board & Port", + "configDialog1": "Select both a Board and a Port if you want to upload a sketch.", + "configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.", + "pleasePickBoard": "Please pick a board connected to the port you have selected.", + "showAllAvailablePorts": "Shows all available ports when enabled", + "programmer": "Programmer", + "succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}", + "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}", + "couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?", + "reselectLater": "Reselect later", + "noneSelected": "No boards selected.", + "noPortsSelected": "No ports selected for board: '{0}'.", + "noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?", + "openBoardsConfig": "Select other board and port…", + "boardListItem": "{0} at {1}", + "selectBoardForInfo": "Please select a board to obtain board info.", + "platformMissing": "The platform for the selected '{0}' board is not installed.", + "selectPortForInfo": "Please select a port to obtain board info.", + "boardInfo": "开发板信息", + "board": "Board{0}", + "port": "Port{0}", + "getBoardInfo": "Get Board Info", + "inSketchbook": " (in Sketchbook)" + }, + "boardsManager": "开发板管理器", + "about": { + "label": "About {0}", + "detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}" + }, + "contributions": { + "addFile": " 添加文件...", + "replaceTitle": "Replace", + "fileAdded": "One file added to the sketch." + }, + "replaceMsg": "Replace the existing version of {0}?", + "library": { + "addZip": "添加 .ZIP 库...", + "zipLibrary": "Library", + "overwriteExistingLibrary": "Do you want to overwrite the existing library?", + "successfullyInstalledZipLibrary": "Successfully installed library from {0} archive", + "namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?", + "libraryAlreadyExists": "A library already exists. Do you want to overwrite it?", + "include": "Include Library", + "manageLibraries": "Manage Libraries...", + "arduinoLibraries": "Arduino libraries", + "contributedLibraries": "Contributed libraries", + "title": "Library Manager", + "needsOneDependency": "The library {0}:{1} needs another dependency currently not installed:", + "needsMultipleDependencies": "The library {0}:{1} needs some other dependencies currently not installed:", + "installOneMissingDependency": "Would you like to install the missing dependency?", + "installMissingDependencies": "Would you like to install all the missing dependencies?", + "dependenciesForLibrary": "Dependencies for library {0}:{1}", + "installAll": "Install all", + "installOnly": "Install {0} only", + "installedSuccessfully": "Successfully installed library {0}:{1}", + "uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}" + }, + "selectZip": "Select a zip file containing the library you'd like to add", + "sketch": { + "archiveSketch": "项目存档", + "saveSketchAs": "Save sketch folder as...", + "createdArchive": "Created archive '{0}'.", + "new": "New", + "openRecent": "Open Recent", + "showFolder": "Show Sketch Folder", + "sketch": "Sketch", + "moving": "Moving", + "movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?", + "cantOpen": "文件夹\"{0}\"已存在。无法打开项目。", + "saveFolderAs": "Save sketch folder as...", + "sketchbook": "Sketchbook", + "upload": "上传", + "uploadUsingProgrammer": "Upload Using Programmer", + "userFieldsNotFoundError": "Can't find user fields for connected board", + "doneUploading": "Done uploading.", + "configureAndUpload": "Configure And Upload", + "verifyOrCompile": "Verify/Compile", + "exportBinary": "Export Compiled Binary", + "verify": "Verify", + "doneCompiling": "Done compiling.", + "couldNotConnectToSerial": "Could not reconnect to serial port. {0}", + "openSketchInNewWindow": "Open Sketch in New Window", + "openFolder": "Open Folder", + "titleLocalSketchbook": "Local Sketchbook", + "titleSketchbook": "Sketchbook", + "close": "Are you sure you want to close the sketch?" + }, + "bootloader": { + "burnBootloader": "烧录引导程序", + "doneBurningBootloader": "Done burning bootloader." + }, + "editor": { + "copyForForum": "Copy for Forum (Markdown)", + "commentUncomment": "注释/取消注释", + "increaseIndent": "Increase Indent", + "decreaseIndent": "Decrease Indent", + "increaseFontSize": "Increase Font Size", + "decreaseFontSize": "减小字号", + "autoFormat": "自动格式化" + }, + "examples": { + "menu": "Examples", + "couldNotInitializeExamples": "Could not initialize built-in examples.", + "builtInExamples": "内置示例", + "customLibrary": "Examples from Custom Libraries", + "for": "Examples for {0}", + "forAny": "Examples for any board" + }, + "help": { + "search": "Search on Arduino.cc", + "keyword": "Type a keyword", + "gettingStarted": "Getting Started", + "environment": "Environment", + "troubleshooting": "Troubleshooting", + "reference": "Reference", + "findInReference": "Find in Reference", + "faq": "Frequently Asked Questions", + "visit": "Visit Arduino.cc" + }, + "certificate": { + "uploadRootCertificates": "Upload SSL Root Certificates", + "openContext": "Open context", + "remove": "删除", + "upload": "上传", + "addURL": "Add URL to fetch SSL certificate", + "enterURL": "Enter URL", + "selectCertificateToUpload": "1. Select certificate to upload", + "addNew": "Add New", + "selectDestinationBoardToUpload": "2. Select destination board and upload certificate", + "uploadingCertificates": "Uploading certificates.", + "certificatesUploaded": "Certificates uploaded.", + "uploadFailed": "Upload failed. Please try again.", + "selectBoard": "Select a board...", + "boardAtPort": "{0} at {1}", + "noSupportedBoardConnected": "No supported board connected" + }, + "firmware": { + "updater": "WiFi101 / WiFiNINA Firmware Updater", + "selectBoard": "Select Board", + "checkUpdates": "Check Updates", + "selectVersion": "Select firmware version", + "install": "安装", + "overwriteSketch": "Installation will overwrite the Sketch on the board.", + "installingFirmware": "Installing firmware.", + "successfullyInstalled": "Firmware succesfully installed.", + "failedInstall": "Installation failed. Please try again." + }, + "dialog": { + "dontAskAgain": "Don't ask again" + }, + "userFields": { + "cancel": "Cancel", + "upload": "上传" + }, + "serial": { + "toggleTimestamp": "Toggle Timestamp", + "autoscroll": "自动滚屏", + "timestamp": "Timestamp", + "noLineEndings": "没有结束符", + "newLine": "换行符", + "carriageReturn": "回车", + "newLineCarriageReturn": "NL 和 CR", + "notConnected": "Not connected. Select a board and a port to connect automatically.", + "message": "Message ({0} + Enter to send message to '{1}' on '{2}'", + "connectionBusy": "Connection failed. Serial port is busy: {0}", + "disconnected": "Disconnected {0} from {1}.", + "unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.", + "failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.", + "reconnect": "Reconnecting {0} to {1} in {2} seconds..." + }, + "component": { + "uninstall": "Uninstall", + "uninstallMsg": "Do you want to uninstall {0}?", + "by": "by", + "version": "版本 {0}", + "moreInfo": "More info", + "install": "安装", + "filterSearch": "对搜索进行过滤..." + }, + "electron": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "unsavedChanges": "Any unsaved changes will not be saved." + }, + "compile": { + "error": "Compilation error: {0}" + }, + "upload": { + "error": "{0} error: {1}" + }, + "burnBootloader": { + "error": "Error while burning the bootloader: {0}" + } + }, + "theia": { + "core": { + "couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.", + "offline": "Offline", + "daemonOffline": "CLI Daemon Offline", + "cannotConnectBackend": "Cannot connect to the backend.", + "cannotConnectDaemon": "Cannot connect to the CLI daemon." + }, + "debug": { + "start": "Start...", + "typeNotSupported": "The debug session type \"{0}\" is not supported.", + "startError": "There was an error starting the debug session, check the logs for more details." + }, + "editor": { + "unsavedTitle": "Unsaved – {0}" + }, + "messages": { + "expand": "Expand", + "collapse": "Collapse" + }, + "workspace": { + "fileNewName": "Name for new file", + "invalidFilename": "Invalid filename.", + "invalidExtension": "“.{0}”不是有效扩展名", + "newFileName": "New name for file", + "deleteCurrentSketch": "Do you want to delete the current sketch?", + "sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected." + } + }, + "cloud": { + "GoToCloud": "GO TO CLOUD" + } +} diff --git a/package.json b/package.json index a890b3733..ff224f571 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "arduino-ide", - "version": "2.0.0-rc1", + "version": "2.0.0-rc2", "description": "Arduino IDE", "repository": "https://github.com/arduino/arduino-ide.git", "author": "Arduino SA", @@ -43,7 +43,7 @@ "test": "lerna run test", "download:plugins": "theia download:plugins", "update:version": "node ./scripts/update-version.js", - "i18n:generate": "theia nls-extract -e vscode -f '+(arduino-ide-extension|browser-app|electron|electron-app|plugins)/**/*.ts?(x)' -o ./i18n/en.json", + "i18n:generate": "theia nls-extract -e vscode -f \"+(arduino-ide-extension|browser-app|electron|electron-app|plugins)/**/*.ts?(x)\" -o ./i18n/en.json", "i18n:check": "yarn i18n:generate && git add -N ./i18n && git diff --exit-code ./i18n", "i18n:push": "node ./scripts/i18n/transifex-push.js ./i18n/en.json", "i18n:pull": "node ./scripts/i18n/transifex-pull.js ./i18n/" diff --git a/yarn.lock b/yarn.lock index b6cdcfa7f..fe560582c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2064,24 +2064,38 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== -"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": +"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0": version "1.8.2" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b" integrity sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== +"@sinonjs/commons@^1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^7.0.4", "@sinonjs/fake-timers@^7.1.0": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" + integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== dependencies: "@sinonjs/commons" "^1.7.0" -"@sinonjs/samsam@^5.3.1": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f" - integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg== +"@sinonjs/fake-timers@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/samsam@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-6.0.2.tgz#a0117d823260f282c04bff5f8704bdc2ac6910bb" + integrity sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ== dependencies: "@sinonjs/commons" "^1.6.0" lodash.get "^4.4.2" @@ -3246,16 +3260,26 @@ "@types/mime" "^1" "@types/node" "*" +"@types/sinon-chai@^3.2.6": + version "3.2.6" + resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.6.tgz#3504a744e2108646394766fb1339f52ea5d6bd0f" + integrity sha512-Z57LprQ+yOQNu9d6mWdHNvnmncPXzDWGSeLj+8L075/QahToapC4Q13zAFRVKV4clyBmdJ5gz4xBfVkOso5lXw== + dependencies: + "@types/chai" "*" + "@types/sinon" "*" + +"@types/sinon@*", "@types/sinon@^10.0.6": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.6.tgz#bc3faff5154e6ecb69b797d311b7cf0c1b523a1d" + integrity sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg== + dependencies: + "@sinonjs/fake-timers" "^7.1.0" + "@types/sinon@^2.3.5": version "2.3.7" resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-2.3.7.tgz#e92c2fed3297eae078d78d1da032b26788b4af86" integrity sha512-w+LjztaZbgZWgt/y/VMP5BUAWLtSyoIJhXyW279hehLPyubDoBNwvhcj3WaSptcekuKYeTCVxrq60rdLc6ImJA== -"@types/sinon@^7.5.2": - version "7.5.2" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.5.2.tgz#5e2f1d120f07b9cda07e5dedd4f3bf8888fccdb9" - integrity sha512-T+m89VdXj/eidZyejvmoP9jivXgBDdkOSBVQjU9kF349NEx10QdPNGxHeZUaj1IlJ32/ewdyXJjnJxyxJroYwg== - "@types/tar-fs@^1.16.1": version "1.16.3" resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-1.16.3.tgz#425b2b817c405d13d051f36ec6ec6ebd25e31069" @@ -6075,10 +6099,10 @@ diff@3.5.0, diff@^3.4.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -diff@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diff@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== dir-glob@^2.0.0, dir-glob@^2.2.2: version "2.2.2" @@ -10214,13 +10238,13 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nise@^4.0.4: - version "4.1.0" - resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6" - integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA== +nise@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.0.tgz#713ef3ed138252daef20ec035ab62b7a28be645c" + integrity sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ== dependencies: "@sinonjs/commons" "^1.7.0" - "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/fake-timers" "^7.0.4" "@sinonjs/text-encoding" "^0.7.1" just-extend "^4.0.2" path-to-regexp "^1.7.0" @@ -12881,17 +12905,22 @@ simple-get@^3.0.3: once "^1.3.1" simple-concat "^1.0.0" -sinon@^9.0.1: - version "9.2.4" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b" - integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg== - dependencies: - "@sinonjs/commons" "^1.8.1" - "@sinonjs/fake-timers" "^6.0.1" - "@sinonjs/samsam" "^5.3.1" - diff "^4.0.2" - nise "^4.0.4" - supports-color "^7.1.0" +sinon-chai@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.7.0.tgz#cfb7dec1c50990ed18c153f1840721cf13139783" + integrity sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g== + +sinon@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-12.0.1.tgz#331eef87298752e1b88a662b699f98e403c859e9" + integrity sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg== + dependencies: + "@sinonjs/commons" "^1.8.3" + "@sinonjs/fake-timers" "^8.1.0" + "@sinonjs/samsam" "^6.0.2" + diff "^5.0.0" + nise "^5.1.0" + supports-color "^7.2.0" slash@^1.0.0: version "1.0.0" @@ -13511,7 +13540,7 @@ supports-color@^5.3.0, supports-color@^5.4.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==