Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions app/controllers/maglev/api/page_translations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

module Maglev
module Api
class PageTranslationsController < ::Maglev::ApiController
before_action :set_page

def index
translations = @page.translations_for(:sections, params[:locale])
render json: { translated: translations&.size.to_i > 0 }
end

def create
translate_page(@page)
head :ok
end

private

def set_page
@page ||= resources.find(params[:page_id])
end

def translate_page(page)
services.translate_page.call(
page: page,
locale: params[:locale],
source_locale: maglev_site.default_locale_prefix.to_s
)
end

def resources
::Maglev::Page
end
end
end
end
1 change: 1 addition & 0 deletions app/frontend/editor/assets/remixicons/ri-translate.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 20 additions & 1 deletion app/frontend/editor/components/sidebar-nav/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
:tooltipMessage="$t('sidebarNav.managePageSectionsTooltip')"
/>
</li>
<li v-if="isTranslatable">
<sidebar-nav-link
:isRouterLink="false"
iconName="ri-translate"
:tooltipMessage="$t('sidebarNav.translateTooltip')"
@click.prevent="openTranslateModal"
/>
</li>
<li v-if="hasStyle">
<sidebar-nav-link
:routerLinkName="'editStyle'"
Expand Down Expand Up @@ -59,12 +67,14 @@

<script>
import ImageLibrary from '@/components/image-library/index.vue'
import TranslateDialog from '@/components/translate-dialog/index.vue'
import SidebarNavLink from './link.vue'

export default {
name: 'SidebarNav',
components: {
SidebarNavLink
SidebarNavLink,
TranslateDialog
},
computed: {
hasStyle() {
Expand All @@ -85,6 +95,9 @@ export default {
isEditPageActive() {
return this.$route.name === 'editPageSettings'
},
isTranslatable() {
return this.currentSectionList.length === 0 && (this.currentLocale !== this.currentDefaultLocale)
},
leaveEditorUrl() {
return window.leaveUrl
},
Expand All @@ -97,6 +110,12 @@ export default {
props: { modalClass: 'w-216' },
})
},
openTranslateModal() {
this.openModal({
title: this.$t('translateDialog.title'),
component: TranslateDialog,
})
}
},
}
</script>
94 changes: 94 additions & 0 deletions app/frontend/editor/components/translate-dialog/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<template>
<div class="flex flex-col space-y-8">

<i18n
path="translateDialog.description"
tag="div"
class="text-gray-700"
>
<template v-slot:locale>
<strong class="text-gray-900">{{ localeName }}</strong>
</template>
<template v-slot:sourceLocale>
<strong class="text-gray-900">{{ sourceLocaleName }}</strong>
</template>
</i18n>

<div>
<uikit-submit-button
type="button"
class="big-submit-button"
defaultColorClass="bg-editor-primary"
:labels="$t('translateDialog.submitButton')"
:buttonState="submitState"
@click="translate"
>
{{ $t('page.edit.submitButton') }}
</uikit-submit-button>
<button
class="cancel-button"
@click="$emit('on-close')"
>
{{ $t('translateDialog.cancelButton') }}
</button>
</div>
</div>
</template>

