feat: add Ford VP44 unlock progress dialog, K-Line fast unlock, localization, safety dialogs, and settings

Unlock progress UI:
- UnlockProgressDialog with dark-themed progress ring, phase indicator, elapsed
  time, and cancel/close buttons (non-modal, draggable borderless window)
- UnlockProgressViewModel with event-driven progress tracking via IUnlockService
- Triggers on pump selection (manual or K-Line auto-detect), not test start

UnlockService rewrite:
- Persistent CAN senders that outlive the unlock sequence (StopSenders on pump change)
- Concurrent K-Line fast unlock: awaits session Connected, sends RAM timer shortcut
  ({02 88 02 03 A8 01 00}), verifies via CAN TestUnlock before skipping wait
- Fix Type 1 verification (Value == 0 means unlocked, was inverted)

K-Line fast unlock support:
- IKwpService.TryFastUnlockAsync / KwpService implementation

Additional features:
- ILocalizationService with ES/EN resource dictionaries and runtime switching
- Safety dialogs: VoltageWarning, OilPumpConfirm, RpmSafetyWarning
- SettingsDialog for app configuration
- BenchService enhancements, ConfigurationService improvements, PDF report updates
- All UI strings localized via DynamicResource

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 13:22:48 +02:00
parent c617854c09
commit 37d099cdbd
55 changed files with 3207 additions and 379 deletions

322
Resources/Strings.en.xaml Normal file
View File

