fixed sysuser and removed redundant fallbacks
This commit is contained in:
parent
d1f0b3c3d6
commit
994664f0b4
27 changed files with 140 additions and 177 deletions
|
|
@ -28,20 +28,13 @@ function _getModeOptions(t: (key: string) => string): { value: ScheduleMode; tit
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
const MONTH_NAMES_DE = [
|
function _monthNames(t: (k: string) => string): string[] {
|
||||||
'Januar',
|
return [
|
||||||
'Februar',
|
t('Januar'), t('Februar'), t('März'), t('April'),
|
||||||
'März',
|
t('Mai'), t('Juni'), t('Juli'), t('August'),
|
||||||
'April',
|
t('September'), t('Oktober'), t('November'), t('Dezember'),
|
||||||
'Mai',
|
];
|
||||||
'Juni',
|
}
|
||||||
'Juli',
|
|
||||||
'August',
|
|
||||||
'September',
|
|
||||||
'Oktober',
|
|
||||||
'November',
|
|
||||||
'Dezember',
|
|
||||||
];
|
|
||||||
|
|
||||||
function _getIntervalUnits(t: (key: string) => string): { value: IntervalUnit; label: string; title: string }[] {
|
function _getIntervalUnits(t: (key: string) => string): { value: IntervalUnit; label: string; title: string }[] {
|
||||||
return [
|
return [
|
||||||
|
|
@ -285,7 +278,7 @@ export const ScheduleStartNodeConfig: React.FC<NodeConfigRendererProps> = ({ par
|
||||||
<div className={styles.scheduleFieldCol}>
|
<div className={styles.scheduleFieldCol}>
|
||||||
<span className={styles.scheduleFieldLabel}>{t('Wochentage')}</span>
|
<span className={styles.scheduleFieldLabel}>{t('Wochentage')}</span>
|
||||||
<div className={styles.scheduleWeekdayToggles}>
|
<div className={styles.scheduleWeekdayToggles}>
|
||||||
{WEEKDAYS_MO_SO.map(({ cronDow, label }) => (
|
{WEEKDAYS_MO_SO.map(({ cronDow }) => (
|
||||||
<button
|
<button
|
||||||
key={cronDow}
|
key={cronDow}
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -294,7 +287,7 @@ export const ScheduleStartNodeConfig: React.FC<NodeConfigRendererProps> = ({ par
|
||||||
}
|
}
|
||||||
onClick={() => toggleWeekday(cronDow)}
|
onClick={() => toggleWeekday(cronDow)}
|
||||||
>
|
>
|
||||||
{t(label)}
|
{cronDow === 1 ? t('Mo') : cronDow === 2 ? t('Di') : cronDow === 3 ? t('Mi') : cronDow === 4 ? t('Do') : cronDow === 5 ? t('Fr') : cronDow === 6 ? t('Sa') : t('So')}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -365,9 +358,9 @@ export const ScheduleStartNodeConfig: React.FC<NodeConfigRendererProps> = ({ par
|
||||||
value={spec.monthIndex}
|
value={spec.monthIndex}
|
||||||
onChange={(e) => push({ ...spec, monthIndex: Number(e.target.value) })}
|
onChange={(e) => push({ ...spec, monthIndex: Number(e.target.value) })}
|
||||||
>
|
>
|
||||||
{MONTH_NAMES_DE.map((name, i) => (
|
{_monthNames(t).map((name, i) => (
|
||||||
<option key={i + 1} value={i + 1}>
|
<option key={i + 1} value={i + 1}>
|
||||||
{t(name)}
|
{name}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ export const SwitchNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, up
|
||||||
<option value="">{t('MIME-Typ wählen')}</option>
|
<option value="">{t('MIME-Typ wählen')}</option>
|
||||||
{mimeTypeOptions.map((o) => (
|
{mimeTypeOptions.map((o) => (
|
||||||
<option key={o.value} value={o.value}>
|
<option key={o.value} value={o.value}>
|
||||||
{t(o.label)} ({o.value})
|
{o.label} ({o.value})
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -221,7 +221,7 @@ export const SwitchNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, up
|
||||||
>
|
>
|
||||||
{operators.map((o) => (
|
{operators.map((o) => (
|
||||||
<option key={o.value} value={o.value}>
|
<option key={o.value} value={o.value}>
|
||||||
{t(o.label)}
|
{o.label}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ export function CustomActionButton<T = any>({
|
||||||
if (typeof title === 'function') {
|
if (typeof title === 'function') {
|
||||||
buttonTitle = title(row);
|
buttonTitle = title(row);
|
||||||
} else if (typeof title === 'string') {
|
} else if (typeof title === 'string') {
|
||||||
buttonTitle = t(title, title); // Try to translate, fallback to original
|
buttonTitle = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the final tooltip
|
// Determine the final tooltip
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ export const MandateNavigation: React.FC = () => {
|
||||||
for (const sg of systemBlock.subgroups) {
|
for (const sg of systemBlock.subgroups) {
|
||||||
children.push({
|
children.push({
|
||||||
id: sg.id,
|
id: sg.id,
|
||||||
label: t(sg.title),
|
label: sg.title,
|
||||||
children: sg.items.map(i => navigationItemToTreeNode(i, t)),
|
children: sg.items.map(i => navigationItemToTreeNode(i, t)),
|
||||||
defaultExpanded: true,
|
defaultExpanded: true,
|
||||||
});
|
});
|
||||||
|
|
@ -288,7 +288,7 @@ export const MandateNavigation: React.FC = () => {
|
||||||
if (items.length > 0) items.push({ type: 'separator' });
|
if (items.length > 0) items.push({ type: 'separator' });
|
||||||
const subgroupNodes: TreeNodeItem[] = adminSubgroups.map(sg => ({
|
const subgroupNodes: TreeNodeItem[] = adminSubgroups.map(sg => ({
|
||||||
id: sg.id,
|
id: sg.id,
|
||||||
label: t(sg.title),
|
label: sg.title,
|
||||||
children: sg.items.map(i => navigationItemToTreeNode(i, t)),
|
children: sg.items.map(i => navigationItemToTreeNode(i, t)),
|
||||||
defaultExpanded: false,
|
defaultExpanded: false,
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,9 @@ const _PROVIDER_LABEL_KEYS: Record<string, string> = {
|
||||||
internal: 'Internal',
|
internal: 'Internal',
|
||||||
};
|
};
|
||||||
|
|
||||||
function _providerLabel(provider: string, t: (key: string) => string): string {
|
function _providerLabel(provider: string): string {
|
||||||
const key = _PROVIDER_LABEL_KEYS[provider];
|
const key = _PROVIDER_LABEL_KEYS[provider];
|
||||||
return key ? t(key) : provider;
|
return key ? key : provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PROVIDER_ICONS: Record<string, string> = {
|
const PROVIDER_ICONS: Record<string, string> = {
|
||||||
|
|
@ -135,7 +135,7 @@ export const ProviderSelect: React.FC<ProviderSelectProps> = ({ value,
|
||||||
const providerOptions = useMemo(() => {
|
const providerOptions = useMemo(() => {
|
||||||
return allowedProviders.map((provider) => ({
|
return allowedProviders.map((provider) => ({
|
||||||
value: provider,
|
value: provider,
|
||||||
label: `${PROVIDER_ICONS[provider] || '🔌'} ${_providerLabel(provider, t)}`,
|
label: `${PROVIDER_ICONS[provider] || '🔌'} ${_providerLabel(provider)}`,
|
||||||
}));
|
}));
|
||||||
}, [allowedProviders, t]);
|
}, [allowedProviders, t]);
|
||||||
|
|
||||||
|
|
@ -325,7 +325,7 @@ export const ProviderMultiSelect: React.FC<ProviderMultiSelectProps> = ({
|
||||||
/>
|
/>
|
||||||
<span className={styles.icon}>{PROVIDER_ICONS[provider] || '🔌'}</span>
|
<span className={styles.icon}>{PROVIDER_ICONS[provider] || '🔌'}</span>
|
||||||
<span className={styles.providerName}>
|
<span className={styles.providerName}>
|
||||||
{_providerLabel(provider, t)}
|
{_providerLabel(provider)}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
|
|
@ -361,7 +361,7 @@ export const ProviderBadges: React.FC<ProviderBadgesProps> = ({
|
||||||
<div className={`${styles.providerBadges} ${className || ''}`}>
|
<div className={`${styles.providerBadges} ${className || ''}`}>
|
||||||
{providers.map((provider) => (
|
{providers.map((provider) => (
|
||||||
<span key={provider} className={styles.badge}>
|
<span key={provider} className={styles.badge}>
|
||||||
{PROVIDER_ICONS[provider] || '🔌'} {_providerLabel(provider, t)}
|
{PROVIDER_ICONS[provider] || '🔌'} {_providerLabel(provider)}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -144,21 +144,8 @@ const CreateButton: React.FC<CreateButtonProps> = ({
|
||||||
|
|
||||||
const isDisabled = disabled || loading || isCreating;
|
const isDisabled = disabled || loading || isCreating;
|
||||||
|
|
||||||
// Resolve language text for popup title
|
const resolvedPopupTitle = popupTitle;
|
||||||
const resolvedPopupTitle = typeof popupTitle === 'string'
|
const resolvedAttributes = attributes;
|
||||||
? t(popupTitle, popupTitle)
|
|
||||||
: popupTitle;
|
|
||||||
|
|
||||||
// Resolve language text for attributes
|
|
||||||
const resolvedAttributes: AttributeDefinition[] = useMemo(() => {
|
|
||||||
return attributes.map(attr => ({
|
|
||||||
...attr,
|
|
||||||
label: typeof attr.label === 'string' ? t(attr.label, attr.label) : attr.label,
|
|
||||||
placeholder: attr.placeholder
|
|
||||||
? (typeof attr.placeholder === 'string' ? t(attr.placeholder, attr.placeholder) : attr.placeholder)
|
|
||||||
: undefined
|
|
||||||
}));
|
|
||||||
}, [attributes, t]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,13 @@ const getStatusBadgeClass = (status?: string | null): string => {
|
||||||
|
|
||||||
const Log: React.FC<LogProps> = ({
|
const Log: React.FC<LogProps> = ({
|
||||||
className = '',
|
className = '',
|
||||||
emptyMessage = 'Keine Log-Informationen verfügbar',
|
emptyMessage,
|
||||||
dashboardTree,
|
dashboardTree,
|
||||||
onToggleOperationExpanded,
|
onToggleOperationExpanded,
|
||||||
getChildOperations
|
getChildOperations
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useLanguage();
|
const { t } = useLanguage();
|
||||||
const resolvedEmptyMessage = typeof emptyMessage === 'string' ? t(emptyMessage, emptyMessage) : emptyMessage;
|
const resolvedEmptyMessage = emptyMessage || t('Keine Log-Informationen verfügbar');
|
||||||
const formatLogTimestamp = (timestamp: number): string => {
|
const formatLogTimestamp = (timestamp: number): string => {
|
||||||
try {
|
try {
|
||||||
const formatted = formatUnixTimestamp(timestamp, undefined, {
|
const formatted = formatUnixTimestamp(timestamp, undefined, {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ const MapViewLeaflet: React.FC<MapViewProps> = ({ parcels = [],
|
||||||
onParcelClick,
|
onParcelClick,
|
||||||
height = '600px',
|
height = '600px',
|
||||||
className = '',
|
className = '',
|
||||||
emptyMessage = 'Klicken Sie auf die Karte, um einen Standort auszuwählen',
|
emptyMessage,
|
||||||
showWfsParcels = false,
|
showWfsParcels = false,
|
||||||
parcelsApiBaseUrl = ''
|
parcelsApiBaseUrl = ''
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -361,7 +361,7 @@ const MapViewLeaflet: React.FC<MapViewProps> = ({ parcels = [],
|
||||||
<div ref={mapContainerRef} style={{ width: '100%', height: '100%' }} />
|
<div ref={mapContainerRef} style={{ width: '100%', height: '100%' }} />
|
||||||
{parcels.length === 0 && !center && (
|
{parcels.length === 0 && !center && (
|
||||||
<div className={styles.emptyStateOverlay}>
|
<div className={styles.emptyStateOverlay}>
|
||||||
<p>{typeof emptyMessage === 'string' ? t(emptyMessage, emptyMessage) : emptyMessage}</p>
|
<p>{emptyMessage || t('Klicken Sie auf die Karte, um einen Standort auszuwählen')}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{showWfsParcels && isWfsLoading && (
|
{showWfsParcels && isWfsLoading && (
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const Messages: React.FC<MessagesProps> = ({
|
||||||
showProgress = true,
|
showProgress = true,
|
||||||
renderMessage,
|
renderMessage,
|
||||||
renderDocument,
|
renderDocument,
|
||||||
emptyMessage = 'Noch keine Nachrichten',
|
emptyMessage,
|
||||||
onFileDelete,
|
onFileDelete,
|
||||||
onFileRemove,
|
onFileRemove,
|
||||||
onFileView,
|
onFileView,
|
||||||
|
|
@ -30,7 +30,7 @@ const Messages: React.FC<MessagesProps> = ({
|
||||||
deletingMessages
|
deletingMessages
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useLanguage();
|
const { t } = useLanguage();
|
||||||
const resolvedEmptyMessage = typeof emptyMessage === 'string' ? t(emptyMessage, emptyMessage) : emptyMessage;
|
const resolvedEmptyMessage = emptyMessage || t('Noch keine Nachrichten');
|
||||||
if (!messages || messages.length === 0) {
|
if (!messages || messages.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.messagesContainer} ${styles.emptyContainer} ${className}`}>
|
<div className={`${styles.messagesContainer} ${styles.emptyContainer} ${className}`}>
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,14 @@ interface UnifiedDataBarProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _TAB_KEYS: Record<UdbTab, string> = {
|
function _tabLabel(tab: UdbTab, t: (k: string) => string): string {
|
||||||
chats: 'Chatverläufe',
|
switch (tab) {
|
||||||
files: 'Dateien',
|
case 'chats': return t('Chatverläufe');
|
||||||
sources: 'Quellen',
|
case 'files': return t('Dateien');
|
||||||
};
|
case 'sources': return t('Quellen');
|
||||||
|
default: return tab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const UnifiedDataBar: React.FC<UnifiedDataBarProps> = ({
|
const UnifiedDataBar: React.FC<UnifiedDataBarProps> = ({
|
||||||
context,
|
context,
|
||||||
|
|
@ -72,7 +75,7 @@ const UnifiedDataBar: React.FC<UnifiedDataBarProps> = ({
|
||||||
className={`${styles.tab} ${currentTab === tab ? styles.tabActive : ''}`}
|
className={`${styles.tab} ${currentTab === tab ? styles.tabActive : ''}`}
|
||||||
onClick={() => _handleTabChange(tab)}
|
onClick={() => _handleTabChange(tab)}
|
||||||
>
|
>
|
||||||
{t(_TAB_KEYS[tab])}
|
{_tabLabel(tab, t)}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -82,23 +82,10 @@ export const SidebarProvider: React.FC<SidebarProviderProps> = ({ children }) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to resolve node name
|
// Helper function to resolve node name
|
||||||
const resolveNodeName = (pathSegment: string, fullPath: string, page?: GenericPageData): string => {
|
const resolveNodeName = (pathSegment: string, _fullPath: string, page?: GenericPageData): string => {
|
||||||
if (page) {
|
if (page) {
|
||||||
return resolveLanguageText(page.name, t);
|
return resolveLanguageText(page.name, t);
|
||||||
}
|
}
|
||||||
// Try translation key (e.g., "start.real-estate.title")
|
|
||||||
const translationKey = `${fullPath}.title`;
|
|
||||||
const translated = t(translationKey);
|
|
||||||
if (translated !== translationKey) {
|
|
||||||
return translated;
|
|
||||||
}
|
|
||||||
// Try just the segment (e.g., "real-estate.title")
|
|
||||||
const segmentKey = `${pathSegment}.title`;
|
|
||||||
const segmentTranslated = t(segmentKey);
|
|
||||||
if (segmentTranslated !== segmentKey) {
|
|
||||||
return segmentTranslated;
|
|
||||||
}
|
|
||||||
// Fallback to capitalized segment
|
|
||||||
return pathSegment.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' ');
|
return pathSegment.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' ');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,11 +82,11 @@ export const FeatureLayout: React.FC = () => {
|
||||||
? navFeature.instances.find(i => i.id === instanceId)
|
? navFeature.instances.find(i => i.id === instanceId)
|
||||||
: undefined;
|
: undefined;
|
||||||
return {
|
return {
|
||||||
mandate: t(navMandate.uiLabel),
|
mandate: navMandate.uiLabel,
|
||||||
feature: navFeature ? t(navFeature.uiLabel) : undefined,
|
feature: navFeature ? navFeature.uiLabel : undefined,
|
||||||
instance: navInstance ? t(navInstance.uiLabel) : undefined,
|
instance: navInstance ? navInstance.uiLabel : undefined,
|
||||||
};
|
};
|
||||||
}, [dynamicBlock, mandateId, featureCode, instanceId, t]);
|
}, [dynamicBlock, mandateId, featureCode, instanceId]);
|
||||||
|
|
||||||
// Warten bis Features geladen sind
|
// Warten bis Features geladen sind
|
||||||
if (!initialized || loading || isLoading) {
|
if (!initialized || loading || isLoading) {
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ export const AutomationsDashboardPage: React.FC = () => {
|
||||||
filterable: true,
|
filterable: true,
|
||||||
formatter: (v: string) => (
|
formatter: (v: string) => (
|
||||||
<span style={{ color: _STATUS_COLORS[v] || 'inherit', fontWeight: 600 }}>
|
<span style={{ color: _STATUS_COLORS[v] || 'inherit', fontWeight: 600 }}>
|
||||||
{t(v === 'completed' ? 'Abgeschlossen' : v === 'failed' ? 'Fehlgeschlagen' : v === 'running' ? 'Laufend' : v)}
|
{v === 'completed' ? t('Abgeschlossen') : v === 'failed' ? t('Fehlgeschlagen') : v === 'running' ? t('Laufend') : v}
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,9 @@ const STORE_FEATURE_DESCRIPTION_FALLBACK: Record<string, string> = {
|
||||||
commcoach: 'CommCoach: Kommunikation trainieren mit KI-gestütztem Coaching und Feedback.',
|
commcoach: 'CommCoach: Kommunikation trainieren mit KI-gestütztem Coaching und Feedback.',
|
||||||
};
|
};
|
||||||
|
|
||||||
function _storeCardDescription(feature: StoreFeature, t: (key: string, fallback?: string) => string): string {
|
function _storeCardDescription(feature: StoreFeature): string {
|
||||||
const raw =
|
return (feature.description && feature.description.trim()) ||
|
||||||
(feature.description && feature.description.trim()) ||
|
STORE_FEATURE_DESCRIPTION_FALLBACK[feature.featureCode] || '';
|
||||||
STORE_FEATURE_DESCRIPTION_FALLBACK[feature.featureCode];
|
|
||||||
return raw ? t(raw) : '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FeatureCardProps {
|
interface FeatureCardProps {
|
||||||
|
|
@ -62,13 +60,13 @@ const FeatureCard: React.FC<FeatureCardProps> = ({
|
||||||
<div className={styles.cardHeader}>
|
<div className={styles.cardHeader}>
|
||||||
{icon && <span className={styles.cardIcon}>{icon}</span>}
|
{icon && <span className={styles.cardIcon}>{icon}</span>}
|
||||||
<h3 className={styles.cardTitle}>
|
<h3 className={styles.cardTitle}>
|
||||||
{t(feature.label)}
|
{feature.label}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.cardBody}>
|
<div className={styles.cardBody}>
|
||||||
<p className={styles.cardDescription}>
|
<p className={styles.cardDescription}>
|
||||||
{_storeCardDescription(feature, t)}
|
{_storeCardDescription(feature)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ function getMandateName(mandate: Mandate): string {
|
||||||
return mandate.label || mandate.name || mandate.id;
|
return mandate.label || mandate.name || mandate.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFeatureLabel(feature: Feature, t: (k: string) => string): string {
|
function getFeatureLabel(feature: Feature): string {
|
||||||
return t(feature.label || feature.code);
|
return feature.label || feature.code;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InstanceWithStats extends FeatureInstance {
|
export interface InstanceWithStats extends FeatureInstance {
|
||||||
|
|
@ -165,7 +165,7 @@ export const AccessManagementHub: React.FC = () => {
|
||||||
instance,
|
instance,
|
||||||
mandateId: mandateId || '',
|
mandateId: mandateId || '',
|
||||||
mandateName: mandate ? getMandateName(mandate) : mandateId || '',
|
mandateName: mandate ? getMandateName(mandate) : mandateId || '',
|
||||||
featureLabel: feature ? getFeatureLabel(feature, t) : instance.featureCode,
|
featureLabel: feature ? getFeatureLabel(feature) : instance.featureCode,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -294,7 +294,7 @@ export const AccessManagementHub: React.FC = () => {
|
||||||
return {
|
return {
|
||||||
id: inst.id,
|
id: inst.id,
|
||||||
label: inst.label,
|
label: inst.label,
|
||||||
featureLabel: feature ? getFeatureLabel(feature, t) : inst.featureCode,
|
featureLabel: feature ? getFeatureLabel(feature) : inst.featureCode,
|
||||||
userCount: inst.userCount ?? 0,
|
userCount: inst.userCount ?? 0,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
@ -363,7 +363,7 @@ export const AccessManagementHub: React.FC = () => {
|
||||||
<option value="">{t('Alle')}</option>
|
<option value="">{t('Alle')}</option>
|
||||||
{features.map((f) => (
|
{features.map((f) => (
|
||||||
<option key={f.code} value={f.code}>
|
<option key={f.code} value={f.code}>
|
||||||
{getFeatureLabel(f, t)}
|
{getFeatureLabel(f)}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -424,7 +424,7 @@ export const AccessManagementHub: React.FC = () => {
|
||||||
instancesByMandate={instancesByMandate}
|
instancesByMandate={instancesByMandate}
|
||||||
instanceUsersMap={instanceUsersMap}
|
instanceUsersMap={instanceUsersMap}
|
||||||
features={features}
|
features={features}
|
||||||
getFeatureLabel={(f) => getFeatureLabel(f, t)}
|
getFeatureLabel={getFeatureLabel}
|
||||||
loading={hierarchyUsersLoading}
|
loading={hierarchyUsersLoading}
|
||||||
onOpenDetail={handleOpenDetail}
|
onOpenDetail={handleOpenDetail}
|
||||||
/>
|
/>
|
||||||
|
|
@ -526,7 +526,7 @@ export const AccessManagementHub: React.FC = () => {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={hubStyles.instanceMeta}>
|
<div className={hubStyles.instanceMeta}>
|
||||||
<span>{getFeatureLabel(features.find((f) => f.code === inst.featureCode) || { code: inst.featureCode, label: inst.featureCode }, t)}</span>
|
<span>{getFeatureLabel(features.find((f) => f.code === inst.featureCode) || { code: inst.featureCode, label: inst.featureCode })}</span>
|
||||||
<span>
|
<span>
|
||||||
{inst.userCount ?? '—'} {t('Benutzer')}
|
{inst.userCount ?? '—'} {t('Benutzer')}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -92,10 +92,7 @@ export const AdminFeatureAccessPage: React.FC = () => {
|
||||||
{ key: 'featureCode', label: t('Feature'), type: 'string' as const, sortable: true, filterable: true, width: 150,
|
{ key: 'featureCode', label: t('Feature'), type: 'string' as const, sortable: true, filterable: true, width: 150,
|
||||||
render: (value: string) => {
|
render: (value: string) => {
|
||||||
const feature = features.find(f => f.code === value);
|
const feature = features.find(f => f.code === value);
|
||||||
if (feature) {
|
return feature ? (feature.label || value) : value;
|
||||||
return t(feature.label || value);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ key: 'enabled', label: t('Aktiv'), type: 'boolean' as const, sortable: true, filterable: true, width: 80 },
|
{ key: 'enabled', label: t('Aktiv'), type: 'boolean' as const, sortable: true, filterable: true, width: 80 },
|
||||||
|
|
@ -323,10 +320,7 @@ export const AdminFeatureAccessPage: React.FC = () => {
|
||||||
// Get feature label
|
// Get feature label
|
||||||
const getFeatureLabel = (code: string) => {
|
const getFeatureLabel = (code: string) => {
|
||||||
const feature = features.find(f => f.code === code);
|
const feature = features.find(f => f.code === code);
|
||||||
if (feature) {
|
return feature ? (feature.label || code) : code;
|
||||||
return t(feature.label || code);
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (error && !selectedMandateId) {
|
if (error && !selectedMandateId) {
|
||||||
|
|
@ -514,7 +508,7 @@ export const AdminFeatureAccessPage: React.FC = () => {
|
||||||
<DropdownSelect
|
<DropdownSelect
|
||||||
items={features.map(f => ({
|
items={features.map(f => ({
|
||||||
id: f.code,
|
id: f.code,
|
||||||
label: t(f.label || f.code),
|
label: f.label || f.code,
|
||||||
value: f.code
|
value: f.code
|
||||||
}))}
|
}))}
|
||||||
selectedItemId={createFeatureCode}
|
selectedItemId={createFeatureCode}
|
||||||
|
|
|
||||||
|
|
@ -367,10 +367,7 @@ export const AdminFeatureInstanceUsersPage: React.FC = () => {
|
||||||
// Get feature label
|
// Get feature label
|
||||||
const getFeatureLabel = (code: string) => {
|
const getFeatureLabel = (code: string) => {
|
||||||
const feature = features.find(f => f.code === code);
|
const feature = features.find(f => f.code === code);
|
||||||
if (feature) {
|
return feature ? (feature.label || code) : code;
|
||||||
return t(feature.label || code);
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get selected instance info from combined options
|
// Get selected instance info from combined options
|
||||||
|
|
|
||||||
|
|
@ -260,10 +260,8 @@ export const AdminFeatureRolesPage: React.FC = () => {
|
||||||
setEditingRole(role);
|
setEditingRole(role);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get feature name - Backend uses 'label' field (German i18n key or legacy multilingual)
|
|
||||||
const getFeatureName = (feature: Feature) => {
|
const getFeatureName = (feature: Feature) => {
|
||||||
const raw = getTextValue(feature.label || feature.name);
|
return feature.label || feature.name || '-';
|
||||||
return raw === '-' ? '-' : t(raw);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (error && !selectedFeatureCode) {
|
if (error && !selectedFeatureCode) {
|
||||||
|
|
|
||||||
|
|
@ -109,10 +109,7 @@ export const AdminMandateWizardPage: React.FC = () => {
|
||||||
|
|
||||||
const getFeatureLabel = (code: string): string => {
|
const getFeatureLabel = (code: string): string => {
|
||||||
const f = features.find(feat => feat.code === code);
|
const f = features.find(feat => feat.code === code);
|
||||||
if (f) {
|
return f ? (f.label || code) : code;
|
||||||
return t(f.label || code);
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUserDisplayName = (u: { fullName?: string; firstname?: string | null; lastname?: string | null; username: string }): string => {
|
const getUserDisplayName = (u: { fullName?: string; firstname?: string | null; lastname?: string | null; username: string }): string => {
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,8 @@ export const FeatureInstanceWizard: React.FC<FeatureInstanceWizardProps> = ({ ma
|
||||||
const [selectedUserRoles, setSelectedUserRoles] = useState<Array<{ userId: string; roleIds: string[] }>>([]);
|
const [selectedUserRoles, setSelectedUserRoles] = useState<Array<{ userId: string; roleIds: string[] }>>([]);
|
||||||
|
|
||||||
const featureOptions = useMemo(
|
const featureOptions = useMemo(
|
||||||
() => features.map((f) => ({ value: f.code, label: t(f.label || f.code) })),
|
() => features.map((f) => ({ value: f.code, label: f.label || f.code })),
|
||||||
[features, t]
|
[features]
|
||||||
);
|
);
|
||||||
const mandateOptions = useMemo(
|
const mandateOptions = useMemo(
|
||||||
() => mandates.map((m) => ({ value: m.id, label: getMandateName(m) })),
|
() => mandates.map((m) => ({ value: m.id, label: getMandateName(m) })),
|
||||||
|
|
|
||||||
|
|
@ -858,7 +858,7 @@ export const CommcoachDossierView: React.FC<CommcoachDossierViewProps> = ({ pers
|
||||||
{_groupScoresByDimension(coach.scores).map(group => (
|
{_groupScoresByDimension(coach.scores).map(group => (
|
||||||
<div key={group.dimension} className={styles.scoreGroup}>
|
<div key={group.dimension} className={styles.scoreGroup}>
|
||||||
<div className={styles.scoreDimension}>
|
<div className={styles.scoreDimension}>
|
||||||
<span className={styles.scoreDimensionLabel}>{t(_dimensionLabel(group.dimension))}</span>
|
<span className={styles.scoreDimensionLabel}>{_dimensionLabel(group.dimension, t)}</span>
|
||||||
<span className={styles.scoreLatest}>{Math.round(group.latest.score)}/100</span>
|
<span className={styles.scoreLatest}>{Math.round(group.latest.score)}/100</span>
|
||||||
<span className={`${styles.scoreTrend} ${styles[`trend_${group.latest.trend}`]}`}>
|
<span className={`${styles.scoreTrend} ${styles[`trend_${group.latest.trend}`]}`}>
|
||||||
{group.latest.trend === 'improving' ? 'steigend' : group.latest.trend === 'declining' ? 'sinkend' : 'stabil'}
|
{group.latest.trend === 'improving' ? 'steigend' : group.latest.trend === 'declining' ? 'sinkend' : 'stabil'}
|
||||||
|
|
@ -926,13 +926,15 @@ function _groupScoresByDimension(scores: any[]): ScoreGroup[] {
|
||||||
return Object.values(groups);
|
return Object.values(groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _dimensionLabel(dim: string): string {
|
function _dimensionLabel(dim: string, t: (k: string) => string): string {
|
||||||
const labels: Record<string, string> = {
|
switch (dim) {
|
||||||
empathy: 'Einfühlungsvermögen', clarity: 'Klarheit',
|
case 'empathy': return t('Einfühlungsvermögen');
|
||||||
assertiveness: 'Durchsetzung', listening: 'Zuhören',
|
case 'clarity': return t('Klarheit');
|
||||||
selfReflection: 'Selbstreflexion',
|
case 'assertiveness': return t('Durchsetzung');
|
||||||
};
|
case 'listening': return t('Zuhören');
|
||||||
return labels[dim] || dim;
|
case 'selfReflection': return t('Selbstreflexion');
|
||||||
|
default: return dim;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _formatToolPayload(payload: Record<string, unknown>): string {
|
function _formatToolPayload(payload: Record<string, unknown>): string {
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,18 @@ import styles from './Automation2WorkflowsTasks.module.css';
|
||||||
|
|
||||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||||
|
|
||||||
const NODE_TYPE_LABELS: Record<string, string> = {
|
function _nodeTypeLabel(nodeType: string, t: (k: string) => string): string {
|
||||||
'input.form': 'Formular',
|
switch (nodeType) {
|
||||||
'input.approval': 'Genehmigung',
|
case 'input.form': return t('Formular');
|
||||||
'input.upload': 'Upload',
|
case 'input.approval': return t('Genehmigung');
|
||||||
'input.comment': 'Kommentar',
|
case 'input.upload': return t('Upload');
|
||||||
'input.review': 'Prüfung',
|
case 'input.comment': return t('Kommentar');
|
||||||
'input.selection': 'Auswahl',
|
case 'input.review': return t('Prüfung');
|
||||||
'input.confirmation': 'Bestätigung',
|
case 'input.selection': return t('Auswahl');
|
||||||
};
|
case 'input.confirmation': return t('Bestätigung');
|
||||||
|
default: return nodeType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function formatTimestamp(ts?: number): string {
|
function formatTimestamp(ts?: number): string {
|
||||||
if (ts == null || ts <= 0) return '—';
|
if (ts == null || ts <= 0) return '—';
|
||||||
|
|
@ -941,7 +944,7 @@ const TaskCard: React.FC<TaskCardProps> = ({
|
||||||
<div className={styles.taskMetaRow}>
|
<div className={styles.taskMetaRow}>
|
||||||
<span className={styles.metaLabel}>{t('Typ')}</span>
|
<span className={styles.metaLabel}>{t('Typ')}</span>
|
||||||
<span className={styles.metaValue}>
|
<span className={styles.metaValue}>
|
||||||
{t(NODE_TYPE_LABELS[nodeType] ?? nodeType)}
|
{_nodeTypeLabel(nodeType, t)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,19 @@ const _TABS: TabDef[] = [
|
||||||
{ id: 'year-end', templateTag: 'template:trustee-year-end-check', icon: '\u2705', color: '#795548' },
|
{ id: 'year-end', templateTag: 'template:trustee-year-end-check', icon: '\u2705', color: '#795548' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const _TAB_LABEL_KEYS: Record<string, string> = {
|
function _tabLabel(tabId: string, t: (k: string) => string): string {
|
||||||
'year-end': 'Jahresabschluss prüfen',
|
switch (tabId) {
|
||||||
};
|
case 'year-end': return t('Jahresabschluss prüfen');
|
||||||
|
default: return tabId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const _TAB_DESCRIPTION_KEYS: Record<string, string> = {
|
function _tabDescription(tabId: string, t: (k: string) => string): string {
|
||||||
'year-end':
|
switch (tabId) {
|
||||||
'Automatische Prüfungen für den Jahresabschluss: Saldovalidierung, Vorjahresvergleich, gesetzliche Checks.',
|
case 'year-end': return t('Automatische Prüfungen für den Jahresabschluss: Saldovalidierung, Vorjahresvergleich, gesetzliche Checks.');
|
||||||
};
|
default: return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Types
|
// Types
|
||||||
|
|
@ -225,7 +230,7 @@ export const TrusteeAbschlussView: React.FC = () => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ marginRight: '0.375rem' }}>{tab.icon}</span>
|
<span style={{ marginRight: '0.375rem' }}>{tab.icon}</span>
|
||||||
{t(_TAB_LABEL_KEYS[tab.id] || tab.id)}
|
{_tabLabel(tab.id, t)}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -234,7 +239,7 @@ export const TrusteeAbschlussView: React.FC = () => {
|
||||||
{/* Tab content */}
|
{/* Tab content */}
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
||||||
<p className={styles.sectionDescription}>
|
<p className={styles.sectionDescription}>
|
||||||
{_TAB_DESCRIPTION_KEYS[activeTab] ? t(_TAB_DESCRIPTION_KEYS[activeTab]) : ''}
|
{_tabDescription(activeTab, t)}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{workflowsLoading ? (
|
{workflowsLoading ? (
|
||||||
|
|
|
||||||
|
|
@ -33,19 +33,25 @@ const _TABS: TabDef[] = [
|
||||||
{ id: 'forecast', templateTag: 'template:trustee-forecast', icon: '\uD83D\uDCC8', color: '#E91E63' },
|
{ id: 'forecast', templateTag: 'template:trustee-forecast', icon: '\uD83D\uDCC8', color: '#E91E63' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const _TAB_LABEL_KEYS: Record<string, string> = {
|
function _tabLabel(tabId: string, t: (k: string) => string): string {
|
||||||
budget: 'Budget-Vergleich',
|
switch (tabId) {
|
||||||
kpi: 'KPI-Dashboard',
|
case 'budget': return t('Budget-Vergleich');
|
||||||
cashflow: 'Cashflow-Rechnung',
|
case 'kpi': return t('KPI-Dashboard');
|
||||||
forecast: 'Prognose',
|
case 'cashflow': return t('Cashflow-Rechnung');
|
||||||
};
|
case 'forecast': return t('Prognose');
|
||||||
|
default: return tabId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const _TAB_DESCRIPTION_KEYS: Record<string, string> = {
|
function _tabDescription(tabId: string, t: (k: string) => string): string {
|
||||||
budget: 'Soll/Ist-Vergleich der Buchhaltung mit Budget-Excel',
|
switch (tabId) {
|
||||||
kpi: 'Kennzahlen berechnen und visualisieren',
|
case 'budget': return t('Soll/Ist-Vergleich der Buchhaltung mit Budget-Excel');
|
||||||
cashflow: 'Cashflow berechnen und analysieren',
|
case 'kpi': return t('Kennzahlen berechnen und visualisieren');
|
||||||
forecast: 'Trend-Analyse und Prognose der nächsten Monate',
|
case 'cashflow': return t('Cashflow berechnen und analysieren');
|
||||||
};
|
case 'forecast': return t('Trend-Analyse und Prognose der nächsten Monate');
|
||||||
|
default: return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Types
|
// Types
|
||||||
|
|
@ -240,7 +246,7 @@ export const TrusteeAnalyseView: React.FC = () => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ marginRight: '0.375rem' }}>{tab.icon}</span>
|
<span style={{ marginRight: '0.375rem' }}>{tab.icon}</span>
|
||||||
{t(_TAB_LABEL_KEYS[tab.id] || tab.id)}
|
{_tabLabel(tab.id, t)}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -248,7 +254,7 @@ export const TrusteeAnalyseView: React.FC = () => {
|
||||||
{/* Tab content */}
|
{/* Tab content */}
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
||||||
<p className={styles.sectionDescription}>
|
<p className={styles.sectionDescription}>
|
||||||
{_TAB_DESCRIPTION_KEYS[activeTab] ? t(_TAB_DESCRIPTION_KEYS[activeTab]) : ''}
|
{_tabDescription(activeTab, t)}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{workflowsLoading ? (
|
{workflowsLoading ? (
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
* - Full details shown on error for debugging
|
* - Full details shown on error for debugging
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useCallback } from 'react';
|
import React, { useState} from 'react';
|
||||||
import type { ToolActivity } from './useWorkspace';
|
import type { ToolActivity } from './useWorkspace';
|
||||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||||
|
|
||||||
|
|
@ -124,7 +124,7 @@ export const ToolActivityLog: React.FC<ToolActivityLogProps> = ({ activities })
|
||||||
const { t } = useLanguage();
|
const { t } = useLanguage();
|
||||||
const [expandedId, setExpandedId] = useState<string | null>(null);
|
const [expandedId, setExpandedId] = useState<string | null>(null);
|
||||||
|
|
||||||
const translate = useCallback<TranslateFn>((key, params) => t(key, params), [t]);
|
const translate: TranslateFn = t;
|
||||||
|
|
||||||
if (!activities.length) {
|
if (!activities.length) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,19 @@ import { formatBinaryDataSizeBytes } from '../../../utils/formatDataSize';
|
||||||
|
|
||||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||||
|
|
||||||
const MIME_LABELS: Record<string, string> = {
|
function _mimeLabel(key: string, t: (k: string) => string): string {
|
||||||
pdf: 'PDF',
|
switch (key) {
|
||||||
office_doc: 'Office (Text)',
|
case 'pdf': return t('PDF');
|
||||||
office_sheet: 'Office (Tabellen)',
|
case 'office_doc': return t('Office (Text)');
|
||||||
office_slides: 'Office (Folien)',
|
case 'office_sheet': return t('Office (Tabellen)');
|
||||||
text: 'Text',
|
case 'office_slides': return t('Office (Folien)');
|
||||||
image: 'Bild',
|
case 'text': return t('Text');
|
||||||
html: 'HTML',
|
case 'image': return t('Bild');
|
||||||
other: 'Sonstige',
|
case 'html': return t('HTML');
|
||||||
};
|
case 'other': return t('Sonstige');
|
||||||
|
default: return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const CHART_COLORS = ['#1976d2', '#00897b', '#6a1b9a', '#e65100', '#5d4037', '#455a64', '#c62828'];
|
const CHART_COLORS = ['#1976d2', '#00897b', '#6a1b9a', '#e65100', '#5d4037', '#455a64', '#c62828'];
|
||||||
|
|
||||||
|
|
@ -118,7 +121,7 @@ export const WorkspaceRagInsightsPage: React.FC = () => {
|
||||||
const kpis = stats?.kpis;
|
const kpis = stats?.kpis;
|
||||||
const timeline = stats?.timelineIndexedDocuments ?? [];
|
const timeline = stats?.timelineIndexedDocuments ?? [];
|
||||||
const mimeRows = Object.entries(stats?.documentsByMimeCategory ?? {}).map(([key, value]) => ({
|
const mimeRows = Object.entries(stats?.documentsByMimeCategory ?? {}).map(([key, value]) => ({
|
||||||
name: t(MIME_LABELS[key] ?? key),
|
name: _mimeLabel(key, t),
|
||||||
value,
|
value,
|
||||||
}));
|
}));
|
||||||
const statusRows = Object.entries(stats?.indexedDocumentsByStatus ?? {}).map(([name, value]) => ({
|
const statusRows = Object.entries(stats?.indexedDocumentsByStatus ?? {}).map(([name, value]) => ({
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ type TranslateParams = Record<string, string | number | boolean | null | undefin
|
||||||
interface LanguageContextType {
|
interface LanguageContextType {
|
||||||
currentLanguage: Language;
|
currentLanguage: Language;
|
||||||
setLanguage: (language: Language) => void;
|
setLanguage: (language: Language) => void;
|
||||||
t: (key: string, paramsOrFallback?: TranslateParams | string) => string;
|
t: (key: string, params?: TranslateParams) => string;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
reloadLanguage: () => Promise<void>;
|
reloadLanguage: () => Promise<void>;
|
||||||
availableLanguages: I18nCodeInfo[];
|
availableLanguages: I18nCodeInfo[];
|
||||||
|
|
@ -123,18 +123,8 @@ export const LanguageProvider: React.FC<LanguageProviderProps> = ({ children })
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
const t = (key: string, paramsOrFallback?: TranslateParams | string): string => {
|
const t = (key: string, params?: TranslateParams): string => {
|
||||||
let params: TranslateParams | undefined;
|
const resolved = translations[key] ?? `[${key}]`;
|
||||||
if (typeof paramsOrFallback === 'string') {
|
|
||||||
params = undefined;
|
|
||||||
} else {
|
|
||||||
params = paramsOrFallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolved =
|
|
||||||
translations[key] ??
|
|
||||||
(typeof paramsOrFallback === 'string' ? paramsOrFallback : undefined) ??
|
|
||||||
`[${key}]`;
|
|
||||||
return _applyParams(resolved, params);
|
return _applyParams(resolved, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue