diff --git a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts
index d6e9571f7..0a6df55ff 100644
--- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts
+++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts
@@ -53,8 +53,6 @@ import {
   DockPanelRenderer as TheiaDockPanelRenderer,
   TabBarRendererFactory,
   ContextMenuRenderer,
-  createTreeContainer,
-  TreeWidget,
 } from '@theia/core/lib/browser';
 import { MenuContribution } from '@theia/core/lib/common/menu';
 import {
@@ -207,12 +205,8 @@ import { WorkspaceVariableContribution as TheiaWorkspaceVariableContribution } f
 import { WorkspaceVariableContribution } from './theia/workspace/workspace-variable-contribution';
 import { DebugConfigurationManager } from './theia/debug/debug-configuration-manager';
 import { DebugConfigurationManager as TheiaDebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
-import { SearchInWorkspaceWidget as TheiaSearchInWorkspaceWidget } from '@theia/search-in-workspace/lib/browser/search-in-workspace-widget';
-import { SearchInWorkspaceWidget } from './theia/search-in-workspace/search-in-workspace-widget';
 import { SearchInWorkspaceFactory as TheiaSearchInWorkspaceFactory } from '@theia/search-in-workspace/lib/browser/search-in-workspace-factory';
 import { SearchInWorkspaceFactory } from './theia/search-in-workspace/search-in-workspace-factory';
-import { SearchInWorkspaceResultTreeWidget as TheiaSearchInWorkspaceResultTreeWidget } from '@theia/search-in-workspace/lib/browser/search-in-workspace-result-tree-widget';
-import { SearchInWorkspaceResultTreeWidget } from './theia/search-in-workspace/search-in-workspace-result-tree-widget';
 import { MonacoEditorProvider } from './theia/monaco/monaco-editor-provider';
 import {
   MonacoEditorFactory,
@@ -605,9 +599,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
   bind(MonacoEditorProvider).toSelf().inSingletonScope();
   rebind(TheiaMonacoEditorProvider).toService(MonacoEditorProvider);
 
-  bind(SearchInWorkspaceWidget).toSelf();
-  rebind(TheiaSearchInWorkspaceWidget).toService(SearchInWorkspaceWidget);
-
   // Disabled reference counter in the editor manager to avoid opening the same editor (with different opener options) multiple times.
   bind(EditorManager).toSelf().inSingletonScope();
   rebind(TheiaEditorManager).toService(EditorManager);
@@ -617,17 +608,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
     .to(SearchInWorkspaceFactory)
     .inSingletonScope();
 
-  rebind(TheiaSearchInWorkspaceResultTreeWidget).toDynamicValue(
-    ({ container }) => {
-      const childContainer = createTreeContainer(container);
-      childContainer.bind(SearchInWorkspaceResultTreeWidget).toSelf();
-      childContainer
-        .rebind(TreeWidget)
-        .toService(SearchInWorkspaceResultTreeWidget);
-      return childContainer.get(SearchInWorkspaceResultTreeWidget);
-    }
-  );
-
   // Show a disconnected status bar, when the daemon is not available
   bind(ApplicationConnectionStatusContribution).toSelf().inSingletonScope();
   rebind(TheiaApplicationConnectionStatusContribution).toService(
diff --git a/arduino-ide-extension/src/browser/boards/boards-config.tsx b/arduino-ide-extension/src/browser/boards/boards-config.tsx
index c145ec924..df5ed5a33 100644
--- a/arduino-ide-extension/src/browser/boards/boards-config.tsx
+++ b/arduino-ide-extension/src/browser/boards/boards-config.tsx
@@ -259,9 +259,12 @@ export class BoardsConfig extends React.Component<
   override render(): React.ReactNode {
     return (
       <>
-        {this.renderContainer('boards', this.renderBoards.bind(this))}
         {this.renderContainer(
-          'ports',
+          nls.localize('arduino/board/boards', 'boards'),
+          this.renderBoards.bind(this)
+        )}
+        {this.renderContainer(
+          nls.localize('arduino/board/ports', 'ports'),
           this.renderPorts.bind(this),
           this.renderPortsFooter.bind(this)
         )}
@@ -384,7 +387,9 @@ export class BoardsConfig extends React.Component<
             defaultChecked={this.state.showAllPorts}
             onChange={this.toggleFilterPorts}
           />
-          <span>Show all ports</span>
+          <span>
+            {nls.localize('arduino/board/showAllPorts', 'Show all ports')}
+          </span>
         </label>
       </div>
     );
diff --git a/arduino-ide-extension/src/browser/contributions/board-selection.ts b/arduino-ide-extension/src/browser/contributions/board-selection.ts
index 21acc221b..037587d99 100644
--- a/arduino-ide-extension/src/browser/contributions/board-selection.ts
+++ b/arduino-ide-extension/src/browser/contributions/board-selection.ts
@@ -5,7 +5,6 @@ import {
   DisposableCollection,
   Disposable,
 } from '@theia/core/lib/common/disposable';
-import { firstToUpperCase } from '../../common/utils';
 import { BoardsConfig } from '../boards/boards-config';
 import { MainMenuManager } from '../../common/main-menu-manager';
 import { BoardsListWidget } from '../boards/boards-list-widget';
@@ -267,7 +266,11 @@ PID: ${PID}`;
       ];
       const placeholder = new PlaceholderMenuNode(
         menuPath,
-        `${firstToUpperCase(protocol)} ports`,
+        nls.localize(
+          'arduino/board/typeOfPorts',
+          '{0} ports',
+          Port.Protocols.protocolLabel(protocol)
+        ),
         { order: protocolOrder.toString() }
       );
       this.menuModelRegistry.registerMenuNode(menuPath, placeholder);
diff --git a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx
index f6d380c4d..dac80d61c 100644
--- a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx
+++ b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx
@@ -10,6 +10,7 @@ import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog/fil
 import { DisposableCollection } from '@theia/core/lib/common/disposable';
 import {
   AdditionalUrls,
+  CompilerWarnings,
   CompilerWarningLiterals,
   Network,
   ProxySettings,
@@ -260,7 +261,7 @@ export class SettingsComponent extends React.Component<
               >
                 {CompilerWarningLiterals.map((value) => (
                   <option key={value} value={value}>
-                    {value}
+                    {CompilerWarnings.labelOf(value)}
                   </option>
                 ))}
               </select>
@@ -398,10 +399,22 @@ export class SettingsComponent extends React.Component<
           </form>
           <div className="flex-line proxy-settings">
             <div className="column">
-              <div className="flex-line">Host name:</div>
-              <div className="flex-line">Port number:</div>
-              <div className="flex-line">Username:</div>
-              <div className="flex-line">Password:</div>
+              <div className="flex-line">{`${nls.localize(
+                'arduino/preferences/proxySettings/hostname',
+                'Host name'
+              )}:`}</div>
+              <div className="flex-line">{`${nls.localize(
+                'arduino/preferences/proxySettings/port',
+                'Port number'
+              )}:`}</div>
+              <div className="flex-line">{`${nls.localize(
+                'arduino/preferences/proxySettings/username',
+                'Username'
+              )}:`}</div>
+              <div className="flex-line">{`${nls.localize(
+                'arduino/preferences/proxySettings/password',
+                'Password'
+              )}:`}</div>
             </div>
             <div className="column stretch">
               <div className="flex-line">
diff --git a/arduino-ide-extension/src/browser/serial/plotter/plotter-frontend-contribution.ts b/arduino-ide-extension/src/browser/serial/plotter/plotter-frontend-contribution.ts
index 3914c061a..c403c4201 100644
--- a/arduino-ide-extension/src/browser/serial/plotter/plotter-frontend-contribution.ts
+++ b/arduino-ide-extension/src/browser/serial/plotter/plotter-frontend-contribution.ts
@@ -18,6 +18,7 @@ import {
   CLOSE_PLOTTER_WINDOW,
   SHOW_PLOTTER_WINDOW,
 } from '../../../common/ipc-communication';
+import { nls } from '@theia/core/lib/common/nls';
 
 const queryString = require('query-string');
 
@@ -107,7 +108,12 @@ export class PlotterFrontendContribution extends Contribution {
     if (wsPort) {
       this.open(wsPort);
     } else {
-      this.messageService.error(`Couldn't open serial plotter`);
+      this.messageService.error(
+        nls.localize(
+          'arduino/contributions/plotter/couldNotOpen',
+          "Couldn't open serial plotter"
+        )
+      );
     }
   }
 
diff --git a/arduino-ide-extension/src/browser/style/list-widget.css b/arduino-ide-extension/src/browser/style/list-widget.css
index f60159e34..843be61f9 100644
--- a/arduino-ide-extension/src/browser/style/list-widget.css
+++ b/arduino-ide-extension/src/browser/style/list-widget.css
@@ -111,13 +111,15 @@
     font-weight: bold;
     max-height: calc(1em + 4px);
     color: var(--theia-button-foreground);
-    content: 'INSTALLED';
+    content: attr(install);
+    text-transform: uppercase;
 }
 
 .component-list-item .header .installed:hover:before {
     background-color: var(--theia-button-foreground);
     color: var(--theia-button-background);
-    content: 'UNINSTALL';
+    content: attr(uninstall);
+    text-transform: uppercase;
 }
 
 .component-list-item[min-width~="170px"] .footer {
diff --git a/arduino-ide-extension/src/browser/theia/search-in-workspace/search-in-workspace-result-tree-widget.ts b/arduino-ide-extension/src/browser/theia/search-in-workspace/search-in-workspace-result-tree-widget.ts
deleted file mode 100644
index e831cd402..000000000
--- a/arduino-ide-extension/src/browser/theia/search-in-workspace/search-in-workspace-result-tree-widget.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { injectable } from '@theia/core/shared/inversify';
-import URI from '@theia/core/lib/common/uri';
-import {
-  SearchInWorkspaceFileNode,
-  SearchInWorkspaceResultTreeWidget as TheiaSearchInWorkspaceResultTreeWidget,
-} from '@theia/search-in-workspace/lib/browser/search-in-workspace-result-tree-widget';
-import { MEMORY_TEXT } from '@theia/core/lib/common/resource';
-
-/**
- * Workaround for https://github.com/eclipse-theia/theia/pull/9192/.
- */
-@injectable()
-export class SearchInWorkspaceResultTreeWidget extends TheiaSearchInWorkspaceResultTreeWidget {
-  protected override async createReplacePreview(
-    node: SearchInWorkspaceFileNode
-  ): Promise<URI> {
-    const fileUri = new URI(node.fileUri).withScheme('file');
-    const openedEditor = this.editorManager.all.find(
-      ({ editor }) => editor.uri.toString() === fileUri.toString()
-    );
-    let content: string;
-    if (openedEditor) {
-      content = openedEditor.editor.document.getText();
-    } else {
-      const resource = await this.fileResourceResolver.resolve(fileUri);
-      content = await resource.readContents();
-    }
-
-    const lines = content.split('\n');
-    node.children.map((l) => {
-      const leftPositionedNodes = node.children.filter(
-        (rl) => rl.line === l.line && rl.character < l.character
-      );
-      const diff =
-        (this._replaceTerm.length - this.searchTerm.length) *
-        leftPositionedNodes.length;
-      const start = lines[l.line - 1].substr(0, l.character - 1 + diff);
-      const end = lines[l.line - 1].substr(l.character - 1 + diff + l.length);
-      lines[l.line - 1] = start + this._replaceTerm + end;
-    });
-
-    return fileUri.withScheme(MEMORY_TEXT).withQuery(lines.join('\n'));
-  }
-}
diff --git a/arduino-ide-extension/src/browser/theia/search-in-workspace/search-in-workspace-widget.tsx b/arduino-ide-extension/src/browser/theia/search-in-workspace/search-in-workspace-widget.tsx
deleted file mode 100644
index cae633024..000000000
--- a/arduino-ide-extension/src/browser/theia/search-in-workspace/search-in-workspace-widget.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import { injectable, postConstruct } from '@theia/core/shared/inversify';
-import * as React from '@theia/core/shared/react';
-import { Key, KeyCode } from '@theia/core/lib/browser';
-import { SearchInWorkspaceWidget as TheiaSearchInWorkspaceWidget } from '@theia/search-in-workspace/lib/browser/search-in-workspace-widget';
-
-/**
- * Workaround for https://github.com/eclipse-theia/theia/pull/9183.
- */
-@injectable()
-export class SearchInWorkspaceWidget extends TheiaSearchInWorkspaceWidget {
-  @postConstruct()
-  protected override init(): void {
-    super.init();
-    this.title.iconClass = 'fa fa-arduino-search';
-  }
-
-  protected override renderGlobField(kind: 'include' | 'exclude'): React.ReactNode {
-    const currentValue = this.searchInWorkspaceOptions[kind];
-    const value = (currentValue && currentValue.join(', ')) || '';
-    return (
-      <div className="glob-field">
-        <div className="label">{'files to ' + kind}</div>
-        <input
-          className="theia-input"
-          type="text"
-          size={1}
-          defaultValue={value}
-          id={kind + '-glob-field'}
-          onKeyUp={(e) => {
-            if (e.target) {
-              const targetValue = (e.target as HTMLInputElement).value || '';
-              let shouldSearch =
-                Key.ENTER.keyCode ===
-                KeyCode.createKeyCode(e.nativeEvent).key?.keyCode;
-              const currentOptions = (this.searchInWorkspaceOptions[kind] || [])
-                .slice()
-                .map((s) => s.trim())
-                .sort();
-              const candidateOptions = this.splitOnComma(targetValue)
-                .map((s) => s.trim())
-                .sort();
-              const sameAs = (left: string[], right: string[]) => {
-                if (left.length !== right.length) {
-                  return false;
-                }
-                for (let i = 0; i < left.length; i++) {
-                  if (left[i] !== right[i]) {
-                    return false;
-                  }
-                }
-                return true;
-              };
-              if (!sameAs(currentOptions, candidateOptions)) {
-                this.searchInWorkspaceOptions[kind] =
-                  this.splitOnComma(targetValue);
-                shouldSearch = true;
-              }
-              if (shouldSearch) {
-                this.resultTreeWidget.search(
-                  this.searchTerm,
-                  this.searchInWorkspaceOptions
-                );
-              }
-            }
-          }}
-          onFocus={
-            kind === 'include'
-              ? this.handleFocusIncludesInputBox
-              : this.handleFocusExcludesInputBox
-          }
-          onBlur={
-            kind === 'include'
-              ? this.handleBlurIncludesInputBox
-              : this.handleBlurExcludesInputBox
-          }
-        ></input>
-      </div>
-    );
-  }
-}
diff --git a/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx b/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx
index 537f4d415..ea9e257ec 100644
--- a/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx
+++ b/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx
@@ -55,7 +55,14 @@ export class ListItemRenderer<T extends ArduinoComponent> {
             item.installedVersion
           )}
         </span>
-        <span className="installed" onClick={onClickUninstall} />
+        <span
+          className="installed"
+          onClick={onClickUninstall}
+          {...{
+            install: nls.localize('arduino/component/install', 'INSTALL'),
+            uninstall: nls.localize('arduino/component/uninstall', 'Uninstall'),
+          }}
+        />
       </div>
     );
 
diff --git a/arduino-ide-extension/src/common/protocol/boards-service.ts b/arduino-ide-extension/src/common/protocol/boards-service.ts
index 7be49725e..763fc9bd6 100644
--- a/arduino-ide-extension/src/common/protocol/boards-service.ts
+++ b/arduino-ide-extension/src/common/protocol/boards-service.ts
@@ -285,6 +285,29 @@ export namespace Port {
       return false;
     };
   }