@@ -0,0 +1,322 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=System.Runtime">
<!-- ══════════════════════════════════════════════════════════════════
English (ENG)
══════════════════════════════════════════════════════════════════ -->
<!-- ── App ──────────────────────────────────────────────────────────── -->
<sys:String x:Key="App.Title">HC_APTBS — Herlic Test Bench</sys:String>
<sys:String x:Key="App.LanguageLabel">ESP</sys:String>
<!-- ── Menu ─────────────────────────────────────────────────────────── -->
<sys:String x:Key="Menu.Settings">Settings</sys:String>
<!-- ── Status bar / connection indicators ───────────────────────────── -->
<sys:String x:Key="Status.Label">Status:</sys:String>
<sys:String x:Key="Status.Can">CAN</sys:String>
<sys:String x:Key="Status.Bench">Bench</sys:String>
<sys:String x:Key="Status.Pump">Pump</sys:String>
<sys:String x:Key="Status.KLine">K-Line</sys:String>
<sys:String x:Key="Status.Connected">Connected</sys:String>
<sys:String x:Key="Status.ConnectionFailed">Connection failed</sys:String>
<sys:String x:Key="Status.Disconnected">Disconnected</sys:String>
<!-- ── Bench section ────────────────────────────────────────────────── -->
<sys:String x:Key="Bench.Header">Bench</sys:String>
<sys:String x:Key="Bench.ConnectCan">Connect CAN</sys:String>
<sys:String x:Key="Bench.DisconnectCan">Disconnect CAN</sys:String>
<sys:String x:Key="Bench.Rpm">rpm</sys:String>
<sys:String x:Key="Bench.Target">Target:</sys:String>
<sys:String x:Key="Bench.Voltage">V:</sys:String>
<sys:String x:Key="Bench.TempIn">T. In:</sys:String>
<sys:String x:Key="Bench.TempOut">T. Out:</sys:String>
<sys:String x:Key="Bench.Temp4">T. 4:</sys:String>
<sys:String x:Key="Bench.TempTank">T. Tank:</sys:String>
<sys:String x:Key="Bench.P1">P1:</sys:String>
<sys:String x:Key="Bench.P2">P2:</sys:String>
<sys:String x:Key="Bench.QDelivery">Q-Del.:</sys:String>
<sys:String x:Key="Bench.QOver">Q-Over:</sys:String>
<sys:String x:Key="Bench.CcStroke">cc/stroke</sys:String>
<sys:String x:Key="Bench.PumpRpm">P-RPM:</sys:String>
<sys:String x:Key="Bench.PumpTemp">P-Temp:</sys:String>
<sys:String x:Key="Bench.PumpMe">P-ME:</sys:String>
<sys:String x:Key="Bench.PumpFbkw">P-FBkW:</sys:String>
<sys:String x:Key="Bench.PsgEncoder">PSG Encoder:</sys:String>
<!-- ── Bench controls ───────────────────────────────────────────────── -->
<sys:String x:Key="Bench.Direction">Direction</sys:String>
<sys:String x:Key="Bench.Left">LEFT</sys:String>
<sys:String x:Key="Bench.Right">RIGHT</sys:String>
<sys:String x:Key="Bench.Motor">Bench Motor</sys:String>
<sys:String x:Key="Bench.Start">START</sys:String>
<sys:String x:Key="Bench.Stop">STOP</sys:String>
<sys:String x:Key="Bench.SetRpm">Set RPM:</sys:String>
<sys:String x:Key="Bench.Go">GO</sys:String>
<sys:String x:Key="Bench.OilPump">Oil Pump</sys:String>
<sys:String x:Key="Bench.OilOff">OIL OFF</sys:String>
<sys:String x:Key="Bench.OilOn">OIL ON</sys:String>
<sys:String x:Key="Bench.Counter">Counter</sys:String>
<sys:String x:Key="Bench.Turns">Turns:</sys:String>
<sys:String x:Key="Bench.Send">Send</sys:String>
<sys:String x:Key="Bench.Relays">Relays</sys:String>
<sys:String x:Key="Bench.Electronic">Electronic</sys:String>
<sys:String x:Key="Bench.DepositCooler">Deposit Cooler</sys:String>
<sys:String x:Key="Bench.DepositHeater">Deposit Heater</sys:String>
<!-- ── Pump live data ───────────────────────────────────────────────── -->
<sys:String x:Key="Pump.THyb">T-hyb</sys:String>
<sys:String x:Key="Pump.Rpm">RPM</sys:String>
<sys:String x:Key="Pump.TEin">T-ein</sys:String>
<sys:String x:Key="Pump.UnitRpm">1/min</sys:String>
<!-- ── Pump identification ──────────────────────────────────────────── -->
<sys:String x:Key="PumpId.Label">Pump:</sys:String>
<sys:String x:Key="PumpId.Dfi">DFI:</sys:String>
<sys:String x:Key="PumpId.PumpId">Pump ID:</sys:String>
<sys:String x:Key="PumpId.SerialNo">Serial No:</sys:String>
<sys:String x:Key="PumpId.ModelRef">Model Ref:</sys:String>
<sys:String x:Key="PumpId.DataRecord">Data Record:</sys:String>
<sys:String x:Key="PumpId.PumpCtrl">Pump Ctrl:</sys:String>
<sys:String x:Key="PumpId.ModelIndex">Model Index:</sys:String>
<sys:String x:Key="PumpId.SwVer1">SW Ver 1:</sys:String>
<sys:String x:Key="PumpId.SwVer2">SW Ver 2:</sys:String>
<sys:String x:Key="PumpId.Errors">Errors:</sys:String>
<sys:String x:Key="PumpId.Error">Error:</sys:String>
<sys:String x:Key="PumpId.ReadKLine">Read K-Line</sys:String>
<sys:String x:Key="PumpId.Disconnect">Disconnect</sys:String>
<sys:String x:Key="PumpId.NoKLineDevice">No K-Line device found</sys:String>
<!-- ── DFI management ───────────────────────────────────────────────── -->
<sys:String x:Key="Dfi.Read">READ</sys:String>
<sys:String x:Key="Dfi.Write">WRITE</sys:String>
<sys:String x:Key="Dfi.Label">DFI:</sys:String>
<sys:String x:Key="Dfi.Auto">AUTO</sys:String>
<!-- ── Pump control sliders ─────────────────────────────────────────── -->
<sys:String x:Key="PumpCtrl.Fbkw">FBKW - Advance Control</sys:String>
<sys:String x:Key="PumpCtrl.Me">ME - Quantity Control</sys:String>
<sys:String x:Key="PumpCtrl.PreInj">ME - Pre-inj Quantity</sys:String>
<sys:String x:Key="PumpCtrl.MinStepMax">Min / Step / Max</sys:String>
<sys:String x:Key="PumpCtrl.Min">Min</sys:String>
<sys:String x:Key="PumpCtrl.Step">Step</sys:String>
<sys:String x:Key="PumpCtrl.Max">Max</sys:String>
<!-- ── Bench parameter config ───────────────────────────────────────── -->
<sys:String x:Key="BenchParam.CanBusId">CAN-Bus ID (0x)</sys:String>
<sys:String x:Key="BenchParam.ByteL">Byte L</sys:String>
<sys:String x:Key="BenchParam.ByteH">Byte H</sys:String>
<sys:String x:Key="BenchParam.FilterAlpha">Filter α</sys:String>
<sys:String x:Key="BenchParam.EnableFormula">Enable formula</sys:String>
<!-- ── Angle display ────────────────────────────────────────────────── -->
<sys:String x:Key="Angle.Header">ADVANCE MONITORING</sys:String>
<sys:String x:Key="Angle.Psg">PSG:</sys:String>
<sys:String x:Key="Angle.Inj">INJ:</sys:String>
<sys:String x:Key="Angle.AbsDeg">ABS °:</sys:String>
<sys:String x:Key="Angle.LockDeg">LOCK °:</sys:String>
<sys:String x:Key="Angle.SetPsgZero">Set PSG zero reference</sys:String>
<sys:String x:Key="Angle.SetInjZero">Set INJ zero reference</sys:String>
<!-- ── Test panel ───────────────────────────────────────────────────── -->
<sys:String x:Key="Test.StartTest">▶ START TEST</sys:String>
<sys:String x:Key="Test.Stop">■ STOP</sys:String>
<sys:String x:Key="Test.Report">📄 Report</sys:String>
<sys:String x:Key="Test.ShowValues">Show values</sys:String>
<sys:String x:Key="Test.CheckAll">Check All</sys:String>
<sys:String x:Key="Test.SecondsRemaining">s remaining</sys:String>
<sys:String x:Key="Test.Condition">Cond:</sys:String>
<sys:String x:Key="Test.Measurement">Meas:</sys:String>
<sys:String x:Key="Test.MeasPerSec">M/s:</sys:String>
<sys:String x:Key="Test.Required">Required:</sys:String>
<sys:String x:Key="Test.TestLabel">Test:</sys:String>
<sys:String x:Key="Test.Critical">Critical</sys:String>
<sys:String x:Key="Test.Started">Test started...</sys:String>
<sys:String x:Key="Test.Stopped">Test stopped.</sys:String>
<!-- ── Test types ───────────────────────────────────────────────────── -->
<sys:String x:Key="TestType.Warmup">Warm-up</sys:String>
<sys:String x:Key="TestType.Adjustment">Adjustment</sys:String>
<sys:String x:Key="TestType.Flow">Flow</sys:String>
<sys:String x:Key="TestType.ServoValve">Servo valve</sys:String>
<sys:String x:Key="TestType.Upstroke">Upstroke</sys:String>
<sys:String x:Key="TestType.PreInjection">Pre-injection</sys:String>
<!-- ── Result display ───────────────────────────────────────────────── -->
<sys:String x:Key="Result.Phase">Phase</sys:String>
<sys:String x:Key="Result.Parameter">Parameter</sys:String>
<sys:String x:Key="Result.Target">Target</sys:String>
<sys:String x:Key="Result.Tolerance">Tol ±</sys:String>
<sys:String x:Key="Result.Average">Average</sys:String>
<sys:String x:Key="Result.ResultHeader">Result</sys:String>
<sys:String x:Key="Result.AllTests">All Tests</sys:String>
<!-- ── Common strings ───────────────────────────────────────────────── -->
<sys:String x:Key="Common.Pass">PASS</sys:String>
<sys:String x:Key="Common.Fail">FAIL</sys:String>
<sys:String x:Key="Common.Accept">Accept</sys:String>
<sys:String x:Key="Common.Cancel">Cancel</sys:String>
<sys:String x:Key="Common.Close">Close</sys:String>
<sys:String x:Key="Common.Save">Save</sys:String>
<sys:String x:Key="Common.Ok">OK</sys:String>
<sys:String x:Key="Common.Yes">Yes</sys:String>
<sys:String x:Key="Common.No">No</sys:String>
<sys:String x:Key="Common.Warning">WARNING</sys:String>
<sys:String x:Key="Common.Disabled">disabled</sys:String>
<!-- ── Dialog: Report ───────────────────────────────────────────────── -->
<sys:String x:Key="Dialog.Report.Title">Generate Report</sys:String>
<sys:String x:Key="Dialog.Report.ClientList">Client List</sys:String>
<sys:String x:Key="Dialog.Report.ClientData">Client Data</sys:String>
<sys:String x:Key="Dialog.Report.CompanyData">Company Data</sys:String>
<sys:String x:Key="Dialog.Report.Name">Name:</sys:String>
<sys:String x:Key="Dialog.Report.ClientInfo">Client information</sys:String>
<sys:String x:Key="Dialog.Report.Observations">Observations</sys:String>
<sys:String x:Key="Dialog.Report.Operator">Operator:</sys:String>
<sys:String x:Key="Dialog.Report.Company">Company:</sys:String>
<sys:String x:Key="Dialog.Report.CompanyInfo">Company information</sys:String>
<sys:String x:Key="Dialog.Report.DeleteClient">Delete Client</sys:String>
<sys:String x:Key="Dialog.Report.Generate">Generate</sys:String>
<!-- ── Dialog: User authentication ──────────────────────────────────── -->
<sys:String x:Key="Dialog.UserCheck.Title">User Authentication</sys:String>
<sys:String x:Key="Dialog.UserCheck.Username">Username:</sys:String>
<sys:String x:Key="Dialog.UserCheck.Password">Password:</sys:String>
<!-- ── Dialog: Voltage warning ──────────────────────────────────────── -->
<sys:String x:Key="Dialog.Voltage.Title">Power Supply Warning</sys:String>
<sys:String x:Key="Dialog.Voltage.VoltageLabel">VOLTAGE: </sys:String>
<sys:String x:Key="Dialog.Voltage.PumpRequires">The selected pump requires </sys:String>
<sys:String x:Key="Dialog.Voltage.PowerSupply"> power supply.</sys:String>
<sys:String x:Key="Dialog.Voltage.SwitchTo">SWITCH THE POWER SUPPLY TO </sys:String>
<!-- ── Dialog: Oil pump confirmation ────────────────────────────────── -->
<sys:String x:Key="Dialog.OilPump.Title">Oil Pump Activation</sys:String>
<sys:String x:Key="Dialog.OilPump.Message">You are about to activate the oil pump. Confirm that the oil level is adequate and all connections are secure before proceeding.</sys:String>
<sys:String x:Key="Dialog.OilPump.LeaksChecked">I have checked — no leaks, connections are ready</sys:String>
<!-- ── Dialog: RPM safety warning ───────────────────────────────────── -->
<sys:String x:Key="Dialog.RpmSafety.Title">RPM Safety Warning</sys:String>
<sys:String x:Key="Dialog.RpmSafety.Message">The oil pump is OFF. Running the bench motor without oil circulation can cause bearing damage to the pump.</sys:String>
<sys:String x:Key="Dialog.RpmSafety.OilAndProceed">Turn on oil pump and start the bench</sys:String>
<sys:String x:Key="Dialog.RpmSafety.ProceedWithout">I know what I'm doing — start without oil</sys:String>
<!-- ── Dialog: Unlock progress ──────────────────────────────────────── -->
<sys:String x:Key="Dialog.Unlock.Title">Immobilizer Unlock</sys:String>
<sys:String x:Key="Dialog.Unlock.Progress">PROGRESS</sys:String>
<sys:String x:Key="Dialog.Unlock.Phase1">Phase 1: Sending unlock signals</sys:String>
<sys:String x:Key="Dialog.Unlock.Phase2Testing">Phase 2: Testing</sys:String>
<sys:String x:Key="Dialog.Unlock.Phase2Sending">Phase 2: Sending...</sys:String>
<sys:String x:Key="Dialog.Unlock.Cancelled">CANCELLED</sys:String>
<sys:String x:Key="Dialog.Unlock.Unlocked">UNLOCKED</sys:String>
<sys:String x:Key="Dialog.Unlock.Failed">UNLOCK FAILED</sys:String>
<sys:String x:Key="Dialog.Unlock.TypeLabel">Type {0}</sys:String>
<!-- ── Dialog: K-Line errors ────────────────────────────────────────── -->
<sys:String x:Key="Dialog.KlineErrors.Title">K-Line Fault Codes</sys:String>
<sys:String x:Key="Dialog.KlineErrors.Header">Fault codes:</sys:String>
<sys:String x:Key="Dialog.KlineErrors.Read">Read</sys:String>
<sys:String x:Key="Dialog.KlineErrors.Clear">Clear</sys:String>
<!-- ── Dialog: Settings ───────────────────────────────────────────────── -->
<sys:String x:Key="Dialog.Settings.Title">Settings</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.General">General</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.Safety">Safety</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.Pid">PID</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.Motor">Motor</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.Company">Company</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.KLine">K-Line</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.Advanced">Advanced</sys:String>
<sys:String x:Key="Dialog.Settings.Language">Language:</sys:String>
<sys:String x:Key="Dialog.Settings.DaysKeepLogs">Days to keep logs:</sys:String>
<sys:String x:Key="Dialog.Settings.TempMax">Max. temperature (°C):</sys:String>
<sys:String x:Key="Dialog.Settings.TempMin">Min. temperature (°C):</sys:String>
<sys:String x:Key="Dialog.Settings.SecurityRpmLimit">Safety RPM limit:</sys:String>
<sys:String x:Key="Dialog.Settings.MaxPressureBar">Max. pressure (bar):</sys:String>
<sys:String x:Key="Dialog.Settings.ToleranceUp">UP tolerance extension:</sys:String>
<sys:String x:Key="Dialog.Settings.TolerancePfp">PFP tolerance extension:</sys:String>
<sys:String x:Key="Dialog.Settings.IgnoreTin">Ignore T-in by default</sys:String>
<sys:String x:Key="Dialog.Settings.PidP">Proportional (P):</sys:String>
<sys:String x:Key="Dialog.Settings.PidI">Integral (I):</sys:String>
<sys:String x:Key="Dialog.Settings.PidD">Derivative (D):</sys:String>
<sys:String x:Key="Dialog.Settings.PidLoopMs">Loop period (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.EncoderRes">Encoder resolution:</sys:String>
<sys:String x:Key="Dialog.Settings.VoltMaxRpm">Voltage for max RPM (V):</sys:String>
<sys:String x:Key="Dialog.Settings.MaxRpm">Max RPM:</sys:String>
<sys:String x:Key="Dialog.Settings.RightRelay">Right = relay ON</sys:String>
<sys:String x:Key="Dialog.Settings.Relations">RPM-Voltage Table</sys:String>
<sys:String x:Key="Dialog.Settings.RelRpm">RPM</sys:String>
<sys:String x:Key="Dialog.Settings.RelVoltage">Voltage (V)</sys:String>
<sys:String x:Key="Dialog.Settings.AddRow">Add</sys:String>
<sys:String x:Key="Dialog.Settings.RemoveRow">Remove</sys:String>
<sys:String x:Key="Dialog.Settings.CompanyName">Company name:</sys:String>
<sys:String x:Key="Dialog.Settings.CompanyInfo">Company information:</sys:String>
<sys:String x:Key="Dialog.Settings.ReportLogo">Report logo path:</sys:String>
<sys:String x:Key="Dialog.Settings.BrowseLogoTitle">Select Logo Image</sys:String>
<sys:String x:Key="Dialog.Settings.KLinePort">K-Line port (FTDI):</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshPorts">Refresh</sys:String>
<sys:String x:Key="Dialog.Settings.KLineHint">Select an FTDI device or type the serial number manually.</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshBench">Bench interface (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshReading">While reading K-Line (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshCanBus">CAN bus read (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshPumpReq">Pump request (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshPumpParams">Pump params (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.BlinkInterval">Blink interval (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.FlasherInterval">Flasher interval (ms):</sys:String>
<!-- ── Error messages ───────────────────────────────────────────────── -->
<sys:String x:Key="Error.ReportGeneration">Failed to generate report:\n{0}</sys:String>
<sys:String x:Key="Error.ReportTitle">Report Error</sys:String>
<sys:String x:Key="Error.PsgSync">PSG sync pulse not detected. Check encoder connection.</sys:String>
<sys:String x:Key="Error.PsgTitle">PSG Error</sys:String>
<sys:String x:Key="Error.EmergencyStop">EMERGENCY STOP: {0}</sys:String>
<sys:String x:Key="Error.KLineNotFound">K-Line device not found. Check that the FTDI adapter is connected.</sys:String>
<sys:String x:Key="Error.KLineTitle">K-Line Error</sys:String>
<sys:String x:Key="Error.AuthInvalid">Invalid username or password.\n(Both are case-sensitive.)</sys:String>
<sys:String x:Key="Error.AuthTitle">Authentication Error</sys:String>
<!-- ── PDF Report ───────────────────────────────────────────────────── -->
<sys:String x:Key="Pdf.ReportTitle">VP44 INJECTION PUMP TEST REPORT</sys:String>
<sys:String x:Key="Pdf.GeneratedBy">Generated by HC-APTBS</sys:String>
<sys:String x:Key="Pdf.Page">Page </sys:String>
<sys:String x:Key="Pdf.Of"> of </sys:String>
<sys:String x:Key="Pdf.Date">Date: {0:dd/MM/yyyy HH:mm}</sys:String>
<sys:String x:Key="Pdf.Operator">Operator: {0}</sys:String>
<sys:String x:Key="Pdf.Client">Client: {0}</sys:String>
<sys:String x:Key="Pdf.PumpIdentification">PUMP IDENTIFICATION</sys:String>
<sys:String x:Key="Pdf.PumpId">Pump ID:</sys:String>
<sys:String x:Key="Pdf.Model">Model:</sys:String>
<sys:String x:Key="Pdf.SerialNo">Serial No.:</sys:String>
<sys:String x:Key="Pdf.Injector">Injector:</sys:String>
<sys:String x:Key="Pdf.Tube">Tube:</sys:String>
<sys:String x:Key="Pdf.Valve">Valve:</sys:String>
<sys:String x:Key="Pdf.Tension">Tension:</sys:String>
<sys:String x:Key="Pdf.Rotation">Rotation:</sys:String>
<sys:String x:Key="Pdf.LockAngle">Lock Angle:</sys:String>
<sys:String x:Key="Pdf.Measured">Measured:</sys:String>
<sys:String x:Key="Pdf.Chaveta">Chaveta:</sys:String>
<sys:String x:Key="Pdf.PreInj">Pre-Inj.:</sys:String>
<sys:String x:Key="Pdf.EcuData">ECU DATA (K-Line)</sys:String>
<sys:String x:Key="Pdf.OverallResult">OVERALL TEST RESULT</sys:String>
<sys:String x:Key="Pdf.TestsExecuted">Tests executed: {0} of {1}</sys:String>
<sys:String x:Key="Pdf.ParamsEvaluated">Parameters evaluated: {0} / {1} passed</sys:String>
<sys:String x:Key="Pdf.TestHeader">TEST: {0}</sys:String>
<sys:String x:Key="Pdf.Phase">Phase</sys:String>
<sys:String x:Key="Pdf.Parameter">Parameter</sys:String>
<sys:String x:Key="Pdf.Target">Target</sys:String>
<sys:String x:Key="Pdf.ToleranceHeader">Tolerance ±</sys:String>
<sys:String x:Key="Pdf.Average">Average</sys:String>
<sys:String x:Key="Pdf.Result">Result</sys:String>
<sys:String x:Key="Pdf.ErrorBits"> ⚠ Error bits: {0}</sys:String>
<sys:String x:Key="Pdf.NoSampleData">No sample data available for graphical display.</sys:String>
<sys:String x:Key="Pdf.ChartSamples">Samples: {0} | Target: {1} ± {2} | Average: {3} | Result: {4}</sys:String>
</ResourceDictionary>

322
Resources/Strings.es.xaml Normal file
View File

@@ -0,0 +1,322 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=System.Runtime">
<!-- ══════════════════════════════════════════════════════════════════
Spanish (ESP) — Default language
══════════════════════════════════════════════════════════════════ -->
<!-- ── App ──────────────────────────────────────────────────────────── -->
<sys:String x:Key="App.Title">HC_APTBS — Herlic Banco de Pruebas</sys:String>
<sys:String x:Key="App.LanguageLabel">ENG</sys:String>
<!-- ── Menu ─────────────────────────────────────────────────────────── -->
<sys:String x:Key="Menu.Settings">Configuración</sys:String>
<!-- ── Status bar / connection indicators ───────────────────────────── -->
<sys:String x:Key="Status.Label">Estado:</sys:String>
<sys:String x:Key="Status.Can">CAN</sys:String>
<sys:String x:Key="Status.Bench">Banco</sys:String>
<sys:String x:Key="Status.Pump">Bomba</sys:String>
<sys:String x:Key="Status.KLine">K-Line</sys:String>
<sys:String x:Key="Status.Connected">Conectado</sys:String>
<sys:String x:Key="Status.ConnectionFailed">Fallo de conexión</sys:String>
<sys:String x:Key="Status.Disconnected">Desconectado</sys:String>
<!-- ── Bench section ────────────────────────────────────────────────── -->
<sys:String x:Key="Bench.Header">Banco</sys:String>
<sys:String x:Key="Bench.ConnectCan">Conectar CAN</sys:String>
<sys:String x:Key="Bench.DisconnectCan">Desconectar CAN</sys:String>
<sys:String x:Key="Bench.Rpm">rpm</sys:String>
<sys:String x:Key="Bench.Target">Objetivo:</sys:String>
<sys:String x:Key="Bench.Voltage">V:</sys:String>
<sys:String x:Key="Bench.TempIn">T. Ent.:</sys:String>
<sys:String x:Key="Bench.TempOut">T. Sal.:</sys:String>
<sys:String x:Key="Bench.Temp4">T. 4:</sys:String>
<sys:String x:Key="Bench.TempTank">T. Tanq.:</sys:String>
<sys:String x:Key="Bench.P1">P1:</sys:String>
<sys:String x:Key="Bench.P2">P2:</sys:String>
<sys:String x:Key="Bench.QDelivery">Q-Del.:</sys:String>
<sys:String x:Key="Bench.QOver">Q-Over:</sys:String>
<sys:String x:Key="Bench.CcStroke">cc/golpe</sys:String>
<sys:String x:Key="Bench.PumpRpm">P-RPM:</sys:String>
<sys:String x:Key="Bench.PumpTemp">P-Temp:</sys:String>
<sys:String x:Key="Bench.PumpMe">P-ME:</sys:String>
<sys:String x:Key="Bench.PumpFbkw">P-FBkW:</sys:String>
<sys:String x:Key="Bench.PsgEncoder">Encoder PSG:</sys:String>
<!-- ── Bench controls ───────────────────────────────────────────────── -->
<sys:String x:Key="Bench.Direction">Dirección</sys:String>
<sys:String x:Key="Bench.Left">IZQUIERDA</sys:String>
<sys:String x:Key="Bench.Right">DERECHA</sys:String>
<sys:String x:Key="Bench.Motor">Motor del Banco</sys:String>
<sys:String x:Key="Bench.Start">ARRANCAR</sys:String>
<sys:String x:Key="Bench.Stop">PARAR</sys:String>
<sys:String x:Key="Bench.SetRpm">Fijar RPM:</sys:String>
<sys:String x:Key="Bench.Go">IR</sys:String>
<sys:String x:Key="Bench.OilPump">Bomba de Aceite</sys:String>
<sys:String x:Key="Bench.OilOff">ACEITE OFF</sys:String>
<sys:String x:Key="Bench.OilOn">ACEITE ON</sys:String>
<sys:String x:Key="Bench.Counter">Contador</sys:String>
<sys:String x:Key="Bench.Turns">Vueltas:</sys:String>
<sys:String x:Key="Bench.Send">Enviar</sys:String>
<sys:String x:Key="Bench.Relays">Relés</sys:String>
<sys:String x:Key="Bench.Electronic">Electrónico</sys:String>
<sys:String x:Key="Bench.DepositCooler">Enfriador Depósito</sys:String>
<sys:String x:Key="Bench.DepositHeater">Calentador Depósito</sys:String>
<!-- ── Pump live data ───────────────────────────────────────────────── -->
<sys:String x:Key="Pump.THyb">T-hyb</sys:String>
<sys:String x:Key="Pump.Rpm">RPM</sys:String>
<sys:String x:Key="Pump.TEin">T-ein</sys:String>
<sys:String x:Key="Pump.UnitRpm">1/min</sys:String>
<!-- ── Pump identification ──────────────────────────────────────────── -->
<sys:String x:Key="PumpId.Label">Bomba:</sys:String>
<sys:String x:Key="PumpId.Dfi">DFI:</sys:String>
<sys:String x:Key="PumpId.PumpId">ID Bomba:</sys:String>
<sys:String x:Key="PumpId.SerialNo">Nro. Serie:</sys:String>
<sys:String x:Key="PumpId.ModelRef">Ref. Modelo:</sys:String>
<sys:String x:Key="PumpId.DataRecord">Registro Datos:</sys:String>
<sys:String x:Key="PumpId.PumpCtrl">Ctrl. Bomba:</sys:String>
<sys:String x:Key="PumpId.ModelIndex">Índice Modelo:</sys:String>
<sys:String x:Key="PumpId.SwVer1">SW Ver 1:</sys:String>
<sys:String x:Key="PumpId.SwVer2">SW Ver 2:</sys:String>
<sys:String x:Key="PumpId.Errors">Errores:</sys:String>
<sys:String x:Key="PumpId.Error">Error:</sys:String>
<sys:String x:Key="PumpId.ReadKLine">Leer K-Line</sys:String>
<sys:String x:Key="PumpId.Disconnect">Desconectar</sys:String>
<sys:String x:Key="PumpId.NoKLineDevice">No se encontró dispositivo K-Line</sys:String>
<!-- ── DFI management ───────────────────────────────────────────────── -->
<sys:String x:Key="Dfi.Read">LEER</sys:String>
<sys:String x:Key="Dfi.Write">ESCRIBIR</sys:String>
<sys:String x:Key="Dfi.Label">DFI:</sys:String>
<sys:String x:Key="Dfi.Auto">AUTO</sys:String>
<!-- ── Pump control sliders ─────────────────────────────────────────── -->
<sys:String x:Key="PumpCtrl.Fbkw">FBKW - Control de Avance</sys:String>
<sys:String x:Key="PumpCtrl.Me">ME - Control de Caudal</sys:String>
<sys:String x:Key="PumpCtrl.PreInj">ME - Caudal Pre-inyección</sys:String>
<sys:String x:Key="PumpCtrl.MinStepMax">Mín / Paso / Máx</sys:String>
<sys:String x:Key="PumpCtrl.Min">Mín</sys:String>
<sys:String x:Key="PumpCtrl.Step">Paso</sys:String>
<sys:String x:Key="PumpCtrl.Max">Máx</sys:String>
<!-- ── Bench parameter config ───────────────────────────────────────── -->
<sys:String x:Key="BenchParam.CanBusId">CAN-Bus ID (0x)</sys:String>
<sys:String x:Key="BenchParam.ByteL">Byte L</sys:String>
<sys:String x:Key="BenchParam.ByteH">Byte H</sys:String>
<sys:String x:Key="BenchParam.FilterAlpha">Filtro α</sys:String>
<sys:String x:Key="BenchParam.EnableFormula">Habilitar fórmula</sys:String>
<!-- ── Angle display ────────────────────────────────────────────────── -->
<sys:String x:Key="Angle.Header">MONITOREO DE AVANCE</sys:String>
<sys:String x:Key="Angle.Psg">PSG:</sys:String>
<sys:String x:Key="Angle.Inj">INJ:</sys:String>
<sys:String x:Key="Angle.AbsDeg">ABS º:</sys:String>
<sys:String x:Key="Angle.LockDeg">LOCK º:</sys:String>
<sys:String x:Key="Angle.SetPsgZero">Fijar referencia cero PSG</sys:String>
<sys:String x:Key="Angle.SetInjZero">Fijar referencia cero INJ</sys:String>
<!-- ── Test panel ───────────────────────────────────────────────────── -->
<sys:String x:Key="Test.StartTest">▶ INICIAR TEST</sys:String>
<sys:String x:Key="Test.Stop">■ PARAR</sys:String>
<sys:String x:Key="Test.Report">📄 Informe</sys:String>
<sys:String x:Key="Test.ShowValues">Mostrar valores</sys:String>
<sys:String x:Key="Test.CheckAll">Marcar Todos</sys:String>
<sys:String x:Key="Test.SecondsRemaining">s restantes</sys:String>
<sys:String x:Key="Test.Condition">Cond:</sys:String>
<sys:String x:Key="Test.Measurement">Med:</sys:String>
<sys:String x:Key="Test.MeasPerSec">M/s:</sys:String>
<sys:String x:Key="Test.Required">Requerido:</sys:String>
<sys:String x:Key="Test.TestLabel">Test:</sys:String>
<sys:String x:Key="Test.Critical">Crítico</sys:String>
<sys:String x:Key="Test.Started">Test iniciado...</sys:String>
<sys:String x:Key="Test.Stopped">Test detenido.</sys:String>
<!-- ── Test types ───────────────────────────────────────────────────── -->
<sys:String x:Key="TestType.Warmup">Calentamiento</sys:String>
<sys:String x:Key="TestType.Adjustment">Ajuste</sys:String>
<sys:String x:Key="TestType.Flow">Caudal</sys:String>
<sys:String x:Key="TestType.ServoValve">Servoválvula</sys:String>
<sys:String x:Key="TestType.Upstroke">Carrera Ascendente</sys:String>
<sys:String x:Key="TestType.PreInjection">Pre-inyección</sys:String>
<!-- ── Result display ───────────────────────────────────────────────── -->
<sys:String x:Key="Result.Phase">Fase</sys:String>
<sys:String x:Key="Result.Parameter">Parámetro</sys:String>
<sys:String x:Key="Result.Target">Objetivo</sys:String>
<sys:String x:Key="Result.Tolerance">Tol ±</sys:String>
<sys:String x:Key="Result.Average">Promedio</sys:String>
<sys:String x:Key="Result.ResultHeader">Resultado</sys:String>
<sys:String x:Key="Result.AllTests">Todos los Tests</sys:String>
<!-- ── Common strings ───────────────────────────────────────────────── -->
<sys:String x:Key="Common.Pass">APROBADO</sys:String>
<sys:String x:Key="Common.Fail">REPROBADO</sys:String>
<sys:String x:Key="Common.Accept">Aceptar</sys:String>
<sys:String x:Key="Common.Cancel">Cancelar</sys:String>
<sys:String x:Key="Common.Close">Cerrar</sys:String>
<sys:String x:Key="Common.Save">Guardar</sys:String>
<sys:String x:Key="Common.Ok">OK</sys:String>
<sys:String x:Key="Common.Yes">Sí</sys:String>
<sys:String x:Key="Common.No">No</sys:String>
<sys:String x:Key="Common.Warning">ADVERTENCIA</sys:String>
<sys:String x:Key="Common.Disabled">deshabilitado</sys:String>
<!-- ── Dialog: Report ───────────────────────────────────────────────── -->
<sys:String x:Key="Dialog.Report.Title">Generar Informe</sys:String>
<sys:String x:Key="Dialog.Report.ClientList">Lista de Clientes</sys:String>
<sys:String x:Key="Dialog.Report.ClientData">Datos del Cliente</sys:String>
<sys:String x:Key="Dialog.Report.CompanyData">Datos de la Empresa</sys:String>
<sys:String x:Key="Dialog.Report.Name">Nombre:</sys:String>
<sys:String x:Key="Dialog.Report.ClientInfo">Información del cliente</sys:String>
<sys:String x:Key="Dialog.Report.Observations">Observaciones</sys:String>
<sys:String x:Key="Dialog.Report.Operator">Operador:</sys:String>
<sys:String x:Key="Dialog.Report.Company">Empresa:</sys:String>
<sys:String x:Key="Dialog.Report.CompanyInfo">Información de la empresa</sys:String>
<sys:String x:Key="Dialog.Report.DeleteClient">Eliminar Cliente</sys:String>
<sys:String x:Key="Dialog.Report.Generate">Generar</sys:String>
<!-- ── Dialog: User authentication ──────────────────────────────────── -->
<sys:String x:Key="Dialog.UserCheck.Title">Autenticación de Usuario</sys:String>
<sys:String x:Key="Dialog.UserCheck.Username">Usuario:</sys:String>
<sys:String x:Key="Dialog.UserCheck.Password">Contraseña:</sys:String>
<!-- ── Dialog: Voltage warning ──────────────────────────────────────── -->
<sys:String x:Key="Dialog.Voltage.Title">Advertencia de Fuente de Alimentación</sys:String>
<sys:String x:Key="Dialog.Voltage.VoltageLabel">VOLTAJE: </sys:String>
<sys:String x:Key="Dialog.Voltage.PumpRequires">La bomba seleccionada requiere </sys:String>
<sys:String x:Key="Dialog.Voltage.PowerSupply"> de alimentación.</sys:String>
<sys:String x:Key="Dialog.Voltage.SwitchTo">CAMBIE LA FUENTE DE ALIMENTACIÓN A </sys:String>
<!-- ── Dialog: Oil pump confirmation ────────────────────────────────── -->
<sys:String x:Key="Dialog.OilPump.Title">Activación de Bomba de Aceite</sys:String>
<sys:String x:Key="Dialog.OilPump.Message">Está a punto de activar la bomba de aceite. Confirme que el nivel de aceite es adecuado y que todas las conexiones están seguras antes de continuar.</sys:String>
<sys:String x:Key="Dialog.OilPump.LeaksChecked">He verificado — sin fugas, conexiones listas</sys:String>
<!-- ── Dialog: RPM safety warning ───────────────────────────────────── -->
<sys:String x:Key="Dialog.RpmSafety.Title">Advertencia de Seguridad RPM</sys:String>
<sys:String x:Key="Dialog.RpmSafety.Message">La bomba de aceite está APAGADA. Hacer funcionar el motor del banco sin circulación de aceite puede causar daño a los rodamientos de la bomba.</sys:String>
<sys:String x:Key="Dialog.RpmSafety.OilAndProceed">Encender bomba de aceite e iniciar el banco</sys:String>
<sys:String x:Key="Dialog.RpmSafety.ProceedWithout">Sé lo que hago — iniciar sin aceite</sys:String>
<!-- ── Dialog: Unlock progress ──────────────────────────────────────── -->
<sys:String x:Key="Dialog.Unlock.Title">Desbloqueo de Inmovilizador</sys:String>
<sys:String x:Key="Dialog.Unlock.Progress">PROGRESO</sys:String>
<sys:String x:Key="Dialog.Unlock.Phase1">Fase 1: Enviando señales de desbloqueo</sys:String>
<sys:String x:Key="Dialog.Unlock.Phase2Testing">Fase 2: Probando</sys:String>
<sys:String x:Key="Dialog.Unlock.Phase2Sending">Fase 2: Enviando...</sys:String>
<sys:String x:Key="Dialog.Unlock.Cancelled">CANCELADO</sys:String>
<sys:String x:Key="Dialog.Unlock.Unlocked">DESBLOQUEADO</sys:String>
<sys:String x:Key="Dialog.Unlock.Failed">DESBLOQUEO FALLIDO</sys:String>
<sys:String x:Key="Dialog.Unlock.TypeLabel">Tipo {0}</sys:String>
<!-- ── Dialog: K-Line errors ────────────────────────────────────────── -->
<sys:String x:Key="Dialog.KlineErrors.Title">Códigos de Falla K-Line</sys:String>
<sys:String x:Key="Dialog.KlineErrors.Header">Códigos de falla:</sys:String>
<sys:String x:Key="Dialog.KlineErrors.Read">Leer</sys:String>
<sys:String x:Key="Dialog.KlineErrors.Clear">Borrar</sys:String>
<!-- ── Dialog: Settings ───────────────────────────────────────────────── -->
<sys:String x:Key="Dialog.Settings.Title">Configuración</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.General">General</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.Safety">Seguridad</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.Pid">PID</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.Motor">Motor</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.Company">Empresa</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.KLine">K-Line</sys:String>
<sys:String x:Key="Dialog.Settings.Tab.Advanced">Avanzado</sys:String>
<sys:String x:Key="Dialog.Settings.Language">Idioma:</sys:String>
<sys:String x:Key="Dialog.Settings.DaysKeepLogs">Días de retención de logs:</sys:String>
<sys:String x:Key="Dialog.Settings.TempMax">Temperatura máx. (°C):</sys:String>
<sys:String x:Key="Dialog.Settings.TempMin">Temperatura mín. (°C):</sys:String>
<sys:String x:Key="Dialog.Settings.SecurityRpmLimit">Límite RPM de seguridad:</sys:String>
<sys:String x:Key="Dialog.Settings.MaxPressureBar">Presión máx. (bar):</sys:String>
<sys:String x:Key="Dialog.Settings.ToleranceUp">Extensión tolerancia UP:</sys:String>
<sys:String x:Key="Dialog.Settings.TolerancePfp">Extensión tolerancia PFP:</sys:String>
<sys:String x:Key="Dialog.Settings.IgnoreTin">Ignorar T-in por defecto</sys:String>
<sys:String x:Key="Dialog.Settings.PidP">Proporcional (P):</sys:String>
<sys:String x:Key="Dialog.Settings.PidI">Integral (I):</sys:String>
<sys:String x:Key="Dialog.Settings.PidD">Derivativo (D):</sys:String>
<sys:String x:Key="Dialog.Settings.PidLoopMs">Período del lazo (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.EncoderRes">Resolución encoder:</sys:String>
<sys:String x:Key="Dialog.Settings.VoltMaxRpm">Voltaje para RPM máx. (V):</sys:String>
<sys:String x:Key="Dialog.Settings.MaxRpm">RPM máximo:</sys:String>
<sys:String x:Key="Dialog.Settings.RightRelay">Derecha = relé ON</sys:String>
<sys:String x:Key="Dialog.Settings.Relations">Tabla RPM-Voltaje</sys:String>
<sys:String x:Key="Dialog.Settings.RelRpm">RPM</sys:String>
<sys:String x:Key="Dialog.Settings.RelVoltage">Voltaje (V)</sys:String>
<sys:String x:Key="Dialog.Settings.AddRow">Agregar</sys:String>
<sys:String x:Key="Dialog.Settings.RemoveRow">Eliminar</sys:String>
<sys:String x:Key="Dialog.Settings.CompanyName">Nombre empresa:</sys:String>
<sys:String x:Key="Dialog.Settings.CompanyInfo">Información empresa:</sys:String>
<sys:String x:Key="Dialog.Settings.ReportLogo">Ruta logo de informe:</sys:String>
<sys:String x:Key="Dialog.Settings.BrowseLogoTitle">Seleccionar imagen de logo</sys:String>
<sys:String x:Key="Dialog.Settings.KLinePort">Puerto K-Line (FTDI):</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshPorts">Refrescar</sys:String>
<sys:String x:Key="Dialog.Settings.KLineHint">Seleccione un dispositivo FTDI o ingrese el número de serie manualmente.</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshBench">Interfaz banco (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshReading">Durante lectura K-Line (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshCanBus">Lectura CAN bus (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshPumpReq">Solicitud bomba (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.RefreshPumpParams">Parámetros bomba (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.BlinkInterval">Intervalo parpadeo (ms):</sys:String>
<sys:String x:Key="Dialog.Settings.FlasherInterval">Intervalo flasher (ms):</sys:String>
<!-- ── Error messages ───────────────────────────────────────────────── -->
<sys:String x:Key="Error.ReportGeneration">Error al generar informe:\n{0}</sys:String>
<sys:String x:Key="Error.ReportTitle">Error de Informe</sys:String>
<sys:String x:Key="Error.PsgSync">No se detectó el pulso de sincronización PSG. Verifique la conexión del encoder.</sys:String>
<sys:String x:Key="Error.PsgTitle">Error PSG</sys:String>
<sys:String x:Key="Error.EmergencyStop">PARADA DE EMERGENCIA: {0}</sys:String>
<sys:String x:Key="Error.KLineNotFound">Dispositivo K-Line no encontrado. Verifique que el adaptador FTDI esté conectado.</sys:String>
<sys:String x:Key="Error.KLineTitle">Error K-Line</sys:String>
<sys:String x:Key="Error.AuthInvalid">Usuario o contraseña inválidos.\n(Ambos distinguen mayúsculas y minúsculas.)</sys:String>
<sys:String x:Key="Error.AuthTitle">Error de Autenticación</sys:String>
<!-- ── PDF Report ───────────────────────────────────────────────────── -->
<sys:String x:Key="Pdf.ReportTitle">INFORME DE PRUEBA DE BOMBA INYECTORA VP44</sys:String>
<sys:String x:Key="Pdf.GeneratedBy">Generado por HC-APTBS</sys:String>
<sys:String x:Key="Pdf.Page">Página </sys:String>
<sys:String x:Key="Pdf.Of"> de </sys:String>
<sys:String x:Key="Pdf.Date">Fecha: {0:dd/MM/yyyy HH:mm}</sys:String>
<sys:String x:Key="Pdf.Operator">Operador: {0}</sys:String>
<sys:String x:Key="Pdf.Client">Cliente: {0}</sys:String>
<sys:String x:Key="Pdf.PumpIdentification">IDENTIFICACIÓN DE BOMBA</sys:String>
<sys:String x:Key="Pdf.PumpId">ID Bomba:</sys:String>
<sys:String x:Key="Pdf.Model">Modelo:</sys:String>
<sys:String x:Key="Pdf.SerialNo">Nro. Serie:</sys:String>
<sys:String x:Key="Pdf.Injector">Inyector:</sys:String>
<sys:String x:Key="Pdf.Tube">Tubo:</sys:String>
<sys:String x:Key="Pdf.Valve">Válvula:</sys:String>
<sys:String x:Key="Pdf.Tension">Tensión:</sys:String>
<sys:String x:Key="Pdf.Rotation">Rotación:</sys:String>
<sys:String x:Key="Pdf.LockAngle">Ángulo de Bloqueo:</sys:String>
<sys:String x:Key="Pdf.Measured">Medido:</sys:String>
<sys:String x:Key="Pdf.Chaveta">Chaveta:</sys:String>
<sys:String x:Key="Pdf.PreInj">Pre-Iny.:</sys:String>
<sys:String x:Key="Pdf.EcuData">DATOS ECU (K-Line)</sys:String>
<sys:String x:Key="Pdf.OverallResult">RESULTADO GENERAL DEL TEST</sys:String>
<sys:String x:Key="Pdf.TestsExecuted">Tests ejecutados: {0} de {1}</sys:String>
<sys:String x:Key="Pdf.ParamsEvaluated">Parámetros evaluados: {0} / {1} aprobados</sys:String>
<sys:String x:Key="Pdf.TestHeader">TEST: {0}</sys:String>
<sys:String x:Key="Pdf.Phase">Fase</sys:String>
<sys:String x:Key="Pdf.Parameter">Parámetro</sys:String>
<sys:String x:Key="Pdf.Target">Objetivo</sys:String>
<sys:String x:Key="Pdf.ToleranceHeader">Tolerancia ±</sys:String>
<sys:String x:Key="Pdf.Average">Promedio</sys:String>
<sys:String x:Key="Pdf.Result">Resultado</sys:String>
<sys:String x:Key="Pdf.ErrorBits"> ⚠ Bits de error: {0}</sys:String>
<sys:String x:Key="Pdf.NoSampleData">No hay datos de muestra disponibles para visualización gráfica.</sys:String>
<sys:String x:Key="Pdf.ChartSamples">Muestras: {0} | Objetivo: {1} ± {2} | Promedio: {3} | Resultado: {4}</sys:String>
</ResourceDictionary>