Bug 306492

Summary: WebExtension i18n named placeholders fail when adjacent to non-space characters
Product: WebKit Reporter: Brian Birtles <brian>
Component: WebKit ExtensionsAssignee: Nobody <webkit-unassigned>
Status: RESOLVED FIXED    
Severity: Normal CC: ahmad.saleem792, brian, i.am.kanaru.sato, timothy, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
See Also: https://bugs.webkit.org/show_bug.cgi?id=291956

Brian Birtles
Reported 2026-01-28 19:03:50 PST
Named placeholder substitution in WebExtensions localization (`i18n.getMessage`) fails when the placeholder is immediately preceded by a non-space character. Example: ``` "message": "Data v$version$" ``` does not substitute $version$, whereas: ``` "message": "Data v $version$" ``` does. This appears to be due to [this regex](https://github.com/WebKit/WebKit/blob/3a0d87473c4e6ed8a91d6a8df015cb777743f74f/Source/WebKit/Shared/Extensions/WebExtensionLocalization.cpp#L296): ``` auto localizableStringRegularExpression = JSC::Yarr::RegularExpression("(?:[^$]|^)(\\$([A-Za-z0-9_@]+)\\$)"_s, { }); ``` The way we use it is to look for the `index` and then, it the first character is a literal space, drop it: ``` if (originalKey.startsWith(' ')) originalKey = localizedString.substring(index + 1, matchLength - 1); ``` Previously (probably before converting to C++ in https://github.com/WebKit/WebKit/commit/944567bb72da80b8a98ac4aac76f70b700373989) it was possible to workaround this by using a U+200B character before the initial $, e.g. "Data v\u200b$version$" but that workaround no longer works. This might seem like an edge case but for string in languages that don't use spaces to separate works (e.g. CJK languages) this comes up _everywhere_. Chromium browsers and Gecko don't have this bug.
Attachments
Radar WebKit Bug Importer
Comment 1 2026-01-28 19:03:56 PST
Brian Birtles
Comment 2 2026-01-28 21:08:20 PST
Even worse, it has trouble with placeholders that are separated by only a single character and a space, e.g. "$index$( $volume$巻 $page$頁 )" Due to [this line](https://github.com/WebKit/WebKit/blob/3a0d87473c4e6ed8a91d6a8df015cb777743f74f/Source/WebKit/Shared/Extensions/WebExtensionLocalization.cpp#L350): ``` index += replacement.length() + 2; ``` It will start scanning _two_ characters after the replacement even though at this point we're dealing with positional arguments which only have _one_ dollar sign. As a result, in the string "$index$( $volume$巻 $page$頁 )", $index$ and $page$ will be replaced but $volume$ won't.
Brian Birtles
Comment 3 2026-01-29 21:22:11 PST
> It will start scanning _two_ characters after the replacement even though at this point we're dealing with positional arguments which only have _one_ dollar sign. On further thought, I have no idea why it adds 2. I think 1 was to cover the first group in the regex, "(?:[^$]|^)" despite the fact that when matching at the start of the string that group will have length 0, but I've no idea why it adds 2. I'd be glad to fix this up if it's possible to work on this without having access to a Mac and if someone can point me to where tests for this live.
Kanaru Sato
Comment 4 2026-04-08 16:23:24 PDT
I'd like to work on it :) Will submit a patch soon.
Kanaru Sato
Comment 5 2026-04-08 16:35:53 PDT
Timothy Hatcher
Comment 6 2026-04-21 08:52:16 PDT
(In reply to Brian Birtles from comment #3) > > It will start scanning _two_ characters after the replacement even though at this point we're dealing with positional arguments which only have _one_ dollar sign. > > On further thought, I have no idea why it adds 2. I think 1 was to cover the > first group in the regex, "(?:[^$]|^)" despite the fact that when matching > at the start of the string that group will have length 0, but I've no idea > why it adds 2. > > I'd be glad to fix this up if it's possible to work on this without having > access to a Mac and if someone can point me to where tests for this live. The +2 was likely trying to account for the two dollar signs in `$name$` style matches, but positional uses `$N` (one dollar sign, no trailing). This caused the scanner to jump past the next positional placeholder when replacements were short — exactly the bug in comment #2.
EWS
Comment 7 2026-04-21 08:55:56 PDT
Committed 311685@main (bf4fcdebe481): <https://commits.webkit.org/311685@main> Reviewed commits have been landed. Closing PR #62313 and removing active labels.
Note You need to log in before you can comment on or make changes to this bug.