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
375 changes: 193 additions & 182 deletions .github/actions/translation-tracker/index.js

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions .github/actions/translation-tracker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"@octokit/rest": "^19.0.5"
"@octokit/rest": "^19.0.5",
"js-yaml": "^4.1.0"
}
}
}
22 changes: 22 additions & 0 deletions .github/stewards.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
stewards:
- name: Divyansh013
github: Divyansh013
languages:
- hi
- name: takshittt
github: takshittt
languages:
- hi
- name: hana-cho
github: hana-cho
languages:
- ko
- name: limzykenneth
github: limzykenneth
languages:
- zh-Hans
- name: lirenjie95
github: lirenjie95
languages:
- zh-Hans

16 changes: 15 additions & 1 deletion .github/workflows/translation-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@ name: Translation Sync Tracker

on:
push:
branches: [staging]
branches: [main]
paths:
- 'src/content/examples/en/**'
- 'src/content/reference/en/**'
- 'src/content/tutorials/en/**'
- 'src/content/text-detail/en/**'
- 'src/content/events/en/**'
- 'src/content/libraries/en/**'
pull_request:
branches: [main]
paths:
- 'src/content/examples/en/**'
- 'src/content/reference/en/**'
- 'src/content/tutorials/en/**'
- 'src/content/text-detail/en/**'
- 'src/content/events/en/**'
- 'src/content/libraries/en/**'
workflow_dispatch:

jobs:
Expand Down
88 changes: 88 additions & 0 deletions src/components/OutdatedTranslationBanner/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
interface Props {
englishUrl?: string;
contributeUrl?: string;
title?: string;
message?: string;
locale?: string;
}

const {
englishUrl = '/en',
contributeUrl = 'https://github.com/processing/p5.js-website/tree/main?tab=readme-ov-file#content-changes',
title,
message,
locale = 'en',
} = Astro.props as Props;

const copyByLocale: Record<string, { title: string; message: string; viewEnglish: string; contribute: string; }> = {
'hi': {
title: 'यह अनुवाद पुराना हो सकता है',
message: 'यह पृष्ठ अंग्रेज़ी संस्करण की तुलना में अद्यतन नहीं है।',
viewEnglish: 'अंग्रेज़ी संस्करण देखें',
contribute: 'अनुवाद में योगदान दें',
},
'es': {
title: 'Esta traducción podría estar desactualizada',
message: 'Esta página no está actualizada en comparación con la versión en inglés.',
viewEnglish: 'Ver versión en inglés',
contribute: 'Contribuir a la traducción',
},
'ko': {
title: '이 번역은 오래되었을 수 있습니다',
message: '이 페이지는 영어 버전과 비교하여 최신 상태가 아닙니다.',
viewEnglish: '영어 페이지 보기',
contribute: '번역에 기여하기',
},
'zh-Hans': {
title: '此翻译可能已过期',
message: '与英文版本相比,此页面不是最新的。',
viewEnglish: '查看英文页面',
contribute: '参与翻译',
},
};

const fallback = {
title: 'This translation might be outdated',
message: 'This page is not updated compared to the English version.',
viewEnglish: 'View English page',
contribute: 'Contribute to translation',
};

const copy = copyByLocale[locale] || fallback;
const resolvedTitle = title ?? copy.title;
const resolvedMessage = message ?? copy.message;
---

<section class="outdated-banner" role="status" aria-live="polite">
<div class="icon" aria-hidden="true">⚠️</div>
<div class="content">
<div class="title">{resolvedTitle}</div>
<p class="desc">
{resolvedMessage}
<a class="link" href={englishUrl}>{copy.viewEnglish}</a>
·
<a class="link" href={contributeUrl} target="_blank" rel="noopener noreferrer">{copy.contribute}</a>
</p>
</div>

