Add multilingual blog support and publish OpenClaw Meetup #5#48
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
Warning Review limit reached
Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
📝 WalkthroughWalkthroughThe blog now supports Indonesian and English content, translation-linked articles, localized routes, language-aware SEO metadata, shared article rendering, and sitemap entries for both languages. ChangesMultilingual blog
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Visitor
participant BlogIndex
participant ArticleRoute
participant BlogArticlePage
participant BaseLayout
Visitor->>BlogIndex: open Indonesian or English blog index
BlogIndex->>ArticleRoute: follow localized article URL
ArticleRoute->>BlogArticlePage: pass post and translations
BlogArticlePage->>BaseLayout: pass localized metadata and alternates
BaseLayout-->>Visitor: render localized article page
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
indopensource-org | c17cfc9 | Commit Preview URL Branch Preview URL |
Jul 23 2026, 04:54 PM |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0593e3aef9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <section class="py-16 md:py-20"> | ||
| <div class="mx-auto grid w-[min(1120px,calc(100%-32px))] gap-6"> | ||
| {publishedPosts.map((post) => { | ||
| const articleUrl = articleHref(post); |
There was a problem hiding this comment.
Preserve the base path for English article links
When the site is built with ASTRO_BASE set for a subpath deployment, articleHref(post) returns a root-relative URL such as /en/blog/foo/. The raw title and thumbnail anchors use that value directly, so clicks leave the configured base path and 404, while the button works because BaseButton normalizes its href. Use a base-aware href for the DOM links and keep the unbased value only for display/canonical uses.
Useful? React with 👍 / 👎.
|
|
||
| <!doctype html> | ||
| <html lang="id"> | ||
| <html lang={language}> |
There was a problem hiding this comment.
Mark Indonesian chrome when switching document language
For English blog pages this changes the entire document to lang="en", but the shared layout still renders Indonesian chrome such as the skip link, header, and footer labels. Screen readers will pronounce those Indonesian sections as English on /en/blog/*; either localize the shared chrome for English pages or mark those unchanged regions with lang="id" while the main article content is English.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/layouts/BaseLayout.astro (2)
228-228: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winSkip-link text isn't language-aware.
Lewati ke kontenis hardcoded Indonesian even whenlanguage="en"(e.g. the English blog and its articles), so English pages ship an accessibility string in the wrong language while every other piece of document metadata (html lang,og:locale, JSON-LDinLanguage) now correctly switches.🌐 Suggested fix
- <a href="`#main-content`" class="skip-link">Lewati ke konten</a> + <a href="`#main-content`" class="skip-link">{language === 'en' ? 'Skip to content' : 'Lewati ke konten'}</a>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/layouts/BaseLayout.astro` at line 228, Update the skip-link in BaseLayout to render language-aware text based on the active language, using the English label when language="en" and retaining the Indonesian label for Indonesian pages. Keep the existing href and accessibility behavior unchanged.
229-233: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winLocalize header/footer for English pages
BaseLayoutbranches onlanguage, but<SiteHeader />and<SiteFooter />are rendered unconditionally and hardcode Indonesian text such asProyek,Belajar,Donasi,Lewati ke konten, and the footerDibuat dengan Joko UI · dibikin di Indonesia/Komunitas Open Source Indonesia. Pass the language/context into these components or inject page-level translations so English blog pages do not wrap English content in Indonesian UI.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/layouts/BaseLayout.astro` around lines 229 - 233, Update BaseLayout’s SiteHeader and SiteFooter rendering to receive the current language/context or page-level translations from the existing language branch. Ensure English pages display localized navigation, skip-link, and footer text instead of Indonesian defaults, while preserving the existing Indonesian text for Indonesian pages.
🧹 Nitpick comments (6)
src/layouts/BaseLayout.astro (1)
177-179: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider adding an
x-defaulthreflang entry.Google's multi-lingual guidance recommends an
x-defaultalternate alongside per-language ones for the fallback/default version. Optional, not required for correctness.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/layouts/BaseLayout.astro` around lines 177 - 179, Add an x-default alternate link alongside the entries generated by the alternates.map block in BaseLayout, pointing it to the fallback/default page URL using the existing site-origin URL construction. Preserve all existing per-language alternate links.test/lib.test.mjs (1)
123-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the status-mismatch and missing-
translationKeybranches.The new suite doesn't assert that
articleTranslationsreturns[]whenpost.translationKeyis unset, or that a draft/published status mismatch excludes an otherwise-matching candidate — both are documented behaviors of the function being tested.✅ Suggested additional assertions
it('connects published translations through their shared key', () => { const id = { slug: 'contoh', status: 'published', lang: 'id', translationKey: 'same' }; const en = { slug: 'example', status: 'published', lang: 'en', translationKey: 'same' }; const unrelated = { slug: 'other', status: 'published', lang: 'en', translationKey: 'other' }; assert.deepEqual(articleTranslations([id, en, unrelated], id), [en]); }); + + it('returns no translations without a translationKey', () => { + assert.deepEqual(articleTranslations([{ slug: 'a', status: 'published', lang: 'id' }], { slug: 'a', status: 'published', lang: 'id' }), []); + }); + + it('excludes a translation whose publish status differs', () => { + const published = { slug: 'contoh', status: 'published', lang: 'id', translationKey: 'k' }; + const draftEn = { slug: 'example', status: 'draft', lang: 'en', translationKey: 'k' }; + assert.deepEqual(articleTranslations([published, draftEn], published), []); + }); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/lib.test.mjs` around lines 123 - 137, Extend the “connects published translations” test for articleTranslations to assert that a post without translationKey returns [] and that a candidate sharing the translationKey but having a different status is excluded. Keep the existing matching published translation assertion unchanged.src/pages/blog/[...slug].astro (2)
6-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated
getStaticPathspattern with the English route.This block is identical to
src/pages/en/blog/[...slug].astrolines 6-14 except for the'id'/'en'literal. See consolidated comment below for a suggested shared-helper fix.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/blog/`[...slug].astro around lines 6 - 14, Extract the shared static-path generation logic from getStaticPaths in the Indonesian and English blog routes into a reusable helper that accepts the target language. Update both route-level getStaticPaths functions to call that helper with their respective language identifier, preserving the existing slug, preview, and translation behavior.
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winShared
getStaticPathslogic duplicated across the two language routes. Both dynamic slug routes implement identical filter/params/props construction, differing only by the hardcoded language literal — a single shared helper would remove the duplication and prevent the two from drifting as more languages/behavior are added.
src/pages/blog/[...slug].astro#L6-14: replace the inlinegetStaticPathsbody with a call to a new shared helper, e.g.getLangStaticPaths(blogPosts as BlogPost[], 'id')exported fromsrc/lib/content.ts.src/pages/en/blog/[...slug].astro#L6-14: same replacement, passing'en'instead of'id'.♻️ Suggested shared helper (src/lib/content.ts)
export function getLangStaticPaths(posts: BlogPost[], lang: 'id' | 'en') { return posts .filter((post) => articleLanguage(post) === lang) .map((post) => ({ params: { slug: isPublished(post) ? post.slug : `preview/${post.slug}` }, props: { post, translations: articleTranslations(posts, post) } })); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/blog/`[...slug].astro at line 1, Extract the duplicated language-specific filtering, slug parameter, and props construction into an exported getLangStaticPaths helper in content.ts, accepting BlogPost[] and the 'id' | 'en' language. Replace the inline getStaticPaths implementations in both slug routes with this helper, passing 'id' for the default route and 'en' for the English route, while preserving the existing articleLanguage, isPublished, and articleTranslations behavior.src/pages/blog.astro (1)
79-90: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the already-computed
articleUrlinstead of re-deriving the path.Line 90 hand-builds
/blog/{post.slug}instead of reusingarticleUrl(computed one line above fromarticleHref). It's harmless today since this list is pre-filtered to published/idposts, but it duplicates the href logic and diverges fromsrc/pages/en/blog.astro, which correctly displays{articleUrl}in the equivalent spot.♻️ Suggested fix
- <span class="text-brand-dark">/blog/{post.slug}</span> + <span class="text-brand-dark">{articleUrl}</span>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/blog.astro` around lines 79 - 90, In the blog article metadata rendered by the loop, replace the hand-built `/blog/{post.slug}` display with the already-computed `articleUrl` from `articleHref(post)`, matching the equivalent English blog page and keeping the existing article link logic centralized.src/pages/en/blog/[...slug].astro (1)
6-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated
getStaticPathspattern with the Indonesian route.Identical to
src/pages/blog/[...slug].astrolines 6-14 except for the'en'literal. See consolidated comment below.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/en/blog/`[...slug].astro around lines 6 - 14, Consolidate the duplicated getStaticPaths logic used by the English and Indonesian routes into a shared implementation, preserving each route’s language filter and existing slug and translation behavior. Update the English getStaticPaths and its Indonesian counterpart to reuse that shared implementation instead of maintaining identical mapping logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/layouts/BaseLayout.astro`:
- Line 228: Update the skip-link in BaseLayout to render language-aware text
based on the active language, using the English label when language="en" and
retaining the Indonesian label for Indonesian pages. Keep the existing href and
accessibility behavior unchanged.
- Around line 229-233: Update BaseLayout’s SiteHeader and SiteFooter rendering
to receive the current language/context or page-level translations from the
existing language branch. Ensure English pages display localized navigation,
skip-link, and footer text instead of Indonesian defaults, while preserving the
existing Indonesian text for Indonesian pages.
---
Nitpick comments:
In `@src/layouts/BaseLayout.astro`:
- Around line 177-179: Add an x-default alternate link alongside the entries
generated by the alternates.map block in BaseLayout, pointing it to the
fallback/default page URL using the existing site-origin URL construction.
Preserve all existing per-language alternate links.
In `@src/pages/blog.astro`:
- Around line 79-90: In the blog article metadata rendered by the loop, replace
the hand-built `/blog/{post.slug}` display with the already-computed
`articleUrl` from `articleHref(post)`, matching the equivalent English blog page
and keeping the existing article link logic centralized.
In `@src/pages/blog/`[...slug].astro:
- Around line 6-14: Extract the shared static-path generation logic from
getStaticPaths in the Indonesian and English blog routes into a reusable helper
that accepts the target language. Update both route-level getStaticPaths
functions to call that helper with their respective language identifier,
preserving the existing slug, preview, and translation behavior.
- Line 1: Extract the duplicated language-specific filtering, slug parameter,
and props construction into an exported getLangStaticPaths helper in content.ts,
accepting BlogPost[] and the 'id' | 'en' language. Replace the inline
getStaticPaths implementations in both slug routes with this helper, passing
'id' for the default route and 'en' for the English route, while preserving the
existing articleLanguage, isPublished, and articleTranslations behavior.
In `@src/pages/en/blog/`[...slug].astro:
- Around line 6-14: Consolidate the duplicated getStaticPaths logic used by the
English and Indonesian routes into a shared implementation, preserving each
route’s language filter and existing slug and translation behavior. Update the
English getStaticPaths and its Indonesian counterpart to reuse that shared
implementation instead of maintaining identical mapping logic.
In `@test/lib.test.mjs`:
- Around line 123-137: Extend the “connects published translations” test for
articleTranslations to assert that a post without translationKey returns [] and
that a candidate sharing the translationKey but having a different status is
excluded. Keep the existing matching published translation assertion unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: aa24dfc8-1793-41f2-9b17-ffcb2ae734f7
📒 Files selected for processing (11)
scripts/sync-blog-posts.mjssrc/components/BlogArticlePage.astrosrc/data/blog-posts.jsonsrc/layouts/BaseLayout.astrosrc/lib/content.tssrc/pages/blog.astrosrc/pages/blog/[...slug].astrosrc/pages/en/blog.astrosrc/pages/en/blog/[...slug].astrosrc/pages/sitemap.xml.tstest/lib.test.mjs
# Conflicts: # src/data/blog-posts.json
What changed
translationKeyhtml lang, Open Graph locale, andhreflangmetadataWhy
IndopenSource articles previously had a single Indonesian-only route and metadata model. This adds a small bilingual foundation while preserving every existing Indonesian URL.
Validation
npm test— 22 passingnpm run build— 113 pages, 0 errors/warnings/hintshtml lang,og:locale, reciprocalhreflang, language switch links, centered inline photo, and sitemap entriesSummary by CodeRabbit
New Features
#5articles in Indonesian and English.Bug Fixes
Tests