Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
158 commits
Select commit Hold shift + click to select a range
09c3d57
feat(i18n): add timestamped logging + token summary
myelinated-wackerow Mar 23, 2026
dd76d9a
fix(i18n): code comment translation bugs
wackerow Mar 24, 2026
9563b1a
fix(i18n): clean up workflow log output
myelinated-wackerow Mar 24, 2026
94ed356
feat(i18n): add exclude_path workflow input
myelinated-wackerow Mar 24, 2026
f4ed2f0
feat(i18n): bump default concurrency to 6
myelinated-wackerow Mar 24, 2026
fd22456
Merge branch 'dev' into gemini-v3
myelinated-wackerow Mar 24, 2026
04fbb7d
fix(i18n): resolve sanitizer paths in CI
myelinated-wackerow Mar 24, 2026
4245a37
fix(i18n): re-run sanitizer after JSX step
myelinated-wackerow Mar 24, 2026
7dd307b
feat(i18n): shared concurrency pool
myelinated-wackerow Mar 24, 2026
11492fe
fix(i18n): sanitizer uses BASE_BRANCH English
myelinated-wackerow Mar 24, 2026
bfef94e
fix(i18n): fence indent, prompt, validation
myelinated-wackerow Mar 25, 2026
7482ad2
chore: autofix to sort imports
wackerow Mar 25, 2026
a04ebdc
fix(i18n): tell Gemini to set lang to target code
myelinated-wackerow Mar 25, 2026
08561aa
fix(i18n): sanitizer forces lang to match locale
myelinated-wackerow Mar 25, 2026
8c1a01a
fix(i18n): protect code block placeholders from Gemini
myelinated-wackerow Mar 25, 2026
e73a3f9
fix(i18n): compare functional code only in fence drift warning
myelinated-wackerow Mar 25, 2026
8c6663b
patch: ai/language-groups instructions
myelinated-wackerow Mar 26, 2026
1b3fabb
feat(i18n): post failed files as PR comment
myelinated-wackerow Mar 27, 2026
470d076
feat(i18n): add JSON batching and HTML extraction
myelinated-wackerow Mar 28, 2026
574ffd7
feat(i18n): add safety settings and response diagnostics
myelinated-wackerow Mar 28, 2026
4e829ad
fix(i18n): use SDK enum types for safety settings
myelinated-wackerow Mar 28, 2026
e4a9d66
fix(i18n): use relative paths for sanitizer commits
myelinated-wackerow Mar 28, 2026
f6b9b81
fix(i18n): only fail when both title and desc untranslated
myelinated-wackerow Mar 28, 2026
a2ee406
fix(i18n): use relative paths for JSX attribute commits
myelinated-wackerow Mar 28, 2026
e686b82
merge: resolve test conflicts (accept dev additions)
myelinated-wackerow Mar 28, 2026
f3ee459
feat(i18n): integrate selective glossary lookup
myelinated-wackerow Mar 28, 2026
7ffa1a8
docs(i18n): add follow-up task for Supabase glossary removal
myelinated-wackerow Mar 28, 2026
e776bea
docs(i18n): add image translation to future roadmap
myelinated-wackerow Mar 28, 2026
37143cf
feat(i18n): add 15 translation prompt rules
myelinated-wackerow Mar 28, 2026
4ee8fc6
refactor(i18n): consolidate prompt rules per Gemini review
myelinated-wackerow Mar 29, 2026
4a3d307
feat(i18n): add glossary-authoritative loanword rule
myelinated-wackerow Mar 29, 2026
e63f144
refactor(i18n): resolve contradictions from Gemini review
myelinated-wackerow Mar 29, 2026
0995aa9
feat(i18n): add glossary context-match escape clause
myelinated-wackerow Mar 29, 2026
6e81c85
feat(i18n): add merkle trie manifest generator
myelinated-wackerow Mar 29, 2026
8bf1d04
feat(i18n): add per-file manifest I/O and drift scanner
myelinated-wackerow Mar 29, 2026
af3fde4
fix(i18n): move JSON tracking manifest out of src/intl/
myelinated-wackerow Mar 29, 2026
d2cc35e
fix(i18n): keep JSON manifests in src/intl, filter dotfiles
myelinated-wackerow Mar 30, 2026
6fc43fb
docs(i18n): add CI header ID enforcement to FUTURE
myelinated-wackerow Mar 30, 2026
e247a3c
refactor(i18n): separate label/content hashes, fix nesting
myelinated-wackerow Mar 30, 2026
4aa6b83
feat(i18n): stamp manifests after each translation
myelinated-wackerow Mar 30, 2026
f365046
feat(i18n): sync glossary data from Atlas (486 terms x 24 langs)
myelinated-wackerow Mar 31, 2026
942fab6
refactor(i18n): extract shared patterns to DRY
myelinated-wackerow Apr 1, 2026
b6495cb
feat(i18n): add content normalizer for manifest
myelinated-wackerow Apr 1, 2026
dc8b595
refactor(i18n): integrate normalizer, prune dead code
myelinated-wackerow Apr 1, 2026
646ba07
Merge remote-tracking branch 'origin/dev' into gemini-v4
myelinated-wackerow Apr 1, 2026
24966c6
feat(i18n): extract embedded HTML tags, add placeholderOrder
myelinated-wackerow Apr 1, 2026
9e6dab0
fix(i18n): recursive normalization of component children
myelinated-wackerow Apr 1, 2026
b789272
fix(i18n): self-closing syntax for block placeholders
myelinated-wackerow Apr 1, 2026
f27c9c8
fix(i18n): preserve whitespace in normalized output
myelinated-wackerow Apr 1, 2026
98e4e0b
fix(i18n): heading ID regex preserves blank lines
myelinated-wackerow Apr 1, 2026
77a9151
feat(i18n): wire normalizer into translation pipeline
myelinated-wackerow Apr 1, 2026
fcb7cc0
feat(i18n): enable normalizer for markdown translations
myelinated-wackerow Apr 2, 2026
fd49a98
fix(i18n): wrapper placeholders for components with children
myelinated-wackerow Apr 2, 2026
829d472
fix(i18n): restore heading anchor IDs from English
myelinated-wackerow Apr 2, 2026
f259814
feat(i18n): populate placeholderMap + placeholderOrder
myelinated-wackerow Apr 2, 2026
ef6e62a
fix(i18n): commit manifest via GitHub API, not local disk
myelinated-wackerow Apr 2, 2026
00b0c88
feat(i18n): translate prose inside markdown code fences
myelinated-wackerow Apr 2, 2026
b670085
feat(i18n): add inert-value propagation script
myelinated-wackerow Apr 2, 2026
c89e64d
feat(i18n): content-addressed placeholders for JSON HTML
myelinated-wackerow Apr 2, 2026
efb23e2
fix(i18n): replace-all for duplicate placeholders, JSON manifests via…
myelinated-wackerow Apr 3, 2026
56d369e
fix(i18n): handle duplicate wrapper placeholders
myelinated-wackerow Apr 3, 2026
dbba214
docs(i18n): add package extraction + housekeeping to FUTURE
myelinated-wackerow Apr 3, 2026
6bb47cf
feat(i18n): BiDi sanitizer for bare LTR values in RTL
myelinated-wackerow Apr 3, 2026
d277079
feat(i18n): condensed BiDi prompt rules for RTL
myelinated-wackerow Apr 3, 2026
0bae269
fix(i18n): realign misaligned closing code fences
myelinated-wackerow Apr 4, 2026
cc548ba
docs(i18n): remove stale v0.2.0 roadmap
myelinated-wackerow Apr 4, 2026
211089b
Merge branch 'dev' into gemini-v4
myelinated-wackerow Apr 4, 2026
071ce9d
fix(i18n): remove double indent on code fence placeholders
myelinated-wackerow Apr 4, 2026
e931576
feat(i18n): add first-mention expansion + idiom prompt rules
myelinated-wackerow Apr 4, 2026
e963c32
feat(i18n): integrate intl-content-tree package for manifests
myelinated-wackerow Apr 4, 2026
1084bca
fix(i18n): add missing DiffEntry import in manifest-adapter
myelinated-wackerow Apr 4, 2026
a31b60d
chore(deps): add intl-content-tree v0.1.3 as devDependency
myelinated-wackerow Apr 5, 2026
ded3023
feat(i18n): wire manifest-translation.json into pipeline
myelinated-wackerow Apr 5, 2026
7e5b9f1
feat(i18n): rewrite inert propagation with security fixes
myelinated-wackerow Apr 5, 2026
a3ed401
feat(i18n): add incremental section-level translation module
myelinated-wackerow Apr 5, 2026
382c44e
chore(deps): update intl-content-tree to v0.1.4
myelinated-wackerow Apr 5, 2026
471184c
feat(i18n): add incremental translation mode to workflow
myelinated-wackerow Apr 5, 2026
78d54bb
feat(i18n): wire Phase 4 prose retranslation in orchestrator
myelinated-wackerow Apr 5, 2026
a02a09b
fix(i18n): correct filterGlossaryFlat call signature
myelinated-wackerow Apr 5, 2026
0161d78
fix(i18n): call committer.init() in incremental pipeline
myelinated-wackerow Apr 6, 2026
d74831f
fix(i18n): fix inert matching + section overlap
myelinated-wackerow Apr 6, 2026
d34fcf6
feat(i18n): auto-detect full vs incremental per file
myelinated-wackerow Apr 6, 2026
88a72f4
fix(i18n): quote workflow step name with colon
myelinated-wackerow Apr 6, 2026
5fbafec
fix(i18n): image alias, heading attr, JSON incremental
myelinated-wackerow Apr 6, 2026
b9480a6
fix(i18n): JSON inert detection and replacement
myelinated-wackerow Apr 6, 2026
d90b721
fix(i18n): generate translation manifest for JSON files
myelinated-wackerow Apr 6, 2026
277a172
fix(i18n): include tagName in placeholder extraction
myelinated-wackerow Apr 6, 2026
5e4808f
feat(i18n): close known gaps before expanded testing
myelinated-wackerow Apr 6, 2026
69018f4
fix(i18n): address review findings before expanded test
myelinated-wackerow Apr 6, 2026
bc03161
fix(i18n): bridge normalizer/tree hash mismatch in inert lookup
myelinated-wackerow Apr 6, 2026
1b9436e
feat(i18n): single staging branch for translations
myelinated-wackerow Apr 6, 2026
32921d9
feat(i18n): prune removed sections + pipeline docs
myelinated-wackerow Apr 6, 2026
ae16404
refactor(i18n): derive translation branch from base branch
myelinated-wackerow Apr 6, 2026
b258531
refactor(i18n): simplify to intl/pending default branch
myelinated-wackerow Apr 6, 2026
96e9b28
fix(i18n): locale-tree fallback for MD inert propagation
myelinated-wackerow Apr 6, 2026
ed03221
feat(i18n): intl-content-tree v0.1.5 + full inert rewrite
myelinated-wackerow Apr 7, 2026
c2db4d1
feat(i18n): git-based old English retrieval for inert detection
myelinated-wackerow Apr 9, 2026
edba407
feat(i18n): deterministic section-scoped inert replacement
myelinated-wackerow Apr 9, 2026
bae6cbc
fix(i18n): component-attribute occurrence + dedup
myelinated-wackerow Apr 9, 2026
31dfdb9
fix(i18n): skip code fences in restoreHeadingIds
myelinated-wackerow Apr 9, 2026
6a63ec6
Merge remote-tracking branch 'origin/dev' into gemini-v4
myelinated-wackerow Apr 9, 2026
edb0298
chore(i18n): v4 freeze prep
myelinated-wackerow Apr 9, 2026
9e7ae67
fix(i18n): checkout base_branch, not ref
myelinated-wackerow Apr 9, 2026
c095db9
feat(i18n): v4 simplification + stamp-only mode
myelinated-wackerow Apr 9, 2026
9a0dd57
update(data): meetup listings
wackerow Apr 10, 2026
88d9990
test: add incremental translation fixtures
myelinated-wackerow Apr 11, 2026
9efd028
docs: add pipeline spec for incremental translation
myelinated-wackerow Apr 11, 2026
84c9be8
test: add incremental pipeline test suite
myelinated-wackerow Apr 11, 2026
d2edd55
feat(intl-pipeline): add incremental pipeline
myelinated-wackerow Apr 11, 2026
642986d
feat(intl-pipeline): add infrastructure lib
myelinated-wackerow Apr 11, 2026
09768c1
chore: wire tests + workflow to new pipeline
myelinated-wackerow Apr 11, 2026
9b4ec11
fix(intl-pipeline): wire glossary into translator
myelinated-wackerow Apr 11, 2026
4ace269
feat(intl-pipeline): add post-translation sanitizer
myelinated-wackerow Apr 11, 2026
f744037
fix(intl-sanitizer): match headings without IDs
myelinated-wackerow Apr 11, 2026
041c558
test: add expected fixtures, 131/131 passing
myelinated-wackerow Apr 12, 2026
233a760
fix(intl-sanitizer): fence-aware header extraction
myelinated-wackerow Apr 12, 2026
8d9172b
refactor(intl-pipeline): rename sanitizer exports
myelinated-wackerow Apr 12, 2026
c3c13cd
feat(intl-pipeline): centralize model config + costs
myelinated-wackerow Apr 12, 2026
0da322c
build(deps): bump next-intl from 4.8.3 to 4.9.1
dependabot[bot] Apr 13, 2026
2cbe1bc
Reapply "Merge pull request #17870 from ethereum/videos-refactor"
wackerow Apr 13, 2026
12c01e7
fix: free disk space after build to prevent ENOSPC on Netlify
pettinarip Apr 14, 2026
748cc0b
docs: add netlify enospc solution doc, fix stale next.js version in s…
pettinarip Apr 14, 2026
25380e0
Merge pull request #17971 from ethereum/fix/build-cache-cleanup
wackerow Apr 14, 2026
7fae1ce
Merge branch 'dev' into videos-refactor-v2
wackerow Apr 14, 2026
e53ad6c
Fix Trigger.dev CI auth: use TRIGGER_ACCESS_TOKEN
pettinarip Apr 14, 2026
c4da302
Merge pull request #17970 from ethereum/videos-refactor-v2
pettinarip Apr 15, 2026
2eedd82
chore(intl-pipeline): restructure lib modules
myelinated-wackerow Apr 12, 2026
a110822
deprecate(i18n): remove old pipeline and Crowdin workflow
myelinated-wackerow Apr 12, 2026
42f84ba
chore: organize specs into tests/specs/
myelinated-wackerow Apr 12, 2026
e12d651
test: add chunking, concurrency, commit strategy
myelinated-wackerow Apr 12, 2026
1a03c8f
chore: move intl-pipeline tests to subdirectory
myelinated-wackerow Apr 12, 2026
fa7716c
feat(intl-pipeline): byte-size-aware chunking
myelinated-wackerow Apr 12, 2026
7eaabca
feat(intl-pipeline): concurrency pool + commit strategy
myelinated-wackerow Apr 13, 2026
770e9f6
fix(intl-pipeline): use SectionForPrompt.content
myelinated-wackerow Apr 13, 2026
e36a710
docs(tests): convert commit strategy fixmes to docs
myelinated-wackerow Apr 13, 2026
bf494da
chore: fix broken test imports, clean up FUTURE.md
myelinated-wackerow Apr 13, 2026
1fb0f99
chore: rename workflow to intl-pipeline.yml
myelinated-wackerow Apr 13, 2026
294ee4e
docs: add i18n pipeline process retrospective
myelinated-wackerow Apr 13, 2026
84eb0e4
feat(intl-pipeline): switch glossary to ETHGlossary API
myelinated-wackerow Apr 14, 2026
e5b6118
Merge remote-tracking branch 'origin/dev' into intl-pipeline-v5-rebas…
myelinated-wackerow Apr 15, 2026
36f8181
fix(intl-pipeline): prevent shell injection in git show
myelinated-wackerow Apr 15, 2026
03ec351
fix(intl-pipeline): address P1 code review findings
myelinated-wackerow Apr 15, 2026
d02ed86
feat(intl-pipeline): wire PR creation + update
myelinated-wackerow Apr 15, 2026
7f244df
chore(intl-pipeline): remove dead code (~850 lines)
myelinated-wackerow Apr 15, 2026
ff9ec6c
refactor(intl-pipeline): flatten gemini/ to gemini.ts
myelinated-wackerow Apr 15, 2026
861c2f2
test: add coverage for output-validation + incremental
myelinated-wackerow Apr 15, 2026
236c8ba
Merge pull request #17963 from ethereum/intl-pipeline-v5
wackerow Apr 16, 2026
032057b
Merge pull request #17960 from ethereum/meetup-cleanup
pettinarip Apr 16, 2026
0c2526f
Merge pull request #17965 from ethereum/dependabot/npm_and_yarn/next-…
wackerow Apr 16, 2026
6c9baaa
Merge pull request #17977 from ethereum/fix/deploy-data-layer-auth
wackerow Apr 16, 2026
f284058
build(deps): bump hono from 4.12.12 to 4.12.14
dependabot[bot] Apr 16, 2026
b7c4375
Merge pull request #17980 from ethereum/dependabot/npm_and_yarn/hono-…
wackerow Apr 16, 2026
b9e8194
build(deps): bump next from 16.2.1 to 16.2.3
dependabot[bot] Apr 16, 2026
e707982
Merge pull request #17961 from ethereum/dependabot/npm_and_yarn/next-…
wackerow Apr 16, 2026
4ce6db7
patch(content): update hk hub listing
myelinated-wackerow Apr 16, 2026
c32e113
Merge pull request #17983 from ethereum/hk-update
wackerow Apr 16, 2026
74f3a96
Merge staging into dev
pettinarip Apr 17, 2026
99665eb
11.4.0
pettinarip Apr 17, 2026
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
110 changes: 110 additions & 0 deletions .github/ISSUE_TEMPLATE/suggest_video.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: Suggest a video
description: Suggest a video to add to the ethereum.org video gallery
title: Suggest a video
labels: ["feature ✨", "content 🖋️"]
body:
- type: markdown
attributes:
value: |
Before suggesting a video, please review [our video listing policy](https://ethereum.org/contributing/adding-videos/) to ensure your suggestion meets the criteria.
- type: markdown
id: video_info
attributes:
value: "## Video info"
- type: input
id: video_title
attributes:
label: Video title
description: What is the title of the video?
validations:
required: true
- type: input
id: video_url
attributes:
label: YouTube URL
description: Please provide the full YouTube URL (e.g. https://www.youtube.com/watch?v=...)
validations:
required: true
- type: textarea
id: video_description
attributes:
label: Video description
description: Provide a brief 1–3 sentence summary of what the video covers
validations:
required: true
- type: input
id: video_author
attributes:
label: Creator / Channel
description: Who created the video? (e.g. "Ethereum Foundation", "Finematics")
validations:
required: true
- type: input
id: video_duration
attributes:
label: Duration
description: "Video length in H:MM:SS or M:SS format (e.g. 1:08:42 or 12:30)"
validations:
required: true
- type: dropdown
id: video_education_level
attributes:
label: Education level
description: What level of Ethereum knowledge does this video assume?
options:
- "Beginner"
- "Intermediate"
- "Advanced"
validations:
required: true
- type: dropdown
id: video_format
attributes:
label: Format
description: What best describes the format of this video?
options:
- "Explainer"
- "Presentation"
- "Interview"
- "Tutorial"
- "Panel"
validations:
required: true
- type: input
id: video_topics
attributes:
label: Topics
description: "Comma-separated topic tags (e.g. scaling, layer-2, rollups). See existing tags at https://ethereum.org/videos/"
validations:
required: true
- type: input
id: video_upload_date
attributes:
label: Original upload date
description: "When was the video originally published? (YYYY-MM-DD format)"
validations:
required: true
- type: textarea
id: video_transcript
attributes:
label: Transcript (optional)
description: |
If you have a transcript, paste it here. Otherwise the team will generate one.
Transcripts should use markdown formatting with section headings and timestamps.
- type: textarea
id: video_justification
attributes:
label: Why should this video be listed?
description: Briefly explain how this video helps ethereum.org users learn about Ethereum
- type: checkboxes
id: video_work_on
attributes:
label: Would you like to work on this issue?
description: If yes, you can open a PR to add the video yourself following our contributing guide
options:
- label: "Yes"
required: false
- label: "No"
required: false
validations:
required: true
90 changes: 0 additions & 90 deletions .github/workflows/crowdin-ai-import.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/deploy-data-layer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ jobs:
- name: Deploy to Trigger.dev
run: pnpm exec trigger deploy
env:
TRIGGER_SECRET_KEY: ${{ secrets.TRIGGER_SECRET_KEY }}
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Gemini Translations
name: Intl Pipeline

on:
workflow_dispatch:
Expand All @@ -7,6 +7,10 @@ on:
description: "Path(s) to translate (comma-separated files, single directory, or blank for all)"
required: false
type: string
exclude_path:
description: "Path(s) to exclude (comma-separated files or directories)"
required: false
type: string
target_languages:
description: "Comma-separated language codes (blank for all locales)"
required: false
Expand All @@ -16,15 +20,20 @@ on:
required: false
default: "dev"
type: string
target_branch:
description: "Override target branch (default: intl/pending)"
required: false
type: string
concurrency:
description: "Max parallel Gemini requests per language"
required: false
default: "3"
default: "16"
type: string
resume_run_id:
description: "Resume an interrupted run by its ID"
stamp_only:
description: "Update manifests only, no translations"
required: false
type: string
default: false
type: boolean
skip_pr:
description: "Skip PR creation?"
required: false
Expand All @@ -35,6 +44,18 @@ on:
required: false
default: "false"
type: boolean
mode:
description: "Translation mode: 'auto' (full for new files, incremental for existing) or 'full' (retranslate everything)"
required: false
default: "auto"
type: choice
options:
- auto
- full

concurrency:
group: i18n-translation
cancel-in-progress: false

jobs:
translate:
Expand All @@ -43,30 +64,36 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v6
with:
ref: ${{ github.event.inputs.base_branch || 'dev' }}
fetch-depth: 0

- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v5

- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 20
node-version-file: ".nvmrc"
cache: "pnpm"

- name: Install dependencies
run: pnpm install

- name: Run Gemini translation
run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/i18n/main-gemini.ts
- name: Run translation pipeline
run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/intl-pipeline/main.ts
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
I18N_GITHUB_API_KEY: ${{ secrets.I18N_GITHUB_TOKEN }}
GITHUB_API_TOKEN: ${{ secrets.I18N_GITHUB_TOKEN }}
TARGET_PATH: ${{ github.event.inputs.target_path }}
EXCLUDE_PATH: ${{ github.event.inputs.exclude_path }}
TARGET_LANGUAGES: ${{ github.event.inputs.target_languages }}
BASE_BRANCH: ${{ github.event.inputs.base_branch }}
GEMINI_CONCURRENCY: ${{ github.event.inputs.concurrency }}
RESUME_RUN_ID: ${{ github.event.inputs.resume_run_id }}
SKIP_PR_CREATION: ${{ github.event.inputs.skip_pr }}
VERBOSE: ${{ github.event.inputs.verbose }}
MODE: ${{ github.event.inputs.mode }}
GITHUB_REPOSITORY: ${{ github.repository }}
TRANSLATION_PIPELINE: Gemini
TARGET_BRANCH: ${{ github.event.inputs.target_branch }}
STAMP_ONLY: ${{ github.event.inputs.stamp_only }}
DRY_RUN: "false"
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ This is the official Ethereum.org website - a Next.js application that serves as
- **content/** - Markdown content files
- **images/** - Image assets
- **docs/** - Development documentation
- **solutions/** - Documented solutions to past problems, organized by category with YAML frontmatter (module, tags, problem_type)

## Code Conventions

Expand Down
3 changes: 2 additions & 1 deletion app/[locale]/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { GHIssue, SlugPageParams } from "@/lib/types"

import I18nProvider from "@/components/I18nProvider"
import mdComponents from "@/components/MdComponents"
import VideoWatch from "@/components/Videos/VideoWatch"

import { dateToString } from "@/lib/utils/date"
import { getLayoutFromSlug } from "@/lib/utils/layout"
Expand Down Expand Up @@ -57,7 +58,7 @@ export default async function Page(props: { params: Promise<SlugPageParams> }) {
} = await getPageData({
locale,
slug,
baseComponents: mdComponents,
baseComponents: { ...mdComponents, VideoWatch },
componentsMapping,
scope: {
gfissues,
Expand Down
57 changes: 57 additions & 0 deletions app/[locale]/videos/[slug]/page-jsonld.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { VideoFrontmatter } from "@/lib/interfaces"

import PageJsonLD from "@/components/PageJsonLD"

import { ethereumFoundationOrganization } from "@/lib/utils/jsonld"
import { stripMarkdown } from "@/lib/utils/md"
import { toIsoDuration } from "@/lib/utils/time"
import { normalizeUrlForJsonLd } from "@/lib/utils/url"
import { getDefaultThumbnailUrl } from "@/lib/utils/videos"

export default function VideoPageJsonLD({
locale,
slug,
frontmatter,
transcript,
}: {
locale: string
slug: string
frontmatter: VideoFrontmatter
transcript: string | null
}) {
const url = normalizeUrlForJsonLd(locale, `/videos/${slug}/`)

const jsonLd: Record<string, unknown> = {
"@context": "https://schema.org",
"@type": "VideoObject",
"@id": `${url}#video`,
name: frontmatter.title,
description: frontmatter.description,
uploadDate: `${frontmatter.uploadDate}T00:00:00+00:00`,
duration: toIsoDuration(frontmatter.duration),
thumbnailUrl:
frontmatter.customThumbnailUrl ||
getDefaultThumbnailUrl(frontmatter.youtubeId),
embedUrl: `https://www.youtube.com/embed/${frontmatter.youtubeId}`,
contentUrl: `https://www.youtube.com/watch?v=${frontmatter.youtubeId}`,
educationalLevel: frontmatter.educationLevel,
inLanguage: frontmatter.lang,
creator: {
"@type": "Person",
name: frontmatter.author,
},
publisher: ethereumFoundationOrganization,
isAccessibleForFree: true,
isFamilyFriendly: true,
}

// Add transcript as plain text if available
if (transcript) {
// Escape < and / to prevent script injection (XSS protection)
jsonLd.transcript = stripMarkdown(transcript, true)
.replace(/</g, "\\u003c")
.replace(/\//g, "\\u002f")
}

return <PageJsonLD structuredData={jsonLd} />
}
Loading
Loading