+
+  export namespace Protocols {
+    export const KnownProtocolLiterals = ['serial', 'network'] as const;
+    export type KnownProtocol = typeof KnownProtocolLiterals[number];
+    export namespace KnownProtocol {
+      export function is(protocol: unknown): protocol is KnownProtocol {
+        return (
+          typeof protocol === 'string' &&
+          KnownProtocolLiterals.indexOf(protocol as KnownProtocol) >= 0
+        );
+      }
+    }
+    export const ProtocolLabels: Record<KnownProtocol, string> = {
+      serial: nls.localize('arduino/portProtocol/serial', 'Serial'),
+      network: nls.localize('arduino/portProtocol/network', 'Network'),
+    };
+    export function protocolLabel(protocol: string): string {
+      if (KnownProtocol.is(protocol)) {
+        return ProtocolLabels[protocol];
+      }
+      return protocol;
+    }
+  }
 }
 
 export interface BoardsPackage extends ArduinoComponent {
diff --git a/arduino-ide-extension/src/common/protocol/core-service.ts b/arduino-ide-extension/src/common/protocol/core-service.ts
index a7124d865..a4b63a604 100644
--- a/arduino-ide-extension/src/common/protocol/core-service.ts
+++ b/arduino-ide-extension/src/common/protocol/core-service.ts
@@ -1,3 +1,4 @@
+import { nls } from '@theia/core/lib/common/nls';
 import { ApplicationError } from '@theia/core/lib/common/application-error';
 import type {
   Location,
@@ -18,6 +19,17 @@ export const CompilerWarningLiterals = [
   'All',
 ] as const;
 export type CompilerWarnings = typeof CompilerWarningLiterals[number];
+export namespace CompilerWarnings {
+  export function labelOf(warning: CompilerWarnings): string {
+    return CompilerWarningLabels[warning];
+  }
+  const CompilerWarningLabels: Record<CompilerWarnings, string> = {
+    None: nls.localize('arduino/core/compilerWarnings/none', 'None'),
+    Default: nls.localize('arduino/core/compilerWarnings/default', 'Default'),
+    More: nls.localize('arduino/core/compilerWarnings/more', 'More'),
+    All: nls.localize('arduino/core/compilerWarnings/all', 'All'),
+  };
+}
 export namespace CoreError {
   export interface ErrorLocationRef {
     readonly message: string;
diff --git a/arduino-ide-extension/src/node/i18n/arduino-localization-contribution.ts b/arduino-ide-extension/src/node/i18n/arduino-localization-contribution.ts
index 3465ab83d..686d28bd7 100644
--- a/arduino-ide-extension/src/node/i18n/arduino-localization-contribution.ts
+++ b/arduino-ide-extension/src/node/i18n/arduino-localization-contribution.ts
@@ -3,150 +3,45 @@ import {
   LocalizationRegistry,
 } from '@theia/core/lib/node/i18n/localization-contribution';
 import { injectable } from '@theia/core/shared/inversify';
+import { join } from 'path';
 
 @injectable()
 export class ArduinoLocalizationContribution
   implements LocalizationContribution
 {
-  async registerLocalizations(registry: LocalizationRegistry): Promise<void> {
-    registry.registerLocalizationFromRequire(
-      'af',
-      require('../../../build/i18n/af.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'en',
-      require('../../../build/i18n/en.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'fr',
-      require('../../../build/i18n/fr.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'ko',
-      require('../../../build/i18n/ko.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'pt-br',
-      require('../../../build/i18n/pt.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'uk_UA',
-      require('../../../build/i18n/uk_UA.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'ar',
-      require('../../../build/i18n/ar.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'es',
-      require('../../../build/i18n/es.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'he',
-      require('../../../build/i18n/he.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'my_MM',
-      require('../../../build/i18n/my_MM.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'ro',
-      require('../../../build/i18n/ro.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'zh-cn',
-      require('../../../build/i18n/zh.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'bg',
-      require('../../../build/i18n/bg.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'eu',
-      require('../../../build/i18n/eu.json')
-    );
+  // 0. index: locale
+  // 1. index: optional JSON file to `require` (if differs from the locale)
+  // If you touch the locales, please keep the alphabetical order. Also in the `package.json` for the VS Code language packs. Thank you! ❤️
+  // Note that IDE2 has more translations than available VS Code language packs. (https://github.com/arduino/arduino-ide/issues/1447)
+  private readonly locales: ReadonlyArray<[string, string?]> = [
+    ['bg'],
+    ['cs'],
+    ['de'],
+    ['es'],
+    ['fr'],
+    ['hu'],
+    // ['id'], Does not have Transifex translations, but has a VS Code language pack available on Open VSX.
+    ['it'],
+    ['ja'],
+    ['ko'],
+    ['nl'],
+    ['pl'],
+    ['pt-br', 'pt'],
+    ['ru'],
+    ['tr'],
+    ['uk', 'uk_UA'],
+    ['zh-cn', 'zh'],
+  ];
 
-    registry.registerLocalizationFromRequire(
-      'hu',
-      require('../../../build/i18n/hu.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'ne',
-      require('../../../build/i18n/ne.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'ru',
-      require('../../../build/i18n/ru.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'zh_TW',
-      require('../../../build/i18n/zh_TW.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'de',
-      require('../../../build/i18n/de.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'fa',
-      require('../../../build/i18n/fa.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'it',
-      require('../../../build/i18n/it.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'nl',
-      require('../../../build/i18n/nl.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'sv_SE',
-      require('../../../build/i18n/sv_SE.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'el',
-      require('../../../build/i18n/el.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'fil',
-      require('../../../build/i18n/fil.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'ja',
-      require('../../../build/i18n/ja.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'pl',
-      require('../../../build/i18n/pl.json')
-    );
-
-    registry.registerLocalizationFromRequire(
-      'tr',
-      require('../../../build/i18n/tr.json')
-    );
+  async registerLocalizations(registry: LocalizationRegistry): Promise<void> {
+    for (const [locale, jsonFilename] of this.locales) {
+      registry.registerLocalizationFromRequire(
+        locale,
+        require(join(
+          __dirname,
+          `../../../build/i18n/${jsonFilename ?? locale}.json`
+        ))
+      );
+    }
   }
 }
diff --git a/arduino-ide-extension/src/node/i18n/localization-backend-contribution.ts b/arduino-ide-extension/src/node/i18n/localization-backend-contribution.ts
index e5bc11f03..75ea0deba 100644
--- a/arduino-ide-extension/src/node/i18n/localization-backend-contribution.ts
+++ b/arduino-ide-extension/src/node/i18n/localization-backend-contribution.ts
@@ -23,8 +23,8 @@ export class LocalizationBackendContribution extends TheiaLocalizationBackendCon
     app.get('/i18n/:locale', async (req, res) => {
       let locale = req.params.locale;
       /*
-        Waiting for the deploy of the language plugins is neecessary to avoid checking the available
-        languages before they're finished to be loaded: https://github.com/eclipse-theia/theia/issues/11471 
+        Waiting for the deploy of the language plugins is necessary to avoid checking the available
+        languages before they're finished to be loaded: https://github.com/eclipse-theia/theia/issues/11471
       */
       const start = performance.now();
       await this.initialized.promise;
diff --git a/electron-app/patch/frontend/index.js b/electron-app/patch/frontend/index.js
index 26afbfedd..c227aef44 100644
--- a/electron-app/patch/frontend/index.js
+++ b/electron-app/patch/frontend/index.js
@@ -17,6 +17,53 @@ const {
   FrontendApplicationConfigProvider,
 } = require('@theia/core/lib/browser/frontend-application-config-provider');
 
+function fetchFrom(path) {
+  const { Endpoint } = require('@theia/core/lib/browser/endpoint');
+  const endpoint = new Endpoint({ path }).getRestUrl().toString();
+  return fetch(endpoint);
+}
+
+async function loadTranslations() {
+  const { nls } = require('@theia/core/lib/common/nls');
+  const defaultLocale = typeof window === 'object' && window && window.localStorage.getItem(nls.localeId) || '';
+  if (defaultLocale && !nls.locale) {
+      Object.assign(nls, {
+          locale: defaultLocale
+      });
+  }
+  if (nls.locale) {
+      const response = await fetchFrom(`/i18n/${nls.locale}`);
+      nls.localization = await response.json();
+  }
+}
+
+async function loadBackendOS() {
+  const response = await fetchFrom('/os');
+  const osType = await response.text();
+  const isWindows = osType === 'Windows';
+  const isOSX = osType === 'OSX';
+  OS.backend.isOSX = isOSX;
+  OS.backend.isWindows = isWindows;
+  OS.backend.type = () => osType;
+}
+
+function customizeMonacoNls() {
+  const MonacoNls = require('@theia/monaco-editor-core/esm/vs/nls');
+  const { nls: TheiaNls } = require('@theia/core/lib/common/nls');
+  const { Localization } = require('@theia/core/lib/common/i18n/localization');
+  Object.assign(MonacoNls, {
+    localize(_, label, ...args) {
+      if (TheiaNls.locale) {
+        const defaultKey = TheiaNls.getDefaultKey(label);
+        if (defaultKey) {
+          return TheiaNls.localize(defaultKey, label, ...args);
+        }
+      }
+      return Localization.format(label, args);
+    }
+  });
+}
+
 // It is a mighty hack to support theme updates in the bundled IDE2.
 // If the custom theme registration happens before the restoration of the existing monaco themes, then any custom theme changes will be ignored.
 // This patch introduces a static deferred promise in the monaco-theming service that will be resolved when the restoration is ready.
@@ -25,8 +72,14 @@ const {
 // This patch customizes the monaco theme service behavior before loading the DI containers via the preload.
 // The preload is called only once before the app loads. The Theia extensions are not loaded at that point, but the app config provider is ready.
 const preloader = require('@theia/core/lib/browser/preloader');
-const originalPreload = preloader.preload;
 preloader.preload = async function () {
+  // Must require the monaco frontend module to activate the NLS customization for monaco.
+  // Otherwise, the NLS customization would trigger after the monaco UI components with all their translations are already loaded.
+  await Promise.allSettled([
+    loadTranslations(),
+    loadBackendOS(),
+  ]);
+  customizeMonacoNls();
   const { MonacoThemingService } = require('@theia/monaco/lib/browser/monaco-theming-service');
   const { MonacoThemeServiceIsReady } = require('arduino-ide-extension/lib/browser/utils/window');
   const { Deferred } = require('@theia/core/lib/common/promise-util');
@@ -42,7 +95,6 @@ preloader.preload = async function () {
     await this.restore();
     ready.resolve();
   }.bind(MonacoThemingService);
-  return originalPreload();
 }.bind(preloader);
 
 const lightTheme = 'arduino-theme';
diff --git a/electron/build/template-package.json b/electron/build/template-package.json
index 5c15d0560..f934a176f 100644
--- a/electron/build/template-package.json
+++ b/electron/build/template-package.json
@@ -137,24 +137,5 @@
         "path": "arduino-ide/nightly"
       }
     ]
-  },
-  "theiaPluginsDir": "plugins",
-  "theiaPlugins": {
-    "vscode-builtin-cpp": "https://open-vsx.org/api/vscode/cpp/1.52.1/file/vscode.cpp-1.52.1.vsix",
-    "vscode-arduino-tools": "https://downloads.arduino.cc/vscode-arduino-tools/vscode-arduino-tools-0.0.2-beta.5.vsix",
-    "vscode-builtin-json": "https://open-vsx.org/api/vscode/json/1.46.1/file/vscode.json-1.46.1.vsix",
-    "vscode-builtin-json-language-features": "https://open-vsx.org/api/vscode/json-language-features/1.46.1/file/vscode.json-language-features-1.46.1.vsix",
-    "cortex-debug": "https://open-vsx.org/api/marus25/cortex-debug/0.3.10/file/marus25.cortex-debug-0.3.10.vsix",
-    "vscode-language-pack-nl": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-nl/1.48.3/file/MS-CEINTL.vscode-language-pack-nl-1.48.3.vsix",
-    "vscode-language-pack-fr": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-fr/1.69.0/file/MS-CEINTL.vscode-language-pack-fr-1.69.0.vsix",
-    "vscode-language-pack-zh-hans": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-zh-hans/1.69.0/file/MS-CEINTL.vscode-language-pack-zh-hans-1.69.0.vsix",
-    "vscode-language-pack-de": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-de/1.69.0/file/MS-CEINTL.vscode-language-pack-de-1.69.0.vsix",
-    "vscode-language-pack-ja": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-ja/1.69.0/file/MS-CEINTL.vscode-language-pack-ja-1.69.0.vsix",
-    "vscode-language-pack-tr": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-tr/1.69.0/file/MS-CEINTL.vscode-language-pack-tr-1.69.0.vsix",
-    "vscode-language-pack-it": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-it/1.69.0/file/MS-CEINTL.vscode-language-pack-it-1.69.0.vsix",
-    "vscode-language-pack-ru":"https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-ru/1.69.0/file/MS-CEINTL.vscode-language-pack-ru-1.69.0.vsix",
-    "vscode-language-pack-es": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-es/1.69.0/file/MS-CEINTL.vscode-language-pack-es-1.69.0.vsix",
-    "vscode-language-pack-pt-BR": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-pt-BR/1.69.0/file/MS-CEINTL.vscode-language-pack-pt-BR-1.69.0.vsix",
-    "vscode-language-pack-cs": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-cs/1.69.0/file/MS-CEINTL.vscode-language-pack-cs-1.69.0.vsix"
   }
 }
diff --git a/electron/packager/index.js b/electron/packager/index.js
index 12b36097a..d769fecc0 100644
--- a/electron/packager/index.js
+++ b/electron/packager/index.js
@@ -123,8 +123,8 @@
   // Save some time: no need to build the projects that are not needed in final app. Currently unused. |
   //---------------------------------------------------------------------------------------------------+
   //@ts-ignore
-  let pkg = require('../working-copy/package.json');
-  const workspaces = pkg.workspaces;
+  const rootPackageJson = require('../working-copy/package.json');
+  const workspaces = rootPackageJson.workspaces;
   // We cannot remove the `electron-app`. Otherwise, there is not way to collect the unused dependencies.
   const dependenciesToRemove = [];
   for (const dependencyToRemove of dependenciesToRemove) {
@@ -133,10 +133,10 @@
       workspaces.splice(index, 1);
     }
   }
-  pkg.workspaces = workspaces;
+  rootPackageJson.workspaces = workspaces;
   fs.writeFileSync(
     path('..', workingCopy, 'package.json'),
-    JSON.stringify(pkg, null, 2)
+    JSON.stringify(rootPackageJson, null, 2)
   );
 
   //-------------------------------------------------------------------------------------------------+
@@ -169,13 +169,13 @@
     if (extension !== 'arduino-ide-extension') {
       // Do not unlink self.
       // @ts-ignore
-      pkg = require(`../working-copy/${extension}/package.json`);
+      rootPackageJson = require(`../working-copy/${extension}/package.json`);
       // @ts-ignore
-      pkg.dependencies['arduino-ide-extension'] =
+      rootPackageJson.dependencies['arduino-ide-extension'] =
         'file:../arduino-ide-extension';
       fs.writeFileSync(
         path('..', workingCopy, extension, 'package.json'),
-        JSON.stringify(pkg, null, 2)
+        JSON.stringify(rootPackageJson, null, 2)
       );
     }
   }
@@ -184,7 +184,7 @@
   // Merge the `working-copy/package.json` with `electron/build/template-package.json`. |
   //------------------------------------------------------------------------------------+
   // @ts-ignore
-  pkg = require('../working-copy/electron-app/package.json');
+  const appPackageJson = require('../working-copy/electron-app/package.json');
   template.build.files = [
     ...template.build.files,
     ...unusedDependencies.map((name) => `!node_modules/${name}`),
@@ -195,25 +195,26 @@
     dependencies[extension] = `file:../working-copy/${extension}`;
   }
   // @ts-ignore
-  pkg.dependencies = { ...pkg.dependencies, ...dependencies };
-  pkg.devDependencies = { ...pkg.devDependencies, ...template.devDependencies };
-  // Deep-merging the Theia application configuration. We enable the electron window reload in dev mode but not for the final product. (arduino/arduino-pro-ide#187)
+  appPackageJson.dependencies = { ...appPackageJson.dependencies, ...dependencies };
+  appPackageJson.devDependencies = { ...appPackageJson.devDependencies, ...template.devDependencies };
+  // Deep-merging the Theia application configuration.
   // @ts-ignore
-  const theia = merge(pkg.theia || {}, template.theia || {});
+  const theia = merge(appPackageJson.theia || {}, template.theia || {});
   const content = {
-    ...pkg,
+    ...appPackageJson,
     ...template,
     theia,
     // @ts-ignore
-    dependencies: pkg.dependencies,
-    devDependencies: pkg.devDependencies,
+    dependencies: appPackageJson.dependencies,
+    devDependencies: appPackageJson.devDependencies,
+    // VS Code extensions and the plugins folder is defined in the top level `package.json`. The template picks them up.
+    theiaPluginsDir: rootPackageJson.theiaPluginsDir,
+    theiaPlugins: rootPackageJson.theiaPlugins,
   };
-  const overwriteMerge = (destinationArray, sourceArray, options) =>
-    sourceArray;
   fs.writeFileSync(
     path('..', 'build', 'package.json'),
     JSON.stringify(
-      merge(content, template, { arrayMerge: overwriteMerge }),
+      merge(content, template, { arrayMerge: (_, sourceArray) => sourceArray }),
       null,
       2
     )
diff --git a/i18n/en.json b/i18n/en.json
index dff1729e5..4db465ba8 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -8,6 +8,7 @@
       "board": "Board{0}",
       "boardConfigDialogTitle": "Select Other Board and Port",
       "boardInfo": "Board Info",
+      "boards": "boards",
       "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 to compile, but not to upload your sketch.",
       "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?",
@@ -25,6 +26,7 @@
       "pleasePickBoard": "Please pick a board connected to the port you have selected.",
       "port": "Port{0}",
       "portLabel": "Port: {0}",
+      "ports": "ports",
       "programmer": "Programmer",
       "reselectLater": "Reselect later",
       "searchBoard": "Search board",
@@ -32,8 +34,10 @@
       "selectBoardForInfo": "Please select a board to obtain board info.",
       "selectPortForInfo": "Please select a port to obtain board info.",
       "showAllAvailablePorts": "Shows all available ports when enabled",
+      "showAllPorts": "Show all ports",
       "succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}",
-      "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}"
+      "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}",
+      "typeOfPorts": "{0} ports"
     },
     "boardsManager": "Boards Manager",
     "boardsType": {
@@ -149,8 +153,19 @@
     "contributions": {
       "addFile": "Add File",
       "fileAdded": "One file added to the sketch.",
+      "plotter": {
+        "couldNotOpen": "Couldn't open serial plotter"
+      },
       "replaceTitle": "Replace"
     },
+    "core": {
+      "compilerWarnings": {
+        "all": "All",
+        "default": "Default",
+        "more": "More",
+        "none": "None"
+      }
+    },
     "coreContribution": {
       "copyError": "Copy error messages",
       "noBoardSelected": "No board selected. Please select your Arduino board from the Tools > Board menu."
@@ -282,6 +297,10 @@
       "unableToCloseWebSocket": "Unable to close websocket",
       "unableToConnectToWebSocket": "Unable to connect to websocket"
     },
+    "portProtocol": {
+      "network": "Network",
+      "serial": "Serial"
+    },
     "preferences": {
       "additionalManagerURLs": "Additional Boards Manager URLs",
       "auth.audience": "The OAuth2 audience.",
@@ -321,6 +340,12 @@
       "network": "Network",
       "newSketchbookLocation": "Select new sketchbook location",
       "noProxy": "No proxy",
+      "proxySettings": {
+        "hostname": "Host name",
+        "password": "Password",
+        "port": "Port number",
+        "username": "Username"
+      },
       "showVerbose": "Show verbose output during",
       "sketchbook.location": "Sketchbook location",
       "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.",
diff --git a/package.json b/package.json
index 9800d23e7..92b49adfe 100644
--- a/package.json
+++ b/package.json
@@ -78,16 +78,21 @@
     "vscode-builtin-json": "https://open-vsx.org/api/vscode/json/1.46.1/file/vscode.json-1.46.1.vsix",
     "vscode-builtin-json-language-features": "https://open-vsx.org/api/vscode/json-language-features/1.46.1/file/vscode.json-language-features-1.46.1.vsix",
     "cortex-debug": "https://open-vsx.org/api/marus25/cortex-debug/0.3.10/file/marus25.cortex-debug-0.3.10.vsix",
+    "vscode-language-pack-bg": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-bg/1.48.3/file/MS-CEINTL.vscode-language-pack-bg-1.48.3.vsix",
+    "vscode-language-pack-cs": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-cs/1.53.2/file/MS-CEINTL.vscode-language-pack-cs-1.53.2.vsix",
+    "vscode-language-pack-de": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-de/1.53.2/file/MS-CEINTL.vscode-language-pack-de-1.53.2.vsix",
+    "vscode-language-pack-es": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-es/1.53.2/file/MS-CEINTL.vscode-language-pack-es-1.53.2.vsix",
+    "vscode-language-pack-fr": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-fr/1.53.2/file/MS-CEINTL.vscode-language-pack-fr-1.53.2.vsix",
+    "vscode-language-pack-hu": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-hu/1.48.3/file/MS-CEINTL.vscode-language-pack-hu-1.48.3.vsix",
+    "vscode-language-pack-it": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-it/1.53.2/file/MS-CEINTL.vscode-language-pack-it-1.53.2.vsix",
+    "vscode-language-pack-ja": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-ja/1.53.2/file/MS-CEINTL.vscode-language-pack-ja-1.53.2.vsix",
+    "vscode-language-pack-ko": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-ko/1.53.2/file/MS-CEINTL.vscode-language-pack-ko-1.53.2.vsix",
     "vscode-language-pack-nl": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-nl/1.48.3/file/MS-CEINTL.vscode-language-pack-nl-1.48.3.vsix",
-    "vscode-language-pack-fr": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-fr/1.69.0/file/MS-CEINTL.vscode-language-pack-fr-1.69.0.vsix",
-    "vscode-language-pack-zh-hans": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-zh-hans/1.69.0/file/MS-CEINTL.vscode-language-pack-zh-hans-1.69.0.vsix",
-    "vscode-language-pack-de": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-de/1.69.0/file/MS-CEINTL.vscode-language-pack-de-1.69.0.vsix",
-    "vscode-language-pack-ja": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-ja/1.69.0/file/MS-CEINTL.vscode-language-pack-ja-1.69.0.vsix",
-    "vscode-language-pack-tr": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-tr/1.69.0/file/MS-CEINTL.vscode-language-pack-tr-1.69.0.vsix",
-    "vscode-language-pack-it": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-it/1.69.0/file/MS-CEINTL.vscode-language-pack-it-1.69.0.vsix",
-    "vscode-language-pack-ru": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-ru/1.69.0/file/MS-CEINTL.vscode-language-pack-ru-1.69.0.vsix",
-    "vscode-language-pack-es": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-es/1.69.0/file/MS-CEINTL.vscode-language-pack-es-1.69.0.vsix",
-    "vscode-language-pack-pt-BR": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-pt-BR/1.69.0/file/MS-CEINTL.vscode-language-pack-pt-BR-1.69.0.vsix",
-    "vscode-language-pack-cs": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-cs/1.69.0/file/MS-CEINTL.vscode-language-pack-cs-1.69.0.vsix"
+    "vscode-language-pack-pl": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-pl/1.53.2/file/MS-CEINTL.vscode-language-pack-pl-1.53.2.vsix",
+    "vscode-language-pack-pt-BR": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-pt-BR/1.53.2/file/MS-CEINTL.vscode-language-pack-pt-BR-1.53.2.vsix",
+    "vscode-language-pack-ru": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-ru/1.53.2/file/MS-CEINTL.vscode-language-pack-ru-1.53.2.vsix",
+    "vscode-language-pack-tr": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-tr/1.53.2/file/MS-CEINTL.vscode-language-pack-tr-1.53.2.vsix",
+    "vscode-language-pack-uk": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-uk/1.48.3/file/MS-CEINTL.vscode-language-pack-uk-1.48.3.vsix",
+    "vscode-language-pack-zh-hans": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-zh-hans/1.53.2/file/MS-CEINTL.vscode-language-pack-zh-hans-1.53.2.vsix"
   }
 }