Portals can now override their name, page title and header text per locale, with a fallback chain of locale override → default locale → base value. Overrides are stored under portal.config.locale_translations and validated with a JSON schema. Editing is exposed as a "Localize content" action in each non-default locale's menu on the Locale page, and all public-facing portal views (classic + documentation layouts) render the localized values. The live chat widget is also loaded in the active portal locale. <img width="494" height="395" alt="Screenshot 2026-06-03 at 2 48 59 PM" src="https://github.com/user-attachments/assets/e55a06e8-f20f-4d8a-9352-92fde28a9a19" /> https://github.com/user-attachments/assets/f5affbd2-7ad4-415a-b376-57c759fc2aaa --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59 lines
2.1 KiB
Ruby
59 lines
2.1 KiB
Ruby
class Public::Api::V1::PortalsController < Public::Api::V1::Portals::BaseController
|
|
before_action :ensure_custom_domain_request, only: [:show]
|
|
before_action :redirect_to_portal_with_locale, only: [:show]
|
|
before_action :portal
|
|
before_action :set_portal_layout
|
|
before_action :set_view_variant
|
|
before_action :ensure_portal_feature_enabled
|
|
before_action :load_home_data, only: [:show], if: -> { @portal_layout == 'documentation' }
|
|
layout 'portal'
|
|
|
|
def show
|
|
@og_image_url = helpers.set_og_image_url('', @portal.localized_value('header_text', @locale))
|
|
end
|
|
|
|
def sitemap
|
|
@help_center_url = @portal.custom_domain || ChatwootApp.help_center_root
|
|
# if help_center_url does not contain a protocol, prepend it with https
|
|
@help_center_url = "https://#{@help_center_url}" unless @help_center_url.include?('://')
|
|
end
|
|
|
|
private
|
|
|
|
def portal
|
|
@portal ||= Portal.find_by!(slug: params[:slug], archived: false)
|
|
@locale = params[:locale] || @portal.default_locale
|
|
end
|
|
|
|
def redirect_to_portal_with_locale
|
|
return if params[:locale].present?
|
|
|
|
portal
|
|
redirect_to "/hc/#{@portal.slug}/#{@portal.default_locale}"
|
|
end
|
|
|
|
def load_home_data
|
|
base_articles = @portal.articles.published.where(locale: @locale).includes(:author, :category)
|
|
@visible_categories = @portal.categories
|
|
.where(locale: @locale)
|
|
.joins(:articles).where(articles: { status: :published })
|
|
.order(position: :asc)
|
|
.group('categories.id')
|
|
@popular_topics = @visible_categories.first(3)
|
|
@featured = base_articles.order_by_views.limit(6)
|
|
@category_contributors = build_category_contributors(@visible_categories)
|
|
end
|
|
|
|
def build_category_contributors(categories)
|
|
category_ids = categories.map(&:id)
|
|
return {} if category_ids.empty?
|
|
|
|
@portal.articles
|
|
.published
|
|
.where(locale: @locale, category_id: category_ids)
|
|
.includes(:author)
|
|
.group_by(&:category_id)
|
|
.transform_values { |articles| articles.filter_map(&:author).uniq.first(3) }
|
|
end
|
|
end
|