Skip to content
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
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- [ ] `dotnet build Rotula.sln -c Release` and `dotnet test Rotula.sln -c Release` pass
- [ ] New behavior has tests; `Rotula.Core` stays at or above 80% line coverage
- [ ] Public members have XML docs and non-obvious logic has a "why" comment (in English)
- [ ] New user-visible text exists in both `Strings.en.resx` and `Strings.es.resx`
- [ ] New user-visible text exists in all four languages: `Strings.en.resx`, `Strings.es.resx`, `Strings.pt-BR.resx` and `Strings.fr-CA.resx`
- [ ] No network calls or telemetry added; light theme only
- [ ] New dependencies are permissively licensed and listed in `THIRD-PARTY-NOTICES.md`
- [ ] `CHANGELOG.md` updated under "Unreleased" when users would notice the change
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- **A gallery of eight showcase labels** in the README (GS1-128 pallet, parcel, food, wine, lab specimen, asset tag, shelf tags with data merge, print conditions). Their `.rotula` files and the CSV data are in `docs/showcase`, and the pictures are rendered with Rotula's own PNG export by a test that only runs when `ROTULA_RENDER_SHOWCASE=1` is set.
- **Text fit for wrapped text** (Properties panel, Text fit): choose what a text block does when its text is longer than the box. "Shrink to fit" uses the largest font size, between a smallest and a largest size you set, at which all the text fits; "Grow height" adds lines, up to a limit you set, so all the text shows. With data merge every record gets the size or the lines its own text needs, the canvas shows the same size that prints, and "Do not warn" hides the does-not-fit warning when cutting the text off is what you want. Labels that do not use it are saved exactly as before; a label that does cannot be opened by older versions of Rotula.
- **Print an object only when you want it**: in the new Printing section of the properties, mark an object "Do not print this object" (a note or guide that never reaches the printer or an export), set "Print only when" a field equals, does not equal, contains, is empty or is not empty a value (so a warning can appear on some labels only), and "Lock position" so nothing moves it by accident. The Objects list and the canvas show small No print, Hidden and Locked badges.
- **Portuguese (Brazil) and French (Canada)**: the whole interface is now also available in Brazilian Portuguese and Canadian French (View > Language, where each language is listed in its own name), and the installer offers both. On first run Rotula follows Windows: a Portuguese desktop gets Portuguese (Brazil), a French one French (Canada), Spanish stays Spanish and anything else English. Numbers and dates follow the chosen language. A language you picked before stays as it was. The "Language of Rotula" policy (Group Policy / Intune) also accepts `pt-BR` and `fr-CA`.

