尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗How many hours of minimum-wage work do rice, a room, a bus ride or an iPhone cost in Indonesia, compared with Southeast Asia, Korea and Japan? Every price on the site is converted into work time.
Status: all sections built: compare two cities, map, one item across cities, wishlist, monthly basket, regions inside Indonesia, Jakarta since 2018, salary calculator, "Kamu di persentil berapa?" (income percentile, see METHOD.md), context, editable data table. Plus /status.html, which shows whether every number is still current. Bahasa Indonesia by default, English toggle, light/dark theme. Lighthouse mobile (local build): performance 92–93, accessibility 100, best practices 100, SEO 100.
npm install
npm run dev # local site
npm run check-data # validate /data (also runs as part of build)
npm test # unit tests for the method
npm run build # check-data + typecheck + static build into dist/
The site builds for the domain root. BASE=/<path>/ npm run build builds for a sub-path instead (e.g. GitHub Pages).
Live at https://indonesia-tanggal-tua.vercel.app/. Vercel builds and publishes every push to main (and gives each pull request a preview link); its build runs npm run build, so a data error stops the deploy. .github/workflows/check.yml runs tests and the build on pull requests.
npm run og screenshots each shareable view into public/og/*.png and writes public/s/<view>.html (Open Graph tags + redirect to #<view>). It needs Playwright (npm i -D playwright && npx playwright install chromium). Re-run after data changes that the share cards show, and commit the output.
data/ all numbers, versioned; nothing numeric lives in code
places.json cities: wage, wage type, monthly hours, FX, rent, transit fare
prices.json groceries and utilities per place
wishlist.json iPhone, Switch 2, Stanley, Starbucks, Big Mac, Netflix per country
regions-id.json 2026 UMP/UMK for Indonesian regions
timeseries-jakarta.json Jakarta UMP 2000–2026 and published rice-price anchors
sources.json every source: title, publisher, URL, access date
notes-id.json Indonesian version of every English note (shown when the page is in Indonesian)
method.json basket quantities, workday length, index base year
wid-indonesia.json WID income distribution, Indonesia 2024 (127 percentile groups) + conversion factors
wid-world.json WID income distribution, world at PPP, 2024
bps-wages.json BPS average take-home pay (Sakernas), national / sex / sector
freshness.json how often each indicator is published (drives /status.html)
scripts/check-data.ts validator (npm run check-data)
scripts/og.ts share images and share pages (npm run og)
src/main.ts page shell, events, source dialog, URL hash
src/sections.ts one renderer per section (HTML strings)
src/map.ts bubble map (d3-geo + world-atlas 110m, loaded on demand)
src/compare.ts two cities side by side
src/rank.ts "Kamu di persentil berapa?" (income percentile; inputs never stored or sent)
src/status.ts /status.html: is every number current?
src/lib/percentile.ts percentile lookup and conversions (METHOD.md)
src/lib/freshness.ts freshness rules shared by /status.html and check-data
src/art/ hand-drawn SVG flags and city landmarks
src/i18n.ts Indonesian / English strings
src/store.ts data + reader's edits (localStorage) + UI state
src/data/types.ts TypeScript shapes of the data files
src/data/units.ts unit parsing shared by the model and the validator
src/lib/model.ts the method: pay per minute, work time, basket, interpolation
DATA_UPDATE.md monthly update checklist
METHOD.md how the income percentile is calculated, and the freshness rules
Every number is a Datum:
{ "value": 5729876, "unit": "IDR/month", "asOf": "2026-01-01", "sourceId": "kemnaker-ump-2026", "status": "official", "note": "optional" }
| field | meaning |
|---|---|
value | a number, or null for "not sold here" (a note saying why is then required) |
unit | <numerator>/<denominator>: IDR/kg, THB/day, KRW/hour, IDR/USD, h/month |
asOf | YYYY-MM-DD the value applies to: effective date, survey date, or the day a shop page was read |
sourceId | an id in sources.json |
status | official (government or seller), snapshot (observed once: shop page, survey, third-party index), estimate (our approximation), interpolated |
null and not guessed. The validator lists these as gaps.monthlyHours and method.workdayHours.fxPerUsd.interpolated. The validator rejects hand-entered interpolated points.sources.json accessed: null means the figure was carried over from the prototype and its source has not been re-opened yet.npm run check-data fails on: a value without unit, date, source or status; an unknown source id; a unit that doesn't match the field (e.g. JPY/item for Thailand, a wage per week); an asOf after the file's updated date; null without a note. It prints gaps (missing values, sources not re-opened, values still citing the prototype) as warnings. npm run check-data:strict turns gaps into failures; use it before calling the data complete. Add --verbose to list every missing value.
note.method.json): one room, 8 kg rice, 3 kg eggs, 4 kg chicken, 1 kg beef, 2 L oil, 90 kWh electricity, 44 transit rides. Leftover = wage − basket.timeseries-jakarta.json and the chart will draw them, with years in between marked interpolated.?lang=en (or the ID | EN switch) gives English, and the switch updates the address so it can be shared as-is.#compare-jakarta_vs_tokyo."bundle": [...] in wishlist.json) has no prices of its own; the site adds up its parts and keeps every part's source.#wishlist-iphone, #item-beef, #map-bigmac, #history-eggs, #regions-umk, #compare-jakarta_vs_tokyo, #rank-t42 (a shared percentile; never the income unless the reader opts in)./status.html): every number's date against its indicator's publishing schedule (): current, due soon or overdue. applies the same rules.See DATA_UPDATE.md.
All rights reserved; see LICENSE. Readers may quote individual figures with credit and a link; the code and the compiled dataset may not be copied, scraped, modified or used commercially without permission. The underlying statistics belong to their publishers (named in every source panel) and remain available from them. The site shows the same terms at /lisensi.html, and public/robots.txt asks AI-training crawlers not to collect the site.
data/freshness.jsonnpm run check-data