feat(admin): add multilingual document editor with DeepL translation
- Display English and French versions side-by-side for all documents - Add 'Translate EN → FR' button using DeepL - Show word counts for each language version - Display translation metadata (translatedBy, approved status) - Mark primary language for each document - Support readonly mode for blog-linked content Documents tab now shows: - Main Article (EN/FR) - Cover Letter (EN/FR) - Author Bio (EN/FR) - Pitch Email (EN/FR) Next: Add translation button click handler and API endpoint
This commit is contained in:
parent
d34ce5fa1e
commit
f5b8f1a85c
1 changed files with 87 additions and 1 deletions
|
|
@ -441,7 +441,93 @@ function renderPublicationRequirements(publicationId) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Helper: Render document editor
|
||||
* Helper: Render multilingual document editor
|
||||
* Supports multiple language versions with translation
|
||||
*/
|
||||
function renderMultilingualDocument(docType, title, submission, article, isStandalone) {
|
||||
const doc = submission.documents?.[docType] || {};
|
||||
const versions = doc.versions || [];
|
||||
const primaryLang = doc.primaryLanguage || 'en';
|
||||
|
||||
// Get English and French versions
|
||||
const enVersion = versions.find(v => v.language === 'en');
|
||||
const frVersion = versions.find(v => v.language === 'fr');
|
||||
|
||||
// For main article, use blog content as English version if no submission version exists
|
||||
let enContent = enVersion?.content || '';
|
||||
if (docType === 'mainArticle' && !enContent && article?.content) {
|
||||
enContent = article.content;
|
||||
}
|
||||
|
||||
const frContent = frVersion?.content || '';
|
||||
|
||||
const enWordCount = enContent ? enContent.split(/\s+/).filter(w => w.length > 0).length : 0;
|
||||
const frWordCount = frContent ? frContent.split(/\s+/).filter(w => w.length > 0).length : 0;
|
||||
|
||||
const readonly = docType === 'mainArticle' && !isStandalone && article;
|
||||
|
||||
return `
|
||||
<div class="border rounded-lg overflow-hidden mb-6">
|
||||
<div class="bg-gray-100 px-4 py-3 flex items-center justify-between">
|
||||
<h4 class="font-semibold text-gray-900">${escapeHtml(title)}</h4>
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
data-action="translate-document"
|
||||
data-doc-type="${docType}"
|
||||
data-from-lang="en"
|
||||
data-to-lang="fr"
|
||||
class="px-3 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700 ${enContent ? '' : 'opacity-50 cursor-not-allowed'}"
|
||||
${enContent ? '' : 'disabled'}
|
||||
title="Translate English to French using DeepL">
|
||||
🌍 EN → FR
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- English Version -->
|
||||
<div class="border-b bg-white">
|
||||
<div class="px-4 py-2 bg-gray-50 flex items-center justify-between border-b">
|
||||
<span class="text-sm font-medium text-gray-700">🇬🇧 English Version${primaryLang === 'en' ? ' (Primary)' : ''}</span>
|
||||
<span class="text-xs text-gray-600">${enWordCount.toLocaleString()} words</span>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<textarea
|
||||
id="doc-${docType}-en"
|
||||
data-doc-type="${docType}"
|
||||
data-lang="en"
|
||||
rows="8"
|
||||
class="w-full border rounded-md p-3 text-sm font-mono resize-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
${readonly ? 'readonly' : ''}
|
||||
placeholder="${readonly ? '' : 'Enter ' + title.toLowerCase() + ' in English...'}"
|
||||
>${escapeHtml(enContent)}</textarea>
|
||||
${readonly ? '<p class="text-xs text-gray-500 mt-2">🔒 Linked from blog post - edit the blog post to change this content</p>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- French Version -->
|
||||
<div class="bg-white">
|
||||
<div class="px-4 py-2 bg-gray-50 flex items-center justify-between border-b">
|
||||
<span class="text-sm font-medium text-gray-700">🇫🇷 French Version${primaryLang === 'fr' ? ' (Primary)' : ''}</span>
|
||||
<span class="text-xs text-gray-600">${frWordCount.toLocaleString()} words</span>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<textarea
|
||||
id="doc-${docType}-fr"
|
||||
data-doc-type="${docType}"
|
||||
data-lang="fr"
|
||||
rows="8"
|
||||
class="w-full border rounded-md p-3 text-sm font-mono resize-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="Enter ${title.toLowerCase()} in French (or translate from English)..."
|
||||
>${escapeHtml(frContent)}</textarea>
|
||||
${frVersion?.translatedBy ? `<p class="text-xs text-gray-500 mt-2">Translated by: ${frVersion.translatedBy}${frVersion.approved ? ' ✓ Approved' : ''}</p>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Render document editor (legacy single-language version)
|
||||
*/
|
||||
function renderDocumentEditor(docType, title, content, wordCount, readonly) {
|
||||
const readonlyAttr = readonly ? 'readonly' : '';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue