discourse/plugins/discourse-ai/lib/translation/category_candidates.rb
Penar Musaraj 90baea1ea7
FEATURE: Switch from opt-in to opt-out for categories in AI translations (#40169)
This PR changes Discourse AI translations from an opt-in category model
to an opt-out model: instead of translating only selected
`ai_translation_target_categories`, it introduces
`ai_translation_excluded_categories`, updates the admin UI copy and save
flow, changes topic/post/category candidate selection and detection jobs
to translate all non-excluded categories by default, and adds a
migration that converts existing target-category settings into the
equivalent excluded-category list for existing sites.

It also updates all related specs.

---------

Co-authored-by: discourse-patch-triage[bot] <272280883+discourse-patch-triage[bot]@users.noreply.github.com>
2026-05-26 14:51:04 -04:00

42 lines
1.4 KiB
Ruby
Vendored

# frozen_string_literal: true
module DiscourseAi
module Translation
class CategoryCandidates < BaseCandidates
private
# all categories that are eligible for translation based on site settings,
# including those without locale detected yet.
def self.get
categories = Category.all
excluded_category_ids = DiscourseAi::Translation.excluded_category_ids
categories =
categories.where.not(id: excluded_category_ids) if excluded_category_ids.present?
categories
end
def self.calculate_completion_per_locale(locale)
base_locale = "#{locale.split("_").first}%"
sql = <<~SQL
WITH eligible_categories AS (
#{get.where.not(categories: { locale: nil }).to_sql}
),
total_count AS (
SELECT COUNT(*) AS count FROM eligible_categories
),
done_count AS (
SELECT COUNT(DISTINCT c.id)
FROM eligible_categories c
LEFT JOIN category_localizations cl ON c.id = cl.category_id AND cl.locale LIKE :base_locale
WHERE c.locale LIKE :base_locale OR cl.category_id IS NOT NULL
)
SELECT d.count AS done, t.count AS total
FROM total_count t, done_count d
SQL
done, total = DB.query_single(sql, base_locale:)
{ done:, total: }
end
end
end
end