[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"site:languages:en":3,"site:settings:en":22,"site:navigation:en":43,"blog:categories:en":169,"blog:list:en":200},[4,13],{"id":5,"code":6,"name":7,"nativeName":7,"direction":8,"flagEmoji":9,"isDefault":10,"isActive":10,"sortOrder":11,"createdAt":12,"updatedAt":12},"cms5x82ws0000p90jaubd6xdp","en","English","LTR","🇬🇧",true,0,"2026-07-29T10:07:30.747Z",{"id":14,"code":15,"name":16,"nativeName":17,"direction":8,"flagEmoji":18,"isDefault":19,"isActive":10,"sortOrder":20,"createdAt":21,"updatedAt":21},"cms5x82x90001p90je35iy8zx","id","Indonesian","Bahasa Indonesia","🇮🇩",false,1,"2026-07-29T10:07:30.766Z",{"site.name":23,"site.shortName":24,"site.logo_dark":25,"site.logo_light":26,"site.url":27,"site.defaultLocale":6,"contact.email":28,"contact.sales_email":29,"contact.phone":30,"contact.whatsapp":31,"contact.address":32,"contact.map_embed_url":33,"contact.office_hours":34,"company.legal_name":23,"company.founded":35,"seo.default_title":36,"seo.default_description":37,"seo.twitter_handle":38,"seo.default_og_image":39,"social.links":40,"features.newsletter_enabled":41,"features.blog_enabled":41,"features.careers_enabled":41,"integrations.google_analytics_id":42,"integrations.recaptcha_site_key":42},"PT Moodkuit Solutions Technologies","Moodkuit","\u002Fimages\u002Flogo\u002Fblack-logo.png","\u002Fimages\u002Flogo\u002Fwhite-logo.png","https:\u002F\u002Fmoodkuit.com","hello@moodkuit.com","sales@moodkuit.com","+62 21 5010 8899","+62 812 1000 8899","Menara Kuningan, 12th Floor, Jl. H. R. Rasuna Said, Jakarta Selatan 12940, Indonesia","https:\u002F\u002Fmaps.google.com\u002F?q=-6.2238,106.8317","Mon–Fri, 09:00–18:00 WIB","2018","Moodkuit — Enterprise Software House","Moodkuit designs, builds and operates enterprise software platforms — custom systems, web, mobile, cloud and data.","@moodkuit","\u002Fimages\u002Fhero\u002Fhero-slider.jpg","[{\"platform\":\"linkedin\",\"url\":\"https:\u002F\u002Flinkedin.com\u002Fcompany\u002Fmoodkuit\"},{\"platform\":\"github\",\"url\":\"https:\u002F\u002Fgithub.com\u002Fmoodkuit\"},{\"platform\":\"x\",\"url\":\"https:\u002F\u002Fx.com\u002Fmoodkuit\"},{\"platform\":\"instagram\",\"url\":\"https:\u002F\u002Finstagram.com\u002Fmoodkuit\"}]","true","",{"HEADER":44,"FOOTER_PRIMARY":93,"FOOTER_SECONDARY":106,"LEGAL":127,"SOCIAL":143},[45,52,57,63,69,75,81,87],{"id":46,"href":47,"target":48,"icon":49,"sortOrder":11,"label":50,"caption":49,"children":51},"cms5x85l800nvp90jgvd1p33h","\u002F","_self",null,"Home",[],{"id":53,"href":54,"target":48,"icon":49,"sortOrder":20,"label":55,"caption":49,"children":56},"cms5x85lq00o1p90j6pq2ixue","\u002Fabout","About",[],{"id":58,"href":59,"target":48,"icon":49,"sortOrder":60,"label":61,"caption":49,"children":62},"cms5x85m700o7p90jfrpuma9c","\u002Fservices",2,"Services",[],{"id":64,"href":65,"target":48,"icon":49,"sortOrder":66,"label":67,"caption":49,"children":68},"cms5x85ml00odp90jhbidzcte","\u002Fportfolio",3,"Portfolio",[],{"id":70,"href":71,"target":48,"icon":49,"sortOrder":72,"label":73,"caption":49,"children":74},"cms5x85mw00ojp90jm01w80sb","\u002Fcase-studies",4,"Case Studies",[],{"id":76,"href":77,"target":48,"icon":49,"sortOrder":78,"label":79,"caption":49,"children":80},"cms5x85n700opp90j8c67qr9t","\u002Fblog",5,"Blog",[],{"id":82,"href":83,"target":48,"icon":49,"sortOrder":84,"label":85,"caption":49,"children":86},"cms5x85nk00ovp90jzoeadygb","\u002Fcareers",6,"Careers",[],{"id":88,"href":89,"target":48,"icon":49,"sortOrder":90,"label":91,"caption":49,"children":92},"cms5x85nu00p1p90jb67ikv77","\u002Fcontact",7,"Contact",[],[94,97,100,103],{"id":95,"href":54,"target":48,"icon":49,"sortOrder":11,"label":55,"caption":49,"children":96},"cms5x85od00p8p90j616esl9i",[],{"id":98,"href":83,"target":48,"icon":49,"sortOrder":20,"label":85,"caption":49,"children":99},"cms5x85ot00pep90jrto5fvh7",[],{"id":101,"href":71,"target":48,"icon":49,"sortOrder":60,"label":73,"caption":49,"children":102},"cms5x85p400pkp90jd8usqva3",[],{"id":104,"href":89,"target":48,"icon":49,"sortOrder":66,"label":91,"caption":49,"children":105},"cms5x85pg00pqp90jvlmbrz8q",[],[107,112,117,122],{"id":108,"href":109,"target":48,"icon":49,"sortOrder":11,"label":110,"caption":49,"children":111},"cms5x85q100pxp90juyka7vco","\u002Fservices\u002Fcustom-software-development","Custom Software",[],{"id":113,"href":114,"target":48,"icon":49,"sortOrder":20,"label":115,"caption":49,"children":116},"cms5x85qj00q3p90jdh85gytx","\u002Fservices\u002Fweb-platform-engineering","Web Platforms",[],{"id":118,"href":119,"target":48,"icon":49,"sortOrder":60,"label":120,"caption":49,"children":121},"cms5x85r400q9p90jdhsjcffv","\u002Fservices\u002Fmobile-application-development","Mobile Apps",[],{"id":123,"href":124,"target":48,"icon":49,"sortOrder":66,"label":125,"caption":49,"children":126},"cms5x85rw00qfp90j1rs0xc9k","\u002Fservices\u002Fcloud-devops","Cloud & DevOps",[],[128,133,138],{"id":129,"href":130,"target":48,"icon":49,"sortOrder":11,"label":131,"caption":49,"children":132},"cms5x85sm00qmp90jki97thzi","\u002Fprivacy","Privacy Policy",[],{"id":134,"href":135,"target":48,"icon":49,"sortOrder":20,"label":136,"caption":49,"children":137},"cms5x85sw00qsp90j96hsi9if","\u002Fterms","Terms of Service",[],{"id":139,"href":140,"target":48,"icon":49,"sortOrder":60,"label":141,"caption":49,"children":142},"cms5x85ta00qyp90je9qxvdrq","\u002Ffaq","FAQ",[],[144,151,157,163],{"id":145,"href":146,"target":147,"icon":148,"sortOrder":11,"label":149,"caption":49,"children":150},"cms5x85to00r5p90jlpp2dj9y","https:\u002F\u002Flinkedin.com\u002Fcompany\u002Fmoodkuit","_blank","linkedin","LinkedIn",[],{"id":152,"href":153,"target":147,"icon":154,"sortOrder":20,"label":155,"caption":49,"children":156},"cms5x85u000rbp90jz220we6w","https:\u002F\u002Fgithub.com\u002Fmoodkuit","github","GitHub",[],{"id":158,"href":159,"target":147,"icon":160,"sortOrder":60,"label":161,"caption":49,"children":162},"cms5x85uc00rhp90j909vvybj","https:\u002F\u002Fx.com\u002Fmoodkuit","x","X",[],{"id":164,"href":165,"target":147,"icon":166,"sortOrder":66,"label":167,"caption":49,"children":168},"cms5x85un00rnp90j4c6x21gw","https:\u002F\u002Finstagram.com\u002Fmoodkuit","instagram","Instagram",[],[170,180,190],{"id":171,"parentId":49,"sortOrder":11,"createdAt":172,"updatedAt":172,"deletedAt":49,"translations":173,"_count":179},"cms5x84dx00a0p90jq4hj1xcq","2026-07-29T10:07:32.661Z",[174],{"id":175,"categoryId":171,"locale":6,"name":176,"slug":177,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":178,"updatedAt":178},"cms5x84e300a2p90jrmk5oan0","Engineering","engineering","2026-07-29T10:07:32.667Z",{"posts":66},{"id":181,"parentId":49,"sortOrder":20,"createdAt":182,"updatedAt":182,"deletedAt":49,"translations":183,"_count":189},"cms5x84eh00a5p90jy18t0toq","2026-07-29T10:07:32.681Z",[184],{"id":185,"categoryId":181,"locale":6,"name":186,"slug":187,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":188,"updatedAt":188},"cms5x84ek00a7p90jtwmn33gk","Product","product","2026-07-29T10:07:32.685Z",{"posts":20},{"id":191,"parentId":49,"sortOrder":60,"createdAt":192,"updatedAt":192,"deletedAt":49,"translations":193,"_count":199},"cms5x84ey00aap90jb6oji1s9","2026-07-29T10:07:32.698Z",[194],{"id":195,"categoryId":191,"locale":6,"name":196,"slug":197,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":198,"updatedAt":198},"cms5x84f100acp90j2y6m0s42","Company","company","2026-07-29T10:07:32.701Z",{"posts":20},{"data":201,"meta":373},[202,246,282,315,344],{"id":203,"authorId":204,"coverImageId":205,"status":206,"publishedAt":207,"readingMinutes":78,"viewCount":208,"isFeatured":19,"allowComments":19,"createdAt":209,"updatedAt":210,"deletedAt":49,"coverImage":211,"author":225,"categories":228,"translation":238},"cms5x84ll00btp90joerbi9uc","cms5x83ok0035p90j1tmyvb7d","cms5x83yp0061p90jub9op0dm","PUBLISHED","2026-06-18T09:00:00.000Z",9,"2026-07-29T10:07:32.937Z","2026-07-29T11:43:56.324Z",{"id":205,"folderId":212,"uploadedById":213,"type":214,"filename":215,"originalName":215,"mimeType":216,"size":217,"width":218,"height":219,"storageKey":220,"url":221,"thumbnailUrl":221,"alt":222,"caption":49,"blurhash":49,"createdAt":223,"updatedAt":224,"deletedAt":49},"cms5x83pu003jp90j5lwg5qs2","cms5x83o00034p90jw5cmslod","IMAGE","post-5.jpg","image\u002Fjpeg",19429,356,240,"seed\u002Fpost\u002Fmoodkuit-opens-its-jakarta-engineering-studio\u002Fpost-5.jpg","https:\u002F\u002Fcdn.moodkuit.com\u002Fmoodkuit\u002Fseed\u002Fpost\u002Fmoodkuit-opens-its-jakarta-engineering-studio\u002Fpost-5.jpg","The Moodkuit engineering studio in South Jakarta","2026-07-29T10:07:32.113Z","2026-07-29T10:31:55.642Z",{"id":204,"name":226,"avatarUrl":227},"Rani Prameswari","\u002Fimages\u002Fnews\u002Fauthor_img2.jpg",[229],{"postId":203,"categoryId":191,"category":230},{"id":191,"parentId":49,"sortOrder":60,"createdAt":192,"updatedAt":192,"deletedAt":49,"translations":231},[232,233],{"id":195,"categoryId":191,"locale":6,"name":196,"slug":197,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":198,"updatedAt":198},{"id":234,"categoryId":191,"locale":15,"name":235,"slug":236,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":237,"updatedAt":237},"cms5x84f600aep90jj3uqmqhi","Perusahaan","perusahaan","2026-07-29T10:07:32.706Z",{"id":239,"postId":203,"locale":6,"title":240,"slug":241,"excerpt":242,"content":243,"seoTitle":244,"seoDescription":242,"seoKeywords":49,"canonicalUrl":49,"ogImageUrl":49,"noIndex":19,"noFollow":19,"createdAt":245,"updatedAt":245},"cms5x84lp00bvp90jcjtrn32f","Moodkuit opens its Jakarta engineering studio","moodkuit-opens-its-jakarta-engineering-studio","A dedicated space for pairing, architecture reviews and client discovery sessions.","\u003Cp>Our new studio in South Jakarta gives delivery teams a permanent home for discovery workshops and architecture reviews, with clients invited into the room rather than the status report.\u003C\u002Fp>","Moodkuit opens its Jakarta engineering studio | Moodkuit","2026-07-29T10:07:32.941Z",{"id":247,"authorId":204,"coverImageId":248,"status":206,"publishedAt":249,"readingMinutes":72,"viewCount":250,"isFeatured":19,"allowComments":19,"createdAt":251,"updatedAt":252,"deletedAt":49,"coverImage":253,"author":263,"categories":264,"translation":274},"cms5x84kj00bnp90j5qo7aas1","cms5x83yk005zp90jsek60xg4","2026-06-10T09:00:00.000Z",11,"2026-07-29T10:07:32.899Z","2026-07-29T11:44:38.639Z",{"id":248,"folderId":212,"uploadedById":213,"type":214,"filename":254,"originalName":254,"mimeType":216,"size":255,"width":256,"height":257,"storageKey":258,"url":259,"thumbnailUrl":259,"alt":260,"caption":49,"blurhash":49,"createdAt":261,"updatedAt":262,"deletedAt":49},"post-4.jpg",36028,850,402,"seed\u002Fpost\u002Fcore-web-vitals-budgets-that-survive-the-second-sprint\u002Fpost-4.jpg","https:\u002F\u002Fcdn.moodkuit.com\u002Fmoodkuit\u002Fseed\u002Fpost\u002Fcore-web-vitals-budgets-that-survive-the-second-sprint\u002Fpost-4.jpg","Performance budget failing a continuous integration run","2026-07-29T10:07:32.108Z","2026-07-29T10:31:55.623Z",{"id":204,"name":226,"avatarUrl":227},[265],{"postId":247,"categoryId":171,"category":266},{"id":171,"parentId":49,"sortOrder":11,"createdAt":172,"updatedAt":172,"deletedAt":49,"translations":267},[268,269],{"id":175,"categoryId":171,"locale":6,"name":176,"slug":177,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":178,"updatedAt":178},{"id":270,"categoryId":171,"locale":15,"name":271,"slug":272,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":273,"updatedAt":273},"cms5x84e900a4p90jp567w33d","Rekayasa","rekayasa","2026-07-29T10:07:32.674Z",{"id":275,"postId":247,"locale":6,"title":276,"slug":277,"excerpt":278,"content":279,"seoTitle":280,"seoDescription":278,"seoKeywords":49,"canonicalUrl":49,"ogImageUrl":49,"noIndex":19,"noFollow":19,"createdAt":281,"updatedAt":281},"cms5x84kn00bpp90jwdu8sgdr","Core Web Vitals budgets that survive the second sprint","core-web-vitals-budgets-that-survive-the-second-sprint","A launch-day Lighthouse score is a snapshot. A CI budget is a guarantee.","\u003Cp>Performance regressions arrive one innocuous import at a time. The fix is structural: budgets enforced in CI, with the build failing when LCP or total bundle size crosses the line.\u003C\u002Fp>","Core Web Vitals budgets that survive the second sprint | Moodkuit","2026-07-29T10:07:32.902Z",{"id":283,"authorId":204,"coverImageId":284,"status":206,"publishedAt":285,"readingMinutes":84,"viewCount":208,"isFeatured":19,"allowComments":19,"createdAt":286,"updatedAt":287,"deletedAt":49,"coverImage":288,"author":296,"categories":297,"translation":307},"cms5x84jx00bhp90ja6t3v8st","cms5x83ye005xp90jvw3zr4ne","2026-06-02T09:00:00.000Z","2026-07-29T10:07:32.877Z","2026-07-29T11:43:44.392Z",{"id":284,"folderId":212,"uploadedById":213,"type":214,"filename":289,"originalName":289,"mimeType":216,"size":290,"width":256,"height":257,"storageKey":291,"url":292,"thumbnailUrl":292,"alt":293,"caption":49,"blurhash":49,"createdAt":294,"updatedAt":295,"deletedAt":49},"post-3.jpg",24710,"seed\u002Fpost\u002Fthe-audit-log-is-a-feature-not-a-debugging-tool\u002Fpost-3.jpg","https:\u002F\u002Fcdn.moodkuit.com\u002Fmoodkuit\u002Fseed\u002Fpost\u002Fthe-audit-log-is-a-feature-not-a-debugging-tool\u002Fpost-3.jpg","Audit trail entries reviewed in an admin interface","2026-07-29T10:07:32.102Z","2026-07-29T10:31:55.674Z",{"id":204,"name":226,"avatarUrl":227},[298],{"postId":283,"categoryId":181,"category":299},{"id":181,"parentId":49,"sortOrder":20,"createdAt":182,"updatedAt":182,"deletedAt":49,"translations":300},[301,302],{"id":185,"categoryId":181,"locale":6,"name":186,"slug":187,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":188,"updatedAt":188},{"id":303,"categoryId":181,"locale":15,"name":304,"slug":305,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":306,"updatedAt":306},"cms5x84er00a9p90jkb1sr9kr","Produk","produk","2026-07-29T10:07:32.689Z",{"id":308,"postId":283,"locale":6,"title":309,"slug":310,"excerpt":311,"content":312,"seoTitle":313,"seoDescription":311,"seoKeywords":49,"canonicalUrl":49,"ogImageUrl":49,"noIndex":19,"noFollow":19,"createdAt":314,"updatedAt":314},"cms5x84k100bjp90j2v8tly9p","The audit log is a feature, not a debugging tool","the-audit-log-is-a-feature-not-a-debugging-tool","If your CMS cannot answer \"who changed this and when\", it is not enterprise software yet.","\u003Cp>Audit logging added late becomes a scattering of ad-hoc writes. Added early, as an interceptor around the mutation boundary, it costs almost nothing and covers everything.\u003C\u002Fp>\u003Ch2>What to record\u003C\u002Fh2>\u003Cp>Actor, action, entity, entity id, a redacted before\u002Fafter diff, request id, IP and user agent. Secrets and password hashes are stripped before the row is written — an audit trail that leaks credentials is a liability, not a control.\u003C\u002Fp>","The audit log is a feature, not a debugging tool | Moodkuit","2026-07-29T10:07:32.882Z",{"id":316,"authorId":204,"coverImageId":317,"status":206,"publishedAt":318,"readingMinutes":78,"viewCount":90,"isFeatured":19,"allowComments":19,"createdAt":319,"updatedAt":320,"deletedAt":49,"coverImage":321,"author":329,"categories":330,"translation":336},"cms5x84j900bbp90jgvsq13h5","cms5x83y9005vp90j0shkjq4a","2026-05-25T09:00:00.000Z","2026-07-29T10:07:32.853Z","2026-07-29T11:30:37.286Z",{"id":317,"folderId":212,"uploadedById":213,"type":214,"filename":322,"originalName":322,"mimeType":216,"size":323,"width":256,"height":257,"storageKey":324,"url":325,"thumbnailUrl":325,"alt":326,"caption":49,"blurhash":49,"createdAt":327,"updatedAt":328,"deletedAt":49},"post-2.jpg",30668,"seed\u002Fpost\u002Fshipping-a-multilingual-site-without-duplicating-your-codebase\u002Fpost-2.jpg","https:\u002F\u002Fcdn.moodkuit.com\u002Fmoodkuit\u002Fseed\u002Fpost\u002Fshipping-a-multilingual-site-without-duplicating-your-codebase\u002Fpost-2.jpg","Locale-prefixed routes mapped out on a whiteboard","2026-07-29T10:07:32.097Z","2026-07-29T10:31:55.658Z",{"id":204,"name":226,"avatarUrl":227},[331],{"postId":316,"categoryId":171,"category":332},{"id":171,"parentId":49,"sortOrder":11,"createdAt":172,"updatedAt":172,"deletedAt":49,"translations":333},[334,335],{"id":175,"categoryId":171,"locale":6,"name":176,"slug":177,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":178,"updatedAt":178},{"id":270,"categoryId":171,"locale":15,"name":271,"slug":272,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":273,"updatedAt":273},{"id":337,"postId":316,"locale":6,"title":338,"slug":339,"excerpt":340,"content":341,"seoTitle":342,"seoDescription":340,"seoKeywords":49,"canonicalUrl":49,"ogImageUrl":49,"noIndex":19,"noFollow":19,"createdAt":343,"updatedAt":343},"cms5x84jd00bdp90j79fx6ejp","Shipping a multilingual site without duplicating your codebase","shipping-a-multilingual-site-without-duplicating-your-codebase","Locale-prefixed routing, hreflang, canonical URLs and per-locale slugs — handled once, in the router.","\u003Cp>Most multilingual sites go wrong in the same place: the URL. Once slugs differ per locale, naive routing forces either duplicated pages or English URLs everywhere.\u003C\u002Fp>\u003Ch2>The routing contract\u003C\u002Fh2>\u003Cp>The default locale is served unprefixed. Every other locale gets a prefix. Slugs are resolved per locale from the database, and each page emits a complete set of \u003Ccode>hreflang\u003C\u002Fcode> alternates plus \u003Ccode>x-default\u003C\u002Fcode>.\u003C\u002Fp>","Shipping a multilingual site without duplicating your codebase | Moodkuit","2026-07-29T10:07:32.857Z",{"id":345,"authorId":204,"coverImageId":346,"status":206,"publishedAt":347,"readingMinutes":72,"viewCount":90,"isFeatured":10,"allowComments":19,"createdAt":348,"updatedAt":349,"deletedAt":49,"coverImage":350,"author":358,"categories":359,"translation":365},"cms5x84i100b5p90j5aybdulu","cms5x83y2005tp90jglrwh85p","2026-05-17T09:00:00.000Z","2026-07-29T10:07:32.810Z","2026-07-29T11:30:34.722Z",{"id":346,"folderId":212,"uploadedById":213,"type":214,"filename":351,"originalName":351,"mimeType":216,"size":352,"width":256,"height":257,"storageKey":353,"url":354,"thumbnailUrl":354,"alt":355,"caption":49,"blurhash":49,"createdAt":356,"updatedAt":357,"deletedAt":49},"post-1.jpg",52352,"seed\u002Fpost\u002Fwhy-we-model-translations-as-rows-not-json\u002Fpost-1.jpg","https:\u002F\u002Fcdn.moodkuit.com\u002Fmoodkuit\u002Fseed\u002Fpost\u002Fwhy-we-model-translations-as-rows-not-json\u002Fpost-1.jpg","Database schema sketch comparing JSON blobs with translation tables","2026-07-29T10:07:32.090Z","2026-07-29T10:31:55.693Z",{"id":204,"name":226,"avatarUrl":227},[360],{"postId":345,"categoryId":171,"category":361},{"id":171,"parentId":49,"sortOrder":11,"createdAt":172,"updatedAt":172,"deletedAt":49,"translations":362},[363,364],{"id":175,"categoryId":171,"locale":6,"name":176,"slug":177,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":178,"updatedAt":178},{"id":270,"categoryId":171,"locale":15,"name":271,"slug":272,"description":49,"seoTitle":49,"seoDescription":49,"createdAt":273,"updatedAt":273},{"id":366,"postId":345,"locale":6,"title":367,"slug":368,"excerpt":369,"content":370,"seoTitle":371,"seoDescription":369,"seoKeywords":49,"canonicalUrl":49,"ogImageUrl":49,"noIndex":19,"noFollow":19,"createdAt":372,"updatedAt":372},"cms5x84i900b7p90jui0vovq3","Why we model translations as rows, not JSON","why-we-model-translations-as-rows-not-json","A JSON translation blob is convenient until the day you need to find every untranslated heading. Relational translation tables cost one join and return that answer in milliseconds.","\u003Cp>Storing translations in a JSON column looks like the pragmatic choice. One column, no joins, no migrations when a locale is added. The bill arrives later.\u003C\u002Fp>\u003Ch2>What breaks\u003C\u002Fh2>\u003Cp>Reporting on translation coverage becomes a full table scan with JSON path expressions. Uniqueness on slugs per locale cannot be enforced by the database. Partial indexes on published, localized content are unavailable. And every consumer must agree on the blob shape forever.\u003C\u002Fp>\u003Ch2>What we do instead\u003C\u002Fh2>\u003Cp>Every translatable entity has a sibling table keyed on \u003Ccode>(entity_id, locale)\u003C\u002Fcode> with a second unique constraint on \u003Ccode>(locale, slug)\u003C\u002Fcode>. Adding Japanese is an insert into \u003Ccode>languages\u003C\u002Fcode> — no migration, no deploy.\u003C\u002Fp>","Why we model translations as rows, not JSON | Moodkuit","2026-07-29T10:07:32.817Z",{"page":20,"limit":208,"total":78,"totalPages":20,"hasNextPage":19,"hasPreviousPage":19}]