This document is for anyone (human or AI coding agent) making changes to the ResXLocalizer extension itself. It maps the code, not the features — for a feature/user-facing overview see README.md.
Every source module also has a short @module JSDoc block at the top of the file; this document
is the map that ties them together.
src/
extension.ts Activation entry point — commands, file watcher, wiring.
webview/ Pure HTML/CSS/JS string builders. No `vscode` API calls; these run
*inside* the sandboxed webview, not the extension host.
renderTable.ts The translation-table webview (the main UI).
panels/ VS Code-side controllers: own a WebviewPanel/WebviewView, receive its
postMessage events, and call into resx/ and locale/ to do the real work.
tablePanel.ts One instance per open `.resx` family tab.
fileListProvider.ts The sidebar's file tree (Activity Bar view).
importLogPanel.ts Read-only "what changed" report after an import with overwrites.
resx/ Everything that reads or writes `.resx` files, plus the pure data
transforms around them. No webview/HTML code lives here.
resxParser.ts Reads `.resx` files from disk -> ResxGroup/ResxFile/ResxEntry.
saveTranslations.ts Patches edits back into `.resx` XML in place (keeps comments/formatting).
resxTemplate.ts Builds the XML for a brand-new empty `.resx` file.
createMaster.ts "Create master .resx file" flow (folder + name prompts -> new file).
exportImport.ts CSV/JSON build + parse + validate + import-plan (no I/O — pure functions).
locale/ Locale-code utilities, independent of any specific `.resx` file.
locales.ts Common-languages QuickPick for "Add new" locale.
localeConvention.ts Matches a new locale's naming style to its siblings (`de` vs `de-DE`).
neutralLanguage.ts Detects a .NET project's declared neutral/master language.
docs/
ARCHITECTURE.md This file.
flowchart LR
disk[(".resx files\non disk")]
watcher["FileSystemWatcher\n(extension.ts)"]
parser["resx/resxParser"]
sidebar["panels/fileListProvider\n(sidebar tree)"]
tablePanel["panels/tablePanel\n(one per open tab)"]
renderTable["webview/renderTable\n(HTML/CSS/JS)"]
webviewUI(("Table webview\n(sandboxed)"))
save["resx/saveTranslations"]
exportImport["resx/exportImport"]
importLog["panels/importLogPanel"]
disk -- read --> parser
parser -- "ResxGroup[]" --> sidebar
parser -- "ResxGroup" --> tablePanel
tablePanel -- "renderTableHtml(group)" --> renderTable
renderTable -- HTML --> webviewUI
webviewUI -- "postMessage(save/delete/addKey/export/import)" --> tablePanel
tablePanel -- edits --> save
tablePanel -- "csv/json content, parsed rows" --> exportImport
exportImport -- "ImportPlan (edits + log)" --> tablePanel
tablePanel -- "if overwrites" --> importLog
save -- write --> disk
disk -- "onDidChange/Create/Delete" --> watcher
watcher -- "refreshAll()" --> tablePanel
watcher -- "refresh()" --> sidebar
Key point: the webview never touches the filesystem or vscode API directly. It only ever
sends a postMessage describing what happened (e.g. {command: "save", edits: [...]}); the
owning panel class in panels/ decides whether/how to act on it, and does all the real work by
calling into resx/.
- One
TablePanelperdir::baseName.TablePanel.update(group)always stores the group ascurrentGroup— export/import read from that, not from re-parsing the webview's DOM. saveTranslationspatches XML in place with targeted regexes instead of re-serializing the whole document. This is deliberate: it's what preserves a file's existing<comment>blocks and formatting. If you need to change how a<data>block is written, edit the regex/template there rather than swapping in a full XML writer.- Webview CSP only allows scripts with the per-render nonce (
script-src 'nonce-...'); inlinestyleis allowed without a nonce (style-src 'unsafe-inline'). Any new inline<script>you add to a webview's HTML must carry that samenonceattribute, or the browser silently blocks it — this only matters when adding scripts, not styles. resx/exportImport.tshas zerovscodeimports on purpose — it's the one module that's trivially unit-testable in plain Node (see "Verifying changes" below). Keep it that way; do file dialogs/reads/writes inpanels/tablePanel.tsand pass plain strings in..vscodeignoreexcludes sample/dev-only folders (e.g.locales/**) by exact name. If you rename such a folder, update the matching ignore pattern too, or it silently ships inside the.vsixagain.typescriptis on the native (Go-ported) 7.x compiler, not the classic JS one. Two consequences worth knowing: (1)tsconfig.jsonneeds an explicit"types": ["node"]— this compiler doesn't auto-include every package undernode_modules/@typesthe way 5.x did, so without it,Bufferand friends fail to resolve. (2)@typescript-eslintdoesn't support TS 7 yet (itspeerDependenciescap out at<6.1.0), so linting uses@babel/eslint-parser+@babel/preset-typescriptinstead — a syntax-only TypeScript parser that doesn't care whattypescriptversion (or even whether "typescript" is installed at all) is innode_modules. One practical effect: ESLint here is not type-aware —tsc --noEmit(vianoUnusedLocals/noUnusedParametersintsconfig.json) is what now catches unused locals/parameters, not an ESLint rule. Keep that split in mind before assuming an ESLint rule would catch a type-level issue.
webview/renderTable.ts— add the button's markup/CSS/icon, and avscode.postMessage({command: "..."})call in its click handler.panels/tablePanel.ts— add a branch for thatcommandin the constructor'sonDidReceiveMessagehandler, delegating to a new privatehandleX()method.- If the action reads/writes
.resxdata, put the actual logic inresx/(a pure function if possible) and call it fromhandleX(). - Call
await this.refresh()afterwards if the table's data changed on disk.
There's no automated test suite yet. In practice, changes here have been verified with:
npm run check-typesandnpm run lint— must stay clean.npm run vsixthencode --install-extension resxlocalizer-<version>.vsix --force, followed by a manual Reload Window — for anything that needs to be seen running inside real VS Code (native dialogs/QuickPicks, the file watcher, actual file I/O).- For webview HTML/CSS changes specifically: the render functions in
webview/renderTable.tsand the panels' inline HTML builders are plain functions that take data and return a string — they can be transpiled standalone (esbuild --bundle=false) and rendered with sampleResxGroupdata in a headless browser to check layout/CSS without touching the actual extension host. This is how the screenshots in the README were produced.