mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-15 19:10:47 +02:00
fix(i18n): wrap hard-coded English strings in t() across UI (#6566)
## Summary
Audit + bulk fix of hard-coded English UI strings - `aria-label`,
`title`, `placeholder`, `label`, and raw JSX literals that bypassed i18n
entirely. Each literal now goes through `t("key", "English Default")`
from `react-i18next`, and every new key has a corresponding entry in
`en-GB/translation.toml` so translators can pick it up.
## What this fixes
Strings were rendered untranslated in every non-EN locale because they
never went through `t()` at all (not just "value not translated yet").
Affects screen-reader labels, tooltips, form placeholders, empty/loading
states, plan card content, and the entire workflow ParticipantView.
## Coverage (~143 keys / 50 files)
- **Viewer chrome** - search bar (close, clear, prev/next, "of N"
results), link/signature/redaction actions, viewer error state, zoom
labels
- **Page editor** - undo/redo/rotate/delete toolbar tooltips, empty
state, bulk selection operator chip tooltips
- **Shared primitives** - Tooltip close, InfoBanner dismiss, TextInput
clear, Toast dismiss/toggle, UpdateModal close, EditableSecretField
edit, DropdownListWithFooter search, FileCard/FileDropdownMenu actions,
EmptyFilesState + AddFileCard upload
- **Tools** - Image upload + hint, ColorControl eyedropper, sign Use
Signature, CompressSettings, OCR loading, PageLayout
margin/border/row/col placeholders, FormFill switch + save + re-scan
- **Proprietary admin** - OverviewHeader signed-in line + logout,
AdminPremiumSection moved-features list (via `<Trans>`),
AdminPlanSection no-data alert, AdminAdvancedSection temp-dir
placeholders, AdminEndpointsSection multiselect placeholders,
AdminMailSection + AdminDatabaseSection password placeholders
- **Onboarding** - MFASetupSlide QR loading + auth code label,
SecurityCheckSlide role select + options
- **ParticipantView** - entire sign-document UI (~30 strings: loading,
error, badges, headings, cert-type Select, all input labels and
placeholders, action buttons, completion + expired alerts) - file
previously imported `useTranslation` but only used `t()` for cert
validation
- **planConstants.ts refactor** - replaced `PLAN_FEATURES` /
`PLAN_HIGHLIGHTS` const exports with `usePlanFeatures()` /
`usePlanHighlights()` hooks. Service layer (`licenseService.getPlans`)
updated to accept feature/highlight maps so it stays hook-free. Callers
(`usePlans`, `CheckoutContext`) resolve the hooks at the React boundary
- **Previously catalogued offenders** - `FileSidebarFileItem`
open/close-viewer aria-labels, `quickAccessBar/ActiveToolButton` "Back
to all tools" tooltip + aria, `AppConfigModal` close button
## Notes
- One small refactor in `usePageSelectionTips.ts` was needed to resolve
a TOML key-shape conflict: the existing scalar keys
`bulkSelection.operators.{and,not,comma}` needed to become tables to
hold the new `.title` subkeys for OperatorsSection's chip tooltips. The
existing descriptions moved to `[bulkSelection.operators.descriptions]`
and the three i18n key paths in usePageSelectionTips were updated to
match.
- Viewer sidebar close buttons
(Bookmark/Layer/Thumbnail/Attachment/Comments) were on the audit list
but are NOT on main - they're added by the unmerged PR #6552
(feat/viewer-sidebar-ux). Those particular strings will need wrapping
when that PR lands.
- TOML hook (`toml-sort-fix`) ran and re-sorted the translation file.
## Test plan
- [ ] `task frontend:typecheck` passes (core + proprietary + desktop
variants)
- [ ] `task frontend:lint` passes
- [ ] Switching language to Deutsch / Русский: previously-English
`aria-label`s + tooltips + placeholders + plan card bullets now render
translated (when the locale has values) or fall back to the English
default (when it doesn't)
- [ ] Plan page bullet points in EN render unchanged
- [ ] Sign-document flow (ParticipantView) renders unchanged in EN
This commit is contained in:
@@ -39,13 +39,15 @@ export function OverviewHeader() {
|
||||
</Text>
|
||||
{user?.email && (
|
||||
<Text size="xs" c="dimmed" mt="0.25rem">
|
||||
Signed in as: {user.email}
|
||||
{t("account.overview.signedInAs", "Signed in as: {{email}}", {
|
||||
email: user.email,
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
{user && (
|
||||
<Button color="red" variant="filled" onClick={handleLogout}>
|
||||
Log out
|
||||
{t("account.overview.logOut", "Log out")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+12
-3
@@ -809,7 +809,10 @@ export default function AdminAdvancedSection() {
|
||||
},
|
||||
})
|
||||
}
|
||||
placeholder="Default: java.io.tmpdir/stirling-pdf"
|
||||
placeholder={t(
|
||||
"admin.settings.advanced.tempFileManagement.baseTmpDir.placeholder",
|
||||
"Default: java.io.tmpdir/stirling-pdf",
|
||||
)}
|
||||
disabled={!loginEnabled}
|
||||
/>
|
||||
</div>
|
||||
@@ -834,7 +837,10 @@ export default function AdminAdvancedSection() {
|
||||
},
|
||||
})
|
||||
}
|
||||
placeholder="Default: baseTmpDir/libreoffice"
|
||||
placeholder={t(
|
||||
"admin.settings.advanced.tempFileManagement.libreofficeDir.placeholder",
|
||||
"Default: baseTmpDir/libreoffice",
|
||||
)}
|
||||
disabled={!loginEnabled}
|
||||
/>
|
||||
</div>
|
||||
@@ -859,7 +865,10 @@ export default function AdminAdvancedSection() {
|
||||
},
|
||||
})
|
||||
}
|
||||
placeholder="System temp directory path"
|
||||
placeholder={t(
|
||||
"admin.settings.advanced.tempFileManagement.systemTempDir.placeholder",
|
||||
"System temp directory path",
|
||||
)}
|
||||
disabled={!loginEnabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+4
-1
@@ -698,7 +698,10 @@ export default function AdminDatabaseSection() {
|
||||
onChange={(value) =>
|
||||
setSettings({ ...settings, password: value })
|
||||
}
|
||||
placeholder="Enter database password"
|
||||
placeholder={t(
|
||||
"admin.settings.database.password.placeholder",
|
||||
"Enter database password",
|
||||
)}
|
||||
disabled={!loginEnabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+8
-2
@@ -296,7 +296,10 @@ export default function AdminEndpointsSection() {
|
||||
}))}
|
||||
searchable
|
||||
clearable
|
||||
placeholder="Select endpoints to disable"
|
||||
placeholder={t(
|
||||
"admin.settings.endpoints.toRemove.placeholder",
|
||||
"Select endpoints to disable",
|
||||
)}
|
||||
comboboxProps={{ zIndex: 1400 }}
|
||||
disabled={!loginEnabled}
|
||||
/>
|
||||
@@ -330,7 +333,10 @@ export default function AdminEndpointsSection() {
|
||||
}))}
|
||||
searchable
|
||||
clearable
|
||||
placeholder="Select groups to disable"
|
||||
placeholder={t(
|
||||
"admin.settings.endpoints.groupsToRemove.placeholder",
|
||||
"Select groups to disable",
|
||||
)}
|
||||
comboboxProps={{ zIndex: 1400 }}
|
||||
disabled={!loginEnabled}
|
||||
/>
|
||||
|
||||
+4
-1
@@ -286,7 +286,10 @@ export default function AdminMailSection() {
|
||||
onChange={(value) =>
|
||||
setSettings({ ...settings, password: value })
|
||||
}
|
||||
placeholder="Enter SMTP password"
|
||||
placeholder={t(
|
||||
"admin.settings.mail.password.placeholder",
|
||||
"Enter SMTP password",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
+8
-2
@@ -183,8 +183,14 @@ const AdminPlanSection: React.FC = () => {
|
||||
|
||||
if (!plans || plans.length === 0) {
|
||||
return (
|
||||
<Alert color="yellow" title="No data available">
|
||||
Plans data is not available at the moment.
|
||||
<Alert
|
||||
color="yellow"
|
||||
title={t("admin.settings.plan.noData.title", "No data available")}
|
||||
>
|
||||
{t(
|
||||
"admin.settings.plan.noData.message",
|
||||
"Plans data is not available at the moment.",
|
||||
)}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
+16
-4
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import {
|
||||
TextInput,
|
||||
Switch,
|
||||
@@ -132,17 +132,29 @@ export default function AdminPremiumSection() {
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text size="sm" component="span">
|
||||
<strong>Custom Metadata</strong> (PRO) - General
|
||||
<Trans
|
||||
i18nKey="admin.settings.premium.movedFeatures.customMetadata"
|
||||
defaults="<0>Custom Metadata</0> (PRO) - General"
|
||||
components={[<strong />]}
|
||||
/>
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text size="sm" component="span">
|
||||
<strong>Audit Logging</strong> (ENTERPRISE) - Security
|
||||
<Trans
|
||||
i18nKey="admin.settings.premium.movedFeatures.auditLogging"
|
||||
defaults="<0>Audit Logging</0> (ENTERPRISE) - Security"
|
||||
components={[<strong />]}
|
||||
/>
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text size="sm" component="span">
|
||||
<strong>Database Configuration</strong> (ENTERPRISE) - Database
|
||||
<Trans
|
||||
i18nKey="admin.settings.premium.movedFeatures.databaseConfiguration"
|
||||
defaults="<0>Database Configuration</0> (ENTERPRISE) - Database"
|
||||
components={[<strong />]}
|
||||
/>
|
||||
</Text>
|
||||
</List.Item>
|
||||
</List>
|
||||
|
||||
+12
-7
@@ -11,7 +11,10 @@ import {
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { alert } from "@app/components/toast";
|
||||
import { LicenseInfo, mapLicenseToTier } from "@app/services/licenseService";
|
||||
import { PLAN_FEATURES, PLAN_HIGHLIGHTS } from "@app/constants/planConstants";
|
||||
import {
|
||||
usePlanFeatures,
|
||||
usePlanHighlights,
|
||||
} from "@app/constants/planConstants";
|
||||
import FeatureComparisonTable from "@app/components/shared/config/configSections/plan/FeatureComparisonTable";
|
||||
import StaticCheckoutModal from "@app/components/shared/config/configSections/plan/StaticCheckoutModal";
|
||||
import LicenseKeySection from "@app/components/shared/config/configSections/plan/LicenseKeySection";
|
||||
@@ -32,6 +35,8 @@ const StaticPlanSection: React.FC<StaticPlanSectionProps> = ({
|
||||
currentLicenseInfo,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const planFeatures = usePlanFeatures();
|
||||
const planHighlights = usePlanHighlights();
|
||||
const [showComparison, setShowComparison] = useState(false);
|
||||
|
||||
// Static checkout modal state
|
||||
@@ -88,8 +93,8 @@ const StaticPlanSection: React.FC<StaticPlanSectionProps> = ({
|
||||
price: 0,
|
||||
currency: "£",
|
||||
period: "",
|
||||
highlights: PLAN_HIGHLIGHTS.FREE,
|
||||
features: PLAN_FEATURES.FREE,
|
||||
highlights: planHighlights.FREE,
|
||||
features: planFeatures.FREE,
|
||||
maxUsers: 5,
|
||||
},
|
||||
{
|
||||
@@ -99,8 +104,8 @@ const StaticPlanSection: React.FC<StaticPlanSectionProps> = ({
|
||||
currency: "",
|
||||
period: "",
|
||||
popular: false,
|
||||
highlights: PLAN_HIGHLIGHTS.SERVER_MONTHLY,
|
||||
features: PLAN_FEATURES.SERVER,
|
||||
highlights: planHighlights.SERVER_MONTHLY,
|
||||
features: planFeatures.SERVER,
|
||||
maxUsers: "Unlimited users",
|
||||
},
|
||||
{
|
||||
@@ -109,8 +114,8 @@ const StaticPlanSection: React.FC<StaticPlanSectionProps> = ({
|
||||
price: 0,
|
||||
currency: "",
|
||||
period: "",
|
||||
highlights: PLAN_HIGHLIGHTS.ENTERPRISE_MONTHLY,
|
||||
features: PLAN_FEATURES.ENTERPRISE,
|
||||
highlights: planHighlights.ENTERPRISE_MONTHLY,
|
||||
features: planFeatures.ENTERPRISE,
|
||||
maxUsers: "Custom",
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user