Skip to content

feat(loc): detect each project's source root from its URLs - #870

Open
amol-anand wants to merge 3 commits into
loc-per-row-source-pathsfrom
locfeat
Open

amol-anand wants to merge 3 commits into
loc-per-row-source-pathsfrom
locfeat

Conversation

@amol-anand

@amol-anand amol-anand commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor
  • Stacked on fix(loc): resolve URLs under per-row source locations #793: base is loc-per-row-source-paths; merge fix(loc): resolve URLs under per-row source locations #793 first.
  • Add opt-in source.language.detect config row. Without it, nothing changes.
  • Validate: the longest matching root across row locations, row sources and the configured source becomes the project's source.
  • Validate: URLs from more than one root show an error right after URL checks and Next stays blocked. The error names the URLs outside the root with the most URLs (up to 5, then "and N more") so authors know what to uncheck, e.g. auto-discovered fragments from another root.
  • Validate: URLs matching no root (e.g. /fragments/...) are ignored for detection. Because source.language becomes the detected root, sync copies them into that root (e.g. /eu/fragments/...) rather than the configured source, so rollout from the detected root can find them.
  • Validate: the detected source is persisted on the project as detectedSource (null when nothing is detected or the flag is off). Options uses it instead of re-resolving, so a config change mid-flight can't change a project's source. Projects without detectedSource fall back to resolving from config.
  • Options: replace source.language with the detected root and narrow rows to actions that start from it (Translate/Copy when the row's source is the root; Rollout when the row's location is the root or Translate/Copy is shown). Rows left with only Skip are hidden.
  • Options: add a Source card showing the detected root. If detection errors, show a message and fall back to unfiltered behavior.
  • Root matching reuses findSourceLocation from fix(loc): resolve URLs under per-row source locations #793; /root.html and /root.json still match.
  • Rollout, translate and sync need no changes: they read source.language and the fix(loc): resolve URLs under per-row source locations #793 source locations.
  • Blank locales cells are handled by fix(loc): resolve URLs under per-row source locations #793's formatLangs change; this PR keeps the regression tests.
  • Tests: root resolution, row filtering, mixed-root validation and error message, blank locales, persisted sourceLocations and detectedSource, getProjectSource, and finalizeOptions source-location handling.
  • Manual test: https://da.live/apps/loc?nx=locfeat#/dashboard/aemsites/fedex with source.language = Global English and source.language.detect = true. /global/... URLs give Global rollout only, /apac/... give APAC rollout only, /en-to/... give Tongan translate only, and mixing /global/... with /apac/... shows the error.

Documentation

Edited in adobe/aem-website, not yet published. Publish after #793 and this PR release.

@aem-code-sync

aem-code-sync Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch.
In case there are problems, just click the checkbox below to rerun the respective action.

  • Re-sync branch
Commits

markdaugherty
markdaugherty previously approved these changes Oct 19, 2026
amol-anand and others added 2 commits October 27, 2026 14:51
Add an opt-in `source.language.detect` config row. When enabled, each
loc project resolves its own source root from the URLs added at
Validate, so flat structures can run multi-level rollouts and
translations as separate projects.

- Validate: pick the longest matching root across row locations, row
  sources and the configured source; error on URLs from more than one
  root and keep URLs matching no root on today's sync behavior
- Options: replace `source.language` with the detected root, narrow
  rows to actions that start from it, and show a Source card
- Reuse `findSourceLocation` for root matching, stacked on per-row
  source location support

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assert the detected root is persisted as sourceLocations, and that
finalizeOptions uses persisted locations, derives them for legacy
projects, and does not treat sibling prefixes as inside the source.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
}

async getSourcePrefix() {
const detected = await this.detectSource();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is calculated here, but not persisted, then re-calculated in the options step (re-runs resolveSourceLang). Think we should persist it to avoid accidental drift or projects acting strange if the conifg changes mid-flight.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Fixed in a8a752e: Validate now persists the detected source on the project as detectedSource (null when nothing is detected or the flag is off), and Options uses it via getProjectSource() instead of re-running resolveSourceLang. Only projects without detectedSource (already in flight) fall back to resolving from config.

}
if (!detected) return;

options['source.language'] = sourceLang;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In detect mode, URLs matching no root now sync into the detected root (e.g. /eu/fragments/x), not the configured source. possible this is the intent, but if so, maybe update PR description/docs since it currently states "keep today's sync behavior."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intended. source.language becomes the detected root and rollout reads from it, so an unmatched /fragments/x has to sync into /eu/fragments/x to be rolled out with the rest of an /eu project; syncing it into the configured source would leave it outside the project. Updated the PR description to say this instead of "keep today's sync behavior"; the docs will get the same note.

notChecked = this._urls.filter((url) => !url.status);
}

await this.updateSourceMessage();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-discovered fragments from another root (e.g. /eu page → /global/fragments/...) will trip this, and the error lists roots, not URLs, so authors must hunt for what to uncheck. Consider naming the offending URLs (or the minority-root ones) in the message, and/or a docs note.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a8a752e: the error still lists the roots, then names the URLs outside the root with the most URLs, e.g. To keep /eu, uncheck: /global/fragments/x. It lists up to 5 and adds "and N more" after that.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
locfeat — a8a752ef Deployed Oct 27, 2026 by aem-code-sync[bot]
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.

3 participants