Use scan on raw translate for link_translate building

We changed the way we output translation text in the link_translate
function. By using a SafeBuffer, the original text was already escaped
before it got handled by the link helper.

Instead, we can pass the raw link part of the translation string to the
link helper, allowing it to handle escaping, and output the rest of the
translation manually to the SafeBuffer.

This way, the entire string is subjected to escaping still, but will
allow entities to not be escaped

https://community.openproject.org/work_packages/73513
This commit is contained in:
Oliver Günther
2026-03-30 08:34:49 +02:00
parent 200072ecc4
commit 98c91275e2
2 changed files with 30 additions and 4 deletions
+11 -4
View File
@@ -116,13 +116,20 @@ module Redmine
# @param links [Hash] Link names mapped to URLs.
# @param external [Boolean] Whether the links should be opened as external links, i.e. in a new tab (default: true)
# @param underline [Boolean] Whether to underline links inserted into the text (default: true)
def link_translate(i18n_key, i18n_args: {}, links: {}, external: true, underline: true, **)
def link_translate(i18n_key, i18n_args: {}, links: {}, external: true, underline: true, **) # rubocop:disable Metrics/AbcSize
translation = ApplicationController.helpers.t(i18n_key.to_s, **i18n_args)
output = ActiveSupport::SafeBuffer.new
output << ApplicationController.helpers.t(i18n_key.to_s, **i18n_args)
last_end = 0
output.html_safe_gsub(link_regex) do
create_link_content($3, $2, external:, links:, underline:, **)
translation.scan(link_regex) do
match = Regexp.last_match
output << translation[last_end...match.begin(0)]
output << create_link_content(match[3], match[2], external:, links:, underline:, **)
last_end = match.end(0)
end
output << translation[last_end..]
output
end
##
+19
View File
@@ -211,6 +211,25 @@ module OpenProject
expect(links[1][:href]).to eq("/baz")
end
context "when the link text contains an apostrophe" do
before do
allow(::I18n)
.to receive(:translate)
.with("translation_with_apostrophe", *any_args)
.and_return("Here's [what's new](url) to see.")
end
it "does not double-escape the apostrophe in the link text" do
translated = link_translate :translation_with_apostrophe,
links: { url: "https://example.com" },
external: false
fragment = Capybara.string(translated)
link = fragment.find("a")
expect(link.text).to eq("what's new")
end
end
context "when passing URLs as a list of symbols" do
let(:urls) do
{ url_1: [:a, :b], url_2: [:a, :c] }