Skip to content
Open
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
115 changes: 82 additions & 33 deletions packages/root-cms/core/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ import {
RootConfig,
RouteParams,
} from '@blinkk/root';
import {DocMode, RootCMSClient, translationsForLocale} from './client.js';
import {
BatchRequest,
BatchResponse,
DocMode,
RootCMSClient,
} from './client.js';

export interface RootCMSDoc<Fields = any> {
/** The id of the doc, e.g. "Pages/foo-bar". */
Expand Down Expand Up @@ -86,6 +91,16 @@ export interface RouteContext {
*/
cmsClient: RootCMSClient;

/**
* Batch request helper for fetching CMS data.
*/
batchRequest: BatchRequest;

/**
* Batch response populated after calling `batchRequest.fetch()`.
*/
batchResponse?: BatchResponse;

/**
* URL param map from filesystem routing.
*/
Expand Down Expand Up @@ -223,24 +238,37 @@ export function createRoute(options: CreateRouteOptions): Route {
return resolvePromisesMap(promisesMap);
}

async function generateProps(routeContext: RouteContext, locale: string) {
const {slug, mode} = routeContext;
const translationsTags = [
'common',
`${options.collection}/${slug.replaceAll('/', '--')}`,
];
async function generateProps(
routeContext: RouteContext,
locale: string,
siteLocales: string[]
) {
const {slug, mode, batchRequest} = routeContext;
const normalizedSlug = slug.replaceAll('/', '--');
const docId = `${options.collection}/${normalizedSlug}`;
batchRequest.addDoc(docId);

const docTranslationsId = docId;
const translationsTags = new Set<string>(['common', docTranslationsId]);
if (options.translations) {
const tags = options.translations(routeContext)?.tags || [];
translationsTags.push(...tags);
tags.forEach((tag) => translationsTags.add(tag));
}
const shouldLoadTranslations = siteLocales.length > 1;
if (shouldLoadTranslations) {
translationsTags.forEach((tag) => {
if (tag !== docTranslationsId) {
batchRequest.addTranslations(tag);
}
Comment on lines +257 to +262

Choose a reason for hiding this comment

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

[P1] SSG skips translations when only one locale configured

The new batch request logic now gates translation fetching behind siteLocales.length > 1, so single‑locale static builds no longer load their common or doc translations and return {} from generateProps. Previously getStaticProps always loaded translations regardless of the number of locales, meaning single‑locale sites still received the localized strings for their default language. This regression will cause translation helpers to miss strings on SSG pages for single‑locale sites. Consider always fetching the configured translation tags for SSG or restoring the previous behaviour so one‑locale sites keep their translations.

Useful? React with 👍 / 👎.

});
}

const [doc, translationsMap, data] = await Promise.all([
cmsClient.getDoc<RootCMSDoc>(options.collection, slug, {
mode,
}),
cmsClient.loadTranslations({tags: translationsTags}),
fetchData(routeContext),
]);
const dataPromise = fetchData(routeContext);
const batchResponse = await batchRequest.fetch();
routeContext.batchResponse = batchResponse;
const data = await dataPromise;

const doc = batchResponse.docs[docId] as RootCMSDoc | undefined;
if (!doc) {
return {notFound: true};
}
Expand All @@ -249,7 +277,9 @@ export function createRoute(options: CreateRouteOptions): Route {
return {notFound: true};
}

const translations = translationsForLocale(translationsMap, locale);
const translations = shouldLoadTranslations
? batchResponse.getTranslations(locale)
: {};
let props: any = {...data, locale, mode, slug, doc};
if (options.preRenderHook) {
props = await options.preRenderHook(props, routeContext);
Expand Down Expand Up @@ -282,33 +312,44 @@ export function createRoute(options: CreateRouteOptions): Route {
return ctx.render404();
}

const batchRequest = cmsClient.createBatchRequest({
mode,
translate: siteLocales.length > 1,
});
const routeContext: RouteContext = {
req,
slug,
mode,
cmsClient,
params: ctx.params,
batchRequest,
};

const translationsTags = [
'common',
`${options.collection}/${slug.replaceAll('/', '--')}`,
];
const normalizedSlug = slug.replaceAll('/', '--');
const docId = `${options.collection}/${normalizedSlug}`;
batchRequest.addDoc(docId);

const docTranslationsId = docId;
const translationsTags = new Set<string>(['common', docTranslationsId]);
if (options.translations) {
const tags = options.translations(routeContext)?.tags || [];
translationsTags.push(...tags);
tags.forEach((tag) => translationsTags.add(tag));
}

const shouldLoadTranslations = siteLocales.length > 1;
if (shouldLoadTranslations) {
translationsTags.forEach((tag) => {
if (tag !== docTranslationsId) {
batchRequest.addTranslations(tag);
}
});
}

const [doc, translationsMap, data] = await Promise.all([
cmsClient.getDoc<RootCMSDoc>(options.collection, slug, {
mode,
}),
// Only load translations for sites that have >1 locale configured.
siteLocales.length > 1
? cmsClient.loadTranslations({tags: translationsTags})
: Promise.resolve({}),
fetchData(routeContext),
]);
const dataPromise = fetchData(routeContext);
const batchResponse = await batchRequest.fetch();
routeContext.batchResponse = batchResponse;
const data = await dataPromise;
const doc = batchResponse.docs[docId] as RootCMSDoc | undefined;
if (!doc) {
// console.log(`doc not found: ${options.collection}/${slug}`);
if (options.notFoundHook) {
Expand Down Expand Up @@ -410,7 +451,9 @@ export function createRoute(options: CreateRouteOptions): Route {
}
}

const translations = translationsForLocale(translationsMap, locale);
const translations = shouldLoadTranslations
? batchResponse.getTranslations(locale)
: {};
let props: any = {...data, req, locale, mode, slug, doc, country};
if (options.preRenderHook) {
props = await options.preRenderHook(props, routeContext);
Expand Down Expand Up @@ -470,15 +513,21 @@ export function createRoute(options: CreateRouteOptions): Route {
}
const slug = getSlug(ctx.params);
const mode = options.ssgMode || 'published';
const siteLocales = ctx.rootConfig?.i18n?.locales || ['en'];
const batchRequest = cmsClient.createBatchRequest({
mode,
translate: siteLocales.length > 1,
});
const routeContext: RouteContext = {
req: undefined,
slug,
mode,
cmsClient,
params: ctx.params,
batchRequest,
};

return generateProps(routeContext, ctx.params.$locale);
return generateProps(routeContext, ctx.params.$locale, siteLocales);
};
}

Expand Down