chore: even more i18n and no end in sight
This commit is contained in:
@@ -1245,6 +1245,7 @@ const TaxonomySection: React.FC<{
|
||||
onMappingsAnalyzed: (categoryMappings: Record<string, string>, tagMappings: Record<string, string>) => void;
|
||||
onMappingUpdated: (type: 'category' | 'tag', itemName: string, mappedTo: string | undefined) => void;
|
||||
}> = ({ categories, tags, expanded, onToggle, onMappingsAnalyzed, onMappingUpdated }) => {
|
||||
const { t } = useI18n();
|
||||
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
||||
const [showModelSelector, setShowModelSelector] = useState(false);
|
||||
const [availableModels, setAvailableModels] = useState<ChatModel[]>([]);
|
||||
@@ -1335,10 +1336,10 @@ const TaxonomySection: React.FC<{
|
||||
<div className="import-detail-section">
|
||||
<h3 onClick={onToggle}>
|
||||
<span className={`toggle-icon ${expanded ? 'open' : ''}`}>▶</span>
|
||||
Categories & Tags
|
||||
{t('importAnalysis.taxonomyTitle')}
|
||||
{(mappedCategoriesCount > 0 || mappedTagsCount > 0) && (
|
||||
<span className="taxonomy-mapped-count">
|
||||
{mappedCategoriesCount + mappedTagsCount} mapped
|
||||
{t('importAnalysis.mappedCount', { count: mappedCategoriesCount + mappedTagsCount })}
|
||||
</span>
|
||||
)}
|
||||
</h3>
|
||||
@@ -1354,14 +1355,14 @@ const TaxonomySection: React.FC<{
|
||||
{isAnalyzing ? (
|
||||
<>
|
||||
<span className="import-spinner small" />
|
||||
Analyzing...
|
||||
{t('importAnalysis.analyzing')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
|
||||
</svg>
|
||||
Analyze with...
|
||||
{t('importAnalysis.analyzeWith')}
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" style={{ marginLeft: 4 }}>
|
||||
<path d="M7 10l5 5 5-5z"/>
|
||||
</svg>
|
||||
@@ -1383,14 +1384,14 @@ const TaxonomySection: React.FC<{
|
||||
)}
|
||||
</div>
|
||||
<span className="taxonomy-analyze-hint">
|
||||
AI will suggest mappings from new to existing items to avoid duplicates
|
||||
{t('importAnalysis.aiMappingHint')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{categories.length > 0 && (
|
||||
<div style={{ marginBottom: 12, marginTop: 16 }}>
|
||||
<div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, color: 'var(--vscode-descriptionForeground)', marginBottom: 6 }}>
|
||||
Categories
|
||||
{t('importAnalysis.categories')}
|
||||
</div>
|
||||
<div className="import-taxonomy-list">
|
||||
{categories.map((cat, idx) => (
|
||||
@@ -1414,7 +1415,7 @@ const TaxonomySection: React.FC<{
|
||||
{tags.length > 0 && (
|
||||
<div>
|
||||
<div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, color: 'var(--vscode-descriptionForeground)', marginBottom: 6 }}>
|
||||
Tags
|
||||
{t('importAnalysis.tags')}
|
||||
</div>
|
||||
<div className="import-taxonomy-list">
|
||||
{tags.map((tag, idx) => (
|
||||
@@ -1453,6 +1454,7 @@ const TaxonomyPill: React.FC<{
|
||||
onCancelEdit: () => void;
|
||||
onClearMapping: () => void;
|
||||
}> = ({ item, type: _type, isEditing, editValue, suggestions, onEditValueChange, onStartEdit, onSaveEdit, onCancelEdit, onClearMapping }) => {
|
||||
const { t } = useI18n();
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const [selectedIndex, setSelectedIndex] = useState(-1);
|
||||
@@ -1536,7 +1538,7 @@ const TaxonomyPill: React.FC<{
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={handleBlur}
|
||||
onFocus={() => setShowDropdown(true)}
|
||||
placeholder="Map to..."
|
||||
placeholder={t('importAnalysis.mapToPlaceholder')}
|
||||
/>
|
||||
{showDropdown && filteredSuggestions.length > 0 && (
|
||||
<div className="pill-suggestions-dropdown" ref={dropdownRef}>
|
||||
@@ -1564,7 +1566,13 @@ const TaxonomyPill: React.FC<{
|
||||
const className = `import-taxonomy-pill ${item.existsInProject ? 'exists' : 'new-tax'} ${hasMaping ? 'mapped' : ''}`;
|
||||
|
||||
return (
|
||||
<span className={className} onClick={onStartEdit} title={`Click to ${hasMaping ? 'edit' : 'add'} mapping`}>
|
||||
<span
|
||||
className={className}
|
||||
onClick={onStartEdit}
|
||||
title={t('importAnalysis.mappingTooltip', {
|
||||
action: hasMaping ? t('importAnalysis.mappingActionEdit') : t('importAnalysis.mappingActionAdd'),
|
||||
})}
|
||||
>
|
||||
{item.name}
|
||||
{hasMaping && (
|
||||
<>
|
||||
@@ -1573,7 +1581,7 @@ const TaxonomyPill: React.FC<{
|
||||
<button
|
||||
className="pill-clear-btn"
|
||||
onClick={(e) => { e.stopPropagation(); onClearMapping(); }}
|
||||
title="Clear mapping"
|
||||
title={t('importAnalysis.clearMapping')}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
@@ -1592,6 +1600,7 @@ const MacrosSection: React.FC<{
|
||||
expanded: boolean;
|
||||
onToggle: () => void;
|
||||
}> = ({ macros, expanded, onToggle }) => {
|
||||
const { t } = useI18n();
|
||||
const [expandedMacros, setExpandedMacros] = useState<Set<string>>(new Set());
|
||||
|
||||
const toggleMacro = (name: string) => {
|
||||
@@ -1610,11 +1619,11 @@ const MacrosSection: React.FC<{
|
||||
<div className="import-detail-section">
|
||||
<h3 onClick={onToggle}>
|
||||
<span className={`toggle-icon ${expanded ? 'open' : ''}`}>▶</span>
|
||||
Macros ({macros.total})
|
||||
{t('importAnalysis.macrosWithCount', { count: macros.total })}
|
||||
<span className="macro-summary-counts">
|
||||
<span className="mapped-count">{macros.mappedCount} mapped</span>
|
||||
<span className="mapped-count">{t('importAnalysis.mappedCount', { count: macros.mappedCount })}</span>
|
||||
{macros.unmappedCount > 0 && (
|
||||
<span className="unmapped-count">{macros.unmappedCount} unmapped</span>
|
||||
<span className="unmapped-count">{t('importAnalysis.unmappedCount', { count: macros.unmappedCount })}</span>
|
||||
)}
|
||||
</span>
|
||||
</h3>
|
||||
@@ -1626,15 +1635,22 @@ const MacrosSection: React.FC<{
|
||||
<span className={`toggle-icon small ${expandedMacros.has(macro.name) ? 'open' : ''}`}>▶</span>
|
||||
<span className="macro-name">[{macro.name}]</span>
|
||||
<span className={`macro-status-badge ${macro.mapped ? 'mapped' : 'unmapped'}`}>
|
||||
{macro.mapped ? 'Mapped' : 'Unknown'}
|
||||
{macro.mapped ? t('importAnalysis.macroStatusMapped') : t('importAnalysis.macroStatusUnknown')}
|
||||
</span>
|
||||
<span className="macro-count">{macro.totalCount} uses</span>
|
||||
<span className="macro-count">{t('importAnalysis.macroUses', { count: macro.totalCount })}</span>
|
||||
</div>
|
||||
|
||||
{expandedMacros.has(macro.name) && (
|
||||
<div className="macro-usages">
|
||||
<div className="macro-usages-header">
|
||||
<span className="macro-posts-label">Used in: {macro.postSlugs.slice(0, 5).join(', ')}{macro.postSlugs.length > 5 ? `, +${macro.postSlugs.length - 5} more` : ''}</span>
|
||||
<span className="macro-posts-label">
|
||||
{t('importAnalysis.usedIn', {
|
||||
items: macro.postSlugs.slice(0, 5).join(', '),
|
||||
more: macro.postSlugs.length > 5
|
||||
? t('importAnalysis.moreSuffix', { count: macro.postSlugs.length - 5 })
|
||||
: '',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="macro-usages-list">
|
||||
@@ -1646,7 +1662,7 @@ const MacrosSection: React.FC<{
|
||||
>
|
||||
<div className="macro-usage-params">
|
||||
{Object.keys(usage.params).length === 0 ? (
|
||||
<span className="no-params">(no parameters)</span>
|
||||
<span className="no-params">{t('importAnalysis.noParameters')}</span>
|
||||
) : (
|
||||
Object.entries(usage.params).map(([key, value]) => (
|
||||
<span key={key} className="macro-param">
|
||||
|
||||
@@ -329,7 +329,7 @@ export const SettingsView: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleBrowseDataPath = async () => {
|
||||
const selected = await window.electronAPI?.app.selectFolder('Select Project Data Folder');
|
||||
const selected = await window.electronAPI?.app.selectFolder(t('settings.project.selectDataFolder'));
|
||||
if (selected) {
|
||||
setProjectDataPath(selected);
|
||||
}
|
||||
@@ -668,7 +668,7 @@ export const SettingsView: React.FC = () => {
|
||||
<SettingSection
|
||||
id="settings-section-content"
|
||||
title={t('settings.content.title')}
|
||||
description="Manage the available categories for blog posts. Each post can have one category that determines its display template."
|
||||
description={t('settings.content.description')}
|
||||
hidden={!sectionHasMatches(contentKeywords)}
|
||||
>
|
||||
<div className="categories-list">
|
||||
@@ -677,12 +677,12 @@ export const SettingsView: React.FC = () => {
|
||||
const setting = categorySettings[cat] || { renderInLists: true, showTitle: true };
|
||||
return (
|
||||
<div key={cat} className="category-item">
|
||||
<span className="category-name">{cat}{isProtected && ' (standard)'}</span>
|
||||
<span className="category-name">{cat}{isProtected && t('settings.content.standardSuffix')}</span>
|
||||
<div className="category-settings-controls">
|
||||
<label className="category-setting-toggle" htmlFor={`category-${cat}-render-in-lists`}>
|
||||
<input
|
||||
id={`category-${cat}-render-in-lists`}
|
||||
aria-label={`${cat} render in lists`}
|
||||
aria-label={t('settings.content.renderInListsAria', { category: cat })}
|
||||
type="checkbox"
|
||||
checked={setting.renderInLists}
|
||||
onChange={(event) => handleCategorySettingToggle(cat, 'renderInLists', event.target.checked)}
|
||||
@@ -692,7 +692,7 @@ export const SettingsView: React.FC = () => {
|
||||
<label className="category-setting-toggle" htmlFor={`category-${cat}-show-title`}>
|
||||
<input
|
||||
id={`category-${cat}-show-title`}
|
||||
aria-label={`${cat} show titles`}
|
||||
aria-label={t('settings.content.showTitlesAria', { category: cat })}
|
||||
type="checkbox"
|
||||
checked={setting.showTitle}
|
||||
onChange={(event) => handleCategorySettingToggle(cat, 'showTitle', event.target.checked)}
|
||||
@@ -704,7 +704,7 @@ export const SettingsView: React.FC = () => {
|
||||
<button
|
||||
className="category-remove"
|
||||
onClick={() => handleRemoveCategory(cat)}
|
||||
title={`Remove "${cat}" category`}
|
||||
title={t('settings.content.removeCategoryTitle', { category: cat })}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
@@ -808,7 +808,7 @@ export const SettingsView: React.FC = () => {
|
||||
<SettingSection
|
||||
id="settings-section-ai"
|
||||
title={t('settings.ai.title')}
|
||||
description="Configure the AI chat assistant that helps you manage your blog content."
|
||||
description={t('settings.ai.description')}
|
||||
hidden={!sectionHasMatches(aiKeywords)}
|
||||
>
|
||||
<SettingRow
|
||||
@@ -1059,8 +1059,8 @@ export const SettingsView: React.FC = () => {
|
||||
|
||||
<SettingRow
|
||||
id="rebuild-media"
|
||||
label="Rebuild Media Database"
|
||||
description="Re-scan all media files and sidecar metadata. Regenerates missing entries."
|
||||
label={t('settings.data.rebuildMediaLabel')}
|
||||
description={t('settings.data.rebuildMediaDescription')}
|
||||
>
|
||||
<button
|
||||
className="secondary"
|
||||
@@ -1080,14 +1080,14 @@ export const SettingsView: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Rebuild Media
|
||||
{t('settings.data.rebuildMediaAction')}
|
||||
</button>
|
||||
</SettingRow>
|
||||
|
||||
<SettingRow
|
||||
id="rebuild-links"
|
||||
label="Rebuild Post Links"
|
||||
description="Re-scan all posts and rebuild the internal link graph between posts."
|
||||
label={t('settings.data.rebuildLinksLabel')}
|
||||
description={t('settings.data.rebuildLinksDescription')}
|
||||
>
|
||||
<button
|
||||
className="secondary"
|
||||
@@ -1103,14 +1103,14 @@ export const SettingsView: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Rebuild Links
|
||||
{t('settings.data.rebuildLinksAction')}
|
||||
</button>
|
||||
</SettingRow>
|
||||
|
||||
<SettingRow
|
||||
id="regenerate-thumbnails"
|
||||
label="Regenerate Thumbnails"
|
||||
description="Generate missing thumbnails for all images. Useful after importing media externally."
|
||||
label={t('settings.data.regenerateThumbnailsLabel')}
|
||||
description={t('settings.data.regenerateThumbnailsDescription')}
|
||||
>
|
||||
<button
|
||||
className="secondary"
|
||||
@@ -1132,20 +1132,20 @@ export const SettingsView: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Generate Thumbnails
|
||||
{t('settings.data.regenerateThumbnailsAction')}
|
||||
</button>
|
||||
</SettingRow>
|
||||
</SettingSection>
|
||||
|
||||
<SettingSection
|
||||
title={t('settings.data.fileSystemTitle')}
|
||||
description="Access project data files and directories."
|
||||
description={t('settings.data.fileSystemDescription')}
|
||||
hidden={!sectionHasMatches(dataKeywords)}
|
||||
>
|
||||
<SettingRow
|
||||
id="open-data"
|
||||
label="Open Data Folder"
|
||||
description="Open the project data folder containing posts, media, and database files."
|
||||
label={t('settings.data.openDataFolderLabel')}
|
||||
description={t('settings.data.openDataFolderDescription')}
|
||||
>
|
||||
<button
|
||||
className="secondary"
|
||||
@@ -1156,7 +1156,7 @@ export const SettingsView: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Open Folder
|
||||
{t('settings.data.openFolderAction')}
|
||||
</button>
|
||||
</SettingRow>
|
||||
</SettingSection>
|
||||
|
||||
Reference in New Issue
Block a user