From 7cdc05b9a8b098ca0d369d1ba75396e4d6150c33 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Tue, 20 Jan 2026 14:22:54 +0100 Subject: [PATCH] Add `language` attribute to collections (#37549) --- .../api/v1_alpha/collections_controller.rb | 4 ++-- app/models/collection.rb | 2 ++ .../featured_collection_serializer.rb | 13 ++++++++++++- .../rest/base_collection_serializer.rb | 2 +- ...20260119153538_add_language_to_collections.rb | 7 +++++++ db/schema.rb | 3 ++- spec/models/collection_spec.rb | 6 ++++++ spec/requests/api/v1_alpha/collections_spec.rb | 1 + .../featured_collection_serializer_spec.rb | 16 ++++++++++++++++ .../rest/collection_serializer_spec.rb | 2 ++ 10 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20260119153538_add_language_to_collections.rb diff --git a/app/controllers/api/v1_alpha/collections_controller.rb b/app/controllers/api/v1_alpha/collections_controller.rb index d0c4e0f3f0..9d6b2f9a38 100644 --- a/app/controllers/api/v1_alpha/collections_controller.rb +++ b/app/controllers/api/v1_alpha/collections_controller.rb @@ -81,11 +81,11 @@ class Api::V1Alpha::CollectionsController < Api::BaseController end def collection_creation_params - params.permit(:name, :description, :sensitive, :discoverable, :tag_name, account_ids: []) + params.permit(:name, :description, :language, :sensitive, :discoverable, :tag_name, account_ids: []) end def collection_update_params - params.permit(:name, :description, :sensitive, :discoverable, :tag_name) + params.permit(:name, :description, :language, :sensitive, :discoverable, :tag_name) end def check_feature_enabled diff --git a/app/models/collection.rb b/app/models/collection.rb index b732a3d220..334318b73d 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -8,6 +8,7 @@ # description :text not null # discoverable :boolean not null # item_count :integer default(0), not null +# language :string # local :boolean not null # name :string not null # original_number_of_items :integer @@ -36,6 +37,7 @@ class Collection < ApplicationRecord presence: true, numericality: { greater_than_or_equal: 0 }, if: :remote? + validates :language, language: { if: :local?, allow_nil: true } validate :tag_is_usable validate :items_do_not_exceed_limit diff --git a/app/serializers/activitypub/featured_collection_serializer.rb b/app/serializers/activitypub/featured_collection_serializer.rb index e70d155a1a..af4c554851 100644 --- a/app/serializers/activitypub/featured_collection_serializer.rb +++ b/app/serializers/activitypub/featured_collection_serializer.rb @@ -17,9 +17,12 @@ class ActivityPub::FeaturedCollectionSerializer < ActivityPub::Serializer end end - attributes :id, :type, :total_items, :name, :summary, :attributed_to, + attributes :id, :type, :total_items, :name, :attributed_to, :sensitive, :discoverable, :published, :updated + attribute :summary, unless: :language_present? + attribute :summary_map, if: :language_present? + has_one :tag, key: :topic, serializer: ActivityPub::NoteSerializer::TagSerializer has_many :collection_items, key: :ordered_items, serializer: FeaturedItemSerializer @@ -36,6 +39,10 @@ class ActivityPub::FeaturedCollectionSerializer < ActivityPub::Serializer object.description end + def summary_map + { object.language => object.description } + end + def attributed_to ActivityPub::TagManager.instance.uri_for(object.account) end @@ -51,4 +58,8 @@ class ActivityPub::FeaturedCollectionSerializer < ActivityPub::Serializer def updated object.updated_at.iso8601 end + + def language_present? + object.language.present? + end end diff --git a/app/serializers/rest/base_collection_serializer.rb b/app/serializers/rest/base_collection_serializer.rb index be26aac6fe..6bb75e99a3 100644 --- a/app/serializers/rest/base_collection_serializer.rb +++ b/app/serializers/rest/base_collection_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class REST::BaseCollectionSerializer < ActiveModel::Serializer - attributes :id, :uri, :name, :description, :local, :sensitive, + attributes :id, :uri, :name, :description, :language, :local, :sensitive, :discoverable, :item_count, :created_at, :updated_at belongs_to :tag, serializer: REST::StatusSerializer::TagSerializer diff --git a/db/migrate/20260119153538_add_language_to_collections.rb b/db/migrate/20260119153538_add_language_to_collections.rb new file mode 100644 index 0000000000..066288b070 --- /dev/null +++ b/db/migrate/20260119153538_add_language_to_collections.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddLanguageToCollections < ActiveRecord::Migration[8.0] + def change + add_column :collections, :language, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 8dc1e88fd1..8801882808 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2026_01_15_153219) do +ActiveRecord::Schema[8.0].define(version: 2026_01_19_153538) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -382,6 +382,7 @@ ActiveRecord::Schema[8.0].define(version: 2026_01_15_153219) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "item_count", default: 0, null: false + t.string "language" t.index ["account_id"], name: "index_collections_on_account_id" t.index ["tag_id"], name: "index_collections_on_tag_id" end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb index bcc31fd087..b50969b68a 100644 --- a/spec/models/collection_spec.rb +++ b/spec/models/collection_spec.rb @@ -16,12 +16,18 @@ RSpec.describe Collection do it { is_expected.to_not allow_value(nil).for(:discoverable) } + it { is_expected.to allow_value('en').for(:language) } + + it { is_expected.to_not allow_value('randomstuff').for(:language) } + context 'when collection is remote' do subject { Fabricate.build :collection, local: false } it { is_expected.to validate_presence_of(:uri) } it { is_expected.to validate_presence_of(:original_number_of_items) } + + it { is_expected.to allow_value('randomstuff').for(:language) } end context 'when using a hashtag as category' do diff --git a/spec/requests/api/v1_alpha/collections_spec.rb b/spec/requests/api/v1_alpha/collections_spec.rb index 3921fabfde..b529fc2d92 100644 --- a/spec/requests/api/v1_alpha/collections_spec.rb +++ b/spec/requests/api/v1_alpha/collections_spec.rb @@ -115,6 +115,7 @@ RSpec.describe 'Api::V1Alpha::Collections', feature: :collections do { name: 'Low-traffic bots', description: 'Really nice bots, please follow', + language: 'en', sensitive: '0', discoverable: '1', } diff --git a/spec/serializers/activitypub/featured_collection_serializer_spec.rb b/spec/serializers/activitypub/featured_collection_serializer_spec.rb index b01cce12d8..e6bb4ea4b0 100644 --- a/spec/serializers/activitypub/featured_collection_serializer_spec.rb +++ b/spec/serializers/activitypub/featured_collection_serializer_spec.rb @@ -45,4 +45,20 @@ RSpec.describe ActivityPub::FeaturedCollectionSerializer do 'updated' => match_api_datetime_format, }) end + + context 'when a language is set' do + before do + collection.language = 'en' + end + + it 'uses "summaryMap" to include the language' do + expect(subject).to include({ + 'summaryMap' => { + 'en' => 'These are really amazing', + }, + }) + + expect(subject).to_not have_key('summary') + end + end end diff --git a/spec/serializers/rest/collection_serializer_spec.rb b/spec/serializers/rest/collection_serializer_spec.rb index f0baf7dff8..80ed6a559e 100644 --- a/spec/serializers/rest/collection_serializer_spec.rb +++ b/spec/serializers/rest/collection_serializer_spec.rb @@ -18,6 +18,7 @@ RSpec.describe REST::CollectionSerializer do id: 2342, name: 'Exquisite follows', description: 'Always worth a follow', + language: 'en', local: true, sensitive: true, discoverable: false, @@ -31,6 +32,7 @@ RSpec.describe REST::CollectionSerializer do 'id' => '2342', 'name' => 'Exquisite follows', 'description' => 'Always worth a follow', + 'language' => 'en', 'local' => true, 'sensitive' => true, 'discoverable' => false,