Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

papers: end-to-end i18n on papers.ac index — fetch translations.json, add Japanese

Switcher only swapped 19 of 39 papers because index.html carried a stale
hand-maintained translations object alongside the auto-generated
translations.json. Page now fetches the JSON at runtime, setLang reads
{title, subtitle} and re-appends the format/link suffix from the English
detail (e.g. "· arXiv 5pp", "· <a>interactive timeline</a>"), and papers
without translations fall back to English instead of half-swapping.

Also extended cleanLatex (\acrandname{}, Spanish accents, Danish letters
in {\x}/\x{}/\x\<sp> forms + LaTeX space-gobble), added Japanese to the
dropdown + /ja /jp redirects, and deferred the initial path-language
swap until the JSON has loaded so first paint isn't English-flash.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+332 -214
+37
papers/papermill.mjs
··· 301 301 302 302 function cleanLatex(text) { 303 303 return text 304 + .replace(/\\acrandname\{\}/g, "Aesthetic Computer") 304 305 .replace(/\\ac\{\}/g, "Aesthetic Computer") 305 306 .replace(/\\acos\{\}/g, "AC Native OS") 306 307 .replace(/\\np\{\}/g, "notepat") ··· 314 315 .replace(/\\emph\{([^}]+)\}/g, "$1") 315 316 .replace(/\\url\{([^}]+)\}/g, "$1") 316 317 .replace(/\\href\{[^}]+\}\{([^}]+)\}/g, "$1") 318 + // Accented letters (Spanish) — longer patterns first 319 + .replace(/\\'\{\\i\}/g, "í") // \'{\i} → í 320 + .replace(/\\'\{a\}/g, "á") 321 + .replace(/\\'\{e\}/g, "é") 322 + .replace(/\\'\{i\}/g, "í") 323 + .replace(/\\'\{o\}/g, "ó") 324 + .replace(/\\'\{u\}/g, "ú") 325 + .replace(/\\'a/g, "á") 326 + .replace(/\\'e/g, "é") 327 + .replace(/\\'i/g, "í") 328 + .replace(/\\'o/g, "ó") 329 + .replace(/\\'u/g, "ú") 330 + .replace(/\\~\{n\}/g, "ñ") 331 + .replace(/\\~n/g, "ñ") 332 + // Danish letters. LaTeX rule: a letter-command like `\ae` gobbles one 333 + // following space, so `\ae re` → `ære`. Forms handled: {\x}, \x{}, \x\<sp> 334 + // (explicit space — keeps space), \x followed by space+letter (gobble), 335 + // \x followed by other non-letter (keep). 336 + .replace(/\{\\aa\}/g, "å") 337 + .replace(/\\aa\{\}/g, "å") 338 + .replace(/\\aa\\ /g, "å ") 339 + .replace(/\\aa /g, "å") 340 + .replace(/\\aa(?=[^a-zA-Z])/g, "å") 341 + .replace(/\{\\AA\}/g, "Å") 342 + .replace(/\{\\o\}/g, "ø") 343 + .replace(/\\o\{\}/g, "ø") 344 + .replace(/\\o\\ /g, "ø ") 345 + .replace(/\\o (?=[a-zA-Z])/g, "ø") 346 + .replace(/\\o(?=[^a-zA-Z])/g, "ø") 347 + .replace(/\{\\O\}/g, "Ø") 348 + .replace(/\{\\ae\}/g, "æ") 349 + .replace(/\\ae\{\}/g, "æ") 350 + .replace(/\\ae\\ /g, "æ ") 351 + .replace(/\\ae /g, "æ") 352 + .replace(/\\ae(?=[^a-zA-Z])/g, "æ") 353 + .replace(/\{\\AE\}/g, "Æ") 317 354 .replace(/\\\\/g, "") 318 355 .replace(/\\,/g, "") 319 356 .replace(/\\&/g, "&")
+4
system/public/papers.aesthetic.computer/_redirects
··· 8 8 /cn/ /index.html 200 9 9 /en /index.html 200 10 10 /en/ /index.html 200 11 + /ja /index.html 200 12 + /ja/ /index.html 200 13 + /jp /index.html 200 14 + /jp/ /index.html 200
+149 -158
system/public/papers.aesthetic.computer/index.html
··· 394 394 padding: 0; 395 395 overflow: hidden; 396 396 aspect-ratio: 1536 / 1024; 397 - max-height: 320px; 398 - min-height: 220px; 399 - } 400 - .hero::after { 401 - content: ''; 402 - position: absolute; inset: 0; 403 - background: linear-gradient(to bottom, transparent 0%, var(--bg) 100%); 404 - z-index: 1; 405 - pointer-events: none; 406 - } 407 - .hero-bg { 408 - position: absolute; inset: 0; 409 - background-position: center top; 410 - background-size: cover; 411 - background-repeat: no-repeat; 412 - z-index: 0; 413 - will-change: opacity; 414 - } 415 - .hero-bg-a { background-image: url('/papers-header.png'); } /* v2: Johnny Appleseed */ 416 - .hero-bg-b { background-image: url('/papers-header-v3.png'); } /* v3: first-person POV */ 417 - .hero-bg-c { background-image: url('/papers-header-v1.png'); } /* v1: Washington crossing */ 418 - @media (prefers-reduced-motion: no-preference) { 419 - .hero-bg-a { animation: hero-fade-3a 24s ease-in-out infinite; } 420 - .hero-bg-b { animation: hero-fade-3b 24s ease-in-out infinite; } 421 - .hero-bg-c { animation: hero-fade-3c 24s ease-in-out infinite; } 397 + max-height: 78vh; 398 + min-height: 280px; 422 399 } 423 - @keyframes hero-fade-3a { 424 - 0%, 29% { opacity: 1; } 425 - 33%, 91% { opacity: 0; } 426 - 95%, 100% { opacity: 1; } 427 - } 428 - @keyframes hero-fade-3b { 429 - 0%, 29% { opacity: 0; } 430 - 33%, 62% { opacity: 1; } 431 - 66%, 100% { opacity: 0; } 432 - } 433 - @keyframes hero-fade-3c { 434 - 0%, 62% { opacity: 0; } 435 - 66%, 91% { opacity: 1; } 436 - 95%, 100% { opacity: 0; } 400 + .hero-canvas { 401 + position: absolute; 402 + inset: 0; 403 + width: 100%; 404 + height: 100%; 405 + display: block; 437 406 } 438 407 439 408 /* === Prompt-HUD corner label (modeled after give.aesthetic.computer) === */ ··· 445 414 display: flex; 446 415 justify-content: space-between; 447 416 align-items: center; 448 - padding: 10px 16px 20px; 417 + padding: 10px 16px; 449 418 z-index: 1000; 450 - background: linear-gradient(to bottom, var(--bg) 0%, var(--bg) 60%, transparent 100%); 451 419 pointer-events: none; 452 420 } 453 421 .top-bar > * { pointer-events: auto; } ··· 528 496 <div class="lang-option" data-lang="da" data-flag="dk"><span class="fi fi-dk"></span> Dansk</div> 529 497 <div class="lang-option" data-lang="es" data-flag="es"><span class="fi fi-es"></span> Espa&ntilde;ol</div> 530 498 <div class="lang-option" data-lang="zh" data-flag="cn"><span class="fi fi-cn"></span> 中文</div> 499 + <div class="lang-option" data-lang="ja" data-flag="jp"><span class="fi fi-jp"></span> 日本語</div> 531 500 </div> 532 501 </div> 533 502 </div> 534 503 535 504 <header class="hero" aria-label="papers"> 536 - <div class="hero-bg hero-bg-a"></div> 537 - <div class="hero-bg hero-bg-b"></div> 538 - <div class="hero-bg hero-bg-c"></div> 505 + <canvas class="hero-canvas" id="heroCanvas" aria-hidden="true"></canvas> 539 506 </header> 540 507 541 508 <div class="sub">@jeffrey &middot; <a href="https://orcid.org/0009-0007-4460-4913">ORCID 0009-0007-4460-4913</a></div> ··· 837 804 </div> 838 805 839 806 <script> 807 + // === Hero Ken Burns slideshow (canvas, modeled after give.aesthetic.computer) === 808 + (function initHeroSlideshow() { 809 + const canvas = document.getElementById('heroCanvas'); 810 + if (!canvas) return; 811 + const ctx = canvas.getContext('2d'); 812 + const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; 813 + 814 + const SLIDES = [ 815 + { src: '/papers-header.png', pan: { from: { s: 1.00, x: 0.00, y: 0.00 }, to: { s: 1.08, x: -0.04, y: 0.02 } } }, 816 + { src: '/papers-header-v3.png', pan: { from: { s: 1.06, x: -0.03, y: -0.02 }, to: { s: 1.00, x: 0.03, y: 0.02 } } }, 817 + { src: '/papers-header-v1.png', pan: { from: { s: 1.04, x: 0.02, y: -0.02 }, to: { s: 1.10, x: -0.02, y: 0.03 } } }, 818 + ]; 819 + const SLIDE_DUR = 7000; 820 + const FADE_DUR = 1500; 821 + const CYCLE = SLIDES.length * SLIDE_DUR; 822 + 823 + const images = SLIDES.map(s => { const i = new Image(); i.src = s.src; return i; }); 824 + 825 + let cssW = 0, cssH = 0; 826 + function resize() { 827 + const rect = canvas.getBoundingClientRect(); 828 + cssW = rect.width; cssH = rect.height; 829 + const dpr = window.devicePixelRatio || 1; 830 + canvas.width = Math.max(1, Math.round(cssW * dpr)); 831 + canvas.height = Math.max(1, Math.round(cssH * dpr)); 832 + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); 833 + } 834 + resize(); 835 + window.addEventListener('resize', resize, { passive: true }); 836 + 837 + function easeInOutSine(t) { return -(Math.cos(Math.PI * t) - 1) / 2; } 838 + 839 + function drawSlide(img, pan, progress, alpha) { 840 + if (!img.complete || !img.naturalWidth) return; 841 + const t = easeInOutSine(progress); 842 + const sc = pan.from.s + (pan.to.s - pan.from.s) * t; 843 + const tx = pan.from.x + (pan.to.x - pan.from.x) * t; 844 + const ty = pan.from.y + (pan.to.y - pan.from.y) * t; 845 + const baseScale = Math.max(cssW / img.naturalWidth, cssH / img.naturalHeight); 846 + const scale = baseScale * sc; 847 + const dw = img.naturalWidth * scale; 848 + const dh = img.naturalHeight * scale; 849 + const dx = (cssW - dw) / 2 + tx * cssW; 850 + const dy = (cssH - dh) / 2 + ty * cssH; 851 + ctx.save(); 852 + ctx.globalAlpha = alpha; 853 + ctx.drawImage(img, dx, dy, dw, dh); 854 + ctx.restore(); 855 + } 856 + 857 + const start = performance.now(); 858 + let lastFrame = 0; 859 + function frame(now) { 860 + if (cssW === 0 || cssH === 0) { resize(); } 861 + ctx.clearRect(0, 0, cssW, cssH); 862 + 863 + const elapsed = reduced ? 0 : (now - start); 864 + const cyclePos = ((elapsed % CYCLE) + CYCLE) % CYCLE; 865 + const idx = Math.floor(cyclePos / SLIDE_DUR); 866 + const slideElapsed = cyclePos - idx * SLIDE_DUR; 867 + const slideProg = slideElapsed / SLIDE_DUR; 868 + const fadeStartProg = (SLIDE_DUR - FADE_DUR) / SLIDE_DUR; 869 + 870 + if (slideProg > fadeStartProg) { 871 + // Crossfade: draw incoming next slide underneath at full alpha, 872 + // current slide on top fading out 873 + const fadeT = (slideProg - fadeStartProg) / (1 - fadeStartProg); 874 + const nextIdx = (idx + 1) % SLIDES.length; 875 + drawSlide(images[nextIdx], SLIDES[nextIdx].pan, 0, 1); 876 + drawSlide(images[idx], SLIDES[idx].pan, slideProg, 1 - fadeT); 877 + } else { 878 + drawSlide(images[idx], SLIDES[idx].pan, slideProg, 1); 879 + } 880 + 881 + if (!reduced || lastFrame === 0) requestAnimationFrame(frame); 882 + lastFrame = now; 883 + } 884 + 885 + // Kick off as soon as ANY image is ready (others load progressively) 886 + let kicked = false; 887 + function kick() { if (!kicked) { kicked = true; requestAnimationFrame(frame); } } 888 + images.forEach(img => { 889 + if (img.complete && img.naturalWidth) kick(); 890 + else img.addEventListener('load', kick, { once: true }); 891 + }); 892 + })(); 893 + 840 894 // Cache-bust version — updated by papermill deploy 841 895 window.__papersVersion = '20260328'; 842 896 ··· 858 912 el.textContent = relativeTime(el.dataset.time); 859 913 }); 860 914 861 - // Translations loaded from translations.json (generated by: node papermill.mjs sync-index) 862 - let translations = { 863 - 'who-pays': { 864 - da: { title: 'Hvem betaler for kreative v\u00e6rkt\u00f8jer?', detail: 'Finansiering, udtr\u00e6ndthed og overlevelse i open source kreativ computing \u00b7 arXiv 5pp' }, 865 - es: { title: '\u00bfQui\u00e9n paga por las herramientas creativas?', detail: 'Financiaci\u00f3n, agotamiento y supervivencia en computaci\u00f3n creativa de c\u00f3digo abierto \u00b7 arXiv 5pp' }, 866 - zh: { title: '\u8c01\u4e3a\u521b\u610f\u5de5\u5177\u4ed8\u8d39\uff1f', detail: '\u5f00\u6e90\u521b\u610f\u8ba1\u7b97\u4e2d\u7684\u8d44\u91d1\u3001\u5026\u6020\u4e0e\u751f\u5b58 \u00b7 arXiv 5pp' } 867 - }, 868 - 'whistlegraph': { 869 - da: { title: 'Whistlegraph', detail: 'Tegning, sang og det grafiske partitur som viral form \u00b7 arXiv 4pp' }, 870 - es: { title: 'Whistlegraph', detail: 'Dibujo, canto y la partitura gr\u00e1fica como forma viral \u00b7 arXiv 4pp' }, 871 - zh: { title: 'Whistlegraph', detail: '\u7ed8\u753b\u3001\u6b4c\u5531\u4e0e\u56fe\u5f62\u4e50\u8c31\u4f5c\u4e3a\u75c5\u6bd2\u5f0f\u4f20\u64ad\u5f62\u5f0f \u00b7 arXiv 4pp' } 872 - }, 873 - 'archaeology': { 874 - da: { title: 'Repository-ark\u00e6ologi', detail: 'Sp\u00f8ring af ACs udvikling gennem dens Git-historie \u00b7 arXiv 3pp' }, 875 - es: { title: 'Arqueolog\u00eda de repositorios', detail: 'Rastreando la evoluci\u00f3n de AC a trav\u00e9s de su historial Git \u00b7 arXiv 3pp' }, 876 - zh: { title: '\u4ed3\u5e93\u8003\u53e4\u5b66', detail: '\u901a\u8fc7Git\u5386\u53f2\u8ffd\u6eafAC\u7684\u6f14\u53d8 \u00b7 arXiv 3pp' } 877 - }, 878 - 'pieces': { 879 - da: { title: 'St\u00fckker, ikke programmer', detail: 'St\u00fckket som enhed for kreativ erkendelse \u00b7 arXiv 4pp' }, 880 - es: { title: 'Piezas, no programas', detail: 'La pieza como unidad de cognici\u00f3n creativa \u00b7 arXiv 4pp' }, 881 - zh: { title: '\u4f5c\u54c1\uff0c\u800c\u975e\u7a0b\u5e8f', detail: '\u4f5c\u54c1\u4f5c\u4e3a\u521b\u610f\u8ba4\u77e5\u7684\u5355\u5143 \u00b7 arXiv 4pp' } 882 - }, 883 - 'network-audit': { 884 - da: { title: 'Netv\u00e6rksrevision', detail: 'Hvem bruger Aesthetic Computer og hvad laver de? \u00b7 arXiv 4pp' }, 885 - es: { title: 'Auditor\u00eda de red', detail: '\u00bfQui\u00e9n usa Aesthetic Computer y qu\u00e9 crean? \u00b7 arXiv 4pp' }, 886 - zh: { title: '\u7f51\u7edc\u5ba1\u8ba1', detail: '\u8c01\u5728\u4f7f\u7528Aesthetic Computer\uff0c\u4ed6\u4eec\u521b\u4f5c\u4e86\u4ec0\u4e48\uff1f \u00b7 arXiv 4pp' } 887 - }, 888 - 'kidlisp-ref': { 889 - da: { title: 'KidLisp sprogreference', detail: '118 indbyggede funktioner i 12 kategorier \u00b7 arXiv 4pp' }, 890 - es: { title: 'Referencia del lenguaje KidLisp', detail: '118 funciones integradas en 12 categor\u00edas \u00b7 arXiv 4pp' }, 891 - zh: { title: 'KidLisp \u8bed\u8a00\u53c2\u8003', detail: '12\u4e2a\u7c7b\u522b\u7684118\u4e2a\u5185\u7f6e\u51fd\u6570 \u00b7 arXiv 4pp' } 892 - }, 893 - 'kidlisp': { 894 - da: { title: "KidLisp '26", detail: 'En minimal Lisp til generativ kunst p\u00e5 en social platform \u00b7 arXiv 6pp' }, 895 - es: { title: "KidLisp '26", detail: 'Un Lisp m\u00ednimo para arte generativo en una plataforma social \u00b7 arXiv 6pp' }, 896 - zh: { title: "KidLisp '26", detail: '\u793e\u4ea4\u5e73\u53f0\u4e0a\u7528\u4e8e\u751f\u6210\u827a\u672f\u7684\u6700\u7b80Lisp \u00b7 arXiv 6pp' } 897 - }, 898 - 'dead-ends': { 899 - da: { title: 'Vestigiale funktioner', detail: 'Sovende stier, evolution\u00e6re grene og opgivne tilgange \u00b7 arXiv 4pp' }, 900 - es: { title: 'Caracter\u00edsticas vestigiales', detail: 'Caminos dormidos, ramas evolutivas y enfoques abandonados \u00b7 arXiv 4pp' }, 901 - zh: { title: '\u6b8b\u7559\u7279\u5f81', detail: '\u4f11\u7720\u8def\u5f84\u3001\u8fdb\u5316\u5206\u652f\u4e0e\u653e\u5f03\u7684\u65b9\u6cd5 \u00b7 arXiv 4pp' } 902 - }, 903 - 'diversity': { 904 - da: { title: 'Revision af citatdiversitet', detail: 'Diversitet og inklusion i AC-artikelcitater \u00b7 4pp' }, 905 - es: { title: 'Auditor\u00eda de diversidad de citas', detail: 'Diversidad e inclusi\u00f3n en las citas de art\u00edculos de AC \u00b7 4pp' }, 906 - zh: { title: '\u5f15\u7528\u591a\u6837\u6027\u5ba1\u8ba1', detail: 'AC\u8bba\u6587\u5f15\u7528\u4e2d\u7684\u591a\u6837\u6027\u4e0e\u5305\u5bb9\u6027 \u00b7 4pp' } 907 - }, 908 - 'os': { 909 - da: { title: 'AC Native OS', detail: 'Et bare-metal kreativt computing-operativsystem \u00b7 arXiv 5pp' }, 910 - es: { title: 'AC Native OS', detail: 'Un sistema operativo bare-metal para computaci\u00f3n creativa \u00b7 arXiv 5pp' }, 911 - zh: { title: 'AC Native OS', detail: '\u88f8\u673a\u521b\u610f\u8ba1\u7b97\u64cd\u4f5c\u7cfb\u7edf \u00b7 arXiv 5pp' } 912 - }, 913 - 'ac': { 914 - da: { title: "Aesthetic Computer '26", detail: 'En mobil-f\u00f8rst k\u00f8retid til kreativ computing \u00b7 arXiv 5pp' }, 915 - es: { title: "Aesthetic Computer '26", detail: 'Un entorno de ejecuci\u00f3n m\u00f3vil para computaci\u00f3n creativa \u00b7 arXiv 5pp' }, 916 - zh: { title: "Aesthetic Computer '26", detail: '\u79fb\u52a8\u4f18\u5148\u7684\u521b\u610f\u8ba1\u7b97\u8fd0\u884c\u65f6 \u00b7 arXiv 5pp' } 917 - }, 918 - 'goodiepal': { 919 - da: { title: 'Radikal computerkunst', detail: 'Goodiepalianske tilgange i Aesthetic Computer \u00b7 arXiv 5pp' }, 920 - es: { title: 'Arte computacional radical', detail: 'Enfoques Goodiepalianos en Aesthetic Computer \u00b7 arXiv 5pp' }, 921 - zh: { title: '\u6fc0\u8fdb\u8ba1\u7b97\u673a\u827a\u672f', detail: 'Aesthetic Computer\u4e2d\u7684Goodiepal\u5f0f\u65b9\u6cd5 \u00b7 arXiv 5pp' } 922 - }, 923 - 'api': { 924 - da: { title: 'Fra setup() til boot()', detail: 'Processing i kernen af Piece API \u00b7 arXiv 7pp' }, 925 - es: { title: 'De setup() a boot()', detail: 'Processing en el n\u00facleo de la Piece API \u00b7 arXiv 7pp' }, 926 - zh: { title: '\u4ece setup() \u5230 boot()', detail: 'Piece API\u6838\u5fc3\u4e2d\u7684Processing \u00b7 arXiv 7pp' } 927 - }, 928 - 'notepat': { 929 - da: { title: 'notepat.com', detail: 'Fra tastaturleget\u00f8j til systemets hovedd\u00f8r \u00b7 arXiv 5pp' }, 930 - es: { title: 'notepat.com', detail: 'De juguete de teclado a puerta principal del sistema \u00b7 arXiv 5pp' }, 931 - zh: { title: 'notepat.com', detail: '\u4ece\u952e\u76d8\u73a9\u5177\u5230\u7cfb\u7edf\u524d\u95e8 \u00b7 arXiv 5pp' } 932 - }, 933 - 'folk-songs': { 934 - da: { title: 'Spillbare folkesange', detail: 'Mundtlig tradition m\u00f8der browsertastaturet \u00b7 kort 21pp' }, 935 - es: { title: 'Canciones populares tocables', detail: 'La tradici\u00f3n oral se encuentra con el teclado del navegador \u00b7 tarjetas 21pp' }, 936 - zh: { title: '\u53ef\u6f14\u594f\u7684\u6c11\u6b4c', detail: '\u53e3\u5934\u4f20\u7edf\u4e0e\u6d4f\u89c8\u5668\u952e\u76d8\u7684\u76f8\u9047 \u00b7 \u5361\u7247 21pp' } 937 - }, 938 - 'joss-kidlisp': { 939 - da: { title: "KidLisp '26", detail: 'JOSS-sammendrag \u00b7 3pp' }, 940 - es: { title: "KidLisp '26", detail: 'Resumen JOSS \u00b7 3pp' }, 941 - zh: { title: "KidLisp '26", detail: 'JOSS\u6458\u8981 \u00b7 3pp' } 942 - }, 943 - 'joss-ac': { 944 - da: { title: "Aesthetic Computer '26", detail: 'JOSS-sammendrag \u00b7 2pp' }, 945 - es: { title: "Aesthetic Computer '26", detail: 'Resumen JOSS \u00b7 2pp' }, 946 - zh: { title: "Aesthetic Computer '26", detail: 'JOSS\u6458\u8981 \u00b7 2pp' } 947 - }, 948 - 'complex': { 949 - da: { title: 'Sucking on the Complex', detail: 'Platformhegemoni, kritik-som-indhold og behovet for anti-milj\u00f8er \u00b7 arXiv 5pp' }, 950 - es: { title: 'Sucking on the Complex', detail: 'Hegemon\u00eda de plataformas, cr\u00edtica-como-contenido y la necesidad de anti-entornos \u00b7 arXiv 5pp' }, 951 - zh: { title: 'Sucking on the Complex', detail: '\u5e73\u53f0\u9738\u6743\u3001\u6279\u5224\u4f5c\u4e3a\u5185\u5bb9\u4e0e\u53cd\u73af\u5883\u7684\u9700\u8981 \u00b7 arXiv 5pp' } 952 - }, 953 - 'plork': { 954 - da: { title: "PLOrk'ing the Planet", detail: 'Bærbare orkestre, PLOrk-arven og Aesthetic Computer \u00b7 arXiv' }, 955 - es: { title: "PLOrk'ing the Planet", detail: 'Orquestas de port\u00e1tiles, herencia PLOrk y Aesthetic Computer \u00b7 arXiv' }, 956 - zh: { title: "PLOrk'ing the Planet", detail: '\u7b14\u8bb0\u672c\u7535\u8111\u4e50\u961f\u3001PLOrk\u9057\u4ea7\u4e0eAesthetic Computer \u00b7 arXiv' } 957 - }, 958 - 'els': { 959 - da: { title: 'KidLisp (ELS 2026)', detail: 'En minimal Lisp til generativ kunst med social komposition \u00b7 ELS ACM SIGS 4pp' }, 960 - es: { title: 'KidLisp (ELS 2026)', detail: 'Un Lisp m\u00ednimo para arte generativo con composici\u00f3n social \u00b7 ELS ACM SIGS 4pp' }, 961 - zh: { title: 'KidLisp (ELS 2026)', detail: '\u7528\u4e8e\u751f\u6210\u827a\u672f\u548c\u793e\u4ea4\u521b\u4f5c\u7684\u6700\u7b80Lisp \u00b7 ELS ACM SIGS 4pp' } 962 - } 963 - }; 915 + // Translations fetched from translations.json (regenerate via: node papers/papermill.mjs sync-index) 916 + let translations = {}; 917 + const translationsReady = fetch("/translations.json?v=" + window.__papersVersion) 918 + .then(r => r.ok ? r.json() : {}) 919 + .then(j => { translations = j || {}; return translations; }) 920 + .catch(() => ({})); 964 921 965 922 // Store original English text on first load 966 923 document.querySelectorAll('.p[data-paper-id]').forEach(el => { ··· 975 932 en: { flag: 'us', name: 'English' }, 976 933 da: { flag: 'dk', name: 'Dansk' }, 977 934 es: { flag: 'es', name: 'Espa\u00f1ol' }, 978 - zh: { flag: 'cn', name: '\u4e2d\u6587' } 935 + zh: { flag: 'cn', name: '\u4e2d\u6587' }, 936 + ja: { flag: 'jp', name: '\u65e5\u672c\u8a9e' } 979 937 }; 980 938 let currentLang = 'en'; 939 + 940 + // Split detail into [subtitle, suffix] at the first middot \u2014 the suffix 941 + // (e.g. "\u00b7 arXiv 5pp" or "\u00b7 <a href=...>timeline</a>") is language-agnostic 942 + // and gets re-appended to translated subtitles. innerHTML decodes &middot; 943 + // to U+00B7, so match either form. 944 + function splitDetail(html) { 945 + if (!html) return ['', '']; 946 + const m = html.match(/(?:&middot;|\u00b7)/); 947 + if (!m) return [html.trim(), '']; 948 + const idx = m.index; 949 + return [html.slice(0, idx).trim(), html.slice(idx).trim()]; 950 + } 981 951 982 952 const selector = document.getElementById('langSelector'); 983 953 const flagEl = document.getElementById('lang-flag'); ··· 1030 1000 if (!titleA || !detail) return; 1031 1001 1032 1002 if (lang === 'en') { 1033 - // Restore original English 1034 1003 titleA.childNodes[0].textContent = el.dataset.titleEn; 1035 1004 detail.innerHTML = el.dataset.detailEn; 1036 - } else { 1037 - const t = translations[id]?.[lang]; 1038 - if (t) { 1039 - titleA.childNodes[0].textContent = t.title; 1040 - detail.innerHTML = t.detail; 1041 - } 1005 + return; 1006 + } 1007 + 1008 + const t = translations[id] && translations[id][lang]; 1009 + if (!t) { 1010 + // No translation available — fall back to English content 1011 + titleA.childNodes[0].textContent = el.dataset.titleEn; 1012 + detail.innerHTML = el.dataset.detailEn; 1013 + return; 1014 + } 1015 + 1016 + if (t.title) titleA.childNodes[0].textContent = t.title; 1017 + 1018 + // Translate the subtitle but keep the format/link suffix from English 1019 + const [, suffix] = splitDetail(el.dataset.detailEn); 1020 + if (t.subtitle) { 1021 + detail.innerHTML = suffix 1022 + ? t.subtitle + ' ' + suffix 1023 + : t.subtitle; 1024 + } else if (suffix) { 1025 + detail.innerHTML = suffix.replace(/^(?:&middot;|·)\s*/, ''); 1042 1026 } 1043 1027 }); 1044 1028 } 1045 1029 1046 - // Read language from URL path (/da, /cn, /es) or query param (?lang=da) 1030 + // Read language from URL path (/da, /es, /zh|/cn, /ja|/jp) or query param (?lang=da) 1047 1031 const cleanPath = location.pathname.replace(/\/+$/, ''); 1048 - const pathLang = { '/da': 'da', '/es': 'es', '/cn': 'zh', '/en': 'en', '/zh': 'zh' }[cleanPath]; 1032 + const pathLang = { 1033 + '/da': 'da', '/es': 'es', 1034 + '/cn': 'zh', '/en': 'en', '/zh': 'zh', 1035 + '/ja': 'ja', '/jp': 'ja' 1036 + }[cleanPath]; 1049 1037 const paramLang = new URLSearchParams(location.search).get('lang'); 1050 1038 const initLang = pathLang || paramLang; 1051 - if (initLang && langs[initLang]) setLang(initLang); 1039 + if (initLang && langs[initLang] && initLang !== 'en') { 1040 + // Wait for the JSON before swapping so non-English titles render correctly 1041 + translationsReady.then(() => setLang(initLang)); 1042 + } 1052 1043 1053 1044 // SVG icon templates 1054 1045 const pageSvg = (color) => `<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+142 -56
system/public/papers.aesthetic.computer/translations.json
··· 1 1 { 2 2 "ac": { 3 3 "en": { 4 - "title": "\\acrandname{} '26", 4 + "title": "Aesthetic Computer '26", 5 5 "subtitle": "A Mobile-First Runtime for Creative Computing" 6 6 }, 7 7 "da": { ··· 15 15 "zh": { 16 16 "title": "Aesthetic. Computer '26", 17 17 "subtitle": "面向创意计算的移动优先运行时" 18 + }, 19 + "ja": { 20 + "title": "Aesthetic. Computer '26", 21 + "subtitle": "クリエイティブコンピューティングのためのモバイルファーストランタイム" 18 22 } 19 23 }, 20 24 "api": { ··· 33 37 "zh": { 34 38 "title": "从 setup() 到 boot()", 35 39 "subtitle": "Processing在作品API核心中的延续" 40 + }, 41 + "ja": { 42 + "title": "setup() から boot() へ", 43 + "subtitle": "ピースAPIの核心におけるProcessingの継承" 36 44 } 37 45 }, 38 46 "archaeology": { 39 47 "en": { 40 48 "title": "Repository Archaeology", 41 - "subtitle": "Tracing the Evolution of \\acrandname{} Through Its Git History" 49 + "subtitle": "Tracing the Evolution of Aesthetic Computer Through Its Git History" 42 50 }, 43 51 "da": { 44 52 "title": "Repository-arkæologi", ··· 51 59 "zh": { 52 60 "title": "仓库考古学", 53 61 "subtitle": "通过 Git 历史追溯 Aesthetic Computer 的演变" 62 + }, 63 + "ja": { 64 + "title": "リポジトリ考古学", 65 + "subtitle": "Git履歴を通じてAesthetic Computerの進化を追跡する" 54 66 } 55 67 }, 56 68 "dead-ends": { ··· 69 81 "zh": { 70 82 "title": "残留功能", 71 83 "subtitle": "休眠路径、进化分支和废弃方法" 84 + }, 85 + "ja": { 86 + "title": "痕跡的機能", 87 + "subtitle": "休眠パス、進化的分岐、廃止メソッド" 72 88 } 73 89 }, 74 90 "diversity": { 75 91 "en": { 76 92 "title": "Citation Diversity Audit", 77 - "subtitle": "\\acrandname{} Paper Series, March 2026" 93 + "subtitle": "Aesthetic Computer Paper Series, March 2026" 78 94 }, 79 95 "da": { 80 96 "title": "Citationsdiversitetsrevision", ··· 87 103 "zh": { 88 104 "title": "引用多样性审计", 89 105 "subtitle": "Aesthetic Computer 论文系列,2026年3月" 106 + }, 107 + "ja": { 108 + "title": "引用多様性監査", 109 + "subtitle": "Aesthetic Computer 論文シリーズ、2026年3月" 90 110 } 91 111 }, 92 112 "folk-songs": { 93 113 "en": { 94 114 "title": "Playable Folk Songs", 95 - "subtitle": "Oral Tradition Meets the Browser Keyboard" 115 + "subtitle": "Oral Tradition Meets the Browser Keyboard on notepat.com" 96 116 }, 97 117 "da": { 98 118 "title": "Spillbare folkesange", ··· 105 125 "zh": { 106 126 "title": "可演奏的 民歌", 107 127 "subtitle": "口头传统遇上浏览器键盘" 128 + }, 129 + "ja": { 130 + "title": "演奏可能な 民謡", 131 + "subtitle": "口承伝統とブラウザキーボードの出会い" 108 132 } 109 133 }, 110 134 "goodiepal": { 111 135 "en": { 112 136 "title": "Radical Computer Art", 113 - "subtitle": "Goodiepalian Approaches in \\acrandname{}" 137 + "subtitle": "Goodiepalian Approaches in Aesthetic Computer" 114 138 }, 115 139 "da": { 116 140 "title": "Radikal computerkunst", ··· 123 147 "zh": { 124 148 "title": "激进计算机艺术", 125 149 "subtitle": "Aesthetic Computer 中的 Goodiepal 方法" 150 + }, 151 + "ja": { 152 + "title": "ラディカル・コンピュータ・アート", 153 + "subtitle": "Aesthetic Computer における Goodiepal メソッド" 126 154 } 127 155 }, 128 156 "kidlisp": { ··· 141 169 "zh": { 142 170 "title": "KidLisp '26", 143 171 "subtitle": "面向生成艺术的极简Lisp" 172 + }, 173 + "ja": { 174 + "title": "KidLisp '26", 175 + "subtitle": "ジェネラティブアートのためのミニマルLisp" 144 176 } 145 177 }, 146 178 "kidlisp-ref": { ··· 159 191 "zh": { 160 192 "title": "KidLisp 语言参考", 161 193 "subtitle": "12 个类别中的 118 个生成艺术内置函数" 194 + }, 195 + "ja": { 196 + "title": "KidLisp 言語リファレンス", 197 + "subtitle": "12カテゴリ118個のジェネラティブアート組み込み関数" 162 198 } 163 199 }, 164 200 "network-audit": { 165 201 "en": { 166 202 "title": "Network Audit", 167 - "subtitle": "Who Uses \\acrandname{} and What Do They Make?" 203 + "subtitle": "Who Uses Aesthetic Computer and What Do They Make?" 168 204 }, 169 205 "da": { 170 206 "title": "Netværksrevision", ··· 177 213 "zh": { 178 214 "title": "网络审计", 179 215 "subtitle": "谁在使用 Aesthetic Computer,他们创作了什么?" 216 + }, 217 + "ja": { 218 + "title": "ネットワーク監査", 219 + "subtitle": "Aesthetic Computerを誰が使い、何を作っているか?" 180 220 } 181 221 }, 182 222 "notepat": { ··· 195 235 "zh": { 196 236 "title": "notepat.com", 197 237 "subtitle": "从键盘玩具到系统前门" 238 + }, 239 + "ja": { 240 + "title": "notepat.com", 241 + "subtitle": "キーボード玩具からシステムの正面玄関へ" 198 242 } 199 243 }, 200 244 "os": { ··· 213 257 "zh": { 214 258 "title": "AC Native OS '26", 215 259 "subtitle": "一个裸机创意计算操作系统" 260 + }, 261 + "ja": { 262 + "title": "AC Native OS '26", 263 + "subtitle": "ベアメタルクリエイティブコンピューティングOS" 216 264 } 217 265 }, 218 266 "pieces": { ··· 231 279 "zh": { 232 280 "title": "作品而非程序", 233 281 "subtitle": "作品作为 Aesthetic Computer 中创意认知的单位" 282 + }, 283 + "ja": { 284 + "title": "プログラムではなくピース", 285 + "subtitle": "Aesthetic Computer における創造的認知の単位としてのピース" 234 286 } 235 287 }, 236 288 "who-pays": { ··· 249 301 "zh": { 250 302 "title": "谁为创意工具买单?", 251 303 "subtitle": "开源创意计算中的资金、倦怠与生存" 304 + }, 305 + "ja": { 306 + "title": "誰がクリエイティブツールの代金を払うのか?", 307 + "subtitle": "オープンソース・クリエイティブコンピューティングにおける資金、燃え尽き、そして生存" 252 308 } 253 309 }, 254 310 "whistlegraph": { ··· 267 323 "zh": { 268 324 "title": "Whistlegraph", 269 325 "subtitle": "绘画、歌唱与图形乐谱作为病毒式传播形式" 326 + }, 327 + "ja": { 328 + "title": "Whistlegraph", 329 + "subtitle": "ドローイング、歌唱、そしてバイラルな形式としてのグラフィックスコア" 270 330 } 271 331 }, 272 332 "complex": { ··· 276 336 }, 277 337 "da": { 278 338 "title": "Sucking on the Complex", 279 - "subtitle": "Platformhegemoni, kritik-som-indhold og behovet for anti-milj{\\o}er" 339 + "subtitle": "Platformhegemoni, kritik-som-indhold og behovet for anti-miljøer" 280 340 }, 281 341 "es": { 282 342 "title": "Sucking on the Complex", 283 - "subtitle": "Hegemon\\'{\\i}a de plataformas, cr\\'{\\i}tica-como-contenido y la necesidad de anti-entornos" 343 + "subtitle": "Hegemonía de plataformas, crítica-como-contenido y la necesidad de anti-entornos" 284 344 }, 285 345 "zh": { 286 346 "title": "Sucking on the Complex", 287 347 "subtitle": "平台霸权、批评即内容与反环境的需要" 348 + }, 349 + "ja": { 350 + "title": "Sucking on the Complex", 351 + "subtitle": "プラットフォーム覇権、批評としてのコンテンツ、反環境の必要性" 288 352 } 289 353 }, 290 354 "plork": { 291 355 "en": { 292 356 "title": "PLOrk'ing the Planet", 293 - "subtitle": "From Laptop Orchestra to Planetary Organ" 357 + "subtitle": "From Ivy League Laptop Orchestra to Kid-Friendly Planetary Organ" 294 358 }, 295 359 "da": { 296 360 "title": "PLOrk'ing the Planet", ··· 303 367 "zh": { 304 368 "title": "PLOrk'ing the Planet", 305 369 "subtitle": "从笔记本电脑管弦乐团到行星乐器" 370 + }, 371 + "ja": { 372 + "title": "PLOrk'ing the Planet", 373 + "subtitle": "アイビーリーグのラップトップオーケストラから子どもにやさしい地球規模のオルガンへ" 306 374 } 307 375 }, 308 - "identity": { 376 + "calarts": { 309 377 "en": { 310 - "title": "Handle Identity on the AT Protocol", 311 - "subtitle": "From Auth0 to Decentralized Sign-In on Aesthetic Computer" 378 + "title": "CalArts, Callouts, and Papers", 379 + "subtitle": "Art School as Operating System" 312 380 }, 313 381 "da": { 314 - "title": "Handle-identitet p\\aa{} AT-protokollen", 315 - "subtitle": "Fra Auth0 til decentraliseret login p\\aa{} Aesthetic Computer" 382 + "title": "CalArts, Callouts og Papers", 383 + "subtitle": "Kunstskolen som operativsystem" 316 384 }, 317 385 "es": { 318 - "title": "Identidad de Handle en el Protocolo AT", 319 - "subtitle": "De Auth0 al inicio de sesi\\'on descentralizado en Aesthetic Computer" 386 + "title": "CalArts, Callouts y Papers", 387 + "subtitle": "La escuela de arte como sistema operativo" 320 388 }, 321 389 "zh": { 322 - "title": "AT 协议上的 Handle 身份", 323 - "subtitle": "从 Auth0 到 Aesthetic Computer 上的去中心化登录" 390 + "title": "CalArts、Callouts与论文", 391 + "subtitle": "作为操作系统的艺术学院" 392 + }, 393 + "ja": { 394 + "title": "CalArts、コールアウトと論文", 395 + "subtitle": "オペレーティングシステムとしての芸術大学" 324 396 } 325 397 }, 326 398 "futures": { ··· 329 401 "subtitle": "What Aesthetic Computer Probably Becomes" 330 402 }, 331 403 "da": { 332 - "title": "Om fem \\aa{}r", 404 + "title": "Fem år fra nu", 333 405 "subtitle": "Hvad Aesthetic Computer sandsynligvis bliver" 334 406 }, 335 407 "es": { 336 - "title": "Dentro de cinco a\\~nos", 337 - "subtitle": "En qu\\'e probablemente se convierte Aesthetic Computer" 408 + "title": "Cinco años a partir de ahora", 409 + "subtitle": "En qué probablemente se convierte Aesthetic Computer" 338 410 }, 339 411 "zh": { 340 412 "title": "五年之后", 341 413 "subtitle": "Aesthetic Computer 可能会变成什么" 414 + }, 415 + "ja": { 416 + "title": "5年後", 417 + "subtitle": "Aesthetic Computer が何になりうるか" 342 418 } 343 419 }, 344 - "score-analysis": { 420 + "identity": { 345 421 "en": { 346 - "title": "Reading the Score", 347 - "subtitle": "A Critical Analysis of SCORE.md as Governance, Interface, and Creative Form" 422 + "title": "Handle Identity on the AT Protocol", 423 + "subtitle": "From Auth0 to Decentralized Sign-In on Aesthetic Computer" 348 424 }, 349 425 "da": { 350 - "title": "At l\\ae{}se partituret", 351 - "subtitle": "En kritisk analyse af SCORE.md som styring, gr\\ae{}nseflade og kreativ form" 426 + "title": "Handle-identitet på AT Protocol", 427 + "subtitle": "Fra Auth0 til decentraliseret login på Aesthetic Computer" 352 428 }, 353 429 "es": { 354 - "title": "Leyendo la partitura", 355 - "subtitle": "Un an\\'alisis cr\\'itico de SCORE.md como gobernanza, interfaz y forma creativa" 430 + "title": "Identidad de handle en el AT Protocol", 431 + "subtitle": "De Auth0 al inicio de sesión descentralizado en Aesthetic Computer" 356 432 }, 357 433 "zh": { 358 - "title": "阅读乐谱", 359 - "subtitle": "对 SCORE.md 作为治理、界面和创意形式的批判性分析" 434 + "title": "AT Protocol上的Handle身份", 435 + "subtitle": "从Auth0到Aesthetic Computer上的去中心化登录" 436 + }, 437 + "ja": { 438 + "title": "AT Protocol上のHandle ID", 439 + "subtitle": "Auth0からAesthetic Computerの分散型ログインへ" 360 440 } 361 441 }, 362 442 "open-schools": { 363 443 "en": { 364 - "title": "Get Closed Source Out of Schools", 365 - "subtitle": "Every Chromebook Is a Gateway Denied" 444 + "title": "Get Closed Source Out of Schools" 366 445 }, 367 446 "da": { 368 - "title": "F\\aa{} lukket kildekode ud af skolerne", 369 - "subtitle": "Hver Chromebook er en n\\ae{}gtet adgang" 447 + "title": "Få lukket kildekode ud af skolerne" 370 448 }, 371 449 "es": { 372 - "title": "Saquen el c\\'odigo cerrado de las escuelas", 373 - "subtitle": "Cada Chromebook es una puerta negada" 450 + "title": "Saquen el código cerrado de las escuelas" 374 451 }, 375 452 "zh": { 376 - "title": "让闭源软件离开学校", 377 - "subtitle": "每台 Chromebook 都是一扇被拒绝的大门" 453 + "title": "让闭源软件离开学校" 454 + }, 455 + "ja": { 456 + "title": "クローズドソフトウェアを学校から追い出せ" 378 457 } 379 458 }, 380 459 "kidlisp-cards": { ··· 384 463 }, 385 464 "da": { 386 465 "title": "KidLisp-kort", 387 - "subtitle": "Programmer der kan v\\ae{}re p\\aa{} et kort" 466 + "subtitle": "Programmer der kan være på et kort" 388 467 }, 389 468 "es": { 390 - "title": "Tarjetas KidLisp", 469 + "title": "KidLisp Tarjetas", 391 470 "subtitle": "Programas que caben en una tarjeta" 392 471 }, 393 472 "zh": { 394 473 "title": "KidLisp 卡片", 395 - "subtitle": "能放在一张卡片上的程序" 474 + "subtitle": "一张卡片装得下的程序" 475 + }, 476 + "ja": { 477 + "title": "KidLisp カード", 478 + "subtitle": "1枚のカードに収まるプログラム" 479 + } 480 + }, 481 + "score-analysis": { 482 + "en": {} 483 + }, 484 + "latency": { 485 + "en": { 486 + "title": "Where the Microseconds Go", 487 + "subtitle": "Input and Audio Latency in AC Native OS" 488 + } 489 + }, 490 + "penrose": { 491 + "en": { 492 + "title": "Diagrams from Data", 493 + "subtitle": "A Penrose Pipeline for Aesthetic Computer Illustrations" 396 494 } 397 495 }, 398 - "calarts": { 496 + "keymaps": { 399 497 "en": { 400 - "title": "CalArts, Callouts, and Papers", 401 - "subtitle": "Art School as Operating System" 402 - }, 403 - "da": { 404 - "title": "CalArts, callouts og artikler", 405 - "subtitle": "Kunstskole som styresystem" 406 - }, 407 - "es": { 408 - "title": "CalArts, callouts y art\\'iculos", 409 - "subtitle": "La escuela de arte como sistema operativo" 410 - }, 411 - "zh": { 412 - "title": "CalArts、批评与论文", 413 - "subtitle": "艺术学校作为操作系统" 498 + "title": "Keymaps as Social Software", 499 + "subtitle": "Versioned Virtual Objects via Social Contract" 414 500 } 415 501 } 416 502 }