<style>
.outdated-banner {
display: flex;
align-items: flex-start;
gap: 16px;
padding: 16px 20px;
border-radius: 12px;
background: #d8f532;
color: #000;
margin: 16px 0 24px;
}
.icon { font-size: 22px; line-height: 1; margin-top: 2px; }
.content { flex: 1; }
.title { font-weight: 700; font-size: 16px; margin-bottom: 6px; }
.desc { margin: 0; font-size: 14px; }
.link { color: inherit; font-weight: 700; text-underline-offset: 2px; }
.link:hover { text-decoration: underline; }
</style>
</section>

Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ primitive functions
<a href="https://p5js.org/reference/p5/arc" target="_blank">arc()</a>,
<a href="https://p5js.org/reference/p5/line" target="_blank">line()</a>,
<a href="https://p5js.org/reference/p5/triangle" target="_blank">triangle()</a>,
and <a href="https://p5js.org/reference/p5/quad" target="_blank">quad()</a>.
and <a href="https://p5js.org/reference/p5/quad" target="_blank">quad()</a>.
13 changes: 12 additions & 1 deletion src/layouts/EventLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import RelatedItems from "@components/RelatedItems/index.astro";
import { setJumpToState } from "../globals/state";
import { getCurrentLocale, getUiTranslator } from "../i18n/utils";
import { getRelatedEntriesinCollection } from "../pages/_utils";
import OutdatedTranslationBanner from "@components/OutdatedTranslationBanner/index.astro";
import { checkTranslationBanner } from "../utils/translationBanner";

