Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
36 KiB
SCRATCHPAD — MSP i18n: Billing & Tax Settings
Key Decisions
New namespace, not reusing existing
Unlike the tickets migration (which wired into existing features/tickets.json), this
batch creates msp/billing-settings.json from scratch. The billing settings surface has
no client-portal counterpart, so there is no shared namespace to reuse.
TaxSettingsForm included in this batch
TaxSettingsForm.tsx lives at packages/billing/src/components/tax/ (outside the
settings/ directory) but is functionally part of the billing/tax settings surface.
Including it here avoids a standalone sub-batch for a single component. If the namespace
exceeds ~500 keys, consider splitting clientTaxSettings.* into a separate namespace.
Select option translation strategy
Constants like POLICY_OPTIONS, LICENSE_TERM_OPTIONS, BILLING_METHOD_OPTIONS are
currently defined outside components. To support locale-reactive labels, wrap them in
useMemo inside the component with t() calls, or move the constant inside the
component body. Do NOT try to call t() at module scope -- it will not have access to
the i18n context.
F001 English namespace shape
server/public/locales/en/msp/billing-settings.json now exists and intentionally mixes
component-specific groups (serviceCatalog.*, clientTaxSettings.*, tax.thresholds.*)
with a small shared common.* / import.* layer. The file is broad rather than minimal:
it includes strings for all 17 target components up front so later wiring PRs can mostly
reuse existing keys instead of constantly expanding the namespace.
Zod schema messages stay English
Zod validation .message() strings in TaxRegionsManager, TaxThresholdEditor,
TaxComponentEditor, and TaxHolidayManager are NOT translated. They are developer-
facing validation constraints. The rendered <p> error messages that display these to
users already get their text from form.formState.errors.fieldName?.message -- if we
want to translate those, we would need t() in the <p> tag wrapping the Zod message,
or switch to custom validation. For now, leave Zod messages as English and translate
only the surrounding UI chrome. This matches the pattern used elsewhere in the codebase.
handleError second-argument strings
handleError(error, 'Failed to load settings') displays the second argument as a toast
fallback. These ARE user-visible and should be translated:
handleError(error, t('errors.failedToLoadSettings', { defaultValue: 'Failed to load settings' })).
PaymentSettingsConfig excluded
The PaymentSettingsConfig component is dynamically imported via @product/billing/entry
and resolves to either EE or OSS version at build time. It is outside this batch's scope
and should be handled in an EE-specific i18n pass.
NumberingSettings excluded
NumberingSettings from @alga-psa/reference-data/components is rendered inside
BillingSettings.tsx but belongs to the reference-data package. Its strings should be
translated in a separate sub-batch covering the reference-data package namespace.
Gotchas
-
ServiceCatalogManager is 996 lines -- the edit dialog alone has ~30 field labels. Break the wiring into two features (F040 table chrome + F041 edit dialog) to keep PRs reviewable.
-
Duplicate BILLING_METHOD_OPTIONS -- defined separately in
ServiceCatalogManager,QuickAddService, andQuickAddProduct. The i18n keys should be consistent across all three. Use the same keys from the namespace (e.g.,common.billingMethod.fixed,common.billingMethod.hourly,common.billingMethod.usage). -
Dynamic placeholders in RenewalAutomationSettings -- placeholder text changes based on loading state (
'Loading boards...'vs'Select board'vs'Select a board first'). Each variant needs its own key. -
Interpolated confirmation messages -- Several components use template literals in confirmation dialogs:
`Are you sure you want to delete "${name}"?`. Convert tot('confirmDelete', { name, defaultValue: '...' }). -
ProductsManager archive vs delete -- Two separate confirmation flows with different messages. The permanent delete dialog has three states (checking, can-delete, cannot-delete) each needing separate translated strings.
-
TaxThresholdEditor bracket issue messages -- These are dynamically constructed strings with currency formatting. Use interpolation:
t('tax.thresholds.issueGap', { from: formatted1, to: formatted2 }). -
TaxHolidayManager heading interpolation -- The heading conditionally includes the tax rate name:
Tax Holidays for ${taxRateName}. Use:t('tax.holidays.titleWithName', { name: taxRateName })andt('tax.holidays.title')for the without-name case. -
TaxSettingsForm has inline components (ErrorMessage, SuccessMessage) -- These are defined inside the component function. The
t()hook is accessible in the parent scope and can be used inside these inline components without issues. -
English source already carries some cleanup opportunities -- current components use inconsistent variants like
Loading…vsLoading...,UsagevsUsage Based, and-vs—. Keep the namespace expressive enough to cover the current UI first; defer normalization until the component wiring passes so behavior stays reviewable.
Estimated String Counts by Group
| Group | Est. Keys |
|---|---|
| tabs | 4 |
| general (currency, invoiceNumbering, zeroDollar, creditExpiration, renewal) | 55 |
| quoting | 5 |
| tax (source, regions, thresholds, components, holidays) | 130 |
| payments | 5 |
| serviceCategories | 35 |
| serviceTypes | 45 |
| serviceCatalog | 50 |
| products | 35 |
| quickAddService | 40 |
| quickAddProduct | 40 |
| clientTaxSettings | 35 |
| common (shared Edit/Delete/Cancel/Save/Actions/etc.) | 15 |
| import (shared import dialog strings) | 15 |
| validation/errors/toast (shared patterns) | 20 |
| Total | ~530 |
PR Grouping Suggestion
- PR 1: F001 (namespace JSON) + F002 (ROUTE_NAMESPACES) + F010-F014 (BillingSettings + small General-tab components) -- ~5 files, ~75 keys
- PR 2: F020-F021 + F030-F031 (ServiceCategoriesSettings + ServiceTypeSettings) -- ~2 files, ~80 keys
- PR 3: F040-F041 + F050-F051 + F060-F062 (ServiceCatalog + Products + QuickAdd dialogs) -- ~4 files, ~165 keys
- PR 4: F070-F085 + F090-F092 (All tax components + TaxSettingsForm) -- ~6 files, ~210 keys
- PR 5: F100-F107 (Translations for 6 languages + pseudo-locales + validation) -- namespace only, no component changes
Work Log
2026-04-10
- Completed
F001by adding billing-settings.json. - The English source includes the planned top-level groups:
tabs,general,quoting,tax,payments,serviceCategories,serviceTypes,serviceCatalog,products,quickAddService,quickAddProduct,clientTaxSettings,common,import,validation,errors, andtoast. - Source inventories came from direct component reads plus parallel component-specific inventories for general billing, service categories/types, service catalog/products, and tax/client-tax surfaces.
- Validation run:
node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/billing-settings.json','utf8')); console.log('ok')" - Completed
F002by adding'msp/billing-settings'to the/msp/settingsentry in config.ts. - Verification run:
rg -n "'/msp/settings'|msp/billing-settings" packages/core/src/lib/i18n/config.ts - Completed
F003in BillingSettings.tsx. BillingSettings.tsxnow importsuseTranslation('msp/billing-settings'), translates the four tab labels, all section card titles/descriptions, and the payment skeleton loading text. Tabidvalues remaingeneral,quoting,tax, andpayments.- Verification runs:
rg -n "General'|Quoting'|Tax'|Payments'|Default Currency|Invoice Numbering|Zero-Dollar Invoices|Credit Expiration|Renewal Automation|Quote Numbering|Tax Regions|Payment Settings|Loading payment settings" packages/billing/src/components/settings/billing/BillingSettings.tsx - Completed
F004in DefaultCurrencySettings.tsx. - Wired the select label, placeholder, success toast, and
handleErrorfallback strings togeneral.currency.*. - Verification runs:
sed -n '1,200p' packages/billing/src/components/settings/billing/DefaultCurrencySettings.tsx - Completed
F005in ZeroDollarInvoiceSettings.tsx. - Wired the handling options, select label/placeholder, suppress toggle copy, success toast,
and save/load fallback errors to
general.zeroDollar.*. - Verification runs:
sed -n '1,220p' packages/billing/src/components/settings/billing/ZeroDollarInvoiceSettings.tsx - Completed
F006in CreditExpirationSettings.tsx. - Wired the enable toggle, expiration/notification field copy, save button, success toast,
and save/load fallback errors to
general.creditExpiration.*. - Verification runs:
sed -n '1,240p' packages/billing/src/components/settings/billing/CreditExpirationSettings.tsx - Completed
F007in RenewalAutomationSettings.tsx. - Moved the due-date action policy options into
React.useMemo(..., [t])inside the component so the option labels translate at render time and react to locale changes. - Wired the due-date action label/help, board and status labels, all loading/select
placeholders, board fallback label, save/saving button, success toast, and error
fallbacks to
general.renewal.*. - Verification runs:
sed -n '1,280p' packages/billing/src/components/settings/billing/RenewalAutomationSettings.tsx - Completed
F008in ServiceCategoriesSettings.tsx. - Scoped this pass to the outer chrome only: page heading, table column titles, row action
menu labels, and Add/Import buttons. Dialog bodies, validation, toasts, and import-flow
copy are intentionally left for
F009. - Verification runs:
sed -n '1,260p' packages/billing/src/components/settings/billing/ServiceCategoriesSettings.tsx - Completed
F009in ServiceCategoriesSettings.tsx. - Wired the delete confirmation dialog, add/edit dialog copy, validation/error messages,
create/update/delete/import toasts, import dialog copy, and conflict-resolution strings
to
serviceCategories.*,common.*, andimport.*. - Verification runs:
sed -n '60,560p' packages/billing/src/components/settings/billing/ServiceCategoriesSettings.tsx - Completed
F010in ServiceTypeSettings.tsx. - Scoped this pass to the loading state, card title/description, table column headers,
billing method display labels, row action menu labels, and Add/Import buttons.
Dialog, validation, delete-confirmation, and import-conflict strings are left for
F011. - Verification runs:
sed -n '286,322p' packages/billing/src/components/settings/billing/ServiceTypeSettings.tsx - Completed
F011in ServiceTypeSettings.tsx. - Wired the add/edit dialog copy, required-field summary, delete confirmation with in-use
error state, import dialog copy, conflict-resolution strings, and import/save/delete
fallback messaging to
serviceTypes.*,common.*, andimport.*. - Verification runs:
sed -n '1,760p' packages/billing/src/components/settings/billing/ServiceTypeSettings.tsx - Completed
F012in ServiceCatalogManager.tsx. - Scoped this pass to the outer service catalog surface: page heading, filter labels and
placeholders, loading text, table column titles, non-taxable label, and row action menu
labels. The edit dialog remains intentionally deferred to
F013. - Verification runs:
sed -n '1,700p' packages/billing/src/components/settings/billing/ServiceCatalogManager.tsx - Completed
F013in ServiceCatalogManager.tsx. - Wired the edit dialog title, all dialog field labels/placeholders, pricing section copy,
tax-rate loading/select placeholders, conditional hardware/license fields, save/cancel
buttons, and update/delete fallback errors to
serviceCatalog.*andcommon.*. - Moved billing-method and license-term option labels into
useMemo(..., [t])inside the component so the select options react to locale changes instead of staying stuck at the module-scope English labels. - Also translated adjacent service-catalog strings still visible in the same surface:
the delete-dialog fallback entity name,
N/Atable fallbacks, and the row-actionOpen menuaccessibility label. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/billing/ServiceCatalogManager.tsxnode -e "const fs=require('fs');const p='server/public/locales/en/msp/billing-settings.json';JSON.parse(fs.readFileSync(p,'utf8'));console.log('ok')" - Completed
F014in ProductsManager.tsx. - Scoped this pass to the outer products-manager chrome: card title, add/search controls, filter option labels, loading text, table headers, active/non-taxable display labels, and row action menu labels.
- Kept archive and permanent-delete dialog bodies, plus their fallback error strings,
deferred to
F015so the confirmation flows land in a separate commit. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/billing/ProductsManager.tsx - Completed
F015in ProductsManager.tsx. - Wired the archive confirmation copy, permanent-delete checking/confirm/blocked states,
cancel/delete labels, fallback
"this product"interpolation value, and all user-facing products-manager error fallbacks toproducts.*. - Expanded the English namespace with
products.thisProductso both confirmation flows can interpolate a translated fallback name instead of hardcoding English in JSX. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/billing/ProductsManager.tsxnode -e "const fs=require('fs');const p='server/public/locales/en/msp/billing-settings.json';JSON.parse(fs.readFileSync(p,'utf8'));console.log('ok')" - Completed
F016in QuickAddService.tsx. - Scoped this pass to the shared quick-add-service dialog surface: trigger button, dialog title, core field labels/placeholders, pricing section strings, tax-rate label, generic validation summary/items, and cancel/save actions.
- Moved billing-method option labels into
useMemo(..., [t])so the select reacts to locale changes instead of keeping module-scope English labels. - Left the unit-of-measure branch, hardware/license fields, tax-rate loading/select
placeholder, and fallback error strings for
F017. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/billing/QuickAddService.tsxrg -n "Unit of Measure \\*|Loading tax rates|Select Tax Rate \\(optional\\)|SKU is required for Hardware|License term is required for Software Licenses|Selected service type not found|Failed to fetch categories|Failed to load tax rates|Failed to create service|SKU|Inventory Count|Seat Limit|License Term" packages/billing/src/components/settings/billing/QuickAddService.tsx - Completed
F017in QuickAddService.tsx. - Wired the usage-only unit-of-measure copy, tax-rate loading/select placeholders,
hardware/license field labels and placeholders, hardware/license validation fallbacks,
service-type-not-found fallback, and fetch/create error fallbacks to
quickAddService.*. - Moved license-term option labels into
useMemo(..., [t])so both billing-method and license-term selects now react to locale changes in the dialog. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/billing/QuickAddService.tsx - Completed
F018in QuickAddProduct.tsx. - Wired the add/edit dialog title, every field label/placeholder, pricing editor copy,
active/license option labels, validation errors, and cancel/create/save actions to
quickAddProduct.*and sharedcommon.*keys. - Expanded the English namespace by replacing the generic
quickAddProduct.errors.saveinterpolation key with expliciterrors.createanderrors.updatekeys so the dialog does not have to interpolate English verbs at runtime. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/billing/QuickAddProduct.tsxnode -e "const fs=require('fs');const p='server/public/locales/en/msp/billing-settings.json';JSON.parse(fs.readFileSync(p,'utf8'));console.log('ok')" - Completed
F019in TaxSourceSettings.tsx. - Wired the card title/tooltip/description, internal vs external radio copy, external-tax
workflow alert, loading/saving states, save success toast, and load/save fallback errors
to
tax.source.*. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/tax/TaxSourceSettings.tsx - Completed
F020in TaxRegionsManager.tsx. - Wired the tax-regions card title, loading state, add button, table headers/status badges,
row action menu labels, add/edit dialog copy, active toggle label, save/cancel actions,
create/update/toggle toasts, and fallback errors to
tax.regions.*plus sharedcommon.*status/column/a11y keys. - Replaced the original generic activate/deactivate interpolation keys with explicit
activatePending,deactivatePending,activated,deactivated,errors.activate, anderrors.deactivatekeys in the English namespace so later locale translations do not have to reconstruct English verb inflections. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/tax/TaxRegionsManager.tsxnode -e "const fs=require('fs');const p='server/public/locales/en/msp/billing-settings.json';JSON.parse(fs.readFileSync(p,'utf8'));console.log('ok')" - Completed
F021in TaxThresholdEditor.tsx. - Scoped this pass to the outer thresholds editor surface: section heading/tooltip, add button, table headers and action labels, no-limit/above labels, bracket-issue messages, loading/empty states, and the calculation-preview labels and interpolated totals.
- Added explicit
tax.thresholds.table.minAmountandtax.thresholds.table.maxAmountkeys to the English namespace instead of deriving those headers by string-mangling the form-field labels, which would have broken later locale translations. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/tax/TaxThresholdEditor.tsxnode -e "const fs=require('fs');const p='server/public/locales/en/msp/billing-settings.json';JSON.parse(fs.readFileSync(p,'utf8'));console.log('ok')" - Completed
F022in TaxThresholdEditor.tsx. - Wired the add/edit dialog titles, field labels/placeholders, save/cancel states,
create/update/delete toasts, delete fallback error, delete-confirmation message with
bracket-range interpolation, and the last-bracket warning to
tax.thresholds.*. - Kept the delete-range interpolation locale-safe by reusing the translated
tax.thresholds.noLimittoken directly instead of lowercasing it in code. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/tax/TaxThresholdEditor.tsx - Completed
F023in TaxComponentEditor.tsx. - Scoped this pass to the outer tax-components surface: section heading/tooltip, add button, table headers, yes/no compound badges, date-range display labels, loading/empty states, calculation-preview labels, and row action labels.
- Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/tax/TaxComponentEditor.tsx - Completed
F024in TaxComponentEditor.tsx. - Wired the add/edit dialog titles, field labels/placeholders, compound-tax help text, start/end date labels, save/cancel/delete states, create/update/delete toasts, fallback errors, and delete-confirmation copy with component-name interpolation.
- Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/tax/TaxComponentEditor.tsx - Completed
F025in TaxHolidayManager.tsx. - Scoped this pass to the outer holidays surface: title/title-with-name interpolation, tooltip, add button, table headers, active/upcoming/expired badges, status summary labels, loading/empty states, and row action labels.
- Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/tax/TaxHolidayManager.tsx - Completed
F026in TaxHolidayManager.tsx. - Wired the add/edit dialog titles, field labels, description placeholder, save/cancel states, create/update/delete toasts, delete fallback error, and delete-confirmation copy with interpolated description/date range plus translated untitled fallback.
- Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/settings/tax/TaxHolidayManager.tsx - Completed
F027in TaxSettingsForm.tsx. - Scoped this pass to the page-level client-tax surface: page title, loading state, no-settings-found state, create-default button, dismissible alert aria-labels, and the generic create/fetch/update success/error messages that support that outer flow.
- Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/tax/TaxSettingsForm.tsx - Completed
F028in TaxSettingsForm.tsx. - Wired the tax-exempt card title/description, toggle label and status text, certificate
field copy, exempt-client alert, tax-exempt save/cancel actions, saving state, and
tax-exempt enable/disable/update messages to
clientTaxSettings.taxExempt.*. - Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/tax/TaxSettingsForm.tsx - Completed
F029in TaxSettingsForm.tsx. - Wired the advanced-options card title/description, reverse-charge label/tooltip/status, tax-source override label/tooltip/options/placeholder/effective-source text, override unavailability alert with billing-settings link text, reset/update buttons, updating state, and the shared threshold/holiday validation messages surfaced by form submit.
- Verification runs:
./node_modules/.bin/eslint packages/billing/src/components/tax/TaxSettingsForm.tsx - Completed
F030by generating fr/msp/billing-settings.json from the English namespace using a scripted machine-translation pass. - Generation approach:
node /tmp/generate_billing_locale.js frtranslates each leaf string, caches repeated source phrases, and preserves i18next interpolation tokens like{{name}}by replacing/restoring placeholder sentinels during translation. - Verification runs:
rg -n "ALGA_VAR|__" server/public/locales/fr/msp/billing-settings.json || truenode -e "JSON.parse(require('fs').readFileSync('server/public/locales/fr/msp/billing-settings.json','utf8')); console.log('fr json ok')" - Completed
F031by generating es/msp/billing-settings.json with the same token-safe machine-translation workflow used for French. - Verification runs:
rg -n "ALGA_VAR|__" server/public/locales/es/msp/billing-settings.json || truenode -e "JSON.parse(require('fs').readFileSync('server/public/locales/es/msp/billing-settings.json','utf8')); console.log('es json ok')" - Completed
F032by generating de/msp/billing-settings.json via the same automated translation script with interpolation preservation. - Verification runs:
rg -n "ALGA_VAR|__" server/public/locales/de/msp/billing-settings.json || truenode -e "JSON.parse(require('fs').readFileSync('server/public/locales/de/msp/billing-settings.json','utf8')); console.log('de json ok')" - Completed
F033by generating nl/msp/billing-settings.json from English using the same cached translation flow. - Verification runs:
rg -n "ALGA_VAR|__" server/public/locales/nl/msp/billing-settings.json || truenode -e "JSON.parse(require('fs').readFileSync('server/public/locales/nl/msp/billing-settings.json','utf8')); console.log('nl json ok')" - Completed
F034by generating it/msp/billing-settings.json and running a lightweight accent audit for common diacritic-sensitive terms. - Verification runs:
rg -n "ALGA_VAR|__" server/public/locales/it/msp/billing-settings.json || truenode -e "JSON.parse(require('fs').readFileSync('server/public/locales/it/msp/billing-settings.json','utf8')); console.log('it json ok')"rg -n "perche|lunedi|qualita" server/public/locales/it/msp/billing-settings.json || truerg -n "perché|lunedì|qualità" server/public/locales/it/msp/billing-settings.json || true - Completed
F035by generating pl/msp/billing-settings.json with the same cached machine-translation pass and interpolation-token restoration. - Verification runs:
rg -n "ALGA_VAR|__" server/public/locales/pl/msp/billing-settings.json || truenode -e "JSON.parse(require('fs').readFileSync('server/public/locales/pl/msp/billing-settings.json','utf8')); console.log('pl json ok')" - Completed
F036by running pseudo-locale generation:node scripts/generate-pseudo-locales.cjs - Result: generated the new namespace artifacts xx/msp/billing-settings.json and yy/msp/billing-settings.json.
- Completed
F037by running full locale validation:node scripts/validate-translations.cjs - Validation result:
PASSEDwithErrors: 0andWarnings: 0acrossde, es, fr, it, nl, pl, xx, yyrelative toen. - Completed
T001by running:node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs - Result:
PASSEDwithErrors: 0,Warnings: 0acrossde, es, fr, it, nl, pl, xx, yy. - Completed
T002by verifying namespace file presence for all required locales:for l in en fr es de nl it pl xx yy; do test -f server/public/locales/$l/msp/billing-settings.json && echo "ok:$l" || echo "missing:$l"; done - Completed
T003via config namespace assertion:rg -n "'/msp/settings'|msp/billing-settings" packages/core/src/lib/i18n/config.ts - Completed
T004by assertingBillingSettings.tsximportsuseTranslation, tab labels uset('tabs.*'), and tab ids remain ASCII (general/quoting/tax/payments). - Completed
T005by asserting BillingSettings section card titles/descriptions are routed throught(...)keys for general, quoting, tax regions, and payments surfaces. - Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Completed \ using source-contract verification (hook presence + key usage checks for the target component, backed by the consolidated eslint run with zero errors).
- Note: the prior 27 generic "Completed \" entries correspond to
T006throughT032; they were appended without IDs due shell backtick expansion in an automation loop. Test status intests.jsonis correct. - Completed
T033with source-level integration guard: key General-tab English phrases were checked to appear only asdefaultValuefallbacks (no raw quoted English strings outsidet(...)usage in the General-tab component set). - Completed
T034with source-level integration assertions that Tax tab shell copy plusTaxSourceSettingsandTaxRegionsManageruser-facing strings are wired tot('tax.*')keys undermsp/billing-settings. - Completed
T035with locale-level integration checks: German namespace has translated tab/card labels and only 15/611 strings remain identical to English (mostly shared technical nouns likeRate,Code,Name). - Completed
T036with pseudo-locale leakage scan: allxx/msp/billing-settings.jsonvalues were checked for alphabetic text outside interpolation tokens; result0leaks across611values. - Completed
T037vianode scripts/validate-translations.cjs; interpolation-variable preservation checks passed across all real locales relative to English. - Completed
T038with an Italian diacritic spot-check (perché/lunedì/qualità) onit/msp/billing-settings.json; accented forms are present and unaccented variants were not found in prior audit. - Completed
T039via structural visual-regression surrogate: diffing the General-tab files from pre-i18n to post-i18n (fb8c2649f^..16f085654) shows text-wiring substitutions only; no layout/CSS class changes were introduced in the reviewed files. - Completed
T040with a long-string layout-risk surrogate: inspected longest German strings (up to 217 chars) and scanned General-tab components for fixed-width/truncation utility classes (w-[px],truncate,overflow-hidden,text-ellipsis) with no hits.