discourse/lib/localization_attributes_replacer.rb
Natalie Tay 8a79c788a5
FIX: Do not write localized fancy title to db when fancy_title is null (#37668)
Related:
https://meta.discourse.org/t/yet-another-title-localization-issue/395469

### bug context

tldr; `Topic#fancy_title` saves a fancy_title to db when the fancy_title
is null.

This bug requires a certain incantation to trigger.
- Topic 395465 exists with `title = "Notification level button always
says \"tracking\""` and
`fancy_title = NULL` in the DB
  - A `TopicLocalization` exists for this topic in `zh_CN`
  - `content_localization_enabled` is on

When a crawler hits `GET /t/.../395465?tl=zh_CN`, localization
replacement happens on the topic, which writes the title attribute, so
the state is now
  - `title` = Chinese (modified)
  - `fancy_title` = NULL (untouched, still what was loaded from DB)
  
When serializing via `TopicViewSerializer` which uses
`LocalizedFancyTopicTitleMixin`, we call `topic.fancy_title`. The
`topic#fancy_title` generates the fancy_title from the title value, then
writes the chinese fancy title to db 😢


e935ed63b2/app/models/topic.rb (L532-L545)

### fix

This commit fixes the issue by ensuring the fancy_title is always written
along with the title, preventing the need for invoking
`topic#fancy_title`.
2026-02-10 22:22:07 +08:00

47 lines
1.6 KiB
Ruby

# frozen_string_literal: true
module LocalizationAttributesReplacer
def self.replace_category_attributes(category, crawl_locale)
if loc = get_localization(category, crawl_locale)
category.name = loc.name if loc.name.present?
category.description = loc.description if loc.description.present?
end
while category = category.parent_category
replace_category_attributes(category, crawl_locale)
end
end
def self.replace_topic_attributes(topic, crawl_locale)
if loc = get_localization(topic, crawl_locale)
# assigning directly to title would commit the change to the database
# due to the setter method defined in the Topic model.
# fancy_title must also be set to prevent the lazy DB write in
# Topic#fancy_title from persisting a localized value when fancy_title is NULL.
if loc.title.present?
topic.send(:write_attribute, :title, loc.title)
topic.send(
:write_attribute,
:fancy_title,
loc.fancy_title.presence || Topic.fancy_title(loc.title),
)
end
topic.excerpt = loc.excerpt if loc.excerpt.present?
end
replace_category_attributes(topic.category, crawl_locale) if topic&.category.present?
end
def self.replace_post_attributes(post, crawl_locale)
if loc = get_localization(post, crawl_locale)
post.cooked = loc.cooked if loc.cooked.present?
end
end
private
def self.get_localization(model, crawl_locale)
model.present? && model.locale.present? &&
!LocaleNormalizer.is_same?(model.locale, crawl_locale) && model.get_localization(crawl_locale)
end
end