<script>
export default {
name: 'TranslateDialog',
data() {
return { submitState: 'default', pollingSubmission: false, pollingInterval: null }
},
computed: {
localeName() {
return this.$t(`support.locales.${this.currentLocale}`)
},
sourceLocaleName() {
return this.$t(`support.locales.${this.currentDefaultLocale}`)
}
},
unmounted() {
if (this.pollingInterval) clearInterval(this.pollingInterval)
},
methods: {
translate() {
this.submitState = 'inProgress'
this.services.page.translate(this.currentPage.id, this.currentLocale)
.then(() => {
this.pollingSubmission = true
// Force a refresh
// this.$nextTick(() => { this.$router.go(0) })
})
.catch(() => {
this.submitState = 'fail'
})
},
isTranslated() {
this.services.page.isTranslated(this.currentPage.id, this.currentLocale)
.then((translated) => {
if (translated) {
this.pollingSubmission = false
// Force a refresh
this.$nextTick(() => { this.$router.go(0) })
}
})
.catch(() => {
this.pollingSubmission = false
this.submitState = 'fail'
})
}
},
watch: {
pollingSubmission(newValue, oldValue) {
if (!newValue) {
if (this.pollingInterval) clearInterval(this.pollingInterval)
return
}

this.pollingInterval = setInterval(() => this.isTranslated(), 1000)
}
}
}
</script>
20 changes: 19 additions & 1 deletion app/frontend/editor/locales/editor.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"listPagesTooltip": "Manage the pages of your site",
"managePageSectionsTooltip": "Re-order / delete the sections of the page",
"editStyleTooltip": "Change the style of your site",
"translateTooltip": "Translate the page",
"openImageLibraryTooltip": "Open the gallery of images",
"leaveEditorTooltip": "Back to the main app"
},
Expand All @@ -35,7 +36,7 @@
"withoutLocale": "Your page is blank",
"withLocale": "Your page in %{localeName} is blank"
},
"message": "Please add the first section by clicking on the {icon} button in the left sidebar."
"message": "Please add the first section by clicking on the {icon} button or translate the whole page by clicking on the {translateIcon} button."
}
},
"page": {
Expand Down Expand Up @@ -225,6 +226,16 @@
"emptyLabel": "No items found"
}
},
"translateDialog": {
"title": "Translate the current page",
"description": "This page is not translated into %{locale} yet. Do you want to translate it now from %{sourceLocale}?",
"submitButton": {
"default": "Translate",
"inProgress": "Translating...",
"success": "Translated!"
},
"cancelButton": "Cancel"
},
"pagination": {
"defaultLabel": "%{start} - %{end} of %{totalItems} items",
"defaultNoItems": "None"
Expand All @@ -247,6 +258,13 @@
}
},
"support": {
"locales": {
"en": "English",
"es": "Spanish",
"fr": "French",
"pt-BR": "Portuguese",
"ar": "Arabic"
},
"human": {
"storageUnits": {
"format": "%{number} %{unit}",
Expand Down
3 changes: 3 additions & 0 deletions app/frontend/editor/mixins/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Vue.mixin({
currentLocale() {
return this.$store.state.locale
},
currentDefaultLocale() {
return this.currentSite.locales[0].prefix
},
currentPage() {
return this.$store.state.page
},
Expand Down
10 changes: 10 additions & 0 deletions app/frontend/editor/services/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ export default (api) => ({
return api.post(`/pages/${id}/clones`, {}).then(({ data }) => data)
},

translate: (id, locale) => {
console.log('[PageService] Translating page #', id)
return api.post(`/pages/${id}/translations`, { locale }).then(({ data }) => data)
},

isTranslated: (id, locale) => {
console.log('[PageService] Checking if page is translated #', id, locale)
return api.get(`/pages/${id}/translations`, { params: { locale } }).then(({ data }) => data.translated)
},

destroy: (id) => {
console.log('[PageService] Destroying page #', id)
return api.destroy(`/pages/${id}`)
Expand Down
18 changes: 10 additions & 8 deletions app/frontend/editor/views/page-preview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,28 @@
<template v-slot:localeName>
<span
class="capitalize-first bg-editor-primary bg-opacity-20 px-2"
>{{ currentLocaleName }}</span
>{{ $t(`support.locales.${currentLocale}`) }}</span
>
</template>
</i18n>

<i18n
path="pagePreview.empty.message"
tag="div"
class="flex mt-4 text-gray-600"
class="mt-4 text-gray-600 max-w-md mx-auto leading-9"
>
<template v-slot:icon>
<uikit-icon
name="ri-stack-line"
size="1.5rem"
class="mx-1 text-black"
class="mx-1 text-black inline-block"
/>
</template>
<template v-slot:translateIcon>
<uikit-icon
name="ri-translate"
size="1.5rem"
class="mx-1 text-black inline-block"
/>
</template>
</i18n>
Expand Down Expand Up @@ -105,11 +112,6 @@ export default {
numberOfLocales() {
return this.currentSite.locales.length
},
currentLocaleName() {
return this.currentSite.locales.find(
(locale) => locale.prefix === this.currentLocale,
).label
},
deviceClass() {
switch (this.device) {
case 'mobile':
Expand Down
5 changes: 3 additions & 2 deletions app/models/concerns/maglev/translatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ module Translatable
class UnavailableLocaleError < RuntimeError; end
extend ActiveSupport::Concern

def translations_for(attr)
def translations_for(attr, locale = nil)
# With MySQL, there is no default value for JSON columns, so we need to check for nil
public_send("#{attr}_translations").presence || {}
translations = public_send("#{attr}_translations").presence || {}
locale.present? ? translations[locale.to_s] : translations
end

def translate_attr_in(attr, locale, source_locale)
Expand Down
1 change: 1 addition & 0 deletions app/services/maglev/app_container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class AppContainer
fetch_collection_items get_page_fullpath]
dependency :get_page_section_names, class: Maglev::GetPageSectionNames, depends_on: :fetch_theme
dependency :clone_page, class: Maglev::ClonePage, depends_on: :fetch_site
dependency :translate_page, class: Maglev::TranslatePage, depends_on: %i[fetch_site fetch_theme]
dependency :persist_page, class: Maglev::PersistPage, depends_on: %i[fetch_theme]

def call
Expand Down
Loading