mirror of
https://github.com/discourse/discourse.git
synced 2025-10-03 17:21:20 +08:00
FEATURE: Show translated category names from localizations table if they exist for the user locale (#32380)
This PR adds category localization support to core. - Depends on https://github.com/discourse/discourse/pull/32378 - Creates the required table `category_localizations` - Makes use of the site category cache to display localized categories - Supports name and description - This does not yet include /categories page. (coming in a next PR) ## Before <img width="1089" alt="Screenshot 2025-04-21 at 4 31 43 PM" src="https://github.com/user-attachments/assets/9e49b21b-b16a-43d2-9b16-0fd0324a21ca" /> ## After <img width="1085" alt="Screenshot 2025-04-21 at 4 32 42 PM" src="https://github.com/user-attachments/assets/7bfa21a2-6df2-4cdf-a00a-d044c3526a40" />
This commit is contained in:
parent
d5b2b186b4
commit
b0ab1b2321
9 changed files with 145 additions and 15 deletions
|
@ -36,6 +36,7 @@ class Category < ActiveRecord::Base
|
|||
has_many :category_users
|
||||
has_many :category_featured_topics
|
||||
has_many :featured_topics, through: :category_featured_topics, source: :topic
|
||||
has_many :category_localizations, dependent: :destroy
|
||||
|
||||
has_many :category_groups, dependent: :destroy
|
||||
has_many :category_moderation_groups, dependent: :destroy
|
||||
|
|
33
app/models/category_localization.rb
Normal file
33
app/models/category_localization.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CategoryLocalization < ActiveRecord::Base
|
||||
belongs_to :category
|
||||
|
||||
validates :locale, presence: true, length: { maximum: 20 }
|
||||
validates :name, presence: true, length: { maximum: 50 }
|
||||
validates :category_id, uniqueness: { scope: :locale }
|
||||
|
||||
after_commit :invalidate_site_cache
|
||||
|
||||
def invalidate_site_cache
|
||||
I18n.with_locale(locale) { Site.clear_cache }
|
||||
end
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: category_localizations
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# category_id :bigint not null
|
||||
# locale :string(20) not null
|
||||
# name :string(50) not null
|
||||
# description :text
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_category_localizations_on_category_id (category_id)
|
||||
# index_category_localizations_on_category_id_and_locale (category_id,locale) UNIQUE
|
||||
#
|
|
@ -73,24 +73,35 @@ class Site
|
|||
categories =
|
||||
begin
|
||||
query =
|
||||
Category
|
||||
.includes(
|
||||
:uploaded_logo,
|
||||
:uploaded_logo_dark,
|
||||
:uploaded_background,
|
||||
:uploaded_background_dark,
|
||||
:tags,
|
||||
:tag_groups,
|
||||
:form_templates,
|
||||
category_required_tag_groups: :tag_group,
|
||||
)
|
||||
.joins("LEFT JOIN topics t on t.id = categories.topic_id")
|
||||
.select("categories.*, t.slug topic_slug")
|
||||
.order(:position)
|
||||
Category.includes(
|
||||
:uploaded_logo,
|
||||
:uploaded_logo_dark,
|
||||
:uploaded_background,
|
||||
:uploaded_background_dark,
|
||||
:tags,
|
||||
:tag_groups,
|
||||
:form_templates,
|
||||
category_required_tag_groups: :tag_group,
|
||||
).joins("LEFT JOIN topics t on t.id = categories.topic_id")
|
||||
|
||||
if SiteSetting.experimental_content_localization
|
||||
locale = I18n.locale.to_s
|
||||
query =
|
||||
query.joins(
|
||||
"LEFT JOIN category_localizations cl ON cl.category_id = categories.id AND cl.locale = '#{ActiveRecord::Base.connection.quote_string(locale)}'",
|
||||
).select(
|
||||
"categories.*,
|
||||
t.slug topic_slug,
|
||||
COALESCE(cl.name, categories.name) AS name,
|
||||
COALESCE(cl.description, categories.description) AS description",
|
||||
)
|
||||
else
|
||||
query = query.select("categories.*, t.slug topic_slug")
|
||||
end
|
||||
|
||||
query = query.order(:position)
|
||||
query =
|
||||
DiscoursePluginRegistry.apply_modifier(:site_all_categories_cache_query, query, self)
|
||||
|
||||
query.to_a
|
||||
end
|
||||
|
||||
|
|
|
@ -2768,6 +2768,7 @@ en:
|
|||
about_page_hidden_groups: "Do not show members of specific groups on the /about page."
|
||||
adobe_analytics_tags_url: "Adobe Analytics tags URL (`https://assets.adobedtm.com/...`)"
|
||||
view_raw_email_allowed_groups: "Groups which can view the raw email content of a post if it was created by an incoming email. This includes email headers and other technical information."
|
||||
experimental_content_localization: "Displays localized content for users based on their language preferences. Such content may include categories, tags, posts, and topics. This feature is under heavy development."
|
||||
errors:
|
||||
invalid_css_color: "Invalid color. Enter a color name or hex value."
|
||||
invalid_email: "Invalid email address."
|
||||
|
|
|
@ -3973,3 +3973,6 @@ experimental:
|
|||
client: true
|
||||
default: false
|
||||
hidden: true
|
||||
experimental_content_localization:
|
||||
client: true
|
||||
default: false
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateCategoryLocalizationTable < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
create_table :category_localizations do |t|
|
||||
t.references :category, null: false
|
||||
t.string :locale, limit: 20, null: false
|
||||
t.string :name, limit: 50, null: false
|
||||
t.text :description, null: true
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
add_index :category_localizations, %i[category_id locale], unique: true
|
||||
end
|
||||
end
|
8
spec/fabricators/category_localization_fabricator.rb
Normal file
8
spec/fabricators/category_localization_fabricator.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Fabricator(:category_localization) do
|
||||
category
|
||||
locale "ja"
|
||||
name { sequence(:name) { |i| "ワク" * (i + 1) } }
|
||||
description "日本のディスカッション"
|
||||
end
|
29
spec/models/category_localization_spec.rb
Normal file
29
spec/models/category_localization_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe CategoryLocalization do
|
||||
context "when commit" do
|
||||
it "clears the site cache for the locale" do
|
||||
category = Fabricate(:category, name: "yy")
|
||||
|
||||
I18n.locale = "es"
|
||||
expect(Site.all_categories_cache.pluck(:name)).to include("yy")
|
||||
|
||||
I18n.locale = "en"
|
||||
expect(Site.all_categories_cache.pluck(:name)).to include("yy")
|
||||
|
||||
category.update_columns(name: "zz")
|
||||
|
||||
I18n.locale = "es"
|
||||
expect(Site.all_categories_cache.pluck(:name)).to include("yy")
|
||||
I18n.locale = "en"
|
||||
expect(Site.all_categories_cache.pluck(:name)).to include("yy")
|
||||
|
||||
Fabricate(:category_localization, name: "Japón", locale: "es", category:)
|
||||
|
||||
I18n.locale = "es"
|
||||
expect(Site.all_categories_cache.pluck(:name)).to include("zz")
|
||||
I18n.locale = "en"
|
||||
expect(Site.all_categories_cache.pluck(:name)).to include("yy")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -344,5 +344,33 @@ RSpec.describe Site do
|
|||
&modifier_block
|
||||
)
|
||||
end
|
||||
|
||||
describe "experimental_content_localization" do
|
||||
it "returns localized category names when enabled" do
|
||||
SiteSetting.experimental_content_localization = true
|
||||
|
||||
localization = Fabricate(:category_localization)
|
||||
category = localization.category
|
||||
locale = localization.locale.to_sym
|
||||
|
||||
I18n.locale = locale
|
||||
|
||||
all_categories_cache = Site.all_categories_cache
|
||||
cached_category = all_categories_cache.find { |c| c[:id] == category.id }
|
||||
expect(cached_category[:name]).to eq(localization.name)
|
||||
expect(cached_category[:description]).to eq(localization.description)
|
||||
end
|
||||
|
||||
it "returns original names when enabled" do
|
||||
SiteSetting.experimental_content_localization = true
|
||||
|
||||
category = Fabricate(:category, name: "derp", description: "derp derp")
|
||||
|
||||
all_categories_cache = Site.all_categories_cache
|
||||
cached_category = all_categories_cache.find { |c| c[:id] == category.id }
|
||||
expect(cached_category[:name]).to eq(category.name)
|
||||
expect(cached_category[:description]).to eq(category.description)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue