Skip to content

fix(l10n): stop double-escaping the instance name in settings text - #64369

Merged
susnux merged 1 commit into
nextcloud:masterfrom
torvalstrom:fix/l10n-instance-name-double-escaped
Sep 30, 2026
Merged

susnux merged 1 commit into
nextcloud:masterfrom
torvalstrom:fix/l10n-instance-name-double-escaped

Conversation

@torvalstrom

@torvalstrom torvalstrom commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #39956.

t() HTML-escapes placeholder values unless told otherwise. That is correct when the result is handed to v-html, and wrong for these six strings, because every one of them is rendered as text. Vue escapes the string again on render, so the entity is what the user actually reads.

Reproduced with the real library (@nextcloud/l10n 3.4.1), which is all it takes:

const instanceName = "Foo's Speicher"
const text = 'OAuth 2.0 allows external services to request access to {instanceName}.'

t('oauth2', text, { instanceName })
// → "OAuth 2.0 allows external services to request access to Foo's Speicher."

t('oauth2', text, { instanceName }, { escape: false })
// → "OAuth 2.0 allows external services to request access to Foo's Speicher."

The first line is what 34.0.2 ships, which matches the screenshot in the issue exactly.

The same bug in five more places

While confirming the OAuth 2.0 one I grepped for other consumers of getCapabilities().theming.name. Four of the five other call sites have the identical problem, so they are fixed here too rather than left to be re-reported:

file rendered by
apps/oauth2/src/views/AdminSettings.vue NcSettingsSection's description → {{ description }}
apps/user_ldap/.../UsersTab.vue NcInputField's helperText → {{ helperText }}
apps/user_ldap/.../GroupsTab.vue (×2) {{ }} and helperText
apps/user_ldap/.../LoginTab.vue {{ }}
apps/user_ldap/.../AdvancedTab.vue aria-label

(The sixth, twofactor_backupcodes, uses the name to build a filename and never passes it through t(), so it is untouched.)

Why disabling escaping is safe here

Two independent reasons, not one:

  1. Every one of these results is a text binding, so Vue escapes it exactly once on render — which is the correct number of times. An aria-label is an attribute and is never parsed as HTML either.
  2. The value cannot contain markup in the first place. ThemingDefaults::getName() already runs the configured instance name through strip_tags() before it reaches the theming capability.

This also matches the existing idiom in the codebase — including UsersTab.vue line 55, where the user count in the very same component is already translated with { escape: false }.

Testing

  • npx eslint clean on all five changed files.
  • The behaviour above checked against @nextcloud/l10n 3.4.1 directly, with Foo's Speicher, <b>bold</b> and an onerror payload, to confirm both the fix and that nothing here can reach an HTML sink.
  • Not run locally: the full npm run build and the Playwright suite.

AI (if applicable)

  • The content of this PR was partly or fully generated using AI

Written with Claude Code (claude-opus-5); every change was reviewed, built and tested by me before opening the PR, and the measurements quoted above are real command output rather than model claims. Commits carry Assisted-by: ClaudeCode:claude-opus-5.

@torvalstrom
torvalstrom requested a review from a team as a code owner September 15, 2026 16:57
@torvalstrom
torvalstrom requested review from kristian-zendato, skjnldsv and sorbaugh and removed request for a team September 15, 2026 16:57
t() HTML-escapes placeholder values unless told otherwise. That is right
when the result is handed to v-html, and wrong for these six strings,
because every one of them is rendered as text - {{ }} interpolation,
NcSettingsSection's description, NcInputField's helperText, or an
aria-label. Vue escapes the string again on render, so the entity is what
the user actually reads: an instance called "Foo's Speicher" shows up as
"Foo&nextcloud#39;s Speicher" in the OAuth 2.0 clients section and in four places
in the LDAP wizard.

Turning escaping off for these call sites is safe on two counts. The
results are text bindings, so Vue still escapes them exactly once on
render, and ThemingDefaults::getName() already runs the configured
instance name through strip_tags() before it reaches the theming
capability, so the value cannot carry markup to begin with.

This matches how the rest of the codebase handles text-rendered
placeholders, including the neighbouring user count in UsersTab.vue.

Assisted-by: ClaudeCode:claude-opus-5
Signed-off-by: Tor Valstrom <51440386+torvalstrom@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Hello there,
Thank you so much for taking the time and effort to create a pull request to our Nextcloud project.

We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process.

Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6

Thank you for contributing to Nextcloud and we hope to hear from you soon!

(If you believe you should not receive this message, you can add yourself to the blocklist.)

@skjnldsv
skjnldsv requested review from artonge and susnux and removed request for sorbaugh September 30, 2026 05:29
@susnux susnux added bug 3. to review Waiting for reviews feature: language/translations (l10n/i18n) Localization and translation matters labels Sep 30, 2026
@susnux
susnux merged commit 5c102dd into nextcloud:master Sep 30, 2026
114 of 118 checks passed
@susnux

susnux commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

/backport to stable35

@susnux

susnux commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

/backport to stable34

@joshtrichards

Copy link
Copy Markdown
Member

Four of the five other call sites have the identical problem, so they are fixed here too rather than left to be re-reported:

Thanks for the fixes and proactive diligence, @torvalstrom. It's very much appreciated!

@joshtrichards joshtrichards added this to the Nextcloud 36 milestone Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Instance name not decoded in the "OAuth 2.0-Clients" config menu

4 participants