feat: localize official plugins in Russian

This commit is contained in:
2026-07-11 12:42:37 +08:00
parent 077de94e61
commit 3dd0ec9f1f
57 changed files with 1478 additions and 330 deletions
+34 -23
View File
@@ -273,18 +273,22 @@
var scope = scopeFromProps(props || {});
var entries = [];
var statusText = 'Loading journal...';
function tr(key, params, fallback) {
if (api && api.i18n && typeof api.i18n.t === 'function') return api.i18n.t(key, params, fallback);
return fallback || key;
}
var statusText = tr('ui.loading', null, 'Loading journal...');
var statusClass = '';
var modalHost = el('div', { className: 'journal-modal-host', hidden: 'hidden' });
var toolbar = el('div', { className: 'journal-toolbar' });
var titleEl = el('span', { className: 'journal-title', textContent: scope.mode === 'global' ? 'Journal' : 'Journal · ' + scope.label });
var titleEl = el('span', { className: 'journal-title', textContent: scope.mode === 'global' ? tr('ui.title', null, 'Journal') : tr('ui.workspaceTitle', { workspace: scope.label }, 'Journal · ' + scope.label) });
var countEl = el('span', { className: 'journal-count' });
var statusEl = el('span', { className: 'journal-status' });
var addBtn = el('button', {
className: 'journal-btn primary',
'data-journal-action': 'add',
innerHTML: iconSvg('add') + '<span>Add</span>',
innerHTML: iconSvg('add') + '<span>' + tr('ui.add', null, 'Add') + '</span>',
onClick: function () { showEntryModal(); }
});
toolbar.appendChild(titleEl);
@@ -302,7 +306,7 @@
if (scope.mode !== 'workspace') return Promise.resolve();
if (!api || !api.settings || typeof api.settings.write !== 'function') return Promise.resolve();
return api.settings.write(scope.key, storageEntries(entries)).catch(function (err) {
statusText = 'Could not save journal: ' + (err && err.message ? err.message : String(err));
statusText = tr('ui.saveError', { error: err && err.message ? err.message : String(err) }, 'Could not save journal: ' + (err && err.message ? err.message : String(err)));
statusClass = 'error';
});
}
@@ -316,19 +320,19 @@
all = all.concat(normalizeEntries((settings || {})[key], key));
});
entries = sortEntries(all);
statusText = 'Aggregating worklogs';
statusText = tr('ui.aggregating', null, 'Aggregating worklogs');
statusClass = '';
}).catch(function (err) {
statusText = 'Could not load journal: ' + (err && err.message ? err.message : String(err));
statusText = tr('ui.loadError', { error: err && err.message ? err.message : String(err) }, 'Could not load journal: ' + (err && err.message ? err.message : String(err)));
statusClass = 'error';
});
}
return api.settings.read(scope.key).then(function (stored) {
entries = sortEntries(normalizeEntries(stored, scope.key));
statusText = 'Ready';
statusText = tr('ui.ready', null, 'Ready');
statusClass = '';
}).catch(function (err) {
statusText = 'Could not load journal: ' + (err && err.message ? err.message : String(err));
statusText = tr('ui.loadError', { error: err && err.message ? err.message : String(err) }, 'Could not load journal: ' + (err && err.message ? err.message : String(err)));
statusClass = 'error';
});
}
@@ -344,8 +348,8 @@
var reviewingCandidate = !editing && !!candidate;
var reviewingTodo = !editing && !!completedTodo;
var dateInput = el('input', { className: 'journal-input', type: 'date', value: editing ? existingEntry.date : (reviewingCandidate ? candidateDate(candidate.startedAt) : (reviewingTodo ? candidateDate(completedTodo.completedAt) : today())), 'data-journal-input': 'date' });
var titleInput = el('input', { className: 'journal-input', type: 'text', placeholder: 'Work item', value: editing ? existingEntry.title : (reviewingTodo ? completedTodo.title : ''), 'data-journal-input': 'title' });
var summaryInput = el('textarea', { className: 'journal-input textarea', placeholder: 'Body', 'data-journal-input': 'summary' });
var titleInput = el('input', { className: 'journal-input', type: 'text', placeholder: tr('ui.workItem', null, 'Work item'), value: editing ? existingEntry.title : (reviewingTodo ? completedTodo.title : ''), 'data-journal-input': 'title' });
var summaryInput = el('textarea', { className: 'journal-input textarea', placeholder: tr('ui.body', null, 'Body'), 'data-journal-input': 'summary' });
summaryInput.value = editing ? existingEntry.summary : (reviewingTodo ? completedTodo.description : '');
var minutesInput = el('input', { className: 'journal-input', type: 'number', min: '0', step: '1', value: editing ? existingEntry.minutes : (reviewingCandidate ? candidate.estimatedMinutes : (reviewingTodo ? '0' : '30')), 'data-journal-input': 'minutes' });
var billableInput = el('input', { type: 'checkbox', 'data-journal-input': 'billable' });
@@ -397,20 +401,20 @@
if (event.target === event.currentTarget) closeEntryModal();
} }, [
el('div', { className: 'journal-modal' }, [
el('div', { className: 'journal-modal-title', textContent: editing ? 'Edit journal entry' : (reviewingCandidate ? 'Review possible journal entry' : (reviewingTodo ? 'Create journal entry from completed todo' : 'Add journal entry')) }),
el('div', { className: 'journal-modal-title', textContent: editing ? tr('ui.editEntry', null, 'Edit journal entry') : (reviewingCandidate ? tr('ui.reviewCandidate', null, 'Review possible journal entry') : (reviewingTodo ? tr('ui.fromTodo', null, 'Create journal entry from completed todo') : tr('ui.addEntry', null, 'Add journal entry'))) }),
candidateContext,
todoContext,
el('div', { className: 'journal-modal-grid' }, [
el('label', { className: 'journal-field' }, ['Date', dateInput]),
el('label', { className: 'journal-field' }, ['Minutes', minutesInput]),
el('label', { className: 'journal-field wide' }, ['Title', titleInput]),
el('label', { className: 'journal-field wide' }, ['Body', summaryInput]),
el('label', { className: 'journal-billable' }, [billableInput, 'Billable'])
el('label', { className: 'journal-field' }, [tr('ui.date', null, 'Date'), dateInput]),
el('label', { className: 'journal-field' }, [tr('ui.minutes', null, 'Minutes'), minutesInput]),
el('label', { className: 'journal-field wide' }, [tr('ui.fieldTitle', null, 'Title'), titleInput]),
el('label', { className: 'journal-field wide' }, [tr('ui.body', null, 'Body'), summaryInput]),
el('label', { className: 'journal-billable' }, [billableInput, tr('ui.billable', null, 'Billable')])
]),
candidateActivities,
el('div', { className: 'journal-modal-actions' }, [
el('button', { className: 'journal-btn ghost', type: 'button', textContent: 'Cancel', onClick: closeEntryModal }),
el('button', { className: 'journal-btn primary', type: 'button', 'data-journal-action': 'save-entry', textContent: editing ? 'Save changes' : 'Add entry', onClick: saveEntry })
el('button', { className: 'journal-btn ghost', type: 'button', textContent: tr('ui.cancel', null, 'Cancel'), onClick: closeEntryModal }),
el('button', { className: 'journal-btn primary', type: 'button', 'data-journal-action': 'save-entry', textContent: editing ? tr('ui.saveChanges', null, 'Save changes') : tr('ui.addEntryShort', null, 'Add entry'), onClick: saveEntry })
])
])
]));
@@ -421,7 +425,7 @@
if (scope.mode !== 'workspace') return;
var title = text(formValue && formValue.title).trim();
if (!title) {
statusText = 'Title is required';
statusText = tr('ui.titleRequired', null, 'Title is required');
statusClass = 'error';
render();
return;
@@ -469,7 +473,7 @@
function deleteEntry(entry) {
if (scope.mode !== 'workspace' || !entry) return;
entries = entries.filter(function (item) { return item.entryId !== entry.entryId; });
statusText = 'Entry deleted';
statusText = tr('ui.deleted', null, 'Entry deleted');
statusClass = '';
persist().then(render);
}
@@ -477,7 +481,7 @@
function renderList() {
listEl.innerHTML = '';
if (!entries.length) {
listEl.appendChild(el('div', { className: 'journal-empty', textContent: scope.mode === 'global' ? 'No worklog entries yet.' : 'No worklog entries yet.' }));
listEl.appendChild(el('div', { className: 'journal-empty', textContent: tr('ui.empty', null, 'No worklog entries yet.') }));
return;
}
entries.forEach(function (entry) {
@@ -493,8 +497,8 @@
]),
el('div', { className: 'journal-minutes', textContent: entry.minutes + ' min' }),
scope.mode === 'workspace' ? el('div', { className: 'journal-row-actions' }, [
el('button', { className: 'journal-icon-btn', type: 'button', title: 'Edit', 'aria-label': 'Edit', 'data-journal-action': 'edit', innerHTML: iconSvg('edit'), onClick: function () { showEntryModal(entry); } }),
el('button', { className: 'journal-icon-btn danger', type: 'button', title: 'Delete', 'aria-label': 'Delete', 'data-journal-action': 'delete', innerHTML: iconSvg('trash'), onClick: function () { deleteEntry(entry); } })
el('button', { className: 'journal-icon-btn', type: 'button', title: tr('ui.edit', null, 'Edit'), 'aria-label': tr('ui.edit', null, 'Edit'), 'data-journal-action': 'edit', innerHTML: iconSvg('edit'), onClick: function () { showEntryModal(entry); } }),
el('button', { className: 'journal-icon-btn danger', type: 'button', title: tr('ui.delete', null, 'Delete'), 'aria-label': tr('ui.delete', null, 'Delete'), 'data-journal-action': 'delete', innerHTML: iconSvg('trash'), onClick: function () { deleteEntry(entry); } })
]) : null
]));
});
@@ -516,6 +520,13 @@
if (candidate) showEntryModal(null, candidate);
else if (completedTodo) showEntryModal(null, null, completedTodo);
});
if (api && api.i18n && typeof api.i18n.onDidChangeLocale === 'function') {
api.i18n.onDidChangeLocale(function () {
titleEl.textContent = scope.mode === 'global' ? tr('ui.title', null, 'Journal') : tr('ui.workspaceTitle', { workspace: scope.label }, 'Journal · ' + scope.label);
addBtn.innerHTML = iconSvg('add') + '<span>' + tr('ui.add', null, 'Add') + '</span>';
render();
});
}
};
JournalView.unmount = function (containerEl) {