Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: localize the extension #478

Merged
merged 1 commit into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ parameters:
extends:
template: azure-pipelines/extension/stable.yml@templates
parameters:
l10nSourcePaths: ./src
buildSteps:
- script: npm ci
displayName: Install dependencies
Expand Down
13 changes: 7 additions & 6 deletions media/editor/copyPaste.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { MessageType, PasteMode } from "../../shared/protocol";
import _style from "./copyPaste.css";
import { useUniqueId } from "./hooks";
import { messageHandler } from "./state";
import { strings } from "./strings";
import { throwOnUndefinedAccessInDev } from "./util";
import { VsButton, VsWidgetPopover } from "./vscodeUi";

Expand Down Expand Up @@ -113,7 +114,7 @@ export const PastePopup: React.FC<{

return <VsWidgetPopover anchor={context?.target || null} hide={hide} visible={!!context}>
<div className={style.radioList}>
<span>Paste as:</span>
<span>{strings.pasteAs}:</span>
{encodings.map(e => <EncodingOption
key={e}
value={e}
Expand All @@ -124,15 +125,15 @@ export const PastePopup: React.FC<{
)}
</div>
<div className={style.radioList}>
<span>Paste mode:</span>
<InsertionOption label="Replace" checked={mode == PasteMode.Replace} value={PasteMode.Replace} onChecked={setMode} />
<InsertionOption label="Insert" checked={mode == PasteMode.Insert} value={PasteMode.Insert} onChecked={setMode} />
<span>{strings.pasteMode}:</span>
<InsertionOption label={strings.replace} checked={mode == PasteMode.Replace} value={PasteMode.Replace} onChecked={setMode} />
<InsertionOption label={strings.insert} checked={mode == PasteMode.Insert} value={PasteMode.Insert} onChecked={setMode} />
</div>
<div className={style.buttonWrap}>
<VsButton disabled={!decodedValid} onClick={doReplace}>
{decodedValid
? <>{mode === PasteMode.Replace ? "Replace" : "Insert"} {decoded.length} bytes</>
: "Encoding Error"}
? <>{mode === PasteMode.Replace ? strings.replace : strings.insert} {decoded.length} {strings.bytes}</>
: strings.encodingError}
</VsButton>
</div>
</VsWidgetPopover>;
Expand Down
7 changes: 4 additions & 3 deletions media/editor/dataDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { FocusedElement, dataCellCls, useDisplayContext, useIsFocused, useIsHove
import { DataInspectorAside } from "./dataInspector";
import { useGlobalHandler, useLastAsyncRecoilValue } from "./hooks";
import * as select from "./state";
import { strings } from "./strings";
import { clamp, clsx, getAsciiCharacter, getScrollDimensions, throwOnUndefinedAccessInDev } from "./util";

const style = throwOnUndefinedAccessInDev(_style);
Expand Down Expand Up @@ -59,7 +60,7 @@ export const DataHeader: React.FC = () => {
// Calculated decoded width so that the Data Inspector is displayed at the right position
// Flex-shrink prevents the data inspector overlapping on narrow screens
<DataCellGroup style={{ width: `calc(var(--cell-size) * ${editorSettings.columnWidth * textCellWidth})`, flexShrink: 0 }}>
Decoded Text
{strings.decodedText}
</DataCellGroup>
)}
{inspectorLocation === InspectorLocation.Aside && <DataInspector />}
Expand Down Expand Up @@ -294,7 +295,7 @@ const DataRows: React.FC = () => {

const LoadingDataRow: React.FC<{ width: number; showDecodedText: boolean }> = ({ width, showDecodedText }) => {
const cells: React.ReactNode[] = [];
const text = "LOADING";
const text = strings.loadingUpper;
for (let i = 0; i < width; i++) {
const str = (text[i * 2] || ".") + (text[i * 2 + 1] || ".");
cells.push(<span
Expand Down Expand Up @@ -458,7 +459,7 @@ const DataCell: React.FC<{
return;
}

if (e.key === "Backspace" || e.key === "Delete") {
if (e.key === "Delete") {
// this is a bit of a hack, but this is kind of tricky: we got a delete
// for a range, and the edit must be undoable, but we aren't ensured to
// have the data paged in for the range. So make a separate request
Expand Down
5 changes: 3 additions & 2 deletions media/editor/dataInspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import _style from "./dataInspector.css";
import { inspectableTypes } from "./dataInspectorProperties";
import { useFileBytes, usePersistedState } from "./hooks";
import * as select from "./state";
import { strings } from "./strings";
import { throwOnUndefinedAccessInDev } from "./util";
import { VsTooltipPopover } from "./vscodeUi";

Expand Down Expand Up @@ -42,7 +43,7 @@ export const DataInspectorHover: React.FC = () => {
return <VsTooltipPopover
anchor={anchor}
hide={() => setInspected(undefined)} visible={true}>
<Suspense fallback="Loading...">
<Suspense fallback={strings.loadingDotDotDot}>
<InspectorContents columns={4} offset={inspected.byte} />
</Suspense>
</VsTooltipPopover>;
Expand Down Expand Up @@ -113,6 +114,6 @@ const EndiannessToggle: React.FC<{
checked={endianness === Endianness.Little}
onChange={evt => setEndianness(evt.target.checked ? Endianness.Little : Endianness.Big)}
/>
<label htmlFor="endian-checkbox">Little Endian</label>
<label htmlFor="endian-checkbox">{strings.littleEndian}</label>
</div>
);
36 changes: 19 additions & 17 deletions media/editor/findWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import React, { useCallback, useEffect, useRef, useState } from "react";
import { useRecoilState, useRecoilValue } from "recoil";
import { HexDocumentEditOp, HexDocumentReplaceEdit } from "../../shared/hexDocumentModel";
import { LiteralSearchQuery, MessageType, SearchRequestMessage, SearchResult, SearchResultsWithProgress } from "../../shared/protocol";
import { placeholder1 } from "../../shared/strings";
import { Range } from "../../shared/util/range";
import { FocusedElement, dataCellCls, useDisplayContext } from "./dataDisplayContext";
import _style from "./findWidget.css";
import { usePersistedState } from "./hooks";
import * as select from "./state";
import { strings } from "./strings";
import { clsx, hexDecode, isHexString, parseHexDigit, throwOnUndefinedAccessInDev } from "./util";
import { VsIconButton, VsIconCheckbox, VsProgressIndicator, VsTextFieldGroup } from "./vscodeUi";

Expand Down Expand Up @@ -71,15 +73,15 @@ const getReplaceOrError = (replace: string, isBinaryMode: boolean) => {
if (isBinaryMode) {
return isHexString(replace)
? hexDecode(replace)
: "Only hexadecimal characters (0-9 and a-f) are allowed";
: strings.onlyHexChars;
}

return new TextEncoder().encode(replace);
};

const getSearchQueryOrError = (query: string, isBinaryMode: boolean, isRegexp: boolean): SearchRequestMessage["query"] | string => {
if (isBinaryMode) {
return parseHexStringWithPlaceholders(query) || "Only hexadecimal characters (0-9, a-f, and ?? placeholders) are allowed";
return parseHexStringWithPlaceholders(query) || strings.onlyHexCharsAndPlaceholders;
}

if (isRegexp) {
Expand Down Expand Up @@ -322,33 +324,33 @@ export const FindWidget: React.FC = () => {
buttons={3}
ref={textFieldRef}
outerClassName={style.textField}
placeholder={isBinaryMode ? "Find Bytes (hex)" : "Find Text"}
placeholder={isBinaryMode ? strings.findBytes : strings.findText}
value={query}
onChange={onQueryChange}
onKeyDown={onFindKeyDown}
error={typeof queryOrError === "string" ? queryOrError : undefined}
>
{!isBinaryMode && <VsIconCheckbox checked={isRegexp} onToggle={setIsRegexp} title="Regular Expression Search">
{!isBinaryMode && <VsIconCheckbox checked={isRegexp} onToggle={setIsRegexp} title={strings.regexSearch}>
<RegexIcon />
</VsIconCheckbox>}
<VsIconCheckbox checked={isBinaryMode} onToggle={setIsBinaryMode} title="Search in Binary Mode">
<VsIconCheckbox checked={isBinaryMode} onToggle={setIsBinaryMode} title={strings.searchInBinaryMode}>
<BinaryFile />
</VsIconCheckbox>
<VsIconCheckbox checked={isCaseSensitive} onToggle={setIsCaseSensitive} title="Case Sensitive">
<VsIconCheckbox checked={isCaseSensitive} onToggle={setIsCaseSensitive} title={strings.caseSensitive}>
<CaseSensitive />
</VsIconCheckbox>
</VsTextFieldGroup>
<ResultBadge onUncap={() => setUncapped(true)} results={results} selectedResult={selectedResult} />
<VsIconButton title="Cancel Search" disabled={results.progress === 1} onClick={stopSearch}>
<VsIconButton title={strings.cancelSearch} disabled={results.progress === 1} onClick={stopSearch}>
<SearchStop />
</VsIconButton>
<VsIconButton disabled={results.results.length === 0} onClick={() => navigateResults(-1)} title="Previous Match">
<VsIconButton disabled={results.results.length === 0} onClick={() => navigateResults(-1)} title={strings.previousMatch}>
<ArrowUp />
</VsIconButton>
<VsIconButton disabled={results.results.length === 0} onClick={() => navigateResults(1)} title="Next Match">
<VsIconButton disabled={results.results.length === 0} onClick={() => navigateResults(1)} title={strings.nextMatch}>
<ArrowDown />
</VsIconButton>
<VsIconButton title="Close Widget (Esc)" onClick={closeWidget}>
<VsIconButton title={strings.closeWidget} onClick={closeWidget}>
<Close />
</VsIconButton>
</div>
Expand All @@ -359,13 +361,13 @@ export const FindWidget: React.FC = () => {
value={replace}
onChange={onReplaceChange}
onKeyDown={onReplaceKeyDown}
placeholder="Replace"
placeholder={strings.replace}
error={typeof replaceOrError === "string" ? replaceOrError : undefined}
/>
<VsIconButton disabled={typeof replaceOrError === "string" || selectedResult === undefined} onClick={replaceSelected} title="Replace Selected Match">
<VsIconButton disabled={typeof replaceOrError === "string" || selectedResult === undefined} onClick={replaceSelected} title={strings.replaceSelectedMatch}>
<Replace />
</VsIconButton>
<VsIconButton disabled={typeof replaceOrError === "string" || results.progress < 1 || !results.results.length} onClick={replaceAll} title="Replace All Matches">
<VsIconButton disabled={typeof replaceOrError === "string" || results.progress < 1 || !results.results.length} onClick={replaceAll} title={strings.replaceAllMatches}>
<ReplaceAll />
</VsIconButton>
</div>}
Expand All @@ -381,14 +383,14 @@ const ResultBadge: React.FC<{
}> = ({ results, selectedResult, onUncap }) => {
const resultCountStr = resultCountFormat.format(results.results.length);
const resultCountComponent = results.capped
? <a role="button" title={`More than ${results.results.length} results, click to find all`} onClick={onUncap}>{resultCountStr}+</a>
: <span title={`${results.results.length} results`}>{resultCountStr}</span>;
? <a role="button" title={strings.resultOverflow.replace(placeholder1, results.results.length.toString())} onClick={onUncap}>{resultCountStr}+</a>
: <span title={strings.resultCount.replace(placeholder1, results.results.length.toString())}>{resultCountStr}</span>;

return <div className={style.resultBadge}>
{results.progress < 1
? `Found ${resultCountStr}...`
? strings.foundNResults.replace(placeholder1, resultCountStr)
: !results.results.length
? "No results"
? strings.noResults
: selectedResult !== undefined
? <>{selectedFormat.format(selectedResult + 1)} of {resultCountComponent}</>
: <>{resultCountComponent} results</>}
Expand Down
3 changes: 2 additions & 1 deletion media/editor/hexEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ReadonlyWarning } from "./readonlyWarning";
import { ScrollContainer } from "./scrollContainer";
import { SettingsGear } from "./settings";
import * as select from "./state";
import { strings } from "./strings";
import { throwOnUndefinedAccessInDev } from "./util";
import { VsProgressIndicator } from "./vscodeUi";

Expand Down Expand Up @@ -51,7 +52,7 @@ const Editor: React.FC = () => {

if (isLargeFile && !bypassLargeFilePrompt) {
return <div>
<p>Opening this large file may cause instability. <a id="open-anyway" role="button" onClick={() => setBypassLargeFile(true)}>Open anyways</a></p>
<p>{strings.openLargeFileWarning} <a id="open-anyway" role="button" onClick={() => setBypassLargeFile(true)}>{strings.openAnyways}</a></p>
</div>;
}

Expand Down
3 changes: 2 additions & 1 deletion media/editor/readonlyWarning.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useCallback, useEffect } from "react";
import { useRecoilState } from "recoil";
import * as select from "./state";
import { strings } from "./strings";
import { VsTooltipPopover } from "./vscodeUi";

export const ReadonlyWarning: React.FC = () => {
Expand All @@ -17,6 +18,6 @@ export const ReadonlyWarning: React.FC = () => {
});

return <VsTooltipPopover anchor={anchor} hide={hide} visible={!!anchor}>
Cannot edit in read-only editor
{strings.readonlyWarning}
</VsTooltipPopover>;
};
7 changes: 4 additions & 3 deletions media/editor/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useState } from "react";
import { useRecoilState } from "recoil";
import _style from "./settings.css";
import * as select from "./state";
import { strings } from "./strings";
import { throwOnUndefinedAccessInDev } from "./util";
import { VsIconButton, VsTextField, VsWidgetPopover } from "./vscodeUi";

Expand All @@ -14,7 +15,7 @@ export const SettingsGear: React.FC = () => {

return (
<>
<VsIconButton title="Open Settings" className={style.gear} onClick={() => setIsOpen(!isOpen)} ref={setAnchor}>
<VsIconButton title={strings.openSettings} className={style.gear} onClick={() => setIsOpen(!isOpen)} ref={setAnchor}>
<SettingsGearIcon />
</VsIconButton>
<VsWidgetPopover anchor={anchor} hide={() => setIsOpen(false)} visible={isOpen}>
Expand All @@ -34,7 +35,7 @@ const TextCheckbox: React.FC = () => {

return (
<>
<label htmlFor="text-checkbox">Show Decoded Text</label>
<label htmlFor="text-checkbox">{strings.showDecodedText}</label>
<input
type="checkbox"
id="text-checkbox"
Expand All @@ -58,7 +59,7 @@ const ColumnWidth: React.FC = () => {

return (
<>
<label htmlFor="column-width">Bytes per Row</label>
<label htmlFor="column-width">{strings.bytesPerRow}</label>
<VsTextField
type="number"
id="column-width"
Expand Down
9 changes: 9 additions & 0 deletions media/editor/strings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/

import { ILocalizedStrings } from "../../shared/strings";

declare const LOC_STRINGS: ILocalizedStrings;

export const strings = LOC_STRINGS;
Loading
Loading