Compare commits

...

8 Commits

Author SHA1 Message Date
Christian Schmidt
ba07924297
Merge fcbe8982e5 into 6d62581da1 2024-11-25 17:05:04 +00:00
Matt Jankowski
6d62581da1
Update binstub templates (#32335)
Some checks are pending
Bundler Audit / security (push) Waiting to run
Check i18n / check-i18n (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Check formatting / lint (push) Waiting to run
Haml Linting / lint (push) Waiting to run
JavaScript Linting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
JavaScript Testing / test (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / Libvips tests (.ruby-version) (push) Blocked by required conditions
Ruby Testing / Libvips tests (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
2024-11-25 16:49:24 +00:00
Claire
9a7130d6da
Fix direct inbox delivery pushing posts into inactive followers' timelines (#33067) 2024-11-25 15:54:18 +00:00
Christian Schmidt
fcbe8982e5 Merge remote-tracking branch 'upstream/main' into detect-language
# Conflicts:
#	app/controllers/api/v1/statuses/translations_controller.rb
#	app/services/translate_status_service.rb
2024-10-19 00:53:15 +02:00
Christian Schmidt
807938573b Merge branch 'main' into detect-language
# Conflicts:
#	spec/services/translate_status_service_spec.rb
2024-10-04 21:12:13 +02:00
Christian Schmidt
22d364e1f6 Merge branch 'main' into detect-language
# Conflicts:
#	app/javascript/mastodon/features/status/components/detailed_status.jsx
2024-09-12 18:43:16 +02:00
Christian Schmidt
e8bbce9043 Fix tests 2024-08-27 22:37:52 +02:00
Christian Schmidt
5d939d70ca Offer language detection 2024-08-27 20:11:30 +02:00
32 changed files with 233 additions and 70 deletions

View File

@ -36,4 +36,4 @@ jobs:
bundler-cache: true
- name: Run bundler-audit
run: bundle exec bundler-audit check --update
run: bin/bundler-audit check --update

View File

@ -35,18 +35,18 @@ jobs:
git diff --exit-code
- name: Check locale file normalization
run: bundle exec i18n-tasks check-normalized
run: bin/i18n-tasks check-normalized
- name: Check for unused strings
run: bundle exec i18n-tasks unused
run: bin/i18n-tasks unused
- name: Check for missing strings in English YML
run: |
bundle exec i18n-tasks add-missing -l en
bin/i18n-tasks add-missing -l en
git diff --exit-code
- name: Check for wrong string interpolations
run: bundle exec i18n-tasks check-consistent-interpolations
run: bin/i18n-tasks check-consistent-interpolations
- name: Check that all required locale files exist
run: bundle exec rake repo:check_locales_files
run: bin/rake repo:check_locales_files

View File

@ -46,7 +46,7 @@ jobs:
uses: ./.github/actions/setup-ruby
- name: Run i18n normalize task
run: bundle exec i18n-tasks normalize
run: bin/i18n-tasks normalize
# Create or update the pull request
- name: Create Pull Request

View File

@ -48,7 +48,7 @@ jobs:
uses: ./.github/actions/setup-ruby
- name: Run i18n normalize task
run: bundle exec i18n-tasks normalize
run: bin/i18n-tasks normalize
# Create or update the pull request
- name: Create Pull Request

View File

@ -43,4 +43,4 @@ jobs:
- name: Run haml-lint
run: |
echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json"
bundle exec haml-lint --reporter github
bin/haml-lint --reporter github

View File

@ -1031,7 +1031,7 @@ DEPENDENCIES
xorcist (~> 1.1)
RUBY VERSION
ruby 3.3.5p100
ruby 3.3.6p108
BUNDLED WITH
2.5.22
2.5.23

View File

@ -23,6 +23,6 @@ class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseControl
private
def set_translation
@translation = TranslateStatusService.new.call(@status, I18n.locale.to_s)
@translation = TranslateStatusService.new.call(@status, params[:source_language], I18n.locale.to_s)
end
end

View File

@ -101,6 +101,7 @@ export function normalizeStatusTranslation(translation, status) {
const emojiMap = makeEmojiMap(status.get('emojis').toJS());
const normalTranslation = {
source_language: translation.source_language,
detected_source_language: translation.detected_source_language,
language: translation.language,
provider: translation.provider,

View File

@ -335,10 +335,10 @@ export function toggleStatusCollapse(id, isCollapsed) {
};
}
export const translateStatus = id => (dispatch) => {
export const translateStatus = (id, source_language) => (dispatch) => {
dispatch(translateStatusRequest(id));
api().post(`/api/v1/statuses/${id}/translate`).then(response => {
api().post(`/api/v1/statuses/${id}/translate`, { source_language }).then(response => {
dispatch(translateStatusSuccess(id, response.data));
}).catch(error => {
dispatch(translateStatusFail(id, error));

View File

@ -108,6 +108,7 @@ class Status extends ImmutablePureComponent {
onToggleHidden: PropTypes.func,
onToggleCollapsed: PropTypes.func,
onTranslate: PropTypes.func,
onUndoStatusTranslation: PropTypes.func,
onInteractionModal: PropTypes.func,
muted: PropTypes.bool,
hidden: PropTypes.bool,
@ -188,8 +189,12 @@ class Status extends ImmutablePureComponent {
this.props.onToggleCollapsed(this._properStatus(), isCollapsed);
};
handleTranslate = () => {
this.props.onTranslate(this._properStatus());
handleTranslate = (sourceLanguage) => {
this.props.onTranslate(this._properStatus(), sourceLanguage);
};
handleUndoStatusTranslation = () => {
this.props.onUndoStatusTranslation(this._properStatus());
};
getAttachmentAspectRatio () {
@ -569,6 +574,7 @@ class Status extends ImmutablePureComponent {
status={status}
onClick={this.handleClick}
onTranslate={this.handleTranslate}
onUndoStatusTranslation={this.handleUndoStatusTranslation}
collapsible
onCollapsedToggle={this.handleCollapsedToggle}
{...statusContentProps}

View File

@ -30,32 +30,47 @@ class TranslateButton extends PureComponent {
static propTypes = {
translation: ImmutablePropTypes.map,
onClick: PropTypes.func,
onTranslate: PropTypes.func,
onDetectLanguage: PropTypes.func,
onUndoStatusTranslation: PropTypes.func,
};
render () {
const { translation, onClick } = this.props;
const { translation, onTranslate, onDetectLanguage, onUndoStatusTranslation } = this.props;
if (translation) {
const language = preloadedLanguages.find(lang => lang[0] === translation.get('detected_source_language'));
const languageName = language ? language[2] : translation.get('detected_source_language');
const provider = translation.get('provider');
const languageDetectionButton = onDetectLanguage && (
<>
<button className='link-button' onClick={onDetectLanguage}>
<FormattedMessage id='status.detect_language' defaultMessage='Detect language' />
</button>
·
</>
);
return (
<div className='translate-button'>
<div className='translate-button__meta'>
<FormattedMessage id='status.translated_from_with' defaultMessage='Translated from {lang} using {provider}' values={{ lang: languageName, provider }} />
</div>
<button className='link-button' onClick={onClick}>
<FormattedMessage id='status.show_original' defaultMessage='Show original' />
</button>
<div className='translate-button__buttons'>
{languageDetectionButton}
<button className='link-button' onClick={onUndoStatusTranslation}>
<FormattedMessage id='status.show_original' defaultMessage='Show original' />
</button>
</div>
</div>
);
}
return (
<button className='status__content__translate-button' onClick={onClick}>
<button className='status__content__translate-button' onClick={onTranslate}>
<FormattedMessage id='status.translate' defaultMessage='Translate' />
</button>
);
@ -73,6 +88,7 @@ class StatusContent extends PureComponent {
status: ImmutablePropTypes.map.isRequired,
statusContent: PropTypes.string,
onTranslate: PropTypes.func,
onUndoStatusTranslation: PropTypes.func,
onClick: PropTypes.func,
collapsible: PropTypes.bool,
onCollapsedToggle: PropTypes.func,
@ -215,6 +231,10 @@ class StatusContent extends PureComponent {
this.props.onTranslate();
};
handleDetectLanguage = () => {
this.props.onTranslate('und');
};
setRef = (c) => {
this.node = c;
};
@ -224,9 +244,13 @@ class StatusContent extends PureComponent {
const renderReadMore = this.props.onClick && status.get('collapsed');
const contentLocale = intl.locale.replace(/[_-].*/, '');
const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
const sourceLanguage = status.get('language') || 'und';
const targetLanguages = this.props.languages?.get(sourceLanguage);
const renderTranslate = this.props.onTranslate && this.props.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
const languageDetectionTargetLanguages = this.props.languages?.get('und');
const renderDetectLanguage = sourceLanguage !== 'und' && languageDetectionTargetLanguages?.includes(contentLocale) && status.get('translation')?.get('source_language') !== 'und';
const content = { __html: statusContent ?? getStatusContent(status) };
const language = status.getIn(['translation', 'language']) || status.get('language');
const classNames = classnames('status__content', {
@ -241,7 +265,12 @@ class StatusContent extends PureComponent {
);
const translateButton = renderTranslate && (
<TranslateButton onClick={this.handleTranslate} translation={status.get('translation')} />
<TranslateButton
onTranslate={this.handleTranslate}
onDetectLanguage={renderDetectLanguage && this.handleDetectLanguage}
onUndoStatusTranslation={this.props.onUndoStatusTranslation}
translation={status.get('translation')}
/>
);
const poll = !!status.get('poll') && (

View File

@ -122,12 +122,12 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({
});
},
onTranslate (status) {
if (status.get('translation')) {
dispatch(undoStatusTranslation(status.get('id'), status.get('poll')));
} else {
dispatch(translateStatus(status.get('id')));
}
onTranslate (status, sourceLanguage) {
dispatch(translateStatus(status.get('id'), sourceLanguage));
},
onUndoStatusTranslation (status) {
dispatch(undoStatusTranslation(status.get('id'), status.get('poll')));
},
onDirect (account) {

View File

@ -43,7 +43,8 @@ export const DetailedStatus: React.FC<{
status: any;
onOpenMedia?: (status: any, index: number, lang: string) => void;
onOpenVideo?: (status: any, lang: string, options: VideoModalOptions) => void;
onTranslate?: (status: any) => void;
onTranslate?: (status: any, sourceLanguage?: string) => void;
onUndoStatusTranslation?: (status: any) => void;
measureHeight?: boolean;
onHeightChange?: () => void;
domain: string;
@ -58,6 +59,7 @@ export const DetailedStatus: React.FC<{
onOpenMedia,
onOpenVideo,
onTranslate,
onUndoStatusTranslation,
measureHeight,
onHeightChange,
domain,
@ -110,9 +112,16 @@ export const DetailedStatus: React.FC<{
[_measureHeight],
);
const handleTranslate = useCallback(() => {
if (onTranslate) onTranslate(status);
}, [onTranslate, status]);
const handleTranslate = useCallback(
(sourceLanguage?: string) => {
if (onTranslate) onTranslate(status, sourceLanguage);
},
[onTranslate, status],
);
const handleUndoStatusTranslation = useCallback(() => {
if (onUndoStatusTranslation) onUndoStatusTranslation(status);
}, [onUndoStatusTranslation, status]);
if (!properStatus) {
return null;
@ -350,6 +359,7 @@ export const DetailedStatus: React.FC<{
<StatusContent
status={status}
onTranslate={handleTranslate}
onUndoStatusTranslation={handleUndoStatusTranslation}
{...(statusContentProps as any)}
/>

View File

@ -392,14 +392,16 @@ class Status extends ImmutablePureComponent {
}
};
handleTranslate = status => {
handleTranslate = (status, sourceLanguage) => {
const { dispatch } = this.props;
if (status.get('translation')) {
dispatch(undoStatusTranslation(status.get('id'), status.get('poll')));
} else {
dispatch(translateStatus(status.get('id')));
}
dispatch(translateStatus(status.get('id'), sourceLanguage));
};
handleUndoStatusTranslation = status => {
const { dispatch } = this.props;
dispatch(undoStatusTranslation(status.get('id'), status.get('poll')));
};
handleBlockClick = (status) => {
@ -675,6 +677,7 @@ class Status extends ImmutablePureComponent {
onOpenMedia={this.handleOpenMedia}
onToggleHidden={this.handleToggleHidden}
onTranslate={this.handleTranslate}
onUndoStatusTranslation={this.handleUndoStatusTranslation}
domain={domain}
showMedia={this.state.showMedia}
onToggleMediaVisibility={this.handleToggleMediaVisibility}

View File

@ -824,6 +824,7 @@
"status.copy": "Copy link to post",
"status.delete": "Delete",
"status.detailed_status": "Detailed conversation view",
"status.detect_language": "Detect language",
"status.direct": "Privately mention @{name}",
"status.direct_indicator": "Private mention",
"status.edit": "Edit",

View File

@ -1348,6 +1348,17 @@ body > [data-popper-placement] {
display: flex;
justify-content: space-between;
color: $dark-text-color;
.translate-button__buttons {
margin-right: -6px;
float: right;
white-space: nowrap;
.link-button {
display: inline;
margin: 0 6px;
}
}
}
.status__content__spoiler-link {

View File

@ -58,6 +58,7 @@ class FeedManager
# @param [Boolean] update
# @return [Boolean]
def push_to_home(account, status, update: false)
return false unless account.user&.signed_in_recently?
return false unless add_to_feed(:home, account.id, status, aggregate_reblogs: account.user&.aggregates_reblogs?)
trim(:home, account.id)
@ -83,7 +84,9 @@ class FeedManager
# @param [Boolean] update
# @return [Boolean]
def push_to_list(list, status, update: false)
return false if filter_from_list?(status, list) || !add_to_feed(:list, list.id, status, aggregate_reblogs: list.account.user&.aggregates_reblogs?)
return false if filter_from_list?(status, list)
return false unless list.account.user&.signed_in_recently?
return false unless add_to_feed(:list, list.id, status, aggregate_reblogs: list.account.user&.aggregates_reblogs?)
trim(:list, list.id)
PushUpdateWorker.perform_async(list.account_id, status.id, "timeline:list:#{list.id}", { 'update' => update }) if push_update_required?("timeline:list:#{list.id}")

View File

@ -11,6 +11,7 @@ class TranslationService::DeepL < TranslationService
end
def translate(texts, source_language, target_language)
source_language = nil if source_language == 'und'
form = { text: texts, source_lang: source_language&.upcase, target_lang: target_language, tag_handling: 'html' }
request(:post, '/v2/translate', form: form) do |res|
transform_response(res.body_with_limit)
@ -18,7 +19,7 @@ class TranslationService::DeepL < TranslationService
end
def languages
source_languages = [nil] + fetch_languages('source')
source_languages = ['und'] + fetch_languages('source')
# In DeepL, EN and PT are deprecated in favor of EN-GB/EN-US and PT-BR/PT-PT, so
# they are supported but not returned by the API.

View File

@ -9,7 +9,8 @@ class TranslationService::LibreTranslate < TranslationService
end
def translate(texts, source_language, target_language)
body = Oj.dump(q: texts, source: source_language.presence || 'auto', target: target_language, format: 'html', api_key: @api_key)
source_language = 'auto' if source_language.in? [nil, 'und']
body = Oj.dump(q: texts, source: source_language, target: target_language, format: 'html', api_key: @api_key)
request(:post, '/translate', body: body) do |res|
transform_response(res.body_with_limit, source_language)
end
@ -20,7 +21,7 @@ class TranslationService::LibreTranslate < TranslationService
languages = Oj.load(res.body_with_limit).to_h do |language|
[language['code'], language['targets'].without(language['code'])]
end
languages[nil] = languages.values.flatten.uniq.sort
languages['und'] = languages.values.flatten.uniq.sort
languages
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class Translation < ActiveModelSerializers::Model
attributes :status, :detected_source_language, :language, :provider,
attributes :status, :source_language, :detected_source_language, :language, :provider,
:content, :spoiler_text, :poll_options, :media_attachments
class Option < ActiveModelSerializers::Model

View File

@ -165,6 +165,10 @@ class User < ApplicationRecord
end
end
def signed_in_recently?
current_sign_in_at.present? && current_sign_in_at >= ACTIVE_DURATION.ago
end
def confirmed?
confirmed_at.present?
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class REST::TranslationSerializer < ActiveModel::Serializer
attributes :detected_source_language, :language, :provider, :spoiler_text, :content
attributes :source_language, :detected_source_language, :language, :provider, :spoiler_text, :content
class PollSerializer < ActiveModel::Serializer
attribute :id

View File

@ -6,17 +6,18 @@ class TranslateStatusService < BaseService
include ERB::Util
include FormattingHelper
def call(status, target_language)
def call(status, source_language, target_language)
@status = status
@source_texts = source_texts
@source_language = source_language || @status.language.presence || 'und'
target_language = target_language.split(/[_-]/).first unless target_languages.include?(target_language)
@target_language = target_language
raise Mastodon::NotPermittedError unless permitted?
status_translation = Rails.cache.fetch("v2:translations/#{@status.language}/#{@target_language}/#{content_hash}", expires_in: CACHE_TTL) do
translations = translation_backend.translate(@source_texts.values, @status.language, @target_language)
status_translation = Rails.cache.fetch("v3:translations/#{@source_language}/#{@target_language}/#{content_hash}", expires_in: CACHE_TTL) do
translations = translation_backend.translate(@source_texts.values, @source_language, @target_language)
build_status_translation(translations)
end
@ -38,7 +39,7 @@ class TranslateStatusService < BaseService
end
def languages
Rails.cache.fetch('translation_service/languages', expires_in: 7.days, race_condition_ttl: 1.hour) { translation_backend.languages }
Rails.cache.fetch('v2:translation_service/languages', expires_in: 7.days, race_condition_ttl: 1.hour) { translation_backend.languages }
end
def target_languages
@ -67,6 +68,7 @@ class TranslateStatusService < BaseService
def build_status_translation(translations)
status_translation = Translation.new(
source_language: @source_language,
detected_source_language: translations.first&.detected_source_language,
language: @target_language,
provider: translations.first&.provider,

27
bin/bundler-audit Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'bundler-audit' is installed as part of a gem, and
# this file is here to facilitate running it.
#
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"
load Gem.bin_path("bundler-audit", "bundler-audit")

27
bin/haml-lint Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'haml-lint' is installed as part of a gem, and
# this file is here to facilitate running it.
#
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"
load Gem.bin_path("haml_lint", "haml-lint")

27
bin/i18n-tasks Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'i18n-tasks' is installed as part of a gem, and
# this file is here to facilitate running it.
#
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"
load Gem.bin_path("i18n-tasks", "i18n-tasks")

View File

@ -1,5 +1,6 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
@ -7,9 +8,18 @@
# this file is here to facilitate running it.
#
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"

View File

@ -3,7 +3,7 @@ const config = {
'Capfile|Gemfile|*.{rb,ruby,ru,rake}': 'bin/rubocop --force-exclusion -a',
'*.{js,jsx,ts,tsx}': 'eslint --fix',
'*.{css,scss}': 'stylelint --fix',
'*.haml': 'bundle exec haml-lint -a',
'*.haml': 'bin/haml-lint -a',
'**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit',
};

View File

@ -38,7 +38,7 @@ RSpec.describe TranslationService::DeepL do
.with(body: 'text=Guten+Tag&source_lang&target_lang=en&tag_handling=html')
.to_return(body: '{"translations":[{"detected_source_language":"DE","text":"Good morning"}]}')
translations = service.translate(['Guten Tag'], nil, 'en')
translations = service.translate(['Guten Tag'], 'und', 'en')
expect(translations.size).to eq 1
translation = translations.first
@ -62,7 +62,7 @@ RSpec.describe TranslationService::DeepL do
describe '#languages' do
it 'returns source languages' do
expect(service.languages.keys).to eq [nil, 'en', 'uk']
expect(service.languages.keys).to eq %w(und en uk)
end
it 'returns target languages for each source language' do
@ -71,7 +71,7 @@ RSpec.describe TranslationService::DeepL do
end
it 'returns target languages for auto-detection' do
expect(service.languages[nil]).to eq %w(en pt en-GB zh)
expect(service.languages['und']).to eq %w(en pt en-GB zh)
end
end

View File

@ -15,7 +15,7 @@ RSpec.describe TranslationService::LibreTranslate do
subject(:languages) { service.languages }
it 'returns source languages' do
expect(languages.keys).to eq ['en', 'da', nil]
expect(languages.keys).to eq %w(en da und)
end
it 'returns target languages for each source language' do
@ -24,7 +24,7 @@ RSpec.describe TranslationService::LibreTranslate do
end
it 'returns target languages for auto-detected language' do
expect(languages[nil]).to eq %w(de en es pt)
expect(languages['und']).to eq %w(de en es pt)
end
end

View File

@ -34,7 +34,7 @@ RSpec.describe 'API V1 Statuses Translations' do
translation = TranslationService::Translation.new(text: 'Hello')
service = instance_double(TranslationService::DeepL, translate: [translation])
allow(TranslationService).to receive_messages(configured?: true, configured: service)
Rails.cache.write('translation_service/languages', { 'es' => ['en'] })
Rails.cache.write('v2:translation_service/languages', { 'es' => ['en'] })
post "/api/v1/statuses/#{status.id}/translate", headers: headers
end

View File

@ -33,7 +33,7 @@ RSpec.describe TranslateStatusService do
end
it 'returns translated status content and source language and provider and original status' do
expect(service.call(status, 'es'))
expect(service.call(status, nil, 'es'))
.to have_attributes(
content: '<p>Hola</p>',
detected_source_language: 'en',
@ -47,13 +47,13 @@ RSpec.describe TranslateStatusService do
let(:text) { 'Hello & :highfive:' }
it 'does not translate shortcode' do
expect(service.call(status, 'es').content).to eq '<p>Hola &amp; :highfive:</p>'
expect(service.call(status, nil, 'es').content).to eq '<p>Hola &amp; :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 ''
expect(service.call(status, nil, 'es').spoiler_text).to eq ''
end
end
@ -61,7 +61,7 @@ RSpec.describe TranslateStatusService do
let(:spoiler_text) { 'Hello & Hello!' }
it 'translates the spoiler text' do
expect(service.call(status, 'es').spoiler_text).to eq 'Hola & Hola!'
expect(service.call(status, nil, 'es').spoiler_text).to eq 'Hola & Hola!'
end
end
@ -69,7 +69,7 @@ RSpec.describe TranslateStatusService do
let(:spoiler_text) { 'Hello :highfive:' }
it 'does not translate shortcode' do
expect(service.call(status, 'es').spoiler_text).to eq 'Hola :highfive:'
expect(service.call(status, nil, 'es').spoiler_text).to eq 'Hola :highfive:'
end
end
@ -77,7 +77,7 @@ RSpec.describe TranslateStatusService do
let(:spoiler_text) { 'Hello :Hello:' }
it 'translates the invalid shortcode' do
expect(service.call(status, 'es').spoiler_text).to eq 'Hola :Hola:'
expect(service.call(status, nil, 'es').spoiler_text).to eq 'Hola :Hola:'
end
end
@ -85,7 +85,7 @@ RSpec.describe TranslateStatusService do
let(:poll) { Fabricate(:poll, options: ['Hello 1', 'Hello 2']) }
it 'translates the poll option title' do
status_translation = service.call(status, 'es')
status_translation = service.call(status, nil, 'es')
expect(status_translation.poll_options.size).to eq 2
expect(status_translation.poll_options.first.title).to eq 'Hola 1'
end
@ -95,7 +95,7 @@ RSpec.describe TranslateStatusService do
let(:media_attachments) { [Fabricate(:media_attachment, description: 'Hello & :highfive:')] }
it 'translates the media attachment description' do
status_translation = service.call(status, 'es')
status_translation = service.call(status, nil, 'es')
media_attachment = status_translation.media_attachments.first
expect(media_attachment.id).to eq media_attachments.first.id
@ -105,11 +105,11 @@ RSpec.describe TranslateStatusService do
describe 'target language is regional' do
it 'uses regional variant' do
expect(service.call(status, 'es-MX').language).to eq 'es-MX'
expect(service.call(status, nil, 'es-MX').language).to eq 'es-MX'
end
it 'uses parent locale for unsupported regional variant' do
expect(service.call(status, 'es-XX').language).to eq 'es'
expect(service.call(status, nil, 'es-XX').language).to eq 'es'
end
end
end