diff --git a/app/models/onebox_locale_site_setting.rb b/app/models/onebox_locale_site_setting.rb new file mode 100644 index 00000000000..76dd2f83f9b --- /dev/null +++ b/app/models/onebox_locale_site_setting.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# onebox_locale is just like any other locale setting, except it allows for an empty value, +# which is used to indicate that the default_locale should be used for onebox_locale. +class OneboxLocaleSiteSetting < LocaleSiteSetting + def self.valid_value?(val) + supported_locales.include?(val) || val == "" + end + + @lock = Mutex.new +end diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index ac316fa1cd1..686703270d5 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1803,6 +1803,7 @@ en: block_onebox_on_redirect: "Prevent oneboxing for URLs that lead to a redirecting page. This configuration stops the creation of a visual card (onebox) for any URL that redirects to a different destination, ensuring that direct, non-redirecting URLs are prioritized for oneboxing." allowed_inline_onebox_domains: "A list of domains that will be oneboxed in miniature form if linked without a title" enable_inline_onebox_on_all_domains: "Ignore allowed_inline_onebox_domains site setting and allow inline onebox on all domains." + onebox_locale: "The locale sent to onebox providers. If left blank, the default locale will be used." force_custom_user_agent_hosts: "Hosts for which to use the custom onebox user agent on all requests. (Especially useful for hosts that limit access by user agent)." max_oneboxes_per_post: "Set the maximum number of oneboxes that can be included in a single post. Oneboxes provide a preview of linked content within the post." facebook_app_access_token: "A token generated from your Facebook app ID and secret. Used to generate Instagram oneboxes." @@ -3021,7 +3022,6 @@ en: delete_user_max_post_age: "" delete_user_self_max_post_count: "" desktop_category_page_style: "" - mobile_category_page_style: "" detailed_404: "" detect_custom_avatars: "" digest_logo: "" @@ -3214,11 +3214,11 @@ en: here_mention_allowed_groups: "" hidden_post_visible_groups: "" hide_email_address_taken: "" + hide_new_user_profiles: "" hide_post_sensitivity: "" hide_suspension_reasons: "" hide_user_activity_tab: "" hide_user_profiles_from_public: "" - hide_new_user_profiles: "" high_trust_flaggers_auto_hide_posts: "" highlighted_languages: "" history_hours_high: "" @@ -3356,6 +3356,7 @@ en: min_trust_to_send_messages: "" min_username_length: "" minimum_topics_similar: "" + mobile_category_page_style: "" mobile_logo: "" mobile_logo_dark: "" moderators_change_post_ownership: "" @@ -3387,6 +3388,7 @@ en: num_tl3_users_to_silence_new_user: "" num_users_to_silence_new_user: "" old_post_notice_days: "" + onebox_locale: "" opengraph_image: "" page_loading_indicator: "" password_unique_characters: "" diff --git a/config/site_settings.yml b/config/site_settings.yml index eee9c7556ef..35d423d001f 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -2284,6 +2284,10 @@ onebox: onebox_user_agent: default: "" hidden: true + onebox_locale: + default: "" + type: enum + enum: "OneboxLocaleSiteSetting" force_custom_user_agent_hosts: default: "http://codepen.io" type: list diff --git a/lib/onebox/helpers.rb b/lib/onebox/helpers.rb index eea070c2591..57daa62353c 100644 --- a/lib/onebox/helpers.rb +++ b/lib/onebox/helpers.rb @@ -41,10 +41,11 @@ module Onebox canonical_uri = Addressable::URI.parse(canonical_link) if canonical_link && canonical_uri && "#{canonical_uri.host}#{canonical_uri.path}" != "#{uri.host}#{uri.path}" - canonical_options = Oneboxer.get_final_destination_options(canonical_link) - canonical_options["extra_headers"] = { "Accept-Language" => accept_language } - - uri = FinalDestination.new(canonical_link, canonical_options).resolve + uri = + FinalDestination.new( + canonical_link, + Oneboxer.get_final_destination_options(canonical_link), + ).resolve if uri.present? response = ( @@ -104,7 +105,7 @@ module Onebox headers ||= {} headers["User-Agent"] ||= user_agent if user_agent - headers["Accept-Language"] ||= accept_language if accept_language + headers["Accept-Language"] ||= Oneboxer.accept_language request = Net::HTTP::Get.new(uri.request_uri, headers) start_time = Time.now @@ -236,14 +237,6 @@ module Onebox user_agent end - def self.accept_language - if SiteSetting.default_locale == "en" - "en;q=0.9, *;q=0.5" - else - "#{SiteSetting.default_locale.gsub(/_/, "-")};q=0.9, en;q=0.8, *;q=0.5" - end - end - # Percent-encodes a URI string per RFC3986 - https://tools.ietf.org/html/rfc3986 def self.uri_encode(url) return "" unless url diff --git a/lib/oneboxer.rb b/lib/oneboxer.rb index a124e69023c..417ec9e48ae 100644 --- a/lib/oneboxer.rb +++ b/lib/oneboxer.rb @@ -152,7 +152,7 @@ module Oneboxer end def self.redis_cached_response_body_key(uri) - "CACHED_RESPONSE_#{SiteSetting.default_locale}_#{uri}" + "CACHED_RESPONSE_#{onebox_locale}_#{uri}" end # Parse URLs out of HTML, returning the document when finished. @@ -677,6 +677,9 @@ module Oneboxer force_custom_user_agent_hosts: force_custom_user_agent_hosts, preserve_fragment_url_hosts: preserve_fragment_url_hosts, timeout: 5, + headers: { + "Accept-Language" => accept_language, + }, } uri = URI(url) @@ -705,4 +708,16 @@ module Oneboxer fd_options end + + def self.onebox_locale + SiteSetting.onebox_locale.presence || SiteSetting.default_locale + end + + def self.accept_language + if onebox_locale == "en" + "en;q=0.9, *;q=0.5" + else + "#{onebox_locale.gsub(/_/, "-")};q=0.9, en;q=0.8, *;q=0.5" + end + end end diff --git a/spec/lib/oneboxer_spec.rb b/spec/lib/oneboxer_spec.rb index 96f38fdc5f5..263f53ee399 100644 --- a/spec/lib/oneboxer_spec.rb +++ b/spec/lib/oneboxer_spec.rb @@ -957,11 +957,18 @@ RSpec.describe Oneboxer do end it "separates cache by default_locale" do - preview = Oneboxer.preview(url, invalidate_oneboxes: true) + Oneboxer.preview(url, invalidate_oneboxes: true) expect(Oneboxer.cached_response_body_exists?(url)).to eq(true) SiteSetting.default_locale = "fr" expect(Oneboxer.cached_response_body_exists?(url)).to eq(false) end + + it "separates cache by onebox_locale, when set" do + Oneboxer.preview(url, invalidate_oneboxes: true) + expect(Oneboxer.cached_response_body_exists?(url)).to eq(true) + SiteSetting.onebox_locale = "fr" + expect(Oneboxer.cached_response_body_exists?(url)).to eq(false) + end end describe "register_local_handler" do diff --git a/spec/models/onebox_locale_site_setting_spec.rb b/spec/models/onebox_locale_site_setting_spec.rb new file mode 100644 index 00000000000..834771f251c --- /dev/null +++ b/spec/models/onebox_locale_site_setting_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +RSpec.describe OneboxLocaleSiteSetting do + describe ".valid_value?" do + it "returns true for a locale that we have translations for" do + expect(OneboxLocaleSiteSetting.valid_value?("en")).to eq(true) + end + + it "returns true for an empty value" do + expect(OneboxLocaleSiteSetting.valid_value?("")).to eq(true) + end + + it "returns false for a locale that we do not have translations for" do + expect(OneboxLocaleSiteSetting.valid_value?("swedish-chef")).to eq(false) + end + end +end