{
+ it('returns false and null when contentAttributes is null', () => {
+ const contentAttributes = ref(null);
+ const { hasTranslations, translationContent } =
+ useTranslations(contentAttributes);
+ expect(hasTranslations.value).toBe(false);
+ expect(translationContent.value).toBeNull();
+ });
+
+ it('returns false and null when translations are missing', () => {
+ const contentAttributes = ref({});
+ const { hasTranslations, translationContent } =
+ useTranslations(contentAttributes);
+ expect(hasTranslations.value).toBe(false);
+ expect(translationContent.value).toBeNull();
+ });
+
+ it('returns false and null when translations is an empty object', () => {
+ const contentAttributes = ref({ translations: {} });
+ const { hasTranslations, translationContent } =
+ useTranslations(contentAttributes);
+ expect(hasTranslations.value).toBe(false);
+ expect(translationContent.value).toBeNull();
+ });
+
+ it('returns true and correct translation content when translations exist', () => {
+ const contentAttributes = ref({
+ translations: { en: 'Hello' },
+ });
+ const { hasTranslations, translationContent } =
+ useTranslations(contentAttributes);
+ expect(hasTranslations.value).toBe(true);
+ // Should return the first translation (en: 'Hello')
+ expect(translationContent.value).toBe('Hello');
+ });
+});
diff --git a/app/javascript/dashboard/composables/useTranslations.js b/app/javascript/dashboard/composables/useTranslations.js
new file mode 100644
index 000000000..0b18ea3da
--- /dev/null
+++ b/app/javascript/dashboard/composables/useTranslations.js
@@ -0,0 +1,22 @@
+import { computed } from 'vue';
+
+/**
+ * Composable to extract translation state/content from contentAttributes.
+ * @param {Ref|Reactive} contentAttributes - Ref or reactive object containing `translations` property
+ * @returns {Object} { hasTranslations, translationContent }
+ */
+export function useTranslations(contentAttributes) {
+ const hasTranslations = computed(() => {
+ if (!contentAttributes.value) return false;
+ const { translations = {} } = contentAttributes.value;
+ return Object.keys(translations || {}).length > 0;
+ });
+
+ const translationContent = computed(() => {
+ if (!hasTranslations.value) return null;
+ const translations = contentAttributes.value.translations;
+ return translations[Object.keys(translations)[0]];
+ });
+
+ return { hasTranslations, translationContent };
+}
diff --git a/lib/integrations/google_translate/processor_service.rb b/lib/integrations/google_translate/processor_service.rb
index a1a3e6401..33fe56770 100644
--- a/lib/integrations/google_translate/processor_service.rb
+++ b/lib/integrations/google_translate/processor_service.rb
@@ -1,15 +1,19 @@
require 'google/cloud/translate/v3'
+
class Integrations::GoogleTranslate::ProcessorService
pattr_initialize [:message!, :target_language!]
def perform
- return if message.content.blank?
return if hook.blank?
+ content = translation_content
+ return if content.blank?
+
response = client.translate_text(
- contents: [message.content],
+ contents: [content],
target_language_code: target_language,
- parent: "projects/#{hook.settings['project_id']}"
+ parent: "projects/#{hook.settings['project_id']}",
+ mime_type: mime_type
)
return if response.translations.first.blank?
@@ -19,6 +23,43 @@ class Integrations::GoogleTranslate::ProcessorService
private
+ def email_channel?
+ message&.inbox&.email?
+ end
+
+ def email_content
+ @email_content ||= {
+ html: message.content_attributes.dig('email', 'html_content', 'full'),
+ text: message.content_attributes.dig('email', 'text_content', 'full'),
+ content_type: message.content_attributes.dig('email', 'content_type')
+ }
+ end
+
+ def html_content_available?
+ email_content[:html].present?
+ end
+
+ def plain_text_content_available?
+ email_content[:content_type]&.include?('text/plain') &&
+ email_content[:text].present?
+ end
+
+ def translation_content
+ return message.content unless email_channel?
+ return email_content[:html] if html_content_available?
+ return email_content[:text] if plain_text_content_available?
+
+ message.content
+ end
+
+ def mime_type
+ if email_channel? && html_content_available?
+ 'text/html'
+ else
+ 'text/plain'
+ end
+ end
+
def hook
@hook ||= message.account.hooks.find_by(app_id: 'google_translate')
end