Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/checks/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Multi-locale sites usually list only one language in `llms.txt`, so localized si

- When locale prefixes cover most of the sitemap, the prefix position is detected directly.
- When translations are a minority of the sitemap (an unprefixed default language plus a few translated locales), the check confirms the prefixes by structural duplication: stripping a locale code must produce paths that match unprefixed URLs.
- `--doc-locale <code>` (or `preferredLocale` in config) overrides detection. If the sitemap has no URLs prefixed with that locale, it is treated as the unprefixed default and the localized variants are excluded.
- `--doc-locale <code>` (or `preferredLocale` in config) overrides detection. A regional or script variant of the same language counts as a match (`en` selects `/en-us/`). If the sitemap has no URLs for that language at all, it is treated as the unprefixed default and the localized variants are excluded.

### How to fix

Expand Down
2 changes: 2 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ The resolution order for both flags is:

Use the flags when the base URL doesn't contain locale or version segments but the site organizes content by locale or version.

A locale preference also matches regional and script variants of the same language when the exact code is absent: `en` selects `en-us` pages and sitemaps, and `--doc-locale en-US` selects bare `en` ones. When several variants exist, the one with the most pages is used; sitemap indexes contribute the child sitemaps of every variant of that language so the choice is made over pages, not over sitemap counts.

```bash
# Prefer French locale during discovery
afdocs check https://docs.example.com --doc-locale fr
Expand Down
24 changes: 21 additions & 3 deletions src/checks/observability/llms-txt-coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import {
} from '../../helpers/get-page-urls.js';
import { isSameSite } from '../../helpers/host-equivalence.js';
import { isNonPageUrl } from '../../helpers/to-md-urls.js';
import { isLocaleSegment, detectLocalePosition } from '../../helpers/locale-codes.js';
import {
isLocaleSegment,
detectLocalePosition,
countLocaleCodesAt,
resolveLocaleVariant,
} from '../../helpers/locale-codes.js';
import {
DEFAULT_COVERAGE_PASS_THRESHOLD,
DEFAULT_COVERAGE_WARN_THRESHOLD,
Expand Down Expand Up @@ -324,12 +329,25 @@ async function check(ctx: CheckContext): Promise<CheckResult> {
if (preferred) {
// Explicit locale (--doc-locale / config preferredLocale) wins over
// inference from llms.txt URLs.
const byPreferred = filterByLocale(scopedSitemapUrls, preferred, localePosition);
// Exact raw prefix first, as the page filter does, so a site-specific
// code such as `en-custom` is not displaced by its `en-us` variant. Only
// when absent does a regional or script variant of the same language
// stand in (`--doc-locale en` selects `/en-us/`, issue #164).
const exact = filterByLocale(scopedSitemapUrls, preferred, localePosition);
const resolved =
exact.length > 0
? preferred
: (resolveLocaleVariant(
preferred,
countLocaleCodesAt(scopedSitemapUrls, localePosition),
) ?? preferred);
const byPreferred =
exact.length > 0 ? exact : filterByLocale(scopedSitemapUrls, resolved, localePosition);
const before = scopedSitemapUrls.length;
if (byPreferred.length > 0) {
scopedSitemapUrls = byPreferred;
localeFiltered = scopedSitemapUrls.length < before;
if (localeFiltered) detectedLocale = preferred;
if (localeFiltered) detectedLocale = resolved;
} else {
// No URLs carry the preferred locale as a prefix, so it must be the
// unprefixed default locale: drop the locale-prefixed translations.
Expand Down
Loading
Loading