diff --git a/app/assets/javascripts/pretty-text/engines/discourse-markdown-it.js.es6 b/app/assets/javascripts/pretty-text/engines/discourse-markdown-it.js.es6 index 3c91bf03452..447b5047f3e 100644 --- a/app/assets/javascripts/pretty-text/engines/discourse-markdown-it.js.es6 +++ b/app/assets/javascripts/pretty-text/engines/discourse-markdown-it.js.es6 @@ -287,7 +287,8 @@ export function setup(opts, siteSettings, state) { breaks: opts.discourse.features.newline, xhtmlOut: false, linkify: siteSettings.enable_markdown_linkify, - typographer: siteSettings.enable_markdown_typographer + typographer: siteSettings.enable_markdown_typographer, + quotes: siteSettings.markdown_typographer_quotation_marks.split("|") }); opts.engine.linkify.tlds( diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 6646bcbc4c2..0a7a0af6098 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1423,6 +1423,7 @@ en: enable_markdown_typographer: "Use typography rules to improve readability of text: replace straight quotes ' with curly quotes ’, (c) (tm) with symbols, -- with emdash –, etc" enable_markdown_linkify: "Automatically treat text that looks like a link as a link: www.example.com and https://example.com will be automatically linked" markdown_linkify_tlds: "List of top level domains that get automatically treated as links" + markdown_typographer_quotation_marks: "List of double and single quotes replacement pairs" post_undo_action_window_mins: "Number of minutes users are allowed to undo recent actions on a post (like, flag, etc)." must_approve_users: "Staff must approve all new user accounts before they are allowed to access the site." pending_users_reminder_delay: "Notify moderators if new users have been waiting for approval for longer than this many hours. Set to -1 to disable notifications." @@ -2092,6 +2093,7 @@ en: regex_invalid: "The regular expression is invalid: %{error}" leading_trailing_slash: "The regular expression must not start and end with a slash." unicode_usernames_avatars: "The internal system avatars do not support Unicode usernames." + list_value_count: "The list must contain exactly %{count} values." placeholder: sso_provider_secrets: diff --git a/config/site_settings.yml b/config/site_settings.yml index 1a9fbb94dd1..6e17b3ca335 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -708,6 +708,15 @@ posting: type: list default: "com|net|org|io|co|tv|ru|cn|us|uk|me|de|fr|fi|gov" list_type: compact + markdown_typographer_quotation_marks: + client: true + type: list + list_type: compact + validator: "MarkdownTypographerQuotationMarksValidator" + default: "“|”|‘|’" + locale_default: + de: "„|“|‚|‘" + fr: "«\xA0|\xA0»|‹\xA0|\xA0›" enable_rich_text_paste: client: true default: true diff --git a/lib/validators/markdown_typographer_quotation_marks_validator.rb b/lib/validators/markdown_typographer_quotation_marks_validator.rb new file mode 100644 index 00000000000..30262108da1 --- /dev/null +++ b/lib/validators/markdown_typographer_quotation_marks_validator.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class MarkdownTypographerQuotationMarksValidator + QUOTE_COUNT = 4 + + def initialize(opts = {}) + @opts = opts + end + + def valid_value?(value) + value.present? && value.split("|").size == QUOTE_COUNT + end + + def error_message + I18n.t("site_settings.errors.list_value_count", count: QUOTE_COUNT) + end +end diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index 74c54bd573c..045ec8baf9b 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -1062,6 +1062,14 @@ HTML expect(PrettyText.cook('(tm)')).to eq('

(tm)

') end + it 'uses quotation marks from site settings' do + SiteSetting.enable_markdown_typographer = true + expect(PrettyText.cook(%q|"Do you know," he said, "what 'Discourse' is?"|)).to eq(%q|

“Do you know,” he said, “what ‘Discourse’ is?”

|) + + SiteSetting.markdown_typographer_quotation_marks = "„|“|‚|‘" + expect(PrettyText.cook(%q|"Weißt du", sagte er, "was 'Discourse' ist?"|)).to eq(%q|

„Weißt du“, sagte er, „was ‚Discourse‘ ist?“

|) + end + it 'handles onebox correctly' do expect(PrettyText.cook("http://a.com\nhttp://b.com").split("onebox").length).to eq(3) expect(PrettyText.cook("http://a.com\n\nhttp://b.com").split("onebox").length).to eq(3)