interface Props {
entry: CollectionEntry<"events">;
Expand All @@ -31,9 +33,17 @@ const relatedEvents =
? await getRelatedEntriesinCollection(
"events",
currentLocale,
entry.data.relatedPastEvents.map((r) => r.slug)
entry.data.relatedPastEvents.map((r: any) => r.slug)
)
: [];

const { showBanner, englishUrl } = checkTranslationBanner(
'events',
entry.id,
currentLocale,
Astro.url.pathname,
Astro.url.origin
);
---

<Head
Expand All @@ -43,6 +53,7 @@ const relatedEvents =
/>

<BaseLayout title={title} subtitle={dayString} variant="item" topic="community">
{showBanner && <OutdatedTranslationBanner englishUrl={englishUrl} locale={currentLocale} />}
{
entry.data.featuredImage && entry.data.featuredImageAlt && (
<Image
Expand Down
11 changes: 11 additions & 0 deletions src/layouts/ExampleLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
import BaseLayout from "./BaseLayout.astro";
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";

interface Props {
example: CollectionEntry<"examples">;
Expand Down Expand Up @@ -42,6 +44,14 @@ const relatedReferences =
: [];

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

const { showBanner, englishUrl } = checkTranslationBanner(
'examples',
example.id,
currentLocale,
Astro.url.pathname,
Astro.url.origin
);
---

<Head
Expand All @@ -59,6 +69,7 @@ const { Content } = await example.render();
topic="examples"
className="example"
>
{showBanner && <OutdatedTranslationBanner englishUrl={englishUrl} locale={currentLocale} />}
<div class="mt-xl mb-4xl lg:mb-3xl max-w-[770px]">
<div class="rendered-markdown">
<Content />
Expand Down
10 changes: 10 additions & 0 deletions src/layouts/ReferenceItemLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { setJumpToState } from "../globals/state";
import { p5Version } from "../globals/p5-version";
import flask from "@src/content/ui/images/icons/flask.svg?raw";
import warning from "@src/content/ui/images/icons/warning.svg?raw";
import OutdatedTranslationBanner from "@components/OutdatedTranslationBanner/index.astro";
import { checkTranslationBanner } from "../utils/translationBanner";

const { entry, relatedEntries } = Astro.props;
const currentLocale = getCurrentLocale(Astro.url.pathname);
Expand Down Expand Up @@ -81,6 +83,13 @@ const descriptionParts = description.split(
/(<pre><code class="language-js example">[\s\S]+?<\/code><\/pre>)/gm
);

const { showBanner, englishUrl } = checkTranslationBanner(
'reference',
entry.id,
currentLocale,
Astro.url.pathname,
Astro.url.origin
);
---

<Head title={entry.data.title} locale={currentLocale} />
Expand All @@ -93,6 +102,7 @@ const descriptionParts = description.split(
topic="reference"
className="reference-item"
>
{showBanner && <OutdatedTranslationBanner englishUrl={englishUrl} locale={currentLocale} />}
<div class="content-grid mt-0 max-w-[770px]">
<div class="col-span-9 xl:min-w-[1000px]">
{entry.data.beta && (
Expand Down
11 changes: 11 additions & 0 deletions src/layouts/TextDetailLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import BaseLayout from "./BaseLayout.astro";
import type { CollectionEntry } from "astro:content";
import { setJumpToState } from "../globals/state";
import { getCurrentLocale } from "../i18n/utils";
import OutdatedTranslationBanner from "@components/OutdatedTranslationBanner/index.astro";
import { checkTranslationBanner } from "../utils/translationBanner";

interface Props {
page: CollectionEntry<"text-detail">;
Expand All @@ -19,6 +21,14 @@ const pageTopic = Astro.url.pathname.includes("donate")
const currentLocale = getCurrentLocale(Astro.url.pathname);

setJumpToState(null);

const { showBanner, englishUrl } = checkTranslationBanner(
'text-detail',
page.id,
currentLocale,
Astro.url.pathname,
Astro.url.origin
);
---

<Head title={page.data.title} locale={currentLocale} />
Expand All @@ -30,6 +40,7 @@ setJumpToState(null);
className={pageTopic}
subtitle={null}
>
{showBanner && <OutdatedTranslationBanner englishUrl={englishUrl} locale={currentLocale} />}
<div class="max-w-[770px] [&>*:first-child]:mt-0 rendered-markdown pb-[80px]">
<Content />
</div>
Expand Down
11 changes: 11 additions & 0 deletions src/layouts/TutorialLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
generateJumpToState,
getRelatedEntriesinCollection,
} from "../pages/_utils";
import OutdatedTranslationBanner from "@components/OutdatedTranslationBanner/index.astro";
import { checkTranslationBanner } from "../utils/translationBanner";

const { entry } = Astro.props;
const { Content, components } = await entry.render();
Expand Down Expand Up @@ -44,6 +46,14 @@ const relatedExamples =
entry.data.relatedContent.examples.map((r: any) => r.slug)
)
: [];

const { showBanner, englishUrl } = checkTranslationBanner(
'tutorials',
entry.id,
currentLocale,
Astro.url.pathname,
Astro.url.origin
);
---

<Head
Expand All @@ -61,6 +71,7 @@ const relatedExamples =
topic="tutorials"
className="tutorials"
>
{showBanner && <OutdatedTranslationBanner englishUrl={englishUrl} locale={currentLocale} />}
{entry.data.authors && <section role="group" aria-label="authors">By {entry.data.authors.join(", ")}</section>}
{entry.data.authorsNote && <h7>{entry.data.authorsNote}</h7>}
<div class="rendered-markdown">
Expand Down
59 changes: 59 additions & 0 deletions src/utils/translationBanner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import fs from 'fs';
import path from 'path';

interface BannerCheckResult {
showBanner: boolean;
englishUrl: string;
}

export function checkTranslationBanner(
contentType: string,
itemId: string,
currentLocale: string,
currentPathname: string,
origin: string
): BannerCheckResult {
let showBanner = false;
let englishUrl = currentPathname;

if (currentLocale === 'en') {
return { showBanner: false, englishUrl };
}

englishUrl = currentPathname.replace(`/${currentLocale}/`, '/');

if (!englishUrl.startsWith('http')) {
englishUrl = `${origin}${englishUrl}`;
}

try {
const manifestPath = path.join(process.cwd(), 'public', 'translation-status', `${contentType}.json`);
if (fs.existsSync(manifestPath)) {
const raw = fs.readFileSync(manifestPath, 'utf8');
const manifest = JSON.parse(raw);

const idNoLocale = itemId.replace(/^[\w-]+\//, '');
const withoutExt = idNoLocale.replace(/\.(mdx?|ya?ml)$/, '');
const keyWithDescription = withoutExt;
const keyWithoutDescription = withoutExt.replace(/\/description$/, '');

const entry = manifest[contentType]?.[keyWithoutDescription] || manifest[contentType]?.[keyWithDescription];

if (entry) {
const isOutdated = Array.isArray(entry.outdated) && entry.outdated.includes(currentLocale);
const isMissing = Array.isArray(entry.missing) && entry.missing.includes(currentLocale);
showBanner = isOutdated || isMissing;

if (isMissing) {
const missingEnglishUrl = currentPathname.replace(`/${currentLocale}/`, '/');
englishUrl = `${origin}${missingEnglishUrl}`;
}
}
}
} catch (e) {
console.error('Error checking translation banner:', e);
}

return { showBanner, englishUrl };
}