diff --git a/.env.example b/.env.example index 219cfdca..7f2946bf 100644 --- a/.env.example +++ b/.env.example @@ -37,6 +37,11 @@ NUXT_TURNSTILE_SECRET_KEY=your-turnstile-secret-key NUXT_PUBLIC_FEEDBACK_API_BASE=https://support.gospelambition.org NUXT_PUBLIC_FEEDBACK_PROJECT_ID=your-feedback-project-id +# DeepL (used by the CMS "Translate from English" workflow on /admin/pages/:id) +# Pro endpoint: https://api.deepl.com | Free-tier endpoint: https://api-free.deepl.com +DEEPL_API_KEY=your-deepl-api-key +DEEPL_API_URL=https://api.deepl.com + # OAuth 2.1 server # Issuer is derived from NUXT_PUBLIC_SITE_URL; resource is `${siteUrl}/mcp`. # Generate with: openssl rand -hex 32 diff --git a/app/pages/admin/pages/[id].vue b/app/pages/admin/pages/[id].vue index bb41a237..e8f939ef 100644 --- a/app/pages/admin/pages/[id].vue +++ b/app/pages/admin/pages/[id].vue @@ -202,13 +202,13 @@ for (const l of ENABLED_LANGUAGES) { // active locale's translation (PUT /translations/:locale) in one click. // Passing a statusOverride also flips published/draft on the translation. const saving = ref(false) -async function saveAll(statusOverride?: 'draft' | 'published') { - const locale = activeLocale.value +async function saveAll(statusOverride?: 'draft' | 'published', localeOverride?: string): Promise { + const locale = localeOverride ?? activeLocale.value const f = forms[locale] - if (!f) return + if (!f) return false if (!f.title.trim()) { toast.add({ title: 'Title is required', color: 'error' }) - return + return false } saving.value = true try { @@ -249,12 +249,14 @@ async function saveAll(statusOverride?: 'draft' | 'published') { : 'Saved' toast.add({ title: `${verb} ${locale}`, color: 'success' }) await refresh() + return true } catch (e: any) { toast.add({ title: 'Save failed', description: e?.data?.statusMessage || e?.message, color: 'error' }) + return false } finally { saving.value = false } @@ -300,16 +302,25 @@ const translateModalOpen = ref(false) const translateSource = ref('en') const translateTargets = ref([]) const translateOverwrite = ref(false) +const translateStatus = ref<'draft' | 'published'>('draft') const translating = ref(false) function openTranslateModal() { translateSource.value = 'en' translateTargets.value = ENABLED_LANGUAGES.map(l => l.code).filter(c => c !== 'en') translateOverwrite.value = false + translateStatus.value = 'draft' translateModalOpen.value = true } async function runTranslate() { + // Persist any unsaved edits in the source-locale tab first — the + // translate endpoint reads from the DB, not from the in-memory form. + const sourceForm = forms[translateSource.value] + if (sourceForm && (sourceForm.dirty || !sourceForm.loaded)) { + const ok = await saveAll(undefined, translateSource.value) + if (!ok) return + } translating.value = true try { const res = await $fetch<{ results: Array<{ locale: string; skipped?: boolean; error?: string }> }>( @@ -319,7 +330,8 @@ async function runTranslate() { body: { sourceLocale: translateSource.value, targetLocales: translateTargets.value, - overwrite: translateOverwrite.value + overwrite: translateOverwrite.value, + status: translateStatus.value } } ) @@ -355,7 +367,7 @@ const enabledLanguages = ENABLED_LANGUAGES

Edit page

- Translate from English + Translate from English Delete page
@@ -623,7 +635,8 @@ const enabledLanguages = ENABLED_LANGUAGES :items="enabledLanguages.map(l => ({ label: l.nativeName, value: l.code }))" /> - +
+

Target locales

+
+ + { @@ -34,6 +36,7 @@ export default defineEventHandler(async (event) => { if (targetLocales.length === 0) { throw createError({ statusCode: 400, statusMessage: 'targetLocales is empty' }) } + const status: 'draft' | 'published' = body?.status === 'published' ? 'published' : 'draft' const source = await db .selectFrom('page_translations') @@ -83,7 +86,7 @@ export default defineEventHandler(async (event) => { meta_title: translatedMetaTitle || null, meta_description: translatedMetaDescription || null, og_image: source.og_image, - status: 'draft' + status }) results.push({ locale: target }) } catch (e: any) { diff --git a/server/utils/deepl.ts b/server/utils/deepl.ts index 19d8750d..0c752407 100644 --- a/server/utils/deepl.ts +++ b/server/utils/deepl.ts @@ -58,7 +58,7 @@ export async function translateTexts( params.append('model_type', 'quality_optimized') - const apiUrl = config.deeplApiUrl || 'https://api-free.deepl.com' + const apiUrl = config.deeplApiUrl || 'https://api.deepl.com' console.log(`[DeepL] Translating ${texts.length} text(s) → ${targetLang}${sourceLang ? ` from ${sourceLang}` : ''}${glossaryId ? ` (glossary: ${glossaryId})` : ''}`) const response = await fetch(`${apiUrl}/v2/translate`, {