### Fixed
- **Windows fonts that are named after a style now draw in that style.** "Arial Black", "Segoe UI Semibold", "Segoe UI Black", "Bahnschrift Condensed" and similar names used to be replaced by the default font in the preview, PNG/PDF export and print picture; they now resolve to their parent family at the weight and width the name says.
Expand Down
6 changes: 4 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ These are product rules; changes that break them will not be merged.
anonymous `GET` of the public GitHub "latest release" API a day, with nothing about the user or the computer in it. It never
downloads or installs anything. Do not add other network calls, and do not add anything to that request.
- **Light theme only.**
- **Spanish and English** through `.resx` resources (`src/Rotula.App/Resources/Strings.en.resx` and `Strings.es.resx`). Every new
user-visible string needs both. Core returns message keys, not text.
- **English, Spanish, Portuguese (Brazil) and French (Canada)** through `.resx` resources in `src/Rotula.App/Resources`
(`Strings.en.resx`, `Strings.es.resx`, `Strings.pt-BR.resx`, `Strings.fr-CA.resx`). Every new user-visible string needs all
four: en, es, pt-BR, fr-CA, with the same `{0}` placeholders and `_` access keys. French follows Canadian (OQLF) usage, with
a non-breaking space before `:` `;` `!` `?` and inside « ». Core returns message keys, not text.
- **Permissive dependencies only** (MIT, Apache-2.0, BSD and similar). Add any new package to
[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md) in the same pull request. Discuss new dependencies in an issue first.
- **Plain-language messages**: say what happened and what the user can do next.
Expand Down
8 changes: 5 additions & 3 deletions README.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ salida es determinista y en ASCII puro, así que puedes versionarla, compararla
| **Impresión** | Cola de Windows (RAW, incluido USB) y puerto TCP 9100, detección de DPI y papel, perfiles de impresora, etiqueta de prueba, comprobación del estado de la impresora antes y después de cada trabajo |
| **Puesto de impresión** | Una pantalla bloqueada, solo para imprimir, pensada para el almacén: elige o escanea una etiqueta, responde a sus preguntas con un lector de códigos, imprime y lee la respuesta de la impresora en letra grande y palabras sencillas ([detalles](#puesto-de-impresión)) |
| **Resolución** | Convierte una etiqueta entre 203, 300 y 600 dpi conservando su tamaño físico |
| **Idiomas** | Español e inglés, con cambio en vivo |
| **Idiomas** | Español, inglés, portugués (Brasil) y francés (Canadá), con cambio en vivo |

---

Expand Down Expand Up @@ -450,8 +450,10 @@ confirmación en dos pasos. El instalador lo coloca junto a la aplicación, por

## Idiomas

La interfaz está disponible en **español** y en **inglés**. De forma predeterminada Rotula sigue el idioma de Windows, y
puedes cambiarlo en cualquier momento desde **Ver > Idioma**, sin reiniciar. El instalador también está en ambos idiomas.
La interfaz está disponible en **español**, **inglés**, **portugués (Brasil)** y **francés (Canadá)**. De forma
predeterminada Rotula sigue el idioma de Windows (cualquier portugués usa portugués de Brasil, cualquier francés usa francés
de Canadá, y un idioma que Rotula no tiene usa inglés), y puedes cambiarlo en cualquier momento desde **Ver > Idioma**, sin
reiniciar. Los números y las fechas siguen el idioma elegido. El instalador también está en los cuatro idiomas.

---

Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ deterministic and plain ASCII, so you can version it, diff it or send it with an
| **Printing** | Windows spooler (RAW, including USB) and TCP port 9100, DPI and paper detection, printer profiles, test label, printer status check before and after a job |
| **Print station** | A locked, print-only screen for the warehouse floor: pick or scan a label, answer its questions with a barcode scanner, print, and read the printer's answer in large plain words ([details](#print-station)) |
| **Resolution** | Convert a label between 203, 300 and 600 dpi while keeping its physical size |
| **Languages** | English and Spanish, switchable live |
| **Languages** | English, Spanish, Portuguese (Brazil) and French (Canada), switchable live |

---

Expand Down Expand Up @@ -438,8 +438,10 @@ on in `rotula-mcp.settings.json`. Printing needs a two-step confirmation. The in

## Languages

The interface is available in **English** and **Spanish**. By default Rotula follows the Windows language, and you can
switch at any time from **View > Language**, with no restart. The installer is also available in both languages.
The interface is available in **English**, **Spanish**, **Portuguese (Brazil)** and **French (Canada)**. By default Rotula
follows the Windows language (any Portuguese gets Portuguese (Brazil), any French gets French (Canada), and a language
Rotula does not have gets English), and you can switch at any time from **View > Language**, with no restart. Numbers and
dates follow the language you choose. The installer is also available in all four languages.

---

Expand Down
19 changes: 14 additions & 5 deletions docs/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@
## App (Rotula.App)

### Language switch: live update
The in-app language switch (View > Language: Same as Windows / English / Espanol) updates open windows immediately; no restart.
The in-app language switch (View > Language: Same as Windows / English / Español / Português (Brasil) / Français (Canada))
updates open windows immediately; no restart. Each language is listed in its own language.
All XAML text is bound to the indexer of `Loc` (`{loc:T Key}`), which raises `Item[]` on a change. View-models that cache
translated text (property panel rows, toolbox, layers, status text) rebuild on `Loc.LanguageChanged`. The default follows the
Windows display language (Spanish if it is Spanish, English otherwise). The choice is stored in `%AppData%\Rotula\settings.json`.
Only `Strings.en.resx` and `Strings.es.resx` exist (satellite assemblies, no neutral resources), so `Loc` always asks for "en"
or "es" explicitly and falls back to English per key. `NeutralResourcesLanguage(..., Satellite)` must NOT be used: it also
redirects WPF's own XAML lookup and the application fails to start.
Windows display language: any Spanish is `es`, any Portuguese `pt-BR`, any French `fr-CA`, everything else English. The choice
is stored in `%AppData%\Rotula\settings.json` as `auto` or the culture name; settings saved as `en` or `es` keep working unchanged.
Only `Strings.en.resx`, `Strings.es.resx`, `Strings.pt-BR.resx` and `Strings.fr-CA.resx` exist (satellite assemblies, no neutral
resources), so `Loc` always asks for one of `Loc.SupportedLanguages` by its full culture name and falls back to English per key.
Numbers and dates in UI text are formatted with that culture (decimal comma in es, pt-BR and fr-CA).
`NeutralResourcesLanguage(..., Satellite)` must NOT be used: it also redirects WPF's own XAML lookup and the application fails
to start.
- **One variant per language**: Brazilian Portuguese and Canadian French are the ones shipped; a Portugal or France desktop gets
them rather than English. The French follows Quebec usage (OQLF): "courriel", and a non-breaking space before `:` `;` `!` `?`
and inside « ». Technical tokens (ZPL commands, GS1 AIs, file extensions, the product name) are never translated.
- **Parity is tested**: every `Strings.*.resx` must have exactly the English keys, no empty value and the same `{n}`
placeholders per key, so a new language or a new key cannot ship half done.

### Core messages
Core only returns `ValidationIssue.MessageKey` + args. The UI looks up `Issue.<MessageKey>`; args that are keywords
Expand Down
6 changes: 3 additions & 3 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Other switches:
| `/DIR="D:\Apps\Rotula"` | Install folder |
| `/TASKS="associate"` | Choose the tasks; leave `associate` out to skip the `.rotula` association |
| `/MERGETASKS="desktopicon"` | Also add a desktop icon (off by default) |
| `/LANG=spanish` | Language of the installer itself (`english` or `spanish`) |
| `/LANG=spanish` | Language of the installer itself (`english`, `spanish`, `brazilianportuguese` or `french`) |
| `/CURRENTUSER` | Force a per-user install even when run elevated |

The licence page is accepted automatically by `/VERYSILENT`, so the same note about the licence terms applies as for the MSI.
Expand Down Expand Up @@ -209,14 +209,14 @@ choice is kept in `settings.json` for the day the policy is removed. The MCP ser

| Policy (template name) | Registry value | Type | Values | Effect |
|---|---|---|---|---|
| Language of Rotula | `Language` | REG_SZ | `auto`, `en`, `es` | Sets the language and disables **View > Language**. `auto` follows the Windows display language |
| Language of Rotula | `Language` | REG_SZ | `auto`, `en`, `es`, `pt-BR`, `fr-CA` | Sets the language and disables **View > Language**. `auto` follows the Windows display language (any Portuguese gets `pt-BR`, any French `fr-CA`). Case does not matter |
| Check for updates | `UpdateCheck` | REG_DWORD | `1` on, `0` off | Switches the daily update check on or off and disables **Help > Check for updates once a day**. Not set: each user decides (off until they switch it on) |
| Default printer | `DefaultPrinter` | REG_SZ | A Windows printer name | Preselected in the Print dialog until the user picks a printer. Not locked; ignored quietly if the printer is not installed |
| Allow data source add-ons from the user's folder | `UserAddOns` | REG_DWORD | `0` blocks | Add-ons in `%AppData%\Rotula\packs` are neither loaded nor offered for approval; the installed `packs` folder still works ([data-providers.md](data-providers.md)) |
| Allow the MCP server to print | `McpPrinting` | REG_DWORD | `0` blocks | The MCP server refuses every print request, whatever its settings file says |
| Allow the MCP server to read databases | `McpDatabase` | REG_DWORD | `0` blocks | The MCP server refuses every database query |

A value that Rotula does not understand (a language other than the three above, a number other than 0 or 1) is ignored, so
A value that Rotula does not understand (a language other than the five values above, a number other than 0 or 1) is ignored, so
a typo never forces a wrong setting on users. The MCP policies can only take something away: setting them to `1` does not
switch printing or database access on; the MCP server's own settings file still has to allow it ([mcp.md](mcp.md)).

Expand Down
3 changes: 3 additions & 0 deletions packaging/installer/Rotula.iss
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ VersionInfoProductVersion={#AppVersionNumeric}
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl"
; The same four languages as the application (Loc.SupportedLanguages). Both files ship with Inno Setup itself.
Name: "brazilianportuguese"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl"
Name: "french"; MessagesFile: "compiler:Languages\French.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Expand Down
6 changes: 6 additions & 0 deletions packaging/policies/Rotula.admx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
<item displayName="$(string.Language_Es)">
<value><string>es</string></value>
</item>
<item displayName="$(string.Language_PtBr)">
<value><string>pt-BR</string></value>
</item>
<item displayName="$(string.Language_FrCa)">
<value><string>fr-CA</string></value>
</item>
</enum>
</elements>
</policy>
Expand Down
6 changes: 4 additions & 2 deletions packaging/policies/en-US/Rotula.adml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
<string id="Language">Language of Rotula</string>
<string id="Language_Explain">Sets the language of Rotula and locks it: View &gt; Language is disabled and says "Managed by your organization".

"Same as Windows" uses Spanish when the Windows display language is Spanish and English otherwise.
"Same as Windows" uses Spanish when the Windows display language is Spanish, Portuguese (Brazil) when it is any Portuguese, French (Canada) when it is any French, and English otherwise.

Not configured or disabled: each user chooses the language.

Registry value: Language (text: auto, en or es).</string>
Registry value: Language (text: auto, en, es, pt-BR or fr-CA).</string>
<string id="Language_Auto">Same as Windows</string>
<string id="Language_En">English</string>
<string id="Language_Es">Spanish</string>
<string id="Language_PtBr">Portuguese (Brazil)</string>
<string id="Language_FrCa">French (Canada)</string>

<string id="UpdateCheck">Check for updates</string>
<string id="UpdateCheck_Explain">Controls the daily update check and locks the choice (Help &gt; Check for updates once a day).
Expand Down
6 changes: 4 additions & 2 deletions packaging/policies/es-ES/Rotula.adml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
<string id="Language">Idioma de Rotula</string>
<string id="Language_Explain">Fija el idioma de Rotula y lo bloquea: Ver &gt; Idioma queda desactivado e indica "Administrado por tu organización".

"Igual que Windows" usa el español cuando el idioma de presentación de Windows es español, y el inglés en los demás casos.
"Igual que Windows" usa el español cuando el idioma de presentación de Windows es español, el portugués (Brasil) cuando es cualquier portugués, el francés (Canadá) cuando es cualquier francés, y el inglés en los demás casos.

Sin configurar o deshabilitada: cada usuario elige el idioma.

Valor del registro: Language (texto: auto, en o es).</string>
Valor del registro: Language (texto: auto, en, es, pt-BR o fr-CA).</string>
<string id="Language_Auto">Igual que Windows</string>
<string id="Language_En">Inglés</string>
<string id="Language_Es">Español</string>
<string id="Language_PtBr">Portugués (Brasil)</string>
<string id="Language_FrCa">Francés (Canadá)</string>

<string id="UpdateCheck">Buscar actualizaciones</string>
<string id="UpdateCheck_Explain">Controla la búsqueda diaria de actualizaciones y bloquea la opción (Ayuda &gt; Buscar actualizaciones una vez al día).
Expand Down
4 changes: 2 additions & 2 deletions src/Rotula.App/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// app, or any theme specific resource dictionaries)
)]

// The UI text lives in satellite assemblies (Strings.en.resx / Strings.es.resx) and Loc always asks for "en" or "es"
// explicitly, so no neutral-language attribute is used: NeutralResourcesLanguage(..., Satellite) would also redirect WPF's
// The UI text lives in satellite assemblies (Strings.en / es / pt-BR / fr-CA .resx) and Loc always asks for one of those
// cultures explicitly, so no neutral-language attribute is used: NeutralResourcesLanguage(..., Satellite) would also redirect WPF's
// own XAML resource lookup to a satellite and the application would fail to start.
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Rotula.Tests")]
Loading
Loading