diff --git a/packages/cli/cli/changes/unreleased/fix-translated-page-images.yml b/packages/cli/cli/changes/unreleased/fix-translated-page-images.yml new file mode 100644 index 000000000000..6297f757c548 --- /dev/null +++ b/packages/cli/cli/changes/unreleased/fix-translated-page-images.yml @@ -0,0 +1,5 @@ +- summary: | + Fix images and other local file references in translated pages not rendering. Translated + page content is now run through the same image parse, upload, and file ID replacement + pipeline as default-locale pages. + type: fix diff --git a/packages/cli/docs-markdown-utils/src/__test__/parseImagePaths.test.ts b/packages/cli/docs-markdown-utils/src/__test__/parseImagePaths.test.ts index 56ba7e905120..007782d6b87b 100644 --- a/packages/cli/docs-markdown-utils/src/__test__/parseImagePaths.test.ts +++ b/packages/cli/docs-markdown-utils/src/__test__/parseImagePaths.test.ts @@ -7,7 +7,11 @@ import fs from "fs"; import { resolve } from "path"; import { afterEach, beforeEach, vi } from "vitest"; -import { parseImagePaths, replaceImagePathsAndUrls } from "../parseImagePaths.js"; +import { + parseImagePaths, + replaceImagePathsAndUrls, + replaceImagePathsAndUrlsInTranslatedPage +} from "../parseImagePaths.js"; const CONTEXT = createMockTaskContext(); @@ -1726,3 +1730,37 @@ describe("angle bracket delimited destinations", () => { expect(result.trim()).toBe("[other page]()"); }); }); + +describe("replaceImagePathsAndUrlsInTranslatedPage", () => { + const TRANSLATED_MDX_PATH = AbsoluteFilePath.of("/Volume/git/fern/translations/tr/my/docs/folder/file.mdx"); + const IMAGE_PATH = AbsoluteFilePath.of("/Volume/git/fern/my/docs/folder/path/to/image.png"); + + function replace(markdown: string, fileIds: Map = new Map([[IMAGE_PATH, "fileID"]])) { + return replaceImagePathsAndUrlsInTranslatedPage({ + markdown, + fileIdsMap: fileIds, + markdownFilesToPathName: {}, + absolutePathToFernFolder: DOCS_PATH, + absolutePathToDefaultLocaleMarkdownFile: MDX_PATH, + absolutePathToTranslatedMarkdownFile: TRANSLATED_MDX_PATH, + context: CONTEXT + }).trim(); + } + + it("replaces a path authored relative to the translated page", () => { + expect(replace('')).toBe(''); + }); + + it("replaces a path copied verbatim from the default-locale page", () => { + expect(replace('')).toBe(''); + }); + + it("leaves a reference with no uploaded file as authored", () => { + expect(replace('')).toBe(''); + }); + + it("leaves markdown and code includes untouched", () => { + const page = '\n'; + expect(replace(page)).toBe(page); + }); +}); diff --git a/packages/cli/docs-markdown-utils/src/index.ts b/packages/cli/docs-markdown-utils/src/index.ts index fcf7637236d1..dd59021e65c5 100644 --- a/packages/cli/docs-markdown-utils/src/index.ts +++ b/packages/cli/docs-markdown-utils/src/index.ts @@ -6,13 +6,20 @@ export { isValidRelativeSlug, parseImagePaths, replaceImagePathsAndUrls, + replaceImagePathsAndUrlsInTranslatedPage, trimAnchor } from "./parseImagePaths.js"; export { parseMarkdownBodyToTree, parseMarkdownToTree } from "./parseMarkdownToTree.js"; -export { collectCodeSrcUrls, prefetchCodeSrcUrls, replaceReferencedCode } from "./replaceReferencedCode.js"; +export { + collectCodeSrcUrls, + prefetchCodeSrcUrls, + removeCodeIncludeTags, + replaceReferencedCode +} from "./replaceReferencedCode.js"; export { type ReferencedMarkdownFile, type ReplaceReferencedMarkdownResult, + removeMarkdownIncludeTags, replaceReferencedMarkdown } from "./replaceReferencedMarkdown.js"; export { stripMdxComments } from "./stripMdxComments.js"; diff --git a/packages/cli/docs-markdown-utils/src/parseImagePaths.ts b/packages/cli/docs-markdown-utils/src/parseImagePaths.ts index 60e9968ce6de..fe1ce68b7587 100644 --- a/packages/cli/docs-markdown-utils/src/parseImagePaths.ts +++ b/packages/cli/docs-markdown-utils/src/parseImagePaths.ts @@ -1241,6 +1241,43 @@ export function replaceImagePathsAndUrls( return requoteLeadingZeroValues(grayMatter.stringify(replacedContent, data)); } +/** + * Replaces image paths in a translated page with the uploaded file IDs. + * + * Translated pages either copy the default-locale page's relative paths verbatim or write them + * relative to their own location under `translations//`, so both locations are tried. + * References that resolve to no uploaded file are left as authored. + */ +export function replaceImagePathsAndUrlsInTranslatedPage({ + markdown, + fileIdsMap, + markdownFilesToPathName, + absolutePathToFernFolder, + absolutePathToDefaultLocaleMarkdownFile, + absolutePathToTranslatedMarkdownFile, + context +}: { + markdown: string; + fileIdsMap: ReadonlyMap; + markdownFilesToPathName: Record; + absolutePathToFernFolder: AbsoluteFilePath; + absolutePathToDefaultLocaleMarkdownFile: AbsoluteFilePath; + absolutePathToTranslatedMarkdownFile: AbsoluteFilePath; + context: TaskContext; +}): string { + return [absolutePathToDefaultLocaleMarkdownFile, absolutePathToTranslatedMarkdownFile].reduce( + (replaced, absolutePathToMarkdownFile) => + replaceImagePathsAndUrls( + replaced, + fileIdsMap, + markdownFilesToPathName, + { absolutePathToMarkdownFile, absolutePathToFernFolder }, + context + ), + markdown + ); +} + function getPosition( markdown: string, position: { start: { line: number; column: number }; end: { line: number; column: number } } diff --git a/packages/cli/docs-markdown-utils/src/replaceReferencedCode.ts b/packages/cli/docs-markdown-utils/src/replaceReferencedCode.ts index a752a94a4c92..d0f2d094fd07 100644 --- a/packages/cli/docs-markdown-utils/src/replaceReferencedCode.ts +++ b/packages/cli/docs-markdown-utils/src/replaceReferencedCode.ts @@ -82,6 +82,17 @@ function extractLines(content: string, linesParam: string): string { const CODE_TAG_REGEX = /([ \t]*)]*?)?\s+src={?['"]([^'"]+)['"](?! \+)}?((?:\s+[^>]*)?)\/>/g; +/** + * Removes `` tags, e.g. so that their references are not mistaken for assets when + * scanning content that has not had its includes resolved yet. + */ +export function removeCodeIncludeTags(markdown: string): string { + if (!markdown.includes(" tags and returns the external URLs. */ diff --git a/packages/cli/docs-markdown-utils/src/replaceReferencedMarkdown.ts b/packages/cli/docs-markdown-utils/src/replaceReferencedMarkdown.ts index 1678282be770..eb16a121b78c 100644 --- a/packages/cli/docs-markdown-utils/src/replaceReferencedMarkdown.ts +++ b/packages/cli/docs-markdown-utils/src/replaceReferencedMarkdown.ts @@ -66,6 +66,19 @@ function substituteVariables(content: string, variables: Record) return result; } +const MARKDOWN_TAG_REGEX = /([ \t]*)]+)\/>/g; + +/** + * Removes `` tags, e.g. so that their references are not mistaken for assets + * when scanning content that has not had its includes resolved yet. + */ +export function removeMarkdownIncludeTags(markdown: string): string { + if (!markdown.includes("]+)\/>/g; + const regex = new RegExp(MARKDOWN_TAG_REGEX.source, MARKDOWN_TAG_REGEX.flags); let newMarkdown = markdown; diff --git a/packages/cli/docs-preview/src/runAppPreviewServer.ts b/packages/cli/docs-preview/src/runAppPreviewServer.ts index a48476e1c451..9268df1dee76 100644 --- a/packages/cli/docs-preview/src/runAppPreviewServer.ts +++ b/packages/cli/docs-preview/src/runAppPreviewServer.ts @@ -5,7 +5,7 @@ import { applyTranslatedNavigationOverlays, findIncompatibleTranslatedApiIds, getTranslatedAnnouncement, - replaceImagePathsAndUrls, + replaceImagePathsAndUrlsInTranslatedPage, replaceReferencedCode, replaceReferencedMarkdown, stripMdxComments, @@ -910,17 +910,15 @@ export async function runAppPreviewServer({ // Strip MDX comments let processedMarkdown = stripMdxComments(importsResolved); - // Replace image paths using collected file IDs - processedMarkdown = replaceImagePathsAndUrls( - processedMarkdown, - collectedFileIds, + processedMarkdown = replaceImagePathsAndUrlsInTranslatedPage({ + markdown: processedMarkdown, + fileIdsMap: collectedFileIds, markdownFilesToPathName, - { - absolutePathToMarkdownFile, - absolutePathToFernFolder: docsWorkspacePath - }, + absolutePathToFernFolder: docsWorkspacePath, + absolutePathToDefaultLocaleMarkdownFile: absolutePathToMarkdownFile, + absolutePathToTranslatedMarkdownFile: await resolveLocalePath(absolutePathToMarkdownFile), context - ); + }); translatedPages[pagePath] = { markdown: processedMarkdown, diff --git a/packages/cli/docs-preview/src/runPreviewServer.ts b/packages/cli/docs-preview/src/runPreviewServer.ts index 426aa9720ba5..82583d24a9d1 100644 --- a/packages/cli/docs-preview/src/runPreviewServer.ts +++ b/packages/cli/docs-preview/src/runPreviewServer.ts @@ -2,7 +2,7 @@ import { applyTranslatedFrontmatterToNavTree, applyTranslatedNavigationOverlays, getTranslatedAnnouncement, - replaceImagePathsAndUrls, + replaceImagePathsAndUrlsInTranslatedPage, replaceReferencedCode, replaceReferencedMarkdown, stripMdxComments, @@ -248,17 +248,15 @@ export async function runPreviewServer({ // Strip MDX comments let processedMarkdown = stripMdxComments(importsResolved); - // Replace image paths using collected file IDs - processedMarkdown = replaceImagePathsAndUrls( - processedMarkdown, - collectedFileIds, + processedMarkdown = replaceImagePathsAndUrlsInTranslatedPage({ + markdown: processedMarkdown, + fileIdsMap: collectedFileIds, markdownFilesToPathName, - { - absolutePathToMarkdownFile, - absolutePathToFernFolder: docsWorkspacePath - }, + absolutePathToFernFolder: docsWorkspacePath, + absolutePathToDefaultLocaleMarkdownFile: absolutePathToMarkdownFile, + absolutePathToTranslatedMarkdownFile: await resolveLocalePath(absolutePathToMarkdownFile), context - ); + }); translatedPages[pagePath] = { markdown: processedMarkdown, diff --git a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts index 22fff2dee141..3b51d381cf86 100644 --- a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts +++ b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts @@ -14,6 +14,8 @@ import { parseImagePaths, prefetchCodeSrcUrls, type ReferencedMarkdownFile, + removeCodeIncludeTags, + removeMarkdownIncludeTags, replaceImagePathsAndUrls, replaceReferencedCode, replaceReferencedMarkdown, @@ -625,6 +627,7 @@ export class DocsDefinitionResolver { }); } } + this.collectImageFilesInTranslationPages(filesToUploadSet); const imageParseTime = performance.now() - imageParseStart; this.taskContext.logger.debug(`Parsed image paths in ${imageParseTime.toFixed(0)}ms`); @@ -827,6 +830,61 @@ export class DocsDefinitionResolver { return { config, pages, jsFiles }; } + /** + * Collects assets referenced by translated pages so that images only a translation uses are + * uploaded. The translated markdown itself is rewritten per locale when the translated docs + * definition is built, so it is left untouched here. + */ + private collectImageFilesInTranslationPages(filesToUploadSet: Set): void { + const translationPages = this.parsedDocsConfig.translationPages; + if (translationPages == null) { + return; + } + + for (const [locale, pages] of Object.entries(translationPages)) { + for (const [relativePath, rawMarkdown] of Object.entries(pages)) { + const relativeFilePath = RelativeFilePath.of(relativePath); + // `` and `` includes are inlined per locale + // downstream, so their references are not assets to upload. + const markdown = removeCodeIncludeTags(removeMarkdownIncludeTags(rawMarkdown)); + // Image paths are authored either relative to the translated file or copied verbatim + // from the default-locale page, so both locations are considered. + const filepaths = [ + this.resolveTranslationFilepath(locale, relativeFilePath), + this.resolveFilepath(relativeFilePath) + ].flatMap( + (absolutePathToMarkdownFile) => + parseImagePaths( + markdown, + { + absolutePathToMarkdownFile, + absolutePathToFernFolder: this.docsWorkspace.absoluteFilePath + }, + this.taskContext + ).filepaths + ); + + for (const filepath of filepaths) { + if (existsSync(filepath)) { + filesToUploadSet.add(filepath); + } + } + } + } + } + + /** + * Translated pages live at `translations//`. + */ + private resolveTranslationFilepath(locale: string, relativeFilePath: RelativeFilePath): AbsoluteFilePath { + return join( + this.docsWorkspace.absoluteFilePath, + RelativeFilePath.of("translations"), + RelativeFilePath.of(locale), + relativeFilePath + ); + } + private resolveFilepath(unresolvedFilepath: string): AbsoluteFilePath; private resolveFilepath(unresolvedFilepath: string | undefined): AbsoluteFilePath | undefined; private resolveFilepath(unresolvedFilepath: string | undefined): AbsoluteFilePath | undefined { diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/assets/logo-pt.png b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/assets/logo-pt.png new file mode 100644 index 000000000000..0dd1608e45a9 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/assets/logo-pt.png @@ -0,0 +1,2 @@ +‰PNG + diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/assets/logo-tr.png b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/assets/logo-tr.png new file mode 100644 index 000000000000..0dd1608e45a9 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/assets/logo-tr.png @@ -0,0 +1,2 @@ +‰PNG + diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/assets/logo.png b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/assets/logo.png new file mode 100644 index 000000000000..0dd1608e45a9 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/assets/logo.png @@ -0,0 +1,2 @@ +‰PNG + diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/docs.yml b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/docs.yml new file mode 100644 index 000000000000..9f4f02d76cc7 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/docs.yml @@ -0,0 +1,10 @@ +instances: + - url: https://translation-images.docs.buildwithfern.com +translations: + - lang: en + default: true + - lang: tr + - lang: pt +navigation: + - page: Landing + path: ./pages/landing.mdx diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/fern.config.json b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/fern.config.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/fern.config.json @@ -0,0 +1 @@ +{} diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/pages/landing.mdx b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/pages/landing.mdx new file mode 100644 index 000000000000..5268c8ddd9f9 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/pages/landing.mdx @@ -0,0 +1,3 @@ +# Landing + + diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/snippets/shared.mdx b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/snippets/shared.mdx new file mode 100644 index 000000000000..dc54008c67f5 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/snippets/shared.mdx @@ -0,0 +1 @@ +Shared snippet. diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/translations/pt/pages/landing.mdx b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/translations/pt/pages/landing.mdx new file mode 100644 index 000000000000..b001dcd067b8 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/translations/pt/pages/landing.mdx @@ -0,0 +1,4 @@ +# Página inicial + + + diff --git a/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/translations/tr/pages/landing.mdx b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/translations/tr/pages/landing.mdx new file mode 100644 index 000000000000..a2f24a47d249 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/fixtures/translation-images/fern/translations/tr/pages/landing.mdx @@ -0,0 +1,6 @@ +# Ana sayfa + + + + + diff --git a/packages/cli/docs-resolver/src/__test__/translation-images.test.ts b/packages/cli/docs-resolver/src/__test__/translation-images.test.ts new file mode 100644 index 000000000000..3d42a9dd9348 --- /dev/null +++ b/packages/cli/docs-resolver/src/__test__/translation-images.test.ts @@ -0,0 +1,55 @@ +import { AbsoluteFilePath, RelativeFilePath, resolve } from "@fern-api/fs-utils"; +import { createMockTaskContext } from "@fern-api/task-context"; +import { loadDocsWorkspace } from "@fern-api/workspace-loader"; +import { describe, expect, it } from "vitest"; + +import { DocsDefinitionResolver } from "../DocsDefinitionResolver.js"; + +const context = createMockTaskContext(); + +async function resolveFixture(): Promise<{ uploadedFiles: string[] }> { + const docsWorkspace = await loadDocsWorkspace({ + fernDirectory: resolve(AbsoluteFilePath.of(__dirname), RelativeFilePath.of("fixtures/translation-images/fern")), + context + }); + + if (!docsWorkspace) { + throw new Error("Failed to load docs workspace"); + } + + const uploadedFiles: string[] = []; + const resolver = new DocsDefinitionResolver({ + domain: "https://translation-images.docs.buildwithfern.com", + docsWorkspace, + ossWorkspaces: [], + apiWorkspaces: [], + taskContext: context, + uploadFiles: async (files) => + files.map((file) => { + uploadedFiles.push(String(file.relativeFilePath)); + return { ...file, fileId: String(file.relativeFilePath) }; + }), + registerApi: async () => "" + }); + + await resolver.resolve(); + return { uploadedFiles }; +} + +describe("images in translated pages", () => { + it("uploads images that only a translated page references", async () => { + const { uploadedFiles } = await resolveFixture(); + + // Authored relative to the translated file, and relative to the default-locale page. + expect(uploadedFiles).toContain("assets/logo-tr.png"); + expect(uploadedFiles).toContain("assets/logo-pt.png"); + }); + + it("skips references that are not uploadable assets", async () => { + const { uploadedFiles } = await resolveFixture(); + + // A reference with no file on disk, and a `` include. + expect(uploadedFiles).not.toContain("assets/missing.png"); + expect(uploadedFiles).not.toContain("snippets/shared.mdx"); + }); +}); diff --git a/packages/cli/docs-resolver/src/index.ts b/packages/cli/docs-resolver/src/index.ts index 47a339fe1bbe..e7027ab02b67 100644 --- a/packages/cli/docs-resolver/src/index.ts +++ b/packages/cli/docs-resolver/src/index.ts @@ -3,6 +3,7 @@ import { type docsYml } from "@fern-api/configuration"; // Re-export markdown utilities needed for translation processing export { replaceImagePathsAndUrls, + replaceImagePathsAndUrlsInTranslatedPage, replaceReferencedCode, replaceReferencedMarkdown, stripMdxComments, diff --git a/packages/cli/docs-resolver/vitest.config.ts b/packages/cli/docs-resolver/vitest.config.ts index 8cdbd6488780..e1bb0d0cbfe0 100644 --- a/packages/cli/docs-resolver/vitest.config.ts +++ b/packages/cli/docs-resolver/vitest.config.ts @@ -14,7 +14,8 @@ const include = [ "src/__test__/sidebar-title.test.ts", "src/__test__/product-landing-page.test.ts", "src/__test__/versioned-root-landing-page.test.ts", - "src/__test__/library-hardfail.test.ts" + "src/__test__/library-hardfail.test.ts", + "src/__test__/translation-images.test.ts" ]; export default defineConfig({ diff --git a/packages/cli/generation/remote-generation/remote-workspace-runner/src/buildTranslatedDocsDefinition.ts b/packages/cli/generation/remote-generation/remote-workspace-runner/src/buildTranslatedDocsDefinition.ts index 7711e62420ce..de7a3dc5b2b0 100644 --- a/packages/cli/generation/remote-generation/remote-workspace-runner/src/buildTranslatedDocsDefinition.ts +++ b/packages/cli/generation/remote-generation/remote-workspace-runner/src/buildTranslatedDocsDefinition.ts @@ -3,7 +3,7 @@ import { applyTranslatedNavigationOverlays, type DocsDefinitionResolver, getTranslatedAnnouncement, - replaceImagePathsAndUrls, + replaceImagePathsAndUrlsInTranslatedPage, replaceReferencedCode, replaceReferencedMarkdown, stripMdxComments, @@ -100,16 +100,15 @@ export async function buildTranslatedDocsDefinition({ let processedMarkdown = stripMdxComments(importsResolved); - processedMarkdown = replaceImagePathsAndUrls( - processedMarkdown, - collectedFileIds, + processedMarkdown = replaceImagePathsAndUrlsInTranslatedPage({ + markdown: processedMarkdown, + fileIdsMap: collectedFileIds, markdownFilesToPathName, - { - absolutePathToMarkdownFile, - absolutePathToFernFolder: docsWorkspacePath - }, + absolutePathToFernFolder: docsWorkspacePath, + absolutePathToDefaultLocaleMarkdownFile: absolutePathToMarkdownFile, + absolutePathToTranslatedMarkdownFile: await resolveLocalePath(absolutePathToMarkdownFile), context - ); + }); let editThisPageUrl = basePage?.editThisPageUrl; if (editThisPageUrl != null) { diff --git a/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts b/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts index dc4014eedf74..9dc9b18537f5 100644 --- a/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts +++ b/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts @@ -11,7 +11,7 @@ import { findIncompatibleTranslatedApiIds, getTranslatedAnnouncement, type RegisterApiFn, - replaceImagePathsAndUrls, + replaceImagePathsAndUrlsInTranslatedPage, replaceReferencedCode, replaceReferencedMarkdown, stripMdxComments, @@ -1070,18 +1070,16 @@ export async function publishDocs({ // Strip MDX comments let processedMarkdown = stripMdxComments(importsResolved); - // Replace image paths using the base page's location for resolution - // (translated pages reference the same images as the default locale) - processedMarkdown = replaceImagePathsAndUrls( - processedMarkdown, - collectedFileIds, + processedMarkdown = replaceImagePathsAndUrlsInTranslatedPage({ + markdown: processedMarkdown, + fileIdsMap: collectedFileIds, markdownFilesToPathName, - { - absolutePathToMarkdownFile, - absolutePathToFernFolder: docsWorkspacePath - }, + absolutePathToFernFolder: docsWorkspacePath, + absolutePathToDefaultLocaleMarkdownFile: absolutePathToMarkdownFile, + absolutePathToTranslatedMarkdownFile: + await resolveLocalePath(absolutePathToMarkdownFile), context - ); + }); // Rewrite editThisPageUrl to point to the translated file let editThisPageUrl = basePage?.editThisPageUrl;