Skip to content
Open
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
3 changes: 3 additions & 0 deletions docs/localization.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Each file within these collection folders corresponds to a real page on the rend

The ["ui" content collection](src/content/ui/) is a little different than the others. It contains yaml files that cover strings that are used across different pages for things like the navigation bar.

**Note on Translating Examples:**
When translating Example pages (`.mdx`), you do not need to copy or include the `remix` metadata block in the frontmatter. The website will automatically fall back to the English version to fetch the remix data.

## Routes and Layouts

Astro uses a file-based approach to generating routes. The filepath of each file in `src/pages` becomes the url of the page it renders. Because we need to support a url scheme where English translations of pages are served at a URL with no locale prefix (for example, the English version of the tutorials page is at https://p5.js/tutorials _not_ https://p5.js/en/tutorials), there are 2 sets of routing files and folders: one in `src/pages` and another `src/pages/[locale]`. The `[locale]` set are needed to build and serve the non-English pages (whose URLs are also prefixed by the language codes).
Expand Down
16 changes: 12 additions & 4 deletions src/layouts/ExampleLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import EditableSketch from "@components/EditableSketch/index.astro";
import RelatedItems from "@components/RelatedItems/index.astro";
import OutdatedTranslationBanner from "@components/OutdatedTranslationBanner/index.astro";
import { checkTranslationBanner } from "../utils/translationBanner";
import { getFallbackRemixData } from "../pages/_utils";

interface Props {
example: CollectionEntry<"examples">;
Expand Down Expand Up @@ -45,8 +46,15 @@ const relatedReferences =

const { Content } = await example.render();

// Use the fallback function to retrieve English remix data if the current locale doesn't have any
let remixData = (await getFallbackRemixData(
example.id,
currentLocale,
example.data.remix
)) as typeof example.data.remix;

// Extract the collective attribution year. If multiple provided, uses last shown.
const collectivelyAttributedSince = example.data.remix?.reduce(
const collectivelyAttributedSince = remixData?.reduce(
(acc: number | null, item) => {
if (item.collectivelyAttributedSince) {
return item.collectivelyAttributedSince;
Expand All @@ -57,7 +65,7 @@ const collectivelyAttributedSince = example.data.remix?.reduce(
);

// Boolean value on whether the remix history contains links to code
const remixHistoryHasCodeLinks = example.data.remix?.some(
const remixHistoryHasCodeLinks = remixData?.some(
(item) => Array.isArray(item.code) && item.code.length > 0
);

Expand Down Expand Up @@ -97,7 +105,7 @@ const { showBanner, englishUrl } = checkTranslationBanner(

<a href={Astro.url.pathname}>{example.data.title}</a>:{" "}

{example.data.remix?.map((item, i) => {
{remixData?.map((item, i) => {
const parts = [];

// Each remix entry requires at least one attribution
Expand Down Expand Up @@ -146,7 +154,7 @@ const { showBanner, englishUrl } = checkTranslationBanner(
{remixHistoryHasCodeLinks ? (
<>
{t("attribution", "You can find the code history of these examples here")}{": "}
{example.data.remix
{remixData
.map(item => item?.code)
.flat()
.filter(codeItem => codeItem && codeItem.URL)
Expand Down
34 changes: 34 additions & 0 deletions src/pages/_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,3 +457,37 @@ const getUrl = (
return "";
}
};

/**
* Retrieves fallback remix (attribution/code history) data from the English example
* if the current localized example is missing it.
*
* @param currentId The id of the current example
* @param currentLocale The current locale string
* @param currentRemixData The remix data from the current locale (if any)
* @returns An array of remix data
*/
export const getFallbackRemixData = async (
currentId: string,
currentLocale: string,
currentRemixData: any[] | undefined,
) => {
// Return early if data already exists or if we are already on the English page
if (currentRemixData && currentRemixData.length > 0) {
return currentRemixData;
}
if (currentLocale === "en") {
return currentRemixData;
}
// Main logic
// replace the core path with the English path to find the corresponding English example
// e.g., "zh-Hans/02_Animation_And_Variables/00_Drawing_Lines/description.mdx"
// -> "en/02_Animation_And_Variables/00_Drawing_Lines/description.mdx"
const englishId = currentId.replace(`${currentLocale}/`, "en/");
const allExamples = await getCollection("examples");
const englishExample = allExamples.find((e) => e.id === englishId);
if (englishExample?.data.remix && englishExample.data.remix.length > 0) {
return englishExample.data.remix;
}
return currentRemixData;
}
13 changes: 13 additions & 0 deletions test/pages/_utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
removeContentFileExt,
removeLeadingSlash,
removeLocaleAndExtension,
getFallbackRemixData,
} from "@pages/_utils";

suite("exampleContentSlugToLegacyWebsiteSlug", () => {
Expand Down Expand Up @@ -50,3 +51,15 @@ suite("removeLocaleAndExtensionFromId", () => {
);
});
});

suite("getFallbackRemixData", () => {
test("returns remix data for English example when current locale example has no remix data", () => {
expect(
getFallbackRemixData(
"zh-Hans/02_Animation_And_Variables/00_Drawing_Lines/description.mdx",
"zh-Hans",
undefined
)
).toBeDefined();
});
});
Loading