mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
Chore/v2/translation fixes (#5011)
# Description of Changes <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
@@ -2,7 +2,7 @@ import React, { useMemo, useState, useEffect, useCallback } from 'react';
|
||||
import { Modal, Text, ActionIcon, Tooltip, Group } from '@mantine/core';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import LocalIcon from '@app/components/shared/LocalIcon';
|
||||
import { createConfigNavSections } from '@app/components/shared/config/configNavSections';
|
||||
import { useConfigNavSections } from '@app/components/shared/config/configNavSections';
|
||||
import { NavKey, VALID_NAV_KEYS } from '@app/components/shared/config/types';
|
||||
import { useAppConfig } from '@app/contexts/AppConfigContext';
|
||||
import '@app/components/shared/AppConfigModal.css';
|
||||
@@ -74,13 +74,10 @@ const AppConfigModalInner: React.FC<AppConfigModalProps> = ({ opened, onClose })
|
||||
const loginEnabled = config?.enableLogin ?? false;
|
||||
|
||||
// Left navigation structure and icons
|
||||
const configNavSections = useMemo(() =>
|
||||
createConfigNavSections(
|
||||
isAdmin,
|
||||
runningEE,
|
||||
loginEnabled
|
||||
),
|
||||
[isAdmin, runningEE, loginEnabled]
|
||||
const configNavSections = useConfigNavSections(
|
||||
isAdmin,
|
||||
runningEE,
|
||||
loginEnabled
|
||||
);
|
||||
|
||||
const activeLabel = useMemo(() => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { NavKey } from '@app/components/shared/config/types';
|
||||
import HotkeysSection from '@app/components/shared/config/configSections/HotkeysSection';
|
||||
import GeneralSection from '@app/components/shared/config/configSections/GeneralSection';
|
||||
@@ -27,11 +28,43 @@ export interface ConfigColors {
|
||||
headerBorder: string;
|
||||
}
|
||||
|
||||
export const useConfigNavSections = (
|
||||
_isAdmin: boolean = false,
|
||||
_runningEE: boolean = false,
|
||||
_loginEnabled: boolean = false
|
||||
): ConfigNavSection[] => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sections: ConfigNavSection[] = [
|
||||
{
|
||||
title: t('settings.preferences.title', 'Preferences'),
|
||||
items: [
|
||||
{
|
||||
key: 'general',
|
||||
label: t('settings.general.title', 'General'),
|
||||
icon: 'settings-rounded',
|
||||
component: <GeneralSection />
|
||||
},
|
||||
{
|
||||
key: 'hotkeys',
|
||||
label: t('settings.hotkeys.title', 'Keyboard Shortcuts'),
|
||||
icon: 'keyboard-rounded',
|
||||
component: <HotkeysSection />
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return sections;
|
||||
};
|
||||
|
||||
// Deprecated: Use useConfigNavSections hook instead
|
||||
export const createConfigNavSections = (
|
||||
_isAdmin: boolean = false,
|
||||
_runningEE: boolean = false,
|
||||
_loginEnabled: boolean = false
|
||||
): ConfigNavSection[] => {
|
||||
console.warn('createConfigNavSections is deprecated. Use useConfigNavSections hook instead for proper i18n support.');
|
||||
const sections: ConfigNavSection[] = [
|
||||
{
|
||||
title: 'Preferences',
|
||||
|
||||
@@ -73,10 +73,7 @@ const HotkeysSection: React.FC = () => {
|
||||
const binding = eventToBinding(event as KeyboardEvent);
|
||||
if (!binding) {
|
||||
const osKey = isMac ? 'mac' : 'windows';
|
||||
const fallbackText = isMac
|
||||
? 'Include ⌘ (Command), ⌥ (Option), or another modifier in your shortcut.'
|
||||
: 'Include Ctrl, Alt, or another modifier in your shortcut.';
|
||||
setError(t(`settings.hotkeys.errorModifier.${osKey}`, fallbackText));
|
||||
setError(t(`settings.hotkeys.errorModifier.${osKey}`));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -112,9 +109,9 @@ const HotkeysSection: React.FC = () => {
|
||||
return (
|
||||
<Stack gap="lg">
|
||||
<div>
|
||||
<Text fw={600} size="lg">Keyboard Shortcuts</Text>
|
||||
<Text fw={600} size="lg">{t('settings.hotkeys.title', 'Keyboard Shortcuts')}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
Customize keyboard shortcuts for quick tool access. Click "Change shortcut" and press a new key combination. Press Esc to cancel.
|
||||
{t('settings.hotkeys.description', 'Customize keyboard shortcuts for quick tool access. Click "Change shortcut" and press a new key combination. Press Esc to cancel.')}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Stack, Text, Code, Group, Badge, Alert, Loader } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAppConfig } from '@app/contexts/AppConfigContext';
|
||||
import { OverviewHeader } from '@app/components/shared/config/OverviewHeader';
|
||||
|
||||
const Overview: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { config, loading, error } = useAppConfig();
|
||||
|
||||
const renderConfigSection = (title: string, data: any) => {
|
||||
@@ -58,14 +60,14 @@ const Overview: React.FC = () => {
|
||||
return (
|
||||
<Stack align="center" py="md">
|
||||
<Loader size="sm" />
|
||||
<Text size="sm" c="dimmed">Loading configuration...</Text>
|
||||
<Text size="sm" c="dimmed">{t('config.overview.loading', 'Loading configuration...')}</Text>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Alert color="red" title="Error">
|
||||
<Alert color="red" title={t('config.overview.error', 'Error')}>
|
||||
{error}
|
||||
</Alert>
|
||||
);
|
||||
@@ -77,13 +79,13 @@ const Overview: React.FC = () => {
|
||||
|
||||
{config && (
|
||||
<>
|
||||
{renderConfigSection('Basic Configuration', basicConfig)}
|
||||
{renderConfigSection('Security Configuration', securityConfig)}
|
||||
{renderConfigSection('System Configuration', systemConfig)}
|
||||
{renderConfigSection('Integration Configuration', integrationConfig)}
|
||||
{renderConfigSection(t('config.overview.sections.basic', 'Basic Configuration'), basicConfig)}
|
||||
{renderConfigSection(t('config.overview.sections.security', 'Security Configuration'), securityConfig)}
|
||||
{renderConfigSection(t('config.overview.sections.system', 'System Configuration'), systemConfig)}
|
||||
{renderConfigSection(t('config.overview.sections.integration', 'Integration Configuration'), integrationConfig)}
|
||||
|
||||
{config.error && (
|
||||
<Alert color="yellow" title="Configuration Warning">
|
||||
<Alert color="yellow" title={t('config.overview.warning', 'Configuration Warning')}>
|
||||
{config.error}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
@@ -1,8 +1,39 @@
|
||||
import { createConfigNavSections as createProprietaryConfigNavSections } from '@proprietary/components/shared/config/configNavSections';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useConfigNavSections as useProprietaryConfigNavSections, createConfigNavSections as createProprietaryConfigNavSections } from '@proprietary/components/shared/config/configNavSections';
|
||||
import { ConfigNavSection } from '@core/components/shared/config/configNavSections';
|
||||
import { ConnectionSettings } from '@app/components/ConnectionSettings';
|
||||
|
||||
/**
|
||||
* Hook version of desktop config nav sections with proper i18n support
|
||||
*/
|
||||
export const useConfigNavSections = (
|
||||
isAdmin: boolean = false,
|
||||
runningEE: boolean = false,
|
||||
loginEnabled: boolean = false
|
||||
): ConfigNavSection[] => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Get the proprietary sections (includes core Preferences + admin sections)
|
||||
const sections = useProprietaryConfigNavSections(isAdmin, runningEE, loginEnabled);
|
||||
|
||||
// Add Connection section at the beginning (after Preferences)
|
||||
sections.splice(1, 0, {
|
||||
title: t('settings.connection.title', 'Connection Mode'),
|
||||
items: [
|
||||
{
|
||||
key: 'connectionMode',
|
||||
label: t('settings.connection.title', 'Connection Mode'),
|
||||
icon: 'cloud-rounded',
|
||||
component: <ConnectionSettings />,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return sections;
|
||||
};
|
||||
|
||||
/**
|
||||
* Deprecated: Use useConfigNavSections hook instead
|
||||
* Desktop extension of createConfigNavSections that adds connection settings
|
||||
*/
|
||||
export const createConfigNavSections = (
|
||||
@@ -10,6 +41,8 @@ export const createConfigNavSections = (
|
||||
runningEE: boolean = false,
|
||||
loginEnabled: boolean = false
|
||||
): ConfigNavSection[] => {
|
||||
console.warn('createConfigNavSections is deprecated. Use useConfigNavSections hook instead for proper i18n support.');
|
||||
|
||||
// Get the proprietary sections (includes core Preferences + admin sections)
|
||||
const sections = createProprietaryConfigNavSections(isAdmin, runningEE, loginEnabled);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { createConfigNavSections as createCoreConfigNavSections, ConfigNavSection } from '@core/components/shared/config/configNavSections';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useConfigNavSections as useCoreConfigNavSections, createConfigNavSections as createCoreConfigNavSections, ConfigNavSection } from '@core/components/shared/config/configNavSections';
|
||||
import PeopleSection from '@app/components/shared/config/configSections/PeopleSection';
|
||||
import TeamsSection from '@app/components/shared/config/configSections/TeamsSection';
|
||||
import AdminGeneralSection from '@app/components/shared/config/configSections/AdminGeneralSection';
|
||||
@@ -17,6 +18,196 @@ import AdminUsageSection from '@app/components/shared/config/configSections/Admi
|
||||
import ApiKeys from '@app/components/shared/config/configSections/ApiKeys';
|
||||
|
||||
/**
|
||||
* Hook version of proprietary config nav sections with proper i18n support
|
||||
*/
|
||||
export const useConfigNavSections = (
|
||||
isAdmin: boolean = false,
|
||||
runningEE: boolean = false,
|
||||
loginEnabled: boolean = false
|
||||
): ConfigNavSection[] => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Get the core sections (just Preferences)
|
||||
const sections = useCoreConfigNavSections(isAdmin, runningEE, loginEnabled);
|
||||
|
||||
// Add Admin sections if user is admin OR if login is disabled (but mark as disabled)
|
||||
if (isAdmin || !loginEnabled) {
|
||||
const requiresLogin = !loginEnabled;
|
||||
const enableLoginTooltip = t('settings.tooltips.enableLoginFirst', 'Enable login mode first');
|
||||
const requiresEnterpriseTooltip = t('settings.tooltips.requiresEnterprise', 'Requires Enterprise license');
|
||||
|
||||
// Workspace
|
||||
sections.push({
|
||||
title: t('settings.workspace.title', 'Workspace'),
|
||||
items: [
|
||||
{
|
||||
key: 'people',
|
||||
label: t('settings.workspace.people', 'People'),
|
||||
icon: 'group-rounded',
|
||||
component: <PeopleSection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
{
|
||||
key: 'teams',
|
||||
label: t('settings.workspace.teams', 'Teams'),
|
||||
icon: 'groups-rounded',
|
||||
component: <TeamsSection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Configuration
|
||||
sections.push({
|
||||
title: t('settings.configuration.title', 'Configuration'),
|
||||
items: [
|
||||
{
|
||||
key: 'adminGeneral',
|
||||
label: t('settings.configuration.systemSettings', 'System Settings'),
|
||||
icon: 'settings-rounded',
|
||||
component: <AdminGeneralSection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
{
|
||||
key: 'adminFeatures',
|
||||
label: t('settings.configuration.features', 'Features'),
|
||||
icon: 'extension-rounded',
|
||||
component: <AdminFeaturesSection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
{
|
||||
key: 'adminEndpoints',
|
||||
label: t('settings.configuration.endpoints', 'Endpoints'),
|
||||
icon: 'api-rounded',
|
||||
component: <AdminEndpointsSection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
{
|
||||
key: 'adminDatabase',
|
||||
label: t('settings.configuration.database', 'Database'),
|
||||
icon: 'storage-rounded',
|
||||
component: <AdminDatabaseSection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
{
|
||||
key: 'adminAdvanced',
|
||||
label: t('settings.configuration.advanced', 'Advanced'),
|
||||
icon: 'tune-rounded',
|
||||
component: <AdminAdvancedSection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Security & Authentication
|
||||
sections.push({
|
||||
title: t('settings.securityAuth.title', 'Security & Authentication'),
|
||||
items: [
|
||||
{
|
||||
key: 'adminSecurity',
|
||||
label: t('settings.securityAuth.security', 'Security'),
|
||||
icon: 'shield-rounded',
|
||||
component: <AdminSecuritySection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
{
|
||||
key: 'adminConnections',
|
||||
label: t('settings.securityAuth.connections', 'Connections'),
|
||||
icon: 'link-rounded',
|
||||
component: <AdminConnectionsSection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Licensing & Analytics
|
||||
sections.push({
|
||||
title: t('settings.licensingAnalytics.title', 'Licensing & Analytics'),
|
||||
items: [
|
||||
{
|
||||
key: 'adminPlan',
|
||||
label: t('settings.licensingAnalytics.plan', 'Plan'),
|
||||
icon: 'star-rounded',
|
||||
component: <AdminPlanSection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
{
|
||||
key: 'adminAudit',
|
||||
label: t('settings.licensingAnalytics.audit', 'Audit'),
|
||||
icon: 'fact-check-rounded',
|
||||
component: <AdminAuditSection />,
|
||||
disabled: !runningEE || requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : requiresEnterpriseTooltip
|
||||
},
|
||||
{
|
||||
key: 'adminUsage',
|
||||
label: t('settings.licensingAnalytics.usageAnalytics', 'Usage Analytics'),
|
||||
icon: 'analytics-rounded',
|
||||
component: <AdminUsageSection />,
|
||||
disabled: !runningEE || requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : requiresEnterpriseTooltip
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Policies & Privacy
|
||||
sections.push({
|
||||
title: t('settings.policiesPrivacy.title', 'Policies & Privacy'),
|
||||
items: [
|
||||
{
|
||||
key: 'adminLegal',
|
||||
label: t('settings.policiesPrivacy.legal', 'Legal'),
|
||||
icon: 'gavel-rounded',
|
||||
component: <AdminLegalSection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
{
|
||||
key: 'adminPrivacy',
|
||||
label: t('settings.policiesPrivacy.privacy', 'Privacy'),
|
||||
icon: 'visibility-rounded',
|
||||
component: <AdminPrivacySection />,
|
||||
disabled: requiresLogin,
|
||||
disabledTooltip: requiresLogin ? enableLoginTooltip : undefined
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// Add Developer section if login is enabled
|
||||
if (loginEnabled) {
|
||||
const developerSection: ConfigNavSection = {
|
||||
title: t('settings.developer.title', 'Developer'),
|
||||
items: [
|
||||
{
|
||||
key: 'api-keys',
|
||||
label: t('settings.developer.apiKeys', 'API Keys'),
|
||||
icon: 'key-rounded',
|
||||
component: <ApiKeys />
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Add Developer section after Preferences (or Workspace if it exists)
|
||||
const insertIndex = isAdmin ? 2 : 1;
|
||||
sections.splice(insertIndex, 0, developerSection);
|
||||
}
|
||||
|
||||
return sections;
|
||||
};
|
||||
|
||||
/**
|
||||
* Deprecated: Use useConfigNavSections hook instead
|
||||
* Proprietary extension of createConfigNavSections that adds all admin and workspace sections
|
||||
*/
|
||||
export const createConfigNavSections = (
|
||||
@@ -24,6 +215,8 @@ export const createConfigNavSections = (
|
||||
runningEE: boolean = false,
|
||||
loginEnabled: boolean = false
|
||||
): ConfigNavSection[] => {
|
||||
console.warn('createConfigNavSections is deprecated. Use useConfigNavSections hook instead for proper i18n support.');
|
||||
|
||||
// Get the core sections (just Preferences)
|
||||
const sections = createCoreConfigNavSections(isAdmin, runningEE, loginEnabled);
|
||||
|
||||
|
||||
+3
-3
@@ -336,7 +336,7 @@ export default function AdminGeneralSection() {
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', padding: '0.25rem 0' }}>
|
||||
<img
|
||||
src="/classic-logo/favicon.ico"
|
||||
alt="Classic logo"
|
||||
alt={t('admin.settings.general.logoStyle.classicAlt', 'Classic logo')}
|
||||
style={{ width: '24px', height: '24px' }}
|
||||
/>
|
||||
<span>{t('admin.settings.general.logoStyle.classic', 'Classic')}</span>
|
||||
@@ -349,7 +349,7 @@ export default function AdminGeneralSection() {
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', padding: '0.25rem 0' }}>
|
||||
<img
|
||||
src="/modern-logo/StirlingPDFLogoNoTextLight.svg"
|
||||
alt="Modern logo"
|
||||
alt={t('admin.settings.general.logoStyle.modernAlt', 'Modern logo')}
|
||||
style={{ width: '24px', height: '24px' }}
|
||||
/>
|
||||
<span>{t('admin.settings.general.logoStyle.modern', 'Modern')}</span>
|
||||
@@ -385,7 +385,7 @@ export default function AdminGeneralSection() {
|
||||
]}
|
||||
searchable
|
||||
clearable
|
||||
placeholder="Select languages"
|
||||
placeholder={t('admin.settings.general.languages.placeholder', 'Select languages')}
|
||||
comboboxProps={{ zIndex: 1400 }}
|
||||
disabled={!loginEnabled}
|
||||
/>
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch team details:', error);
|
||||
alert({ alertType: 'error', title: 'Failed to load team details' });
|
||||
alert({ alertType: 'error', title: t('workspace.teams.loadError', 'Failed to load team details') });
|
||||
onBack();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
||||
Reference in New Issue
Block a user