Translate CW, poll options and media descriptions (#24175)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>local
parent
44cd88adc4
commit
69057467cb
25 changed files with 603 additions and 100 deletions
@ -0,0 +1,14 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
class Translation < ActiveModelSerializers::Model |
||||
attributes :status, :detected_source_language, :language, :provider, |
||||
:content, :spoiler_text, :poll_options, :media_attachments |
||||
|
||||
class Option < ActiveModelSerializers::Model |
||||
attributes :title |
||||
end |
||||
|
||||
class MediaAttachment < ActiveModelSerializers::Model |
||||
attributes :id, :description |
||||
end |
||||
end |
@ -1,9 +1,38 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
class REST::TranslationSerializer < ActiveModel::Serializer |
||||
attributes :content, :detected_source_language, :provider |
||||
attributes :detected_source_language, :language, :provider, :spoiler_text, :content |
||||
|
||||
def content |
||||
object.text |
||||
class PollSerializer < ActiveModel::Serializer |
||||
attribute :id |
||||
has_many :options |
||||
|
||||
def id |
||||
object.status.preloadable_poll.id.to_s |
||||
end |
||||
|
||||
def options |
||||
object.poll_options |
||||
end |
||||
|
||||
class OptionSerializer < ActiveModel::Serializer |
||||
attributes :title |
||||
end |
||||
end |
||||
|
||||
has_one :poll, serializer: PollSerializer |
||||
|
||||
class MediaAttachmentSerializer < ActiveModel::Serializer |
||||
attributes :id, :description |
||||
|
||||
def id |
||||
object.id.to_s |
||||
end |
||||
end |
||||
|
||||
has_many :media_attachments, serializer: MediaAttachmentSerializer |
||||
|
||||
def poll |
||||
object if object.status.preloadable_poll |
||||
end |
||||
end |
||||
|
@ -0,0 +1,226 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe TranslateStatusService, type: :service do |
||||
subject(:service) { described_class.new } |
||||
|
||||
let(:status) { Fabricate(:status, text: text, spoiler_text: spoiler_text, language: 'en', preloadable_poll: poll, media_attachments: media_attachments) } |
||||
let(:text) { 'Hello' } |
||||
let(:spoiler_text) { '' } |
||||
let(:poll) { nil } |
||||
let(:media_attachments) { [] } |
||||
|
||||
before do |
||||
Fabricate(:custom_emoji, shortcode: 'highfive') |
||||
end |
||||
|
||||
describe '#call' do |
||||
before do |
||||
translation_service = TranslationService.new |
||||
allow(translation_service).to receive(:languages).and_return({ 'en' => ['es'] }) |
||||
allow(translation_service).to receive(:translate) do |texts| |
||||
texts.map do |text| |
||||
TranslationService::Translation.new( |
||||
text: text.gsub('Hello', 'Hola').gsub('higfive', 'cincoaltos'), |
||||
detected_source_language: 'en', |
||||
provider: 'Dummy' |
||||
) |
||||
end |
||||
end |
||||
|
||||
allow(TranslationService).to receive(:configured?).and_return(true) |
||||
allow(TranslationService).to receive(:configured).and_return(translation_service) |
||||
end |
||||
|
||||
it 'returns translated status content' do |
||||
expect(service.call(status, 'es').content).to eq '<p>Hola</p>' |
||||
end |
||||
|
||||
it 'returns source language' do |
||||
expect(service.call(status, 'es').detected_source_language).to eq 'en' |
||||
end |
||||
|
||||
it 'returns translation provider' do |
||||
expect(service.call(status, 'es').provider).to eq 'Dummy' |
||||
end |
||||
|
||||
it 'returns original status' do |
||||
expect(service.call(status, 'es').status).to eq status |
||||
end |
||||
|
||||
describe 'status has content with custom emoji' do |
||||
let(:text) { 'Hello & :highfive:' } |
||||
|
||||
it 'does not translate shortcode' do |
||||
expect(service.call(status, 'es').content).to eq '<p>Hola & :highfive:</p>' |
||||
end |
||||
end |
||||
|
||||
describe 'status has no spoiler_text' do |
||||
it 'returns an empty string' do |
||||
expect(service.call(status, 'es').spoiler_text).to eq '' |
||||
end |
||||
end |
||||
|
||||
describe 'status has spoiler_text' do |
||||
let(:spoiler_text) { 'Hello & Hello!' } |
||||
|
||||
it 'translates the spoiler text' do |
||||
expect(service.call(status, 'es').spoiler_text).to eq 'Hola & Hola!' |
||||
end |
||||
end |
||||
|
||||
describe 'status has spoiler_text with custom emoji' do |
||||
let(:spoiler_text) { 'Hello :highfive:' } |
||||
|
||||
it 'does not translate shortcode' do |
||||
expect(service.call(status, 'es').spoiler_text).to eq 'Hola :highfive:' |
||||
end |
||||
end |
||||
|
||||
describe 'status has spoiler_text with unmatched custom emoji' do |
||||
let(:spoiler_text) { 'Hello :Hello:' } |
||||
|
||||
it 'translates the invalid shortcode' do |
||||
expect(service.call(status, 'es').spoiler_text).to eq 'Hola :Hola:' |
||||
end |
||||
end |
||||
|
||||
describe 'status has poll' do |
||||
let(:poll) { Fabricate(:poll, options: ['Hello 1', 'Hello 2']) } |
||||
|
||||
it 'translates the poll option title' do |
||||
status_translation = service.call(status, 'es') |
||||
expect(status_translation.poll_options.size).to eq 2 |
||||
expect(status_translation.poll_options.first.title).to eq 'Hola 1' |
||||
end |
||||
end |
||||
|
||||
describe 'status has media attachment' do |
||||
let(:media_attachments) { [Fabricate(:media_attachment, description: 'Hello & :highfive:')] } |
||||
|
||||
it 'translates the media attachment description' do |
||||
status_translation = service.call(status, 'es') |
||||
|
||||
media_attachment = status_translation.media_attachments.first |
||||
expect(media_attachment.id).to eq media_attachments.first.id |
||||
expect(media_attachment.description).to eq 'Hola & :highfive:' |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '#source_texts' do |
||||
before do |
||||
service.instance_variable_set(:@status, status) |
||||
end |
||||
|
||||
describe 'status only has content' do |
||||
it 'returns formatted content' do |
||||
expect(service.send(:source_texts)).to eq({ content: '<p>Hello</p>' }) |
||||
end |
||||
end |
||||
|
||||
describe 'status content contains custom emoji' do |
||||
let(:status) { Fabricate(:status, text: 'Hello :highfive:') } |
||||
|
||||
it 'returns formatted content' do |
||||
source_texts = service.send(:source_texts) |
||||
expect(source_texts[:content]).to eq '<p>Hello <span translate="no">:highfive:</span></p>' |
||||
end |
||||
end |
||||
|
||||
describe 'status content contains tags' do |
||||
let(:status) { Fabricate(:status, text: 'Hello #hola') } |
||||
|
||||
it 'returns formatted content' do |
||||
source_texts = service.send(:source_texts) |
||||
expect(source_texts[:content]).to include '<p>Hello <a' |
||||
expect(source_texts[:content]).to include '/tags/hola' |
||||
end |
||||
end |
||||
|
||||
describe 'status has spoiler text' do |
||||
let(:status) { Fabricate(:status, spoiler_text: 'Hello :highfive:') } |
||||
|
||||
it 'returns formatted spoiler text' do |
||||
source_texts = service.send(:source_texts) |
||||
expect(source_texts[:spoiler_text]).to eq 'Hello <span translate="no">:highfive:</span>' |
||||
end |
||||
end |
||||
|
||||
describe 'status has poll' do |
||||
let(:poll) { Fabricate(:poll, options: %w(Blue Green)) } |
||||
|
||||
it 'returns formatted poll options' do |
||||
source_texts = service.send(:source_texts) |
||||
expect(source_texts.size).to eq 3 |
||||
expect(source_texts.values).to eq %w(<p>Hello</p> Blue Green) |
||||
|
||||
expect(source_texts.keys.first).to eq :content |
||||
|
||||
option1 = source_texts.keys.second |
||||
expect(option1).to be_a Poll::Option |
||||
expect(option1.id).to eq '0' |
||||
expect(option1.title).to eq 'Blue' |
||||
|
||||
option2 = source_texts.keys.third |
||||
expect(option2).to be_a Poll::Option |
||||
expect(option2.id).to eq '1' |
||||
expect(option2.title).to eq 'Green' |
||||
end |
||||
end |
||||
|
||||
describe 'status has poll with custom emoji' do |
||||
let(:poll) { Fabricate(:poll, options: ['Blue', 'Green :highfive:']) } |
||||
|
||||
it 'returns formatted poll options' do |
||||
html = service.send(:source_texts).values.last |
||||
expect(html).to eq 'Green <span translate="no">:highfive:</span>' |
||||
end |
||||
end |
||||
|
||||
describe 'status has media attachments' do |
||||
let(:text) { '' } |
||||
let(:media_attachments) { [Fabricate(:media_attachment, description: 'Hello :highfive:')] } |
||||
|
||||
it 'returns media attachments without custom emoji rendering' do |
||||
source_texts = service.send(:source_texts) |
||||
expect(source_texts.size).to eq 1 |
||||
|
||||
key, text = source_texts.first |
||||
expect(key).to eq media_attachments.first |
||||
expect(text).to eq 'Hello :highfive:' |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '#wrap_emoji_shortcodes' do |
||||
before do |
||||
service.instance_variable_set(:@status, status) |
||||
end |
||||
|
||||
describe 'string contains custom emoji' do |
||||
let(:text) { ':highfive:' } |
||||
|
||||
it 'renders the emoji' do |
||||
html = service.send(:wrap_emoji_shortcodes, 'Hello :highfive:'.html_safe) |
||||
expect(html).to eq 'Hello <span translate="no">:highfive:</span>' |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '#unwrap_emoji_shortcodes' do |
||||
describe 'string contains custom emoji' do |
||||
it 'inserts the shortcode' do |
||||
fragment = service.send(:unwrap_emoji_shortcodes, '<p>Hello <span translate="no">:highfive:</span>!</p>') |
||||
expect(fragment.to_html).to eq '<p>Hello :highfive:!</p>' |
||||
end |
||||
|
||||
it 'preserves other attributes than translate=no' do |
||||
fragment = service.send(:unwrap_emoji_shortcodes, '<p>Hello <span translate="no" class="foo">:highfive:</span>!</p>') |
||||
expect(fragment.to_html).to eq '<p>Hello <span class="foo">:highfive:</span>!</p>' |
||||
end |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue