PhotoAtomic.Internationalization.Tool 0.6.0

dotnet tool install --global PhotoAtomic.Internationalization.Tool --version 0.6.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local PhotoAtomic.Internationalization.Tool --version 0.6.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=PhotoAtomic.Internationalization.Tool&version=0.6.0
                    
nuke :add-package PhotoAtomic.Internationalization.Tool --version 0.6.0
                    

PhotoAtomic.Internationalization.Tool

The CLI companion of PhotoAtomic.Internationalization, packaged as a dotnet tool. It opens your csproj with MSBuildWorkspace (Razor components included), extracts the translation catalog from the T($"...") call sites, and lets you manage translations before shipping instead of at runtime.

Installing and running

dotnet tool install -g PhotoAtomic.Internationalization.Tool

pai18n MyApp.csproj --csv translations.csv --all
                                              # THE one command: prune, translate, lint,
                                              # repair — and report what needs a human
pai18n MyApp.csproj content/rooms --csv translations.csv --all
                                              # code AND content in one catalog
pai18n MyApp.csproj --csv translations.csv --verify
                                              # CI-friendly: fails when the catalog has holes

Sources: code, and the content a compiler never sees

A source is a csproj (opened via MSBuildWorkspace, so source generators run and Razor markup is included), a compiled assembly, or a .json catalog file — or a directory of them — for text that is data: rooms written by an AI, rows in a database, a CMS export. The format is just CatalogEntry written down, and downstream nothing can tell the difference: vocabulary order, lint, repair and pruning work the same for both.

Name every source in the same run. --prune deletes the rows nothing asks for, so a run that only knows about the code would delete every line the content says, and a run that only knows the content would delete the program's own words. Sources are merged on (key, context, kind), so a sentence said by both is one row.

Filling uses the same AI translation pipeline as PhotoAtomic.Internationalization.AI (configure the provider via appsettings / user secrets / environment variables); --verify is meant for pipelines, so a missing translation breaks the build instead of surprising a user.

A scene is a chain: one thing, one name

When a catalog entry declares a Setting — the one line saying where a term lives, which content knows and code does not — that line is also an identity. The units of one setting are filled in order, each request carrying the terms the earlier ones settled on, so a press and its base do not come back as a "pressa" and a "torchio". Different settings stay side by side, and an entry with no setting at all (code, which has no such thing) is a chain of one, so the parallelism is unchanged for everything that had it.

The glossary is seeded from the table before a single call is made: a name translated last week must lead this run as firmly as one translated a second ago, otherwise a room drifts one session at a time. Only the settled terms sharing a word with the key are shown (Lexicon) — a model handed a dozen unrelated words starts working them in. A shared word is found across punctuation too: a description is mostly punctuation, and "authority's writ," used to match neither "authority" nor "writ", so the scene's own word for a thing was judged irrelevant to the sentence describing that very thing.

--all: the whole workflow, in the right order

--all deletes the rows for sentences the code no longer says, translates the values first and the sentences after (sentences are declined against the words already translated — fill a cold table in one pass and nothing is ever declined), then lints and repairs round after round until the count stops falling. What is still wrong is printed and moved to the end of the CSV, so opening the file lands on the work. Exit 0 when nothing is left, 4 when a human is needed; a rerun with nothing to do makes no AI calls and leaves the file byte-identical.

The single steps stay available, for CI and for looking closer:

  • --verify — no AI: every unit has rows for every language, or exit 3;
  • --lint — no AI: what the rows themselves get wrong (holes that never arrive or were invented, variants never declined, a set of variants with no plain row to fall back on, a value that forgot its gender, an example word left in the template, a translation that opens in lowercase where the source opens a sentence, a value carrying a capital it never declared, a common noun that brought its own article along, a sentence nobody says any more). Exit 4 on an error; warnings only inform;
  • --fix — repairs what the lint found: from the table where the answer is already there, by re-asking the model with the complaint in hand where it is not, and lints again so the result is a number rather than a hope;
  • --prune — deletes the dead sentences only (never values), rewriting the CSV. Pass the catalog that covers the whole table, or live rows will look dead.
  • --top-up — asks only for the grammatical cases a translated sentence is missing, and leaves every row it already has untouched. Run it when the table has learnt something new about a language: the day one of its nouns is declared plural, every sentence that can host it needs a row it was never asked for, and redoing the whole unit would cost the most and throw away rows that were already right. Deliberate on purpose — a plain rerun must cost nothing, and a case the model keeps refusing would otherwise be re-asked quietly for ever.

Part of PhotoAtomic.Libraries.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.6.0 77 8/23/2026
0.5.0 162 8/16/2026
0.4.0 94 8/16/2026
0.3.0 92 8/15/2026
0.2.1 103 8/12/2026
0.2.0 85 8/12/2026
0.1.0 85 8/12/2026