Skip to content

Sync with upstream Ghost v6.26.0#62

Open
madewithlove-machine-user wants to merge 366 commits intomainfrom
chore/sync-v6.26.0
Open

Sync with upstream Ghost v6.26.0#62
madewithlove-machine-user wants to merge 366 commits intomainfrom
chore/sync-v6.26.0

Conversation

@madewithlove-machine-user
Copy link
Copy Markdown

Syncing fork to upstream release v6.26.0.

9larsons and others added 30 commits March 11, 2026 12:00
…host#26786)

closes https://linear.app/ghost/issue/BER-3434

- Retention offer previews were using the default `$15.00` tier price
from fixtures, instead of reading the tier value passed by Admin
ref
https://linear.app/ghost/issue/DES-1305/twcss-4-migration-whats-new-banner-shadow-missing

- The custom shadow syntax changed with the recent Tailwind CSS 4
integration, which caused a visual regression in the What's New banner
in the sidebar.
…yGhost#26788)

The waitForResponse listener was registered after the click that
triggers the API call, so the response could complete before Playwright
started listening. Moved the listener setup before the click to ensure
the response is always captured.
no ref

This function never `await`s or returns a promise. Let's make it
synchronous.
ref https://linear.app/ghost/issue/NY-1150/
ref https://linear.app/ghost/issue/NY-1151/
- updated renderer to always use iframe
- replaced iframe embed with post-gating api-level substitution using preview; this isn't my favorite pattern, and we should look at other ways to handle this
…6770)

closes https://linear.app/ghost/issue/NY-1135/add-the-customize-button-and-an-empty-modal

## Summary
- Re-adds the Customize button to the Welcome emails settings section
- Gated behind the `welcomeEmailsDesignCustomization` private feature
flag (Labs → Developer experiments)
- Button opens up a placeholder modal that we can expand on iteratively
in future commits.

---------

