Building a Personal Finance Site for Indonesia — Launch Story
From zero to 89 bilingual articles: building a passive investing education site with a local retirement calculator and SEO-first architecture.
Indonesia has 270 million people and a growing middle class, but financial literacy lags far behind. Most investing content is either in English (inaccessible to most Indonesians) or surface-level clickbait (“5 stocks that will make you rich!”). There’s a gap for substantive, actionable investing education in Indonesian.
I built nabung.id to fill it — a bilingual passive investing education site with 89 articles, a retirement calculator that actually accounts for Indonesia’s social security system (BPJS), and an architecture designed for SEO from day one.
The Gap
The Indonesian financial content landscape has problems:
- Language barrier: The best investing content (Bogleheads, Mr. Money Mustache, Early Retirement Now) is in English
- Get-rich-quick focus: Indonesian content often emphasizes trading, crypto, and stock picking over boring-but-effective index investing
- Ignoring local context: Generic retirement calculators don’t account for BPJS JHT and JP (Indonesia’s social security programs)
I wanted to create content that was substantive, locally relevant, and accessible in both Indonesian and English.
Architecture Decisions
The stack optimizes for three things: performance, cost, and SEO.
Astro generates static HTML at build time. Pages load instantly because there’s no client-side JavaScript rendering. This matters for SEO (Core Web Vitals) and for Indonesian users on slower connections.
Cloudflare Pages hosts everything for free. The site is served from Cloudflare’s global CDN, which includes edge nodes in Jakarta. Indonesian users get fast load times without me paying for regional infrastructure.
# Directory structure
src/
content/
blog/
id/ # Indonesian articles (primary)
en/ # English translations
pages/
id/ # Static pages (about, methodology)
en/
components/
RetirementCalculator.svelte # Interactive calculator
The content structure prioritizes Indonesian as the primary language, with English as a translation layer. This is the opposite of most bilingual sites, which are English-first with translations bolted on.
Content Strategy: Write First, Then Translate
I wrote most articles in Indonesian before touching English content. This might seem backwards — English has more global reach — but the strategy was intentional:
- Identify what resonates: Indonesian readers showed me which topics drove engagement
- Build internal linking: A solid corpus creates a dense web of cross-references
- Translation is mechanical: Once content exists, translation is faster than original creation
The translation process uses AI assistance, but every article gets human review for local idioms and context that AI might miss. Financial terms especially need careful handling — the Indonesian for “compound interest” isn’t a literal translation.
SEO Sprint Methodology
Rather than treating SEO as an ongoing background task, I ran structured sprints:
| Sprint | Focus | Key Changes |
|---|---|---|
| S1 | Structured data | JSON-LD schema for articles, organization, FAQ |
| S2 | Title/description | All meta descriptions ≤155 characters |
| S3 | Image optimization | Significant reduction via WebP conversion |
| S4 | Internal linking | Every article ≥3 internal links |
| S5 | Category pages | Proper EN category landing pages |
| S6 | Technical SEO | Sitemap lastmod + hreflang fixes |
| S7 | Content gaps | New articles targeting keyword opportunities |
Each sprint focused on one aspect of SEO, measured its impact, then moved on. This is more effective than constantly tweaking everything — you can actually attribute changes in traffic to specific interventions.
The image optimization sprint deserves special mention. Financial content doesn’t need hero images, but I’d accumulated bloated stock photos across many articles. Converting to WebP and removing unnecessary images cut total asset size dramatically. Users on mobile connections notice the difference.
The Retirement Calculator
The retirement calculator is the site’s most complex feature. It’s a Svelte component that runs entirely client-side (no server roundtrips needed for calculations).
What makes it different from generic calculators:
Growing annuity formula: Most calculators assume you’ll spend a fixed amount in retirement. That’s wrong — expenses grow with inflation. The calculator uses present value of a growing annuity:
function pvGrowingAnnuity(payment, rate, growth, periods) {
if (Math.abs(rate - growth) < 0.0001) {
// Edge case: rate ≈ growth
return payment * periods / (1 + rate);
}
const factor = (1 - Math.pow((1 + growth) / (1 + rate), periods));
return payment * factor / (rate - growth);
}
BPJS integration: Indonesia’s JHT (Jaminan Hari Tua) and JP (Jaminan Pensiun) programs provide retirement income. The calculator models both:
- JHT: Lump sum at retirement (employee + employer contributions)
- JP: Monthly pension (15% of final salary, capped)
Most Indonesians don’t know how much they’ll receive from BPJS. The calculator shows it, which often reveals that BPJS covers less than expected.
Visualization: A stacked area chart shows funding sources over time — personal savings drawdown, BPJS pension, and any shortfall. Seeing the visual makes abstract numbers concrete.
// Stacked areas for retirement funding sources
const chartData = {
labels: years,
datasets: [
{ label: 'Personal Savings', data: personalDrawdown },
{ label: 'BPJS JP', data: bpjsPension },
{ label: 'Shortfall', data: shortfall }
]
};
Bilingual Methodology
Every financial concept gets a methodology section explaining the math. These are bilingual — the same explanation in Indonesian and English, because math is universal even when language isn’t.
This serves two purposes:
- Transparency: Readers can verify the calculations rather than trusting a black box
- Education: Understanding the formula teaches the underlying concept
The methodology pages are among the most-visited on the site. People want to understand, not just get an answer.
What I’d Do Differently
Start with fewer articles, better interlinked. My initial strategy was “volume first, then optimize.” In retrospect, fewer densely-interlinked articles would have outperformed many loosely-connected ones. Google rewards topical depth over breadth.
Build the calculator earlier. The retirement calculator drives more engagement than any article. Interactive tools create sticky visits — users return to check scenarios, share with friends, and bookmark. I should have prioritized it over filling out the article count.
Submit to Search Console immediately. I waited until I had “enough content” to submit to Google Search Console. That was wrong — earlier submission would have given me indexing data to guide content strategy.
Results
The site has grown steadily through organic search:
- 89 articles indexed across Indonesian and English versions
- Calculator engagement: Users spend significantly more time on the calculator than on articles — interactive tools outperform static content
- Bilingual reach: English pages bring international readers interested in Indonesian markets
- Organic growth: No paid promotion, traffic growing through search and shares
The retirement calculator accounts for a disproportionate share of engagement. Tools outperform text for evergreen traffic.
Technical Notes
Hreflang implementation: Every page declares its language and links to its translation:
<!-- Indonesian page -->
<link rel="alternate" hreflang="id" href="https://nabung.id/blog/id/compound-interest" />
<link rel="alternate" hreflang="en" href="https://nabung.id/blog/en/compound-interest" />
<link rel="alternate" hreflang="x-default" href="https://nabung.id/blog/id/compound-interest" />
The x-default points to Indonesian because that’s the primary audience.
Sitemap structure: Separate sitemaps for Indonesian and English content, both referenced in a sitemap index. This helps Google understand the site structure:
<!-- sitemap-index.xml -->
<sitemap>
<loc>https://nabung.id/sitemap-id.xml</loc>
</sitemap>
<sitemap>
<loc>https://nabung.id/sitemap-en.xml</loc>
</sitemap>
Related Posts
- Multi-Agent Content Pipelines — Orchestrating content production at scale
- Bypass Geo-Blocking with Cloudflare Workers — Accessing restricted APIs with Cloudflare edge nodes
- Building Price Intelligence from Grocery Data — Data engineering for consumer products
Building financial literacy tools at nabung.id.