Co-authored-by: Peter Zimon <peter.zimon@gmail.com>
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [gscan](https://ghost.org/)
([source](https://redirect.github.com/TryGhost/gscan)) | [`5.2.4` →
`5.3.3`](https://renovatebot.com/diffs/npm/gscan/5.2.4/5.3.3) |
![age](https://developer.mend.io/api/mc/badges/age/npm/gscan/5.3.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/gscan/5.2.4/5.3.3?slim=true)
|

---

### Release Notes

<details>
<summary>TryGhost/gscan (gscan)</summary>

###
[`v5.3.3`](https://redirect.github.com/TryGhost/gscan/compare/v5.3.2...v5.3.3)

[Compare
Source](https://redirect.github.com/TryGhost/gscan/compare/v5.3.2...v5.3.3)

###
[`v5.3.2`](https://redirect.github.com/TryGhost/gscan/compare/v5.3.1...v5.3.2)

[Compare
Source](https://redirect.github.com/TryGhost/gscan/compare/v5.3.1...v5.3.2)

###
[`v5.3.1`](https://redirect.github.com/TryGhost/gscan/compare/v5.3.0...v5.3.1)

[Compare
Source](https://redirect.github.com/TryGhost/gscan/compare/v5.3.0...v5.3.1)

###
[`v5.3.0`](https://redirect.github.com/TryGhost/gscan/compare/v5.2.5...v5.3.0)

[Compare
Source](https://redirect.github.com/TryGhost/gscan/compare/v5.2.5...v5.3.0)

###
[`v5.2.5`](https://redirect.github.com/TryGhost/gscan/compare/v5.2.4...v5.2.5)

[Compare
Source](https://redirect.github.com/TryGhost/gscan/compare/v5.2.4...v5.2.5)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - Only on Sunday and Saturday ( * * * * 0,6 ), Between 12:00
AM and 12:59 PM, only on Monday ( * 0-12 * * 1 ) in timezone Etc/UTC.

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/TryGhost/Ghost).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNi41IiwidXBkYXRlZEluVmVyIjoiNDMuNTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Chris Raible <chris@ghost.org>
Co-locates migration creation tooling within ghost/core instead of
relying on an external package. The script creates timestamped migration
files in the correct version folder and auto-bumps package.json to RC
when the first migration lands after a stable release.
…ryGhost#26791)

fixes https://linear.app/ghost/issue/HKG-1648

- When using CDN storage (S3Storage), saveRaw() returns a full CDN URL
(e.g., https://storage.ghost.is/.../content/images/photo.jpg) instead of
a relative path like local storage does (/content/images/photo.jpg)
- The media inliner's storeMediaLocally() passed this CDN URL directly
to callers, which prepended __GHOST_URL__ to it, producing malformed
database entries like __GHOST_URL__https://storage.ghost.is/...
- This broke image references in posts after running content imports on
CDN-configured sites
- Used urlUtils.toTransformReady() in storeMediaLocally() to convert the
storage adapter's return value into a __GHOST_URL__-prefixed relative
path, which correctly handles both CDN URLs and local paths — callers no
longer manually prepend __GHOST_URL__
- The oembed service (the only other saveRaw caller) was verified to not
have this issue — it returns the URL as-is to the admin client, which
flows through the model pipeline where formatOnWrite handles the
CDN-to-__GHOST_URL__ transformation correctly
…ryGhost#26709)

ref https://linear.app/ghost/issue/ONC-1518/
ref TryGhost/framework#434

- Adds a `smarterCounts` private labs flag that automatically optimizes pagination count queries
- When enabled, inspects the query builder for JOINs and uses `COUNT(*)` when safe (no JOINs), falling back to `COUNT(DISTINCT table.id)` when JOINs are present
- The only model with JOINs is `products` (LEFT JOIN on `stripe_prices`) — all other models benefit from the faster `COUNT(*)`
## Summary

Updates `@playwright/test` from 1.55.1 to 1.58.2 across all packages
(activitypub, admin-x-settings, comments-ui, signup-form, stats, e2e,
ghost/core).

## Additional changes required by the upgrade

### Local Network Access enforcement breaks localhost fetch tests
Chromium now enforces Local Network Access restrictions by default
(since Chrome 142), which blocks requests to localhost in tests. Used
Playwright's built-in `local-network-access` permission (available since
v1.56.1) in comments-ui and signup-form configs to grant this permission
cleanly.

### Version bumps
- `comments-ui` bumped to 1.3.11 (was 1.3.10 on main, needed a fresh
version after rebase)
- `signup-form` bumped to 0.3.7

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Chris Raible <chris@ghost.org>
no ref

The yarn registry has been flaky. This adds a little retrying in case
the registry is down.
…TryGhost#26652)

## Problem

CI intermittently fails with a fatal V8 crash during `Build TS
packages`:

```
# Fatal error in , line 0
# unreachable code
v8::internal::Deserializer<v8::internal::Isolate>::ReadSingleBytecodeData
...
v8::internal::CodeSerializer::Deserialize
v8::internal::Compiler::GetSharedFunctionInfoForScriptWithCachedData
```

Example failing run:
https://github.com/TryGhost/Ghost/actions/runs/22564490904/job/65358043431

## Root cause

The dependency chain `ts-node` → `v8-compile-cache-lib` is responsible.
When `ts-node` loads the TypeScript compiler it calls
`v8-compile-cache-lib.install()`, which caches compiled V8 bytecode to
disk in `/tmp`.

With Nx running 4 parallel worker processes (the default), multiple
workers call this simultaneously and race to read/write the same blob
cache files. This corrupts the bytecode, causing the crash on
deserialization.

This is a known Node.js issue:
nodejs/node#51555

The Node.js core team confirmed: _"Incorrect use of the v8 code cache
can lead to corruption and crashes like this... This might be especially
true if you have concurrent processes or threads which need careful
synchronization."_

## Fix

Set `DISABLE_V8_COMPILE_CACHE=1` globally in the CI workflow
environment. This is the confirmed workaround from the Node.js issue
thread.

## Performance impact

None. The bytecode cache is written to `/tmp` and discarded at the end
of each run — it never persists between CI jobs. Every run was already
starting cold.

## Test plan

- [ ] Verify CI passes without the intermittent V8 crash on a few runs

Co-authored-by: Chris Raible <chris@ghost.org>
Closes
https://linear.app/ghost/issue/DES-1308/weird-leading-in-import-in-progress-modal

leading-9 was causing crazy line heights in confirmationmodals. Fixed in
the component and a snowflake implementation for universal import.

| Before | After |
|--------|--------|
| <img width="587" height="331" alt="Screenshot 2026-03-12 at 14 17 07"
src="https://github.com/user-attachments/assets/626d9fb4-45ba-41ba-9c5c-ee254dbbdbab"
/> | <img width="595" height="302" alt="Screenshot 2026-03-12 at 14 15
10"
src="https://github.com/user-attachments/assets/f6bb6b18-d148-458c-89ee-e58bc5a49300"
/> |
| <img width="585" height="294" alt="Screenshot 2026-03-12 at 14 16 48"
src="https://github.com/user-attachments/assets/d1673d36-fec1-42dd-a367-f0f44c8368ba"
/> | <img width="596" height="272" alt="Screenshot 2026-03-12 at 14 15
44"
src="https://github.com/user-attachments/assets/3e02821a-b221-4326-9420-547cad56778d"
/> |
| <img width="593" height="396" alt="Screenshot 2026-03-12 at 14 16 26"
src="https://github.com/user-attachments/assets/978c2254-9529-429f-ab1a-7284a73d1d7b"
/> | <img width="585" height="333" alt="Screenshot 2026-03-12 at 14 16
06"
src="https://github.com/user-attachments/assets/3cd19a5d-2927-4d98-a713-83a94b211d5b"
/> |
no ref

This change should have no user impact. It:

- Creates `Tier` models in parallel, for performance
- Uses `Set` instead of an object

This should slightly improve boot time and makes the code a bit clearer.
## Summary

- Fixed 4 flaky browser tests in `publishing.spec.js` that fail daily
near midnight UTC
- **Root cause:** When scheduling a post to publish in the future, the
timepicker in admin defaults to now + 10 minutes. Tests schedule posts
by changing the scheduled time to `00:00` — a time that is _usually_ in
the past — and the UI autocorrects it to "a few seconds from now." But
when running within ~10 minutes of midnight, the default scheduled date
(`now + 10 min`) rolls to tomorrow — making `00:00 tomorrow` still in
the future, so no auto-correction occurs.
- **Fix:** Explicitly pass yesterday's date alongside `time: '00:00'`,
ensuring the resulting datetime is always in the past regardless of when
the tests run
- Affected tests: "At the scheduled time" (page), "Scheduled Publish and
email", "Scheduled Publish only", "Scheduled Email only"
Closes
https://linear.app/ghost/issue/DES-1310/font-size-regression-in-settings-mini-koenig-editor

Regression from the TW4 update. Also fixed for Newsletters and
Announcements Bar.

| Before | After |
|--------|--------|
| <img width="483" height="324" alt="Screenshot 2026-03-12 at 15 32 50"
src="https://github.com/user-attachments/assets/4d8d8b99-b10f-45d0-93bc-45f1ebeaeabd"
/> | <img width="505" height="397" alt="Screenshot 2026-03-12 at 15 32
15"
src="https://github.com/user-attachments/assets/9fd765cf-bd2e-4b0a-847f-ec84e2ad7a68"
/> |
Closes
https://linear.app/ghost/issue/DES-1311/space-x-classes-in-activitypub-dont-work

Tailwind v4 doesn't like the `space-*` utility bound to responsive
classes (Like `sm:`). This PR tweaks it to `gap` which works more
reliably. Tested across a few different Dialog implementations.

| Before | After |
|--------|--------|
| <img width="589" height="258" alt="Screenshot 2026-03-12 at 16 37 37"
src="https://github.com/user-attachments/assets/2aef7105-8560-4f9e-ad0c-d6ba1b553343"
/> | <img width="611" height="261" alt="Screenshot 2026-03-12 at 16 37
17"
src="https://github.com/user-attachments/assets/f756105a-8278-421d-aec4-405c0700399e"
/> |
## TL;DR

This adds a pre-commit hook that will run eslint on any JS/TS files
staged for commit, and prevent the commit from completing if there are
any errors.

## Motivation

There's nothing more annoying than raising a PR only to see the lint job
fail and having to follow up with a "fixed linter" commit. Especially as
we are not always typing out the code ourselves in our IDEs anymore,
I've been encountering these little annoyances more frequently.

## Summary

- Removed the `main`-branch-only guard from the pre-commit hook so
lint-staged runs on every commit, regardless of branch
- Expanded lint-staged file pattern from `*.js` to
`*.{js,ts,tsx,jsx,cjs}` to cover TypeScript and CJS files
- Verified lint-staged catches errors across all 16 monorepo packages
(apps/*, ghost/*, e2e)
Add a `pr-preview.yml` workflow that dispatches deploy/destroy events to
Ghost-Moya when the `preview` label is added or removed from a PR.

- `labeled` with `preview` → dispatches `preview-deploy` to Ghost-Moya
- `unlabeled` with `preview` → dispatches `preview-destroy`
- `closed` while labeled → dispatches `preview-destroy` (auto-cleanup)

Uses `pull_request_target` so only default-branch code runs — no PR code
is checked out. Only collaborators with write access can add labels.
no ref
- added mock Stripe server
- added webhook builders, client
- added Stripe fixtures
- added first Stripe webhook test to /e2e/

We'd like to build out Stripe mocks in the /e2e/ package in order to
move all existing browser tests into our new /e2e/ testing suite. This
is the first of several slices in an attempt to break up all of the
pieces of Stripe functionality we need.

The first test here is creating a Ghost member on receiving the checkout
webhook, which is effectively the back end of the Portal workflow where
Portal -> Stripe -> Ghost.

The Stripe server is stateful, unlike the other options out there for
Stripe mocking/testing, meaning we are stuck rolling our own.
ref https://linear.app/ghost/issue/NY-1152/
- these tests cover the rendering cases
- tests do not handle the insertion of the card, as that's handled in
component tests
ref https://linear.app/ghost/issue/HKG-1623/

- Added unit tests across meta, icon, settings, importer and serializer paths
- These tests were added to cover gaps identified during manual testing, where some CDN/S3 image migration behaviors were not yet included in the existing automated coverage.
no ref

In an attempt to optimize the E2E tests, creating an image we can pull
from GHCR should save us build time on each shard.
…5183)

fixes TryGhost#25055

This PR improves the layout of the settings section of the admin panel:

1. The main content area now can be fully scrolled by interacting with
the scrollbar - even in the top area, where the scrollbar was covered by
the wrapper of the "X" button before.
2. The left content area now has a visible scrollbar, so that it can be
scrolled by grabbing and dragging the scrollbar.
3. As a drive-by, an issue causing a double scrollbar to appear for the
main content on mobile sizes (width < 860px) was also fixed. Now only
one scrollbar is visible and fully interactable.

- Before


https://github.com/user-attachments/assets/2d7d69c8-62c9-48b6-8c52-d482d04d0ee4

- After


https://github.com/user-attachments/assets/c4e5f706-06fb-434f-a6b5-10df26989ca3
ref https://linear.app/ghost/issue/NY-1149/
- once transistor implements a postMessage for emitting the size, we can
listen to it and adjust accordingly
mike182uk and others added 28 commits April 1, 2026 20:54
ref https://linear.app/ghost/issue/BER-3484

Implemented the gift page content behind the `giftSubscriptions` labs
flag: site header, purchaser email input, monthly/yearly toggle, paid
tier cards with pricing and benefits, and a `Purchase gift` button
The tier filter dropdown only matched on tier name, making it hard to
find tiers when the display name differs from the slug. Including the
slug as a detail field lets users search by either value.
no ref
- build the public app bundles for E2E once in a dedicated job
- build the final E2E Docker image once from the production Ghost image
plus those bundles
- have each E2E shard load the prebuilt image and skip rebuilding app
assets and the overlay image

## Why
Each shard currently runs `yarn workspace @tryghost/e2e build:apps` and `build:docker` during `prepare-ci-e2e-job.sh`, which duplicates the same work eight times.
closes
https://linear.app/ghost/issue/NY-1138/add-automated-email-design-settings-api-endpoint

## Summary
- Added a singleton automated email design Admin API subresource at
`GET` and `PUT /automated_emails/design` for reading and editing the
shared automated email design settings
- The endpoint resolves the shared default design setting by its slug
(`default-automated-email`) rather than requiring clients to know the ID
- Reuses `email_design_setting` permissions (admin/owner only) and
prevents slug modification
- Keeps `email_design_settings` as an internal persistence detail
instead of exposing a generic table-shaped API
- Moved `DEFAULT_EMAIL_DESIGN_SETTING_SLUG` constant to the shared
`constants.js` module to avoid duplication

## Advantages over a more generic `email-design-settings` BREAD resource
- For welcome emails customization, we only really need read and edit of
the default row. Creating a full BREAD resource is more code to maintain
and support, some of which isn't necessary for our current
implementation.
- Better decoupling between our DB schema and the frontend. The frontend
doesn't need to know or care about the `email_design_settings` table, it
only cares about "editing design settings for welcome emails".
Ultimately this means more flexibility in changing the schema in the
future without breaking the API
- Simpler implementation on both the backend and frontend

## Test plan
- [x] E2E tests covering read, edit, slug rejection, field validation,
and role-based permissions (editor, author, contributor denied)

---------

Co-authored-by: Troy Ciesco <tmciesco@gmail.com>
ref https://linear.app/ghost/issue/BER-3313
Cutover from Ghost-Release — releases now run from this repo
ref https://linear.app/ghost/issue/BER-3313

Ghost 5.x has reached end of life. Removed the IS_FIVE flag,
5.x from IS_DEVELOPMENT, and 5.x-specific package publishing
logic (version check and ghost-5x npm tag).

Push triggers remain generic ('v[0-9]+.*', '[0-9]+.x') so CI
tests still run if someone pushes to 5.x — packages just won't
be published.
ref https://linear.app/ghost/issue/BER-3484

After completing a Stripe gift checkout, Portal detects if the purchase
was successful and displays a modal with the shareable redemption link
The docker-container BuildKit driver runs in an isolated container that
cannot see locally loaded images, causing fork PR builds to fail. This
switches to the docker driver when using artifact transfer and skips the
registry cache-from when push is disabled, since the registry is
inaccessible in that context.
ref https://linear.app/ghost/issue/BER-3445

Extracted a composable MultiSelectCombobox component from Shade's existing filter internals, enabling custom item rendering, headers, and footers via slots. Migrated the label filter from a standalone LabelPicker to compose over MultiSelectCombobox, bringing inline label create/edit/delete into the standard filter combobox. Added E2E coverage for both label and offer multiselect filter workflows.
ref https://linear.app/ghost/issue/DES-1325/split-root-exports

- This change introduces clear, layered @tryghost/shade entrypoints
(tokens, primitives, components, patterns, app, utils) so consumers
import from explicit boundaries instead of a monolithic root barrel.
We’re doing this to reduce API drift and accidental coupling, enable
staged migration with soft deprecations, and give engineers/agents
stronger guardrails for consistent reuse and long-term maintainability.
…7015)

no issue

Uses `yarn setup` instead of separate commands, and builds sqlite3
native bindings and `parse-email-address` so e2e tests can run without
prompting agents to perform additional steps.
no issue

- `@tryghost/request` uses v13.0.0 but Ghost's direct usage of `got` was on an earlier v11.x
- updated dependency to match versions and modified all call sites to account for breaking changes in options shape
no issue

- bump `ghost/core`'s explicit `handlebars` dependency from `4.7.8` to `4.7.9`
- update the `split` helper and its unit test to use Ghost's frontend Handlebars runtime consistently when multiple `handlebars` copies are present

## Why
Ghost core frontend helpers rely on `SafeString` constructor identity.
If one helper creates `SafeString` values from a different `handlebars`
module instance than another helper checks with `instanceof`, those
values stop matching even though they look identical. Using the shared
frontend Handlebars runtime keeps helper behavior consistent across
dependency layouts.
ref TryGhost@353552f
ref TryGhost#27084

We recently added some tests which generate new snapshots, but forgot to
commit them. This fixes that.

TryGhost#27084 prevents this from happening again.

All I did was run `cd ghost/core && yarn test:e2e`. I don't really know
what this snapshot is about.
closes https://linear.app/ghost/issue/NY-1201

We aren't using this endpoint today, and its existence makes a future
change more difficult. Let's remove it.
ref
TryGhost@60a8165

This is a fix to the above commit. The `schedulerUrl` was used incorrect
- it was feeding the scheduler's URL to the scheduler, rather than the
`apiUrl`; in effect, it should be sending the destination.

This skips the scheduled publishing e2e tests which will be resolved in a follow-up commit.
TryGhost#27070)

closes
https://linear.app/ghost/issue/NY-1165/wiring-wire-up-the-customization-modal-to-the-api-for-persistence

## Summary

- Wired the welcome email customization modal to the
`automated_emails/design/` API so settings persist
- Added proper save UX with dirty tracking, loading states, error
toasts, and a retry flow
- Replaced `window.confirm` with an `AlertDialog` for unsaved changes
- Normalized semantic color values (`light`, `transparent`, `accent`) to
hex for the color picker

---------

Co-authored-by: Troy Ciesco <tmciesco@gmail.com>
no ref

We effectively had code like this:

```js
router.del = router.delete;

// ...

router.del('/route', handler);
```

This causes type errors and will make our future TypeScript migration
slightly harder. Let's just use `router.delete`.
no refs

## Summary

- Added `compose.dev.sqlite.yaml` Docker Compose override that disables
the MySQL container and configures Ghost to use SQLite
- Added `yarn dev:sqlite` script to `package.json` for running the dev
environment with SQLite instead of MySQL
- Mounts the SQLite database directory to the host for easy access
- Mostly this is useful when testing migrations on sqlite
ref TryGhost@f4eb79e
ref TryGhost/renovate-config@3a32889

Ghost's Renovate automerge has effectively been broken since Nov 1
2025.

The last clear Renovate self-merge after the long run of regular
automerge activity was PR TryGhost#25309 on Nov 1 2025. Since then,
Renovate has continued opening and revisiting PRs, but it has not
been merging them itself.

Our investigation found that automerge was working frequently through
late September and early October 2025. The clearest config
inflection point was Oct 15 2025, when Ghost added
platformAutomerge=false, pinned the automerge schedule to Etc/UTC,
and restricted automerge to weekends and Monday morning. This would
explain a slowdown, but not the complete stop.

Ghost was already carrying a local rebaseWhen=never override before
that change, which is not a recommended Renovate setting because it
prevents Renovate from rebasing. But it does not explain the change
by itself.

The likely problem is the combination: Renovate is now responsible
for merging PRs itself, but is still told not to rebase them when
they fall behind main.

Recent Mend logs show that failure mode repeatedly. Existing PRs are
revisited, Renovate logs rebaseWhen=never so skipping branch update
check, and branches finish as no-work without progressing to merge.
That suggests the longstanding rebaseWhen=never override became
actively harmful once Ghost switched to manual Renovate automerge on
a narrow schedule.

Removing the local override is the smallest targeted change to test
that hypothesis. It preserves Ghost's current schedule and
platformAutomerge=false behaviour while restoring the shared
preset's intended rebaseWhen=automerging support for automergeable
PRs.
…6579)

This PR adds all the missing translations of `zh-Hant`, related to
TryGhost#23361

* The polite form “您” is used for the pronoun “you” for consistency,
although a more informal “你” might match the tone of Ghost better.
However, it has an extra benefit of being gender neutral, so might be a
better choice after all.
* The product names like “AOL Mail”, “Hey”, “Gmail”, etc. are kept as
they are.
* The tricky one is “Stripe billing portal”. The “billing portal” part
is not capitalized in the original string, so I suppose it means a
functionality. However, Stripe actually do have a product called [Stripe
Billing](https://support.stripe.com/topics/billing) with a functionality
called [Billing customer
portal](https://support.stripe.com/questions/billing-customer-portal?locale=en-US).
Based on these, there are two directions:
    1. The original string should be updated as Stripe Billing portal.
1. Keep it as it is and translate it as a functionality. This is the
route I’m taking in this PR, resulting in “Stripe 付款門戶”. For more
context, translating “portal” as “門戶” is based on [Stripe’s official
support documentation in Simplified
Chinese](https://support.stripe.com/questions/billing-customer-portal?locale=zh-CN).

cc @cathysarisky since this is a language PR :) 

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Translation-only changes to `zh-Hant` locale JSON; no runtime logic
changes, with minimal risk beyond wording/terminology accuracy.
> 
> **Overview**
> Fills in previously missing `zh-Hant` strings across comments, Ghost
emails, and Portal so users no longer see blank labels/messages.
> 
> Adds Traditional Chinese translations for commenting states/actions
(e.g. commenting disabled, admin view, missing linked comment),
member/email copy (`Manage your preferences`), and several
billing/support/inbox-provider prompts in Portal (e.g. Stripe billing
portal session errors, billing portal open/update failures, `Next
payment`, and “Open Gmail/Outlook/Yahoo…” actions).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ed8cc29. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cathy Sarisky <42299862+cathysarisky@users.noreply.github.com>
Added the missing translations de-CH (Swiss-GermanI) for the new
retention offers in ghost

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk because this only updates locale JSON strings, but incorrect
placeholder tokens in translations could cause runtime interpolation
issues in affected UI messages.
> 
> **Overview**
> Adds the previously empty `de-CH` translations in `ghost.json` and
`portal.json`, covering member preference messaging and multiple
retention/discount offer copy (free months, amount off, cancel/keep
offer prompts, resume subscription, etc.).
> 
> No logic changes; this PR strictly updates localized text (including
strings with interpolation placeholders like `{amountOff}`, `{months}`,
and `{newBillingDate}`).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
58e89da. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ost#26854)

Added Italian translations to the remaining untranslated labels in the
Portal, Comments, and Ghost sections.
Updated Bulgarian translation of portal.json to include recently added
strings.
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [papaparse](https://www.papaparse.com/)
([source](https://redirect.github.com/mholt/PapaParse)) | [`5.3.2` →
`5.5.3`](https://renovatebot.com/diffs/npm/papaparse/5.3.2/5.5.3) |
![age](https://developer.mend.io/api/mc/badges/age/npm/papaparse/5.5.3?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/papaparse/5.3.2/5.5.3?slim=true)
|

---

### Release Notes

<details>
<summary>mholt/PapaParse (papaparse)</summary>

###
[`v5.5.3`](https://redirect.github.com/mholt/PapaParse/compare/e3c7b2628c68b868fd09862252eea312fbafdd84...a4f8b0f1e30bf08e44da96ff5575ffdae7aa9b12)

[Compare
Source](https://redirect.github.com/mholt/PapaParse/compare/e3c7b2628c68b868fd09862252eea312fbafdd84...a4f8b0f1e30bf08e44da96ff5575ffdae7aa9b12)

###
[`v5.5.2`](https://redirect.github.com/mholt/PapaParse/compare/d0f58aa3c662b7460b2da093bae3f81ef2cb5e30...e3c7b2628c68b868fd09862252eea312fbafdd84)

[Compare
Source](https://redirect.github.com/mholt/PapaParse/compare/d0f58aa3c662b7460b2da093bae3f81ef2cb5e30...e3c7b2628c68b868fd09862252eea312fbafdd84)

###
[`v5.5.1`](https://redirect.github.com/mholt/PapaParse/compare/338af86bde63d0d208ef22582f55fa129a5431d7...d0f58aa3c662b7460b2da093bae3f81ef2cb5e30)

[Compare
Source](https://redirect.github.com/mholt/PapaParse/compare/338af86bde63d0d208ef22582f55fa129a5431d7...d0f58aa3c662b7460b2da093bae3f81ef2cb5e30)

###
[`v5.5.0`](https://redirect.github.com/mholt/PapaParse/compare/5.4.1...338af86bde63d0d208ef22582f55fa129a5431d7)

[Compare
Source](https://redirect.github.com/mholt/PapaParse/compare/5.4.1...338af86bde63d0d208ef22582f55fa129a5431d7)

###
[`v5.4.1`](https://redirect.github.com/mholt/PapaParse/compare/5.4.0...5.4.1)

[Compare
Source](https://redirect.github.com/mholt/PapaParse/compare/5.4.0...5.4.1)

###
[`v5.4.0`](https://redirect.github.com/mholt/PapaParse/releases/tag/5.4.0)

[Compare
Source](https://redirect.github.com/mholt/PapaParse/compare/5.3.2...5.4.0)

We are happy to annunce a new minor release of PapaParse.

This release includes the following change:

Handle parsing utf-8 bom encoded files (See
[#&TryGhost#8203;961](https://redirect.github.com/mholt/PapaParse/issues/961))
Rename duplicate headers (See
[#&TryGhost#8203;956](https://redirect.github.com/mholt/PapaParse/issues/956))
Improve iso-date regex (See
[#&TryGhost#8203;959](https://redirect.github.com/mholt/PapaParse/issues/959))

Thanks to
[@&TryGhost#8203;peteruithoven](https://redirect.github.com/peteruithoven)
[@&TryGhost#8203;fortydegrees](https://redirect.github.com/fortydegrees)
[@&TryGhost#8203;ChALkeR](https://redirect.github.com/ChALkeR) for contributing
such features

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - Only on Sunday and Saturday ( * * * * 0,6 ), Between 12:00
AM and 12:59 PM, only on Monday ( * 0-12 * * 1 ) in timezone Etc/UTC.

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/TryGhost/Ghost).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS45Mi4wIiwidXBkYXRlZEluVmVyIjoiNDMuMTAyLjExIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Added the lastest strings.

---------

Co-authored-by: Cathy Sarisky <42299862+cathysarisky@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.