[{"data":1,"prerenderedAt":3007},["ShallowReactive",2],{"navigation_docs":3,"-docs-reference-localized-content":968,"-docs-reference-localized-content-surround":3003},[4,27,63,129,287,311,893,902,950,964],{"title":5,"path":6,"stem":7,"children":8,"order":10,"icon":11},"Overview","\u002Fdocs\u002Foverview","docs\u002F1.overview\u002Findex",[9,12,18,24],{"title":5,"path":6,"stem":7,"order":10,"icon":11},1,"i-lucide-info",{"title":13,"path":14,"stem":15,"order":16,"icon":17},"Getting Started","\u002Fdocs\u002Foverview\u002Fgetting-started","docs\u002F1.overview\u002F2.getting-started",2,"i-lucide-rocket",{"title":19,"path":20,"stem":21,"order":22,"icon":23},"Contributing","\u002Fdocs\u002Foverview\u002Fcontributing","docs\u002F1.overview\u002F3.contributing",3,"i-lucide-heart",{"title":5,"path":25,"stem":26,"order":10},"\u002Fdocs\u002Foverview\u002F_dir","docs\u002F1.overview\u002F_dir",{"title":28,"path":29,"stem":30,"children":31,"page":62},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F2.concepts",[32,37,42,47,53,59],{"title":33,"path":34,"stem":35,"order":10,"icon":36},"Events","\u002Fdocs\u002Fconcepts\u002Fevents","docs\u002F2.concepts\u002F1.events","i-lucide-zap",{"title":38,"path":39,"stem":40,"order":16,"icon":41},"Transports","\u002Fdocs\u002Fconcepts\u002Ftransports","docs\u002F2.concepts\u002F2.transports","i-lucide-network",{"title":43,"path":44,"stem":45,"order":22,"icon":46},"Module System","\u002Fdocs\u002Fconcepts\u002Fmodules","docs\u002F2.concepts\u002F3.modules","i-lucide-puzzle",{"title":48,"path":49,"stem":50,"order":51,"icon":52},"Flows","\u002Fdocs\u002Fconcepts\u002Fflows","docs\u002F2.concepts\u002F4.flows",4,"i-lucide-workflow",{"title":54,"path":55,"stem":56,"order":57,"icon":58},"Scheduler","\u002Fdocs\u002Fconcepts\u002Fscheduler","docs\u002F2.concepts\u002F5.scheduler",5,"i-lucide-clock",{"title":28,"path":60,"stem":61,"order":16},"\u002Fdocs\u002Fconcepts\u002F_dir","docs\u002F2.concepts\u002F_dir",false,{"title":64,"path":65,"stem":66,"children":67,"page":62},"Guides","\u002Fdocs\u002Fguides","docs\u002F3.guides",[68,73,77,81,85,89,93,98,104,110,116,122,126],{"title":69,"path":70,"stem":71,"order":10,"icon":72},"Creating a Go App","\u002Fdocs\u002Fguides\u002Fcreating-go-app","docs\u002F3.guides\u002F1.creating-go-app","i-lucide-terminal",{"title":74,"path":75,"stem":76},"E2E tests & demo videos","\u002Fdocs\u002Fguides\u002Fe2e-and-demo-videos","docs\u002F3.guides\u002F10.e2e-and-demo-videos",{"title":78,"path":79,"stem":80,"icon":17},"Deploying to production","\u002Fdocs\u002Fguides\u002Fdeploying-to-production","docs\u002F3.guides\u002F11.deploying-to-production",{"title":82,"path":83,"stem":84,"order":16,"icon":46},"Creating a Go Module","\u002Fdocs\u002Fguides\u002Fcreating-go-module","docs\u002F3.guides\u002F2.creating-go-module",{"title":86,"path":87,"stem":88,"order":22,"icon":36},"Adding Events","\u002Fdocs\u002Fguides\u002Fadding-events-to-go-module","docs\u002F3.guides\u002F3.adding-events-to-go-module",{"title":90,"path":91,"stem":92,"order":51,"icon":52},"FlowDSL to Service","\u002Fdocs\u002Fguides\u002Fflowdsl-to-redelay","docs\u002F3.guides\u002F4.flowdsl-to-redelay",{"title":94,"path":95,"stem":96,"order":57,"icon":97},"Choosing a Transport","\u002Fdocs\u002Fguides\u002Fchoosing-a-transport","docs\u002F3.guides\u002F5.choosing-a-transport","i-lucide-git-branch",{"title":99,"path":100,"stem":101,"order":102,"icon":103},"Module Browser (\u002Fmodules)","\u002Fdocs\u002Fguides\u002Fmodules-browser","docs\u002F3.guides\u002F6.modules-browser",6,"i-lucide-layout-dashboard",{"title":105,"path":106,"stem":107,"order":108,"icon":109},"Running Flows","\u002Fdocs\u002Fguides\u002Frunning-flows","docs\u002F3.guides\u002F7.running-flows",7,"i-lucide-play-circle",{"title":111,"path":112,"stem":113,"order":114,"icon":115},"Trace + Live Observability","\u002Fdocs\u002Fguides\u002Fobservability","docs\u002F3.guides\u002F8.observability",8,"i-lucide-activity",{"title":117,"path":118,"stem":119,"order":120,"icon":121},"Project AI Assistant","\u002Fdocs\u002Fguides\u002Fai-assistant","docs\u002F3.guides\u002F9.ai-assistant",9,"i-lucide-sparkles",{"title":123,"path":124,"stem":125},"Debug taps — trace live data through any node","\u002Fdocs\u002Fguides\u002Fdebug-taps","docs\u002F3.guides\u002F9.debug-taps",{"title":64,"path":127,"stem":128,"order":22},"\u002Fdocs\u002Fguides\u002F_dir","docs\u002F3.guides\u002F_dir",{"title":130,"path":131,"stem":132,"children":133,"page":62},"Reference","\u002Fdocs\u002Freference","docs\u002F4.reference",[134,139,143,148,153,158,163,168,172,177,182,187,193,199,205,210,215,219,225,230,236,242,247,253,259,263,267,273,278,284],{"title":135,"path":136,"stem":137,"order":10,"icon":138},"Go Framework","\u002Fdocs\u002Freference\u002Fgo-framework","docs\u002F4.reference\u002F01.go-framework","i-simple-icons-go",{"title":140,"path":141,"stem":142,"order":16,"icon":46},"Built-in Modules","\u002Fdocs\u002Freference\u002Fgo-modules","docs\u002F4.reference\u002F02.go-modules",{"title":144,"path":145,"stem":146,"order":22,"icon":147},"Domain Addon Modules","\u002Fdocs\u002Freference\u002Fgo-modules-addons","docs\u002F4.reference\u002F03.go-modules-addons","i-lucide-blocks",{"title":149,"path":150,"stem":151,"order":51,"icon":152},"Go Events","\u002Fdocs\u002Freference\u002Fgo-events","docs\u002F4.reference\u002F04.go-events","i-lucide-radio",{"title":154,"path":155,"stem":156,"order":57,"icon":157},"Email Module","\u002Fdocs\u002Freference\u002Fgo-module-email","docs\u002F4.reference\u002F05.go-module-email","i-lucide-mail",{"title":159,"path":160,"stem":161,"order":102,"icon":162},"Go Core & IR","\u002Fdocs\u002Freference\u002Fgo-core","docs\u002F4.reference\u002F06.go-core","i-lucide-cpu",{"title":164,"path":165,"stem":166,"order":108,"icon":167},"Module Definition Files","\u002Fdocs\u002Freference\u002Fgo-module-files","docs\u002F4.reference\u002F07.go-module-files","i-lucide-file-code",{"title":169,"path":170,"stem":171,"order":114,"icon":72},"CLI Reference","\u002Fdocs\u002Freference\u002Fgo-cli","docs\u002F4.reference\u002F08.go-cli",{"title":173,"path":174,"stem":175,"order":120,"icon":176},"Go Formats","\u002Fdocs\u002Freference\u002Fgo-formats","docs\u002F4.reference\u002F09.go-formats","i-lucide-file-json",{"title":178,"path":179,"stem":180,"order":181,"icon":152},"AsyncAPI Module","\u002Fdocs\u002Freference\u002Fgo-asyncapi","docs\u002F4.reference\u002F10.go-asyncapi",10,{"title":183,"path":184,"stem":185,"order":186,"icon":52},"Go FlowDSL","\u002Fdocs\u002Freference\u002Fgo-flowdsl","docs\u002F4.reference\u002F11.go-flowdsl",11,{"title":188,"path":189,"stem":190,"order":191,"icon":192},"FlowDSL Examples","\u002Fdocs\u002Freference\u002Fflowdsl-examples","docs\u002F4.reference\u002F12.flowdsl-examples",12,"i-lucide-file-code-2",{"title":194,"path":195,"stem":196,"order":197,"icon":198},"FlowDSL Sync","\u002Fdocs\u002Freference\u002Fflowdsl-sync","docs\u002F4.reference\u002F13.flowdsl-sync",13,"i-lucide-refresh-cw",{"title":200,"path":201,"stem":202,"order":203,"icon":204},"Node.js Framework","\u002Fdocs\u002Freference\u002Fjs-framework","docs\u002F4.reference\u002F14.js-framework",14,"i-simple-icons-nodedotjs",{"title":206,"path":207,"stem":208,"order":203,"icon":209},"Python Framework","\u002Fdocs\u002Freference\u002Fpy-framework","docs\u002F4.reference\u002F14.py-framework","i-simple-icons-python",{"title":211,"path":212,"stem":213,"order":214,"icon":204},"Node.js Events","\u002Fdocs\u002Freference\u002Fjs-events","docs\u002F4.reference\u002F15.js-events",15,{"title":216,"path":217,"stem":218,"order":214,"icon":209},"Python Events","\u002Fdocs\u002Freference\u002Fpy-events","docs\u002F4.reference\u002F15.py-events",{"title":220,"path":221,"stem":222,"order":223,"icon":224},"go-ai — LLM Providers, Guard, Ledger","\u002Fdocs\u002Freference\u002Fgo-ai","docs\u002F4.reference\u002F16.go-ai",16,"i-lucide-brain-circuit",{"title":226,"path":227,"stem":228,"order":229,"icon":41},"Infrastructure Modules","\u002Fdocs\u002Freference\u002Fgeneric-infra-modules","docs\u002F4.reference\u002F17.generic-infra-modules",17,{"title":231,"path":232,"stem":233,"order":234,"icon":235},"Flow Templates","\u002Fdocs\u002Freference\u002Fflow-templates","docs\u002F4.reference\u002F18.flow-templates",18,"i-lucide-library-big",{"title":237,"path":238,"stem":239,"order":240,"icon":241},"Assistant (chat + handoff)","\u002Fdocs\u002Freference\u002Fassistant","docs\u002F4.reference\u002F19.assistant",19,"i-lucide-messages-square",{"title":243,"path":244,"stem":245,"order":246,"icon":41},"Coordination","\u002Fdocs\u002Freference\u002Fcoordination","docs\u002F4.reference\u002F20.coordination",20,{"title":248,"path":249,"stem":250,"order":251,"icon":252},"Search (vector)","\u002Fdocs\u002Freference\u002Fsearch","docs\u002F4.reference\u002F21.search",21,"i-lucide-search",{"title":254,"path":255,"stem":256,"order":257,"icon":258},"Profiles","\u002Fdocs\u002Freference\u002Fprofiles","docs\u002F4.reference\u002F22.profiles",22,"i-lucide-bookmark",{"title":260,"path":261,"stem":262},"Flow-driven HTTP endpoints","\u002Fdocs\u002Freference\u002Fflow-driven-endpoints","docs\u002F4.reference\u002F23.flow-driven-endpoints",{"title":264,"path":265,"stem":266},"Deployments","\u002Fdocs\u002Freference\u002Fdeployments","docs\u002F4.reference\u002F24.deployments",{"title":268,"path":269,"stem":270,"order":271,"icon":272},"i18n (translations)","\u002Fdocs\u002Freference\u002Fi18n","docs\u002F4.reference\u002F25.i18n",25,"i-lucide-languages",{"title":274,"path":275,"stem":276,"order":277,"icon":152},"WebSockets","\u002Fdocs\u002Freference\u002Fwebsockets","docs\u002F4.reference\u002F26.websockets",26,{"title":279,"path":280,"stem":281,"order":282,"icon":283},"Localized content","\u002Fdocs\u002Freference\u002Flocalized-content","docs\u002F4.reference\u002F27.localized-content",27,"i-lucide-file-text",{"title":130,"path":285,"stem":286,"order":51},"\u002Fdocs\u002Freference\u002F_dir","docs\u002F4.reference\u002F_dir",{"title":288,"path":289,"stem":290,"children":291,"page":62},"Integrations","\u002Fdocs\u002Fintegrations","docs\u002F5.integrations",[292,296,300,304,308],{"title":293,"path":294,"stem":295,"order":10,"icon":52},"FlowDSL","\u002Fdocs\u002Fintegrations\u002Fflowdsl","docs\u002F5.integrations\u002F1.flowdsl",{"title":297,"path":298,"stem":299,"order":16,"icon":283},"OpenAPI","\u002Fdocs\u002Fintegrations\u002Fopenapi","docs\u002F5.integrations\u002F2.openapi",{"title":301,"path":302,"stem":303,"order":22,"icon":152},"AsyncAPI","\u002Fdocs\u002Fintegrations\u002Fasyncapi","docs\u002F5.integrations\u002F3.asyncapi",{"title":305,"path":306,"stem":307},"MCP Integration","\u002Fdocs\u002Fintegrations\u002Fmcp","docs\u002F5.integrations\u002F4.mcp",{"title":288,"path":309,"stem":310,"order":57},"\u002Fdocs\u002Fintegrations\u002F_dir","docs\u002F5.integrations\u002F_dir",{"title":312,"path":313,"stem":314,"children":315},"FlowDSL Node Catalog","\u002Fdocs\u002Fnodes","docs\u002F5.nodes\u002Findex",[316,317,321,325,329,333,337,341,345,349,353,357,361,365,369,373,377,381,385,389,393,397,401,405,409,413,417,421,425,429,433,437,441,445,449,453,457,461,465,469,473,477,481,485,489,493,497,501,505,509,513,517,521,525,529,533,537,541,545,549,553,557,561,565,569,573,577,581,585,589,593,597,601,605,609,613,617,621,625,629,633,637,641,645,649,653,657,661,665,669,673,677,681,685,689,693,697,701,705,709,713,717,721,725,729,733,737,741,745,749,753,757,761,765,769,773,777,781,785,789,793,797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865,869,873,877,881,885,889],{"title":312,"path":313,"stem":314},{"title":318,"path":319,"stem":320,"order":57},"Node Catalog","\u002Fdocs\u002Fnodes\u002F_dir","docs\u002F5.nodes\u002F_dir",{"title":322,"path":323,"stem":324},"Record Alert","\u002Fdocs\u002Fnodes\u002Falerts_record","docs\u002F5.nodes\u002Falerts_record",{"title":326,"path":327,"stem":328},"Log to API Log","\u002Fdocs\u002Fnodes\u002Fapilog_record","docs\u002F5.nodes\u002Fapilog_record",{"title":330,"path":331,"stem":332},"ClickHouse Exec","\u002Fdocs\u002Fnodes\u002Fclickhouse_exec","docs\u002F5.nodes\u002Fclickhouse_exec",{"title":334,"path":335,"stem":336},"ClickHouse Insert","\u002Fdocs\u002Fnodes\u002Fclickhouse_insert","docs\u002F5.nodes\u002Fclickhouse_insert",{"title":338,"path":339,"stem":340},"ClickHouse Query","\u002Fdocs\u002Fnodes\u002Fclickhouse_query","docs\u002F5.nodes\u002Fclickhouse_query",{"title":342,"path":343,"stem":344},"Array Op","\u002Fdocs\u002Fnodes\u002Fcore_array-ops","docs\u002F5.nodes\u002Fcore_array-ops",{"title":346,"path":347,"stem":348},"Assert","\u002Fdocs\u002Fnodes\u002Fcore_assert","docs\u002F5.nodes\u002Fcore_assert",{"title":350,"path":351,"stem":352},"Audit Emit","\u002Fdocs\u002Fnodes\u002Fcore_audit-emit","docs\u002F5.nodes\u002Fcore_audit-emit",{"title":354,"path":355,"stem":356},"Catch","\u002Fdocs\u002Fnodes\u002Fcore_catch","docs\u002F5.nodes\u002Fcore_catch",{"title":358,"path":359,"stem":360},"DateTime","\u002Fdocs\u002Fnodes\u002Fcore_datetime","docs\u002F5.nodes\u002Fcore_datetime",{"title":362,"path":363,"stem":364},"Dead Letter","\u002Fdocs\u002Fnodes\u002Fcore_dead-letter","docs\u002F5.nodes\u002Fcore_dead-letter",{"title":366,"path":367,"stem":368},"Delay","\u002Fdocs\u002Fnodes\u002Fcore_delay","docs\u002F5.nodes\u002Fcore_delay",{"title":370,"path":371,"stem":372},"ForEach","\u002Fdocs\u002Fnodes\u002Fcore_foreach","docs\u002F5.nodes\u002Fcore_foreach",{"title":374,"path":375,"stem":376},"Idempotency Key","\u002Fdocs\u002Fnodes\u002Fcore_idempotency-key","docs\u002F5.nodes\u002Fcore_idempotency-key",{"title":378,"path":379,"stem":380},"If","\u002Fdocs\u002Fnodes\u002Fcore_if","docs\u002F5.nodes\u002Fcore_if",{"title":382,"path":383,"stem":384},"Inspect","\u002Fdocs\u002Fnodes\u002Fcore_inspect","docs\u002F5.nodes\u002Fcore_inspect",{"title":386,"path":387,"stem":388},"JSON Merge","\u002Fdocs\u002Fnodes\u002Fcore_json-merge","docs\u002F5.nodes\u002Fcore_json-merge",{"title":390,"path":391,"stem":392},"JSON Schema Validate","\u002Fdocs\u002Fnodes\u002Fcore_json-schema-validate","docs\u002F5.nodes\u002Fcore_json-schema-validate",{"title":394,"path":395,"stem":396},"JSONPath Extract","\u002Fdocs\u002Fnodes\u002Fcore_jsonpath-extract","docs\u002F5.nodes\u002Fcore_jsonpath-extract",{"title":398,"path":399,"stem":400},"Log","\u002Fdocs\u002Fnodes\u002Fcore_log","docs\u002F5.nodes\u002Fcore_log",{"title":402,"path":403,"stem":404},"Math","\u002Fdocs\u002Fnodes\u002Fcore_math","docs\u002F5.nodes\u002Fcore_math",{"title":406,"path":407,"stem":408},"Merge","\u002Fdocs\u002Fnodes\u002Fcore_merge","docs\u002F5.nodes\u002Fcore_merge",{"title":410,"path":411,"stem":412},"Metric Counter","\u002Fdocs\u002Fnodes\u002Fcore_metric-counter","docs\u002F5.nodes\u002Fcore_metric-counter",{"title":414,"path":415,"stem":416},"Metric Gauge","\u002Fdocs\u002Fnodes\u002Fcore_metric-gauge","docs\u002F5.nodes\u002Fcore_metric-gauge",{"title":418,"path":419,"stem":420},"Mock","\u002Fdocs\u002Fnodes\u002Fcore_mock","docs\u002F5.nodes\u002Fcore_mock",{"title":422,"path":423,"stem":424},"No-op","\u002Fdocs\u002Fnodes\u002Fcore_noop","docs\u002F5.nodes\u002Fcore_noop",{"title":426,"path":427,"stem":428},"Object Omit","\u002Fdocs\u002Fnodes\u002Fcore_object-omit","docs\u002F5.nodes\u002Fcore_object-omit",{"title":430,"path":431,"stem":432},"Object Pick","\u002Fdocs\u002Fnodes\u002Fcore_object-pick","docs\u002F5.nodes\u002Fcore_object-pick",{"title":434,"path":435,"stem":436},"Parallel Fan-Out","\u002Fdocs\u002Fnodes\u002Fcore_parallel","docs\u002F5.nodes\u002Fcore_parallel",{"title":438,"path":439,"stem":440},"Raise","\u002Fdocs\u002Fnodes\u002Fcore_raise","docs\u002F5.nodes\u002Fcore_raise",{"title":442,"path":443,"stem":444},"Rate Limit Check","\u002Fdocs\u002Fnodes\u002Fcore_rate-limit-check","docs\u002F5.nodes\u002Fcore_rate-limit-check",{"title":446,"path":447,"stem":448},"Regex Extract","\u002Fdocs\u002Fnodes\u002Fcore_regex-extract","docs\u002F5.nodes\u002Fcore_regex-extract",{"title":450,"path":451,"stem":452},"Require Fields","\u002Fdocs\u002Fnodes\u002Fcore_require-fields","docs\u002F5.nodes\u002Fcore_require-fields",{"title":454,"path":455,"stem":456},"Rethrow","\u002Fdocs\u002Fnodes\u002Fcore_rethrow","docs\u002F5.nodes\u002Fcore_rethrow",{"title":458,"path":459,"stem":460},"Return","\u002Fdocs\u002Fnodes\u002Fcore_return","docs\u002F5.nodes\u002Fcore_return",{"title":462,"path":463,"stem":464},"HMAC Signature Verify","\u002Fdocs\u002Fnodes\u002Fcore_signature-verify","docs\u002F5.nodes\u002Fcore_signature-verify",{"title":466,"path":467,"stem":468},"String Op","\u002Fdocs\u002Fnodes\u002Fcore_string-ops","docs\u002F5.nodes\u002Fcore_string-ops",{"title":470,"path":471,"stem":472},"Subflow Invoke","\u002Fdocs\u002Fnodes\u002Fcore_subflow-invoke","docs\u002F5.nodes\u002Fcore_subflow-invoke",{"title":474,"path":475,"stem":476},"Switch","\u002Fdocs\u002Fnodes\u002Fcore_switch","docs\u002F5.nodes\u002Fcore_switch",{"title":478,"path":479,"stem":480},"Template Render","\u002Fdocs\u002Fnodes\u002Fcore_template-render","docs\u002F5.nodes\u002Fcore_template-render",{"title":482,"path":483,"stem":484},"Throttle","\u002Fdocs\u002Fnodes\u002Fcore_throttle","docs\u002F5.nodes\u002Fcore_throttle",{"title":486,"path":487,"stem":488},"Timeout","\u002Fdocs\u002Fnodes\u002Fcore_timeout","docs\u002F5.nodes\u002Fcore_timeout",{"title":490,"path":491,"stem":492},"Type Cast","\u002Fdocs\u002Fnodes\u002Fcore_type-cast","docs\u002F5.nodes\u002Fcore_type-cast",{"title":494,"path":495,"stem":496},"Webhook Respond","\u002Fdocs\u002Fnodes\u002Fcore_webhook-respond","docs\u002F5.nodes\u002Fcore_webhook-respond",{"title":498,"path":499,"stem":500},"File Delete","\u002Fdocs\u002Fnodes\u002Ffilesys_delete","docs\u002F5.nodes\u002Ffilesys_delete",{"title":502,"path":503,"stem":504},"Directory List","\u002Fdocs\u002Fnodes\u002Ffilesys_list","docs\u002F5.nodes\u002Ffilesys_list",{"title":506,"path":507,"stem":508},"File Read","\u002Fdocs\u002Fnodes\u002Ffilesys_read","docs\u002F5.nodes\u002Ffilesys_read",{"title":510,"path":511,"stem":512},"File Stat","\u002Fdocs\u002Fnodes\u002Ffilesys_stat","docs\u002F5.nodes\u002Ffilesys_stat",{"title":514,"path":515,"stem":516},"File Write","\u002Fdocs\u002Fnodes\u002Ffilesys_write","docs\u002F5.nodes\u002Ffilesys_write",{"title":518,"path":519,"stem":520},"FTP Delete","\u002Fdocs\u002Fnodes\u002Fftp_delete","docs\u002F5.nodes\u002Fftp_delete",{"title":522,"path":523,"stem":524},"FTP Download","\u002Fdocs\u002Fnodes\u002Fftp_download","docs\u002F5.nodes\u002Fftp_download",{"title":526,"path":527,"stem":528},"FTP List","\u002Fdocs\u002Fnodes\u002Fftp_list","docs\u002F5.nodes\u002Fftp_list",{"title":530,"path":531,"stem":532},"FTP Upload","\u002Fdocs\u002Fnodes\u002Fftp_upload","docs\u002F5.nodes\u002Fftp_upload",{"title":534,"path":535,"stem":536},"HTTP DELETE","\u002Fdocs\u002Fnodes\u002Fhttp_delete","docs\u002F5.nodes\u002Fhttp_delete",{"title":538,"path":539,"stem":540},"HTTP GET","\u002Fdocs\u002Fnodes\u002Fhttp_get","docs\u002F5.nodes\u002Fhttp_get",{"title":542,"path":543,"stem":544},"HTTP POST","\u002Fdocs\u002Fnodes\u002Fhttp_post","docs\u002F5.nodes\u002Fhttp_post",{"title":546,"path":547,"stem":548},"HTTP PUT","\u002Fdocs\u002Fnodes\u002Fhttp_put","docs\u002F5.nodes\u002Fhttp_put",{"title":550,"path":551,"stem":552},"HTTP Request","\u002Fdocs\u002Fnodes\u002Fhttp_request","docs\u002F5.nodes\u002Fhttp_request",{"title":554,"path":555,"stem":556},"Fill Translations","\u002Fdocs\u002Fnodes\u002Fi18n_fill-translations","docs\u002F5.nodes\u002Fi18n_fill-translations",{"title":558,"path":559,"stem":560},"Resolve Locale","\u002Fdocs\u002Fnodes\u002Fi18n_resolve-locale","docs\u002F5.nodes\u002Fi18n_resolve-locale",{"title":562,"path":563,"stem":564},"Translate","\u002Fdocs\u002Fnodes\u002Fi18n_translate","docs\u002F5.nodes\u002Fi18n_translate",{"title":566,"path":567,"stem":568},"Mongo AGGREGATE","\u002Fdocs\u002Fnodes\u002Fmongoops_aggregate","docs\u002F5.nodes\u002Fmongoops_aggregate",{"title":570,"path":571,"stem":572},"Mongo DELETE","\u002Fdocs\u002Fnodes\u002Fmongoops_delete","docs\u002F5.nodes\u002Fmongoops_delete",{"title":574,"path":575,"stem":576},"Mongo FIND","\u002Fdocs\u002Fnodes\u002Fmongoops_find","docs\u002F5.nodes\u002Fmongoops_find",{"title":578,"path":579,"stem":580},"Mongo FIND ONE","\u002Fdocs\u002Fnodes\u002Fmongoops_find-one","docs\u002F5.nodes\u002Fmongoops_find-one",{"title":582,"path":583,"stem":584},"Mongo INSERT","\u002Fdocs\u002Fnodes\u002Fmongoops_insert","docs\u002F5.nodes\u002Fmongoops_insert",{"title":586,"path":587,"stem":588},"Mongo UPDATE","\u002Fdocs\u002Fnodes\u002Fmongoops_update","docs\u002F5.nodes\u002Fmongoops_update",{"title":590,"path":591,"stem":592},"Postgres COPY FROM","\u002Fdocs\u002Fnodes\u002Fpostgres_copy-from","docs\u002F5.nodes\u002Fpostgres_copy-from",{"title":594,"path":595,"stem":596},"Postgres Exec","\u002Fdocs\u002Fnodes\u002Fpostgres_exec","docs\u002F5.nodes\u002Fpostgres_exec",{"title":598,"path":599,"stem":600},"Postgres Query","\u002Fdocs\u002Fnodes\u002Fpostgres_query","docs\u002F5.nodes\u002Fpostgres_query",{"title":602,"path":603,"stem":604},"Postgres Transaction","\u002Fdocs\u002Fnodes\u002Fpostgres_transaction","docs\u002F5.nodes\u002Fpostgres_transaction",{"title":606,"path":607,"stem":608},"RabbitMQ Consume","\u002Fdocs\u002Fnodes\u002Frabbitmq_consume","docs\u002F5.nodes\u002Frabbitmq_consume",{"title":610,"path":611,"stem":612},"RabbitMQ Declare","\u002Fdocs\u002Fnodes\u002Frabbitmq_declare","docs\u002F5.nodes\u002Frabbitmq_declare",{"title":614,"path":615,"stem":616},"RabbitMQ Publish","\u002Fdocs\u002Fnodes\u002Frabbitmq_publish","docs\u002F5.nodes\u002Frabbitmq_publish",{"title":618,"path":619,"stem":620},"Auth Login","\u002Fdocs\u002Fnodes\u002Fredelay_auth-login","docs\u002F5.nodes\u002Fredelay_auth-login",{"title":622,"path":623,"stem":624},"Require Permission","\u002Fdocs\u002Fnodes\u002Fredelay_auth-require-permission","docs\u002F5.nodes\u002Fredelay_auth-require-permission",{"title":626,"path":627,"stem":628},"Validate Token","\u002Fdocs\u002Fnodes\u002Fredelay_auth-validate-token","docs\u002F5.nodes\u002Fredelay_auth-validate-token",{"title":630,"path":631,"stem":632},"Render Email Template","\u002Fdocs\u002Fnodes\u002Fredelay_email-render-template","docs\u002F5.nodes\u002Fredelay_email-render-template",{"title":634,"path":635,"stem":636},"Send Email","\u002Fdocs\u002Fnodes\u002Fredelay_email-send","docs\u002F5.nodes\u002Fredelay_email-send",{"title":638,"path":639,"stem":640},"Send Templated Email","\u002Fdocs\u002Fnodes\u002Fredelay_email-send-templated","docs\u002F5.nodes\u002Fredelay_email-send-templated",{"title":642,"path":643,"stem":644},"Event Source","\u002Fdocs\u002Fnodes\u002Fredelay_event-source","docs\u002F5.nodes\u002Fredelay_event-source",{"title":646,"path":647,"stem":648},"Create Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-create-group","docs\u002F5.nodes\u002Fredelay_groups-create-group",{"title":650,"path":651,"stem":652},"Delete Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-delete-group","docs\u002F5.nodes\u002Fredelay_groups-delete-group",{"title":654,"path":655,"stem":656},"Resolve Permissions","\u002Fdocs\u002Fnodes\u002Fredelay_groups-resolve-permissions","docs\u002F5.nodes\u002Fredelay_groups-resolve-permissions",{"title":658,"path":659,"stem":660},"Update Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-update-group","docs\u002F5.nodes\u002Fredelay_groups-update-group",{"title":662,"path":663,"stem":664},"HTTP Endpoint","\u002Fdocs\u002Fnodes\u002Fredelay_http-endpoint","docs\u002F5.nodes\u002Fredelay_http-endpoint",{"title":666,"path":667,"stem":668},"JSON Stream Source","\u002Fdocs\u002Fnodes\u002Fredelay_json-stream-source","docs\u002F5.nodes\u002Fredelay_json-stream-source",{"title":670,"path":671,"stem":672},"JSON → Event","\u002Fdocs\u002Fnodes\u002Fredelay_json-to-event","docs\u002F5.nodes\u002Fredelay_json-to-event",{"title":674,"path":675,"stem":676},"Kafka Produce","\u002Fdocs\u002Fnodes\u002Fredelay_kafka-produce","docs\u002F5.nodes\u002Fredelay_kafka-produce",{"title":678,"path":679,"stem":680},"Create Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-create","docs\u002F5.nodes\u002Fredelay_links-create",{"title":682,"path":683,"stem":684},"Delete Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-delete","docs\u002F5.nodes\u002Fredelay_links-delete",{"title":686,"path":687,"stem":688},"Resolve Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-resolve","docs\u002F5.nodes\u002Fredelay_links-resolve",{"title":690,"path":691,"stem":692},"LLM Chat","\u002Fdocs\u002Fnodes\u002Fredelay_llm-chat","docs\u002F5.nodes\u002Fredelay_llm-chat",{"title":694,"path":695,"stem":696},"LLM Embed","\u002Fdocs\u002Fnodes\u002Fredelay_llm-embed","docs\u002F5.nodes\u002Fredelay_llm-embed",{"title":698,"path":699,"stem":700},"LLM Guard","\u002Fdocs\u002Fnodes\u002Fredelay_llm-guard","docs\u002F5.nodes\u002Fredelay_llm-guard",{"title":702,"path":703,"stem":704},"LLM Tool Router","\u002Fdocs\u002Fnodes\u002Fredelay_llm-tool-router","docs\u002F5.nodes\u002Fredelay_llm-tool-router",{"title":706,"path":707,"stem":708},"NATS Publish","\u002Fdocs\u002Fnodes\u002Fredelay_nats-publish","docs\u002F5.nodes\u002Fredelay_nats-publish",{"title":710,"path":711,"stem":712},"Mark Notification Read","\u002Fdocs\u002Fnodes\u002Fredelay_notifications-mark-read","docs\u002F5.nodes\u002Fredelay_notifications-mark-read",{"title":714,"path":715,"stem":716},"Send Notification","\u002Fdocs\u002Fnodes\u002Fredelay_notifications-send","docs\u002F5.nodes\u002Fredelay_notifications-send",{"title":718,"path":719,"stem":720},"Redis Stream XADD","\u002Fdocs\u002Fnodes\u002Fredelay_redis-stream-publish","docs\u002F5.nodes\u002Fredelay_redis-stream-publish",{"title":722,"path":723,"stem":724},"S3 Copy Object","\u002Fdocs\u002Fnodes\u002Fredelay_s3-copy-object","docs\u002F5.nodes\u002Fredelay_s3-copy-object",{"title":726,"path":727,"stem":728},"S3 Delete Many","\u002Fdocs\u002Fnodes\u002Fredelay_s3-delete-many","docs\u002F5.nodes\u002Fredelay_s3-delete-many",{"title":730,"path":731,"stem":732},"S3 Head Object","\u002Fdocs\u002Fnodes\u002Fredelay_s3-head-object","docs\u002F5.nodes\u002Fredelay_s3-head-object",{"title":734,"path":735,"stem":736},"S3 List Objects","\u002Fdocs\u002Fnodes\u002Fredelay_s3-list-objects","docs\u002F5.nodes\u002Fredelay_s3-list-objects",{"title":738,"path":739,"stem":740},"Create Schedule","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-create-schedule","docs\u002F5.nodes\u002Fredelay_scheduler-create-schedule",{"title":742,"path":743,"stem":744},"Cron Trigger","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-cron-trigger","docs\u002F5.nodes\u002Fredelay_scheduler-cron-trigger",{"title":746,"path":747,"stem":748},"Delete Schedule","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-delete-schedule","docs\u002F5.nodes\u002Fredelay_scheduler-delete-schedule",{"title":750,"path":751,"stem":752},"Interval Trigger","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-interval-trigger","docs\u002F5.nodes\u002Fredelay_scheduler-interval-trigger",{"title":754,"path":755,"stem":756},"Search Query","\u002Fdocs\u002Fnodes\u002Fredelay_search-query","docs\u002F5.nodes\u002Fredelay_search-query",{"title":758,"path":759,"stem":760},"Search Upsert","\u002Fdocs\u002Fnodes\u002Fredelay_search-upsert","docs\u002F5.nodes\u002Fredelay_search-upsert",{"title":762,"path":763,"stem":764},"Delete Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-delete","docs\u002F5.nodes\u002Fredelay_settings-delete",{"title":766,"path":767,"stem":768},"Get Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-get","docs\u002F5.nodes\u002Fredelay_settings-get",{"title":770,"path":771,"stem":772},"Set Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-set","docs\u002F5.nodes\u002Fredelay_settings-set",{"title":774,"path":775,"stem":776},"Delete File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-delete","docs\u002F5.nodes\u002Fredelay_storage-delete",{"title":778,"path":779,"stem":780},"Download File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-download","docs\u002F5.nodes\u002Fredelay_storage-download",{"title":782,"path":783,"stem":784},"Generate Presigned URL","\u002Fdocs\u002Fnodes\u002Fredelay_storage-presign-url","docs\u002F5.nodes\u002Fredelay_storage-presign-url",{"title":786,"path":787,"stem":788},"Upload File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-upload","docs\u002F5.nodes\u002Fredelay_storage-upload",{"title":790,"path":791,"stem":792},"Subworkflow","\u002Fdocs\u002Fnodes\u002Fredelay_subworkflow","docs\u002F5.nodes\u002Fredelay_subworkflow",{"title":794,"path":795,"stem":796},"Activate User","\u002Fdocs\u002Fnodes\u002Fredelay_users-activate","docs\u002F5.nodes\u002Fredelay_users-activate",{"title":798,"path":799,"stem":800},"Create Pending User","\u002Fdocs\u002Fnodes\u002Fredelay_users-create-pending","docs\u002F5.nodes\u002Fredelay_users-create-pending",{"title":802,"path":803,"stem":804},"Create User","\u002Fdocs\u002Fnodes\u002Fredelay_users-create-user","docs\u002F5.nodes\u002Fredelay_users-create-user",{"title":806,"path":807,"stem":808},"Delete User","\u002Fdocs\u002Fnodes\u002Fredelay_users-delete-user","docs\u002F5.nodes\u002Fredelay_users-delete-user",{"title":810,"path":811,"stem":812},"Find User By Email","\u002Fdocs\u002Fnodes\u002Fredelay_users-find-by-email","docs\u002F5.nodes\u002Fredelay_users-find-by-email",{"title":814,"path":815,"stem":816},"Get User","\u002Fdocs\u002Fnodes\u002Fredelay_users-get-user","docs\u002F5.nodes\u002Fredelay_users-get-user",{"title":818,"path":819,"stem":820},"List Users","\u002Fdocs\u002Fnodes\u002Fredelay_users-list-users","docs\u002F5.nodes\u002Fredelay_users-list-users",{"title":822,"path":823,"stem":824},"Update User","\u002Fdocs\u002Fnodes\u002Fredelay_users-update-user","docs\u002F5.nodes\u002Fredelay_users-update-user",{"title":826,"path":827,"stem":828},"Request Email Verification (legacy)","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request","docs\u002F5.nodes\u002Fredelay_verification-request",{"title":830,"path":831,"stem":832},"Request Email Verification","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request-email","docs\u002F5.nodes\u002Fredelay_verification-request-email",{"title":834,"path":835,"stem":836},"Request SMS Verification","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request-sms","docs\u002F5.nodes\u002Fredelay_verification-request-sms",{"title":838,"path":839,"stem":840},"Verify Token","\u002Fdocs\u002Fnodes\u002Fredelay_verification-verify","docs\u002F5.nodes\u002Fredelay_verification-verify",{"title":842,"path":843,"stem":844},"Redis DEL","\u002Fdocs\u002Fnodes\u002Fredisops_del","docs\u002F5.nodes\u002Fredisops_del",{"title":846,"path":847,"stem":848},"Redis EXPIRE","\u002Fdocs\u002Fnodes\u002Fredisops_expire","docs\u002F5.nodes\u002Fredisops_expire",{"title":850,"path":851,"stem":852},"Redis GET","\u002Fdocs\u002Fnodes\u002Fredisops_get","docs\u002F5.nodes\u002Fredisops_get",{"title":854,"path":855,"stem":856},"Redis HGET","\u002Fdocs\u002Fnodes\u002Fredisops_hget","docs\u002F5.nodes\u002Fredisops_hget",{"title":858,"path":859,"stem":860},"Redis HSET","\u002Fdocs\u002Fnodes\u002Fredisops_hset","docs\u002F5.nodes\u002Fredisops_hset",{"title":862,"path":863,"stem":864},"Redis INCR","\u002Fdocs\u002Fnodes\u002Fredisops_incr","docs\u002F5.nodes\u002Fredisops_incr",{"title":866,"path":867,"stem":868},"Redis LPOP","\u002Fdocs\u002Fnodes\u002Fredisops_lpop","docs\u002F5.nodes\u002Fredisops_lpop",{"title":870,"path":871,"stem":872},"Redis LPUSH","\u002Fdocs\u002Fnodes\u002Fredisops_lpush","docs\u002F5.nodes\u002Fredisops_lpush",{"title":874,"path":875,"stem":876},"Redis PUBLISH","\u002Fdocs\u002Fnodes\u002Fredisops_publish","docs\u002F5.nodes\u002Fredisops_publish",{"title":878,"path":879,"stem":880},"Redis SET","\u002Fdocs\u002Fnodes\u002Fredisops_set","docs\u002F5.nodes\u002Fredisops_set",{"title":882,"path":883,"stem":884},"SFTP Download","\u002Fdocs\u002Fnodes\u002Fsftp_download","docs\u002F5.nodes\u002Fsftp_download",{"title":886,"path":887,"stem":888},"SFTP Upload","\u002Fdocs\u002Fnodes\u002Fsftp_upload","docs\u002F5.nodes\u002Fsftp_upload",{"title":890,"path":891,"stem":892},"Send SMS","\u002Fdocs\u002Fnodes\u002Fsms_send","docs\u002F5.nodes\u002Fsms_send",{"title":894,"path":895,"stem":896,"children":897,"page":62},"Rfcs","\u002Fdocs\u002Frfcs","docs\u002F6.rfcs",[898],{"title":899,"path":900,"stem":901},"RFC 0001 — Worker Assignment for Flow Deployments","\u002Fdocs\u002Frfcs\u002F0001-worker-assignment","docs\u002F6.rfcs\u002F0001-worker-assignment",{"title":903,"path":904,"stem":905,"children":906,"page":62},"Admin","\u002Fdocs\u002Fadmin","docs\u002F7.admin",[907,912,917,922,927,932,937,941,946],{"title":5,"path":908,"stem":909,"order":910,"icon":911},"\u002Fdocs\u002Fadmin\u002Foverview","docs\u002F7.admin\u002F00.overview",0,"i-lucide-compass",{"title":913,"path":914,"stem":915,"order":10,"icon":916},"Getting started","\u002Fdocs\u002Fadmin\u002Fgetting-started","docs\u002F7.admin\u002F01.getting-started","i-lucide-play",{"title":918,"path":919,"stem":920,"order":16,"icon":921},"Layered architecture","\u002Fdocs\u002Fadmin\u002Flayered-architecture","docs\u002F7.admin\u002F02.layered-architecture","i-lucide-layers",{"title":923,"path":924,"stem":925,"order":22,"icon":926},"Building a custom admin project","\u002Fdocs\u002Fadmin\u002Fbuilding-a-custom-admin-project","docs\u002F7.admin\u002F03.building-a-custom-admin-project","i-lucide-folder-plus",{"title":928,"path":929,"stem":930,"order":51,"icon":931},"Building admin modules","\u002Fdocs\u002Fadmin\u002Fbuilding-admin-modules","docs\u002F7.admin\u002F04.building-admin-modules","i-lucide-package-plus",{"title":933,"path":934,"stem":935,"order":57,"icon":936},"Base module reference","\u002Fdocs\u002Fadmin\u002Freference-base-modules","docs\u002F7.admin\u002F05.reference-base-modules","i-lucide-boxes",{"title":938,"path":939,"stem":940,"order":102,"icon":17},"Deployment","\u002Fdocs\u002Fadmin\u002Fdeployment","docs\u002F7.admin\u002F06.deployment",{"title":942,"path":943,"stem":944,"order":108,"icon":945},"Consuming the base layer (versioned)","\u002Fdocs\u002Fadmin\u002Fconsuming-the-base-layer","docs\u002F7.admin\u002F07.consuming-the-base-layer","i-lucide-package",{"title":947,"path":948,"stem":949,"order":108},"Admin UI","\u002Fdocs\u002Fadmin\u002F_dir","docs\u002F7.admin\u002F_dir",{"title":951,"path":952,"stem":953,"children":954,"page":62},"Services","\u002Fdocs\u002Fservices","docs\u002F8.services",[955,960],{"title":956,"path":957,"stem":958,"order":10,"icon":959},"Custom development & support services","\u002Fdocs\u002Fservices\u002Fcustom-development","docs\u002F8.services\u002F1.custom-development","i-lucide-briefcase",{"title":951,"path":961,"stem":962,"icon":959,"order":963},"\u002Fdocs\u002Fservices\u002F_dir","docs\u002F8.services\u002F_dir",80,{"title":965,"path":966,"stem":967,"order":910},"Dir","\u002Fdocs\u002F_dir","docs\u002F_dir",{"id":969,"title":279,"body":970,"description":2996,"extension":2997,"links":2998,"meta":2999,"navigation":3000,"path":280,"seo":3001,"stem":281,"__hash__":3002},"docs\u002Fdocs\u002F4.reference\u002F27.localized-content.md",{"type":971,"value":972,"toc":2970},"minimark",[973,977,997,1032,1035,1038,1042,1049,1089,1096,1101,1123,1133,1146,1157,1175,1183,1189,1200,1204,1256,1271,1275,1285,1307,1318,1320,1323,1329,1385,1388,1398,1408,1437,1452,1462,1489,1495,1499,1505,1549,1553,1563,1570,1576,1596,1603,1606,1610,1621,1628,1689,1695,1701,1712,1741,1747,1759,1770,1780,1789,1804,1806,1810,1820,1823,1826,1851,1854,1959,1965,2007,2017,2029,2041,2051,2057,2060,2062,2066,2069,2149,2168,2172,2183,2190,2256,2262,2268,2271,2275,2280,2283,2301,2311,2333,2339,2343,2355,2369,2372,2549,2568,2576,2625,2645,2652,2735,2739,2752,2759,2765,2769,2772,2877,2880,2883,2887,2897,2911,2917,2927,2940,2957,2966],[974,975,279],"h1",{"id":976},"localized-content",[978,979,980,981,985,986,985,989,992,993,996],"p",{},"Three capabilities that only make sense together, and that span four modules —\n",[982,983,984],"code",{},"go-framework\u002Fmodules\u002Fsettings",", ",[982,987,988],{},"go-modules\u002Fcontent",[982,990,991],{},"go-modules\u002Fi18n"," and\n",[982,994,995],{},"go-modules\u002Flegalpages",":",[998,999,1000,1008,1014,1020,1026],"ol",{},[1001,1002,1003,1007],"li",{},[1004,1005,1006],"strong",{},"Settings tokens"," — content holds the prose, settings hold the facts.",[1001,1009,1010,1013],{},[1004,1011,1012],{},"Localized settings"," — for the facts that are themselves prose.",[1001,1015,1016,1019],{},[1004,1017,1018],{},"A shared translation lifecycle"," — is this ready, has it gone stale, does\nsomeone want it redone.",[1001,1021,1022,1025],{},[1004,1023,1024],{},"Localized fragments"," — reusable content blocks, per language.",[1001,1027,1028,1031],{},[1004,1029,1030],{},"Two CLIs"," that fill all of it without an admin session.",[978,1033,1034],{},"The problem they solve as a set: a privacy policy translated into 25 languages\nhas the operator's registered address baked into all 25. Change the address and\nevery translation is wrong, so either you retranslate 25 documents or you ship\n25 documents with the old address in them.",[1036,1037],"hr",{},[1039,1040,1006],"h2",{"id":1041},"settings-tokens",[978,1043,1044,1045,1048],{},"Any string served by the content module or the i18n catalogue may contain\n",[982,1046,1047],{},"[[ module.key ]]",", which is replaced at read time with a public setting.",[1050,1051,1056],"pre",{"className":1052,"code":1053,"language":1054,"meta":1055,"style":1055},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cp>The controller of your personal data is [[ legalpages.entity_name ]],\n   [[ legalpages.entity_form ]], VAT [[ legalpages.tax_id ]].\u003C\u002Fp>\n","html","",[982,1057,1058,1076],{"__ignoreMap":1055},[1059,1060,1062,1066,1069,1072],"span",{"class":1061,"line":10},"line",[1059,1063,1065],{"class":1064},"sMK4o","\u003C",[1059,1067,978],{"class":1068},"swJcz",[1059,1070,1071],{"class":1064},">",[1059,1073,1075],{"class":1074},"sTEyZ","The controller of your personal data is [[ legalpages.entity_name ]],\n",[1059,1077,1078,1081,1084,1086],{"class":1061,"line":16},[1059,1079,1080],{"class":1074},"   [[ legalpages.entity_form ]], VAT [[ legalpages.tax_id ]].",[1059,1082,1083],{"class":1064},"\u003C\u002F",[1059,1085,978],{"class":1068},[1059,1087,1088],{"class":1064},">\n",[978,1090,1091,1092,1095],{},"Stored once, resolved per request. Changing the registered address in the admin\ntakes effect on the next read, ",[1004,1093,1094],{},"in every language",", with no retranslation —\nbecause the token is what every translation contains.",[1097,1098,1100],"h3",{"id":1099},"three-rules-all-load-bearing","Three rules, all load-bearing",[978,1102,1103,1106,1107,1110,1111,1114,1115,1118,1119,1122],{},[1004,1104,1105],{},"1. Default deny."," Only fields whose schema says ",[982,1108,1109],{},"public: true"," and not\n",[982,1112,1113],{},"sensitive: true"," expand — the same test ",[982,1116,1117],{},"GET \u002Fapi\u002Fsettings\u002F{module}\u002F{key}","\napplies. Without it, any admin-editable string becomes an exfiltration\nprimitive: type ",[982,1120,1121],{},"[[ email.smtp_password ]]"," into a CMS fragment and the server\nrenders the secret onto a public page. A nil reader expands nothing, so an app\nthat never wired the settings module fails closed.",[978,1124,1125,1128,1129,1132],{},[1004,1126,1127],{},"2. Unresolved tokens survive verbatim."," A typo, a private field or an unset\nvalue leaves ",[982,1130,1131],{},"[[ legalpages.entity_nme ]]"," visible in the output. The\nalternative — substituting empty — silently deletes a clause from a legal\ndocument, and nobody reviewing the page can see that it happened. An empty\nsetting value is treated as unresolved for the same reason.",[978,1134,1135,1138,1139,1142,1143,1145],{},[1004,1136,1137],{},"3. No recursion."," Values are never rescanned. Otherwise one level of\nindirection defeats rule 1: set a public ",[982,1140,1141],{},"legalpages.site_name"," to the literal\ntext ",[982,1144,1121],{}," and a second pass would resolve it.",[1097,1147,1149,1150,1153,1154],{"id":1148},"why-and-not","Why ",[982,1151,1152],{},"[[ ]]"," and not ",[982,1155,1156],{},"{{ }}",[978,1158,1159,1160,1163,1164,1167,1168,1171,1172,1174],{},"i18n messages are run through Go ",[982,1161,1162],{},"text\u002Ftemplate"," for their parameters\n(",[982,1165,1166],{},"{{.name}}","). A settings token sharing those delimiters does not merely fail to\nexpand — it fails to ",[1004,1169,1170],{},"parse",", and ",[982,1173,1162],{}," abandons the whole template,\nso the message loses its parameters too:",[1050,1176,1181],{"className":1177,"code":1179,"language":1180},[1178],"language-text","\"{{ legalpages.entity_name }} greets {{.name}}\"   →  unchanged, both broken\n\"[[ legalpages.entity_name ]] greets {{.name}}\"   →  \"IHAR FINCHUK greets Ihar\"\n","text",[982,1182,1179],{"__ignoreMap":1055},[978,1184,1185,1186,1188],{},"Vue owns the same delimiters on the frontend. ",[982,1187,1152],{}," collides with neither.",[978,1190,1191,1192,1195,1196,1199],{},"Ordering matters: interpolation runs ",[1004,1193,1194],{},"first",", expansion second, so a settings\nvalue that happens to contain ",[982,1197,1198],{},"{{"," is inserted as literal text rather than\nexecuted as a template.",[1097,1201,1203],{"id":1202},"two-variants","Two variants",[1205,1206,1207,1223],"table",{},[1208,1209,1210],"thead",{},[1211,1212,1213,1217,1220],"tr",{},[1214,1215,1216],"th",{},"Call",[1214,1218,1219],{},"Use for",[1214,1221,1222],{},"Escaping",[1224,1225,1226,1240],"tbody",{},[1211,1227,1228,1234,1237],{},[1229,1230,1231],"td",{},[982,1232,1233],{},"modules.ExpandSettings(ctx, r, s)",[1229,1235,1236],{},"plain text — titles, SEO meta, i18n messages",[1229,1238,1239],{},"none",[1211,1241,1242,1247,1253],{},[1229,1243,1244],{},[982,1245,1246],{},"modules.ExpandSettingsHTML(ctx, r, s)",[1229,1248,1249,1250],{},"rich HTML rendered with ",[982,1251,1252],{},"v-html",[1229,1254,1255],{},"values escaped, surrounding markup untouched",[978,1257,1258,1259,1262,1263,1266,1267,1270],{},"Escaping both would double-encode: a company name containing ",[982,1260,1261],{},"&"," would reach\nthe reader as ",[982,1264,1265],{},"&amp;"," in an ",[982,1268,1269],{},"\u003Ch1>"," that Vue already escapes.",[1097,1272,1274],{"id":1273},"wiring","Wiring",[978,1276,1277,1278,1281,1282,996],{},"The expander takes a ",[982,1279,1280],{},"modules.PublicSettingsReader",", discovered optionally in\n",[982,1283,1284],{},"Configure()",[1050,1286,1290],{"className":1287,"code":1288,"language":1289,"meta":1055,"style":1055},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","if r, ok := registry.Get(\"settings\").(modules.PublicSettingsReader); ok {\n    svc.SetTokenSource(r)\n}\n","go",[982,1291,1292,1297,1302],{"__ignoreMap":1055},[1059,1293,1294],{"class":1061,"line":10},[1059,1295,1296],{},"if r, ok := registry.Get(\"settings\").(modules.PublicSettingsReader); ok {\n",[1059,1298,1299],{"class":1061,"line":16},[1059,1300,1301],{},"    svc.SetTokenSource(r)\n",[1059,1303,1304],{"class":1061,"line":22},[1059,1305,1306],{},"}\n",[978,1308,1309,1310,1313,1314,1317],{},"Deliberately a separate interface from ",[982,1311,1312],{},"SettingsReader",". ",[982,1315,1316],{},"GetSetting"," answers\n\"what is this value\", which is right for a module reading its own config and\nwrong for rendering into a page — it will happily return an SMTP password. A\ndistinct interface makes the safe path the only path an expander can take.",[1036,1319],{},[1039,1321,1012],{"id":1322},"localized-settings",[978,1324,1325,1326,996],{},"A settings field may hold one value ",[1004,1327,1328],{},"per language",[1050,1330,1334],{"className":1331,"code":1332,"language":1333,"meta":1055,"style":1055},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","- key: entity_form\n  type: string\n  public: true\n  localized: true      # \"a sole trader registered in Poland\" is a SENTENCE\n","yaml",[982,1335,1336,1350,1360,1371],{"__ignoreMap":1055},[1059,1337,1338,1341,1344,1346],{"class":1061,"line":10},[1059,1339,1340],{"class":1064},"-",[1059,1342,1343],{"class":1068}," key",[1059,1345,996],{"class":1064},[1059,1347,1349],{"class":1348},"sfazB"," entity_form\n",[1059,1351,1352,1355,1357],{"class":1061,"line":16},[1059,1353,1354],{"class":1068},"  type",[1059,1356,996],{"class":1064},[1059,1358,1359],{"class":1348}," string\n",[1059,1361,1362,1365,1367],{"class":1061,"line":22},[1059,1363,1364],{"class":1068},"  public",[1059,1366,996],{"class":1064},[1059,1368,1370],{"class":1369},"sfNiH"," true\n",[1059,1372,1373,1376,1378,1381],{"class":1061,"line":51},[1059,1374,1375],{"class":1068},"  localized",[1059,1377,996],{"class":1064},[1059,1379,1380],{"class":1369}," true",[1059,1382,1384],{"class":1383},"sHwdD","      # \"a sole trader registered in Poland\" is a SENTENCE\n",[978,1386,1387],{},"Declared per field, and that is the whole point. A registered address, a VAT\nnumber and a contact email are the same characters in every language and must\nnever be translated; only prose that is read inside a sentence should be. A\nblanket \"translate any setting that appears in prose\" would eventually put a tax\nnumber through a language model.",[978,1389,1390,1393,1394,1397],{},[982,1391,1392],{},"Sensitive"," + ",[982,1395,1396],{},"Localized"," is refused at boot: a secret has no translations, and\nstoring one per locale multiplies the places it can leak from.",[978,1399,1400,1403,1404,1407],{},[1004,1401,1402],{},"Resolution"," is the same chain ",[982,1405,1406],{},"pick()"," uses — requested locale, then the\nsource, then any non-empty value. The last step matters: a deployment that has\nonly ever filled Polish should render Polish, because an empty answer deletes\nthe clause the token sits in.",[978,1409,1410,1413,1414,1417,1418,1420,1421,1424,1425,1428,1429,1432,1433,1436],{},[1004,1411,1412],{},"The locale seam."," ",[982,1415,1416],{},"go-framework"," cannot import ",[982,1419,991],{},", so it owns\nonly a key and a string — ",[982,1422,1423],{},"modules.WithLocale"," \u002F ",[982,1426,1427],{},"modules.LocaleFromContext"," —\nexactly the shape of the channel seam beside it. i18n's own ",[982,1430,1431],{},"WithLocale"," writes\nboth. ",[982,1434,1435],{},"\"\""," is a normal answer, not an error: a CLI, a queue consumer and a\nsingle-language deployment all return it, and it means \"render the source\".",[1050,1438,1440],{"className":1287,"code":1439,"language":1289,"meta":1055,"style":1055},"\u002F\u002F Anything that needs the reader's language, without depending on i18n.\nlocale := modules.LocaleFromContext(ctx)\n",[982,1441,1442,1447],{"__ignoreMap":1055},[1059,1443,1444],{"class":1061,"line":10},[1059,1445,1446],{},"\u002F\u002F Anything that needs the reader's language, without depending on i18n.\n",[1059,1448,1449],{"class":1061,"line":16},[1059,1450,1451],{},"locale := modules.LocaleFromContext(ctx)\n",[978,1453,1454,1457,1458,1461],{},[1004,1455,1456],{},"Storage is a locale→string map",", and a plain string is still accepted — which\nis what every existing row holds, so switching a field to ",[982,1459,1460],{},"localized"," needs no\nmigration. Two shape traps, both of which shipped as bugs before being fixed:",[1463,1464,1465,1476],"ul",{},[1001,1466,1467,1468,1471,1472,1475],{},"A nested document round-trips out of Mongo as ",[982,1469,1470],{},"bson.D",", not ",[982,1473,1474],{},"map[string]any",".\nA type switch that handles only the map matches nothing and leaves the token\nunresolved — in the SOURCE language too, so declaring a field localized breaks\nthe value that worked before.",[1001,1477,1478,1480,1481,1484,1485,1488],{},[982,1479,1470],{}," marshals to JSON as ",[982,1482,1483],{},"[{\"Key\":…,\"Value\":…}]"," — an array. An admin read\nthat returns it raw gives the editor a list, and it renders\n",[982,1486,1487],{},"[object Object],[object Object]",". Admin reads project it to an object;\npublic reads pick one string.",[978,1490,1491,1494],{},[1004,1492,1493],{},"In the admin",", a localized field renders a default plus sparse per-locale\noverrides: a chip per language, green where an override exists, and clearing one\nfalls back to the default rather than blanking. If the flag is missing from a UI\nthat predates it — a real risk when the admin layer is versioned separately from\nthe API — the string input detects the object and says so instead of stringifying\nit.",[1039,1496,1498],{"id":1497},"translation-lifecycle","Translation lifecycle",[978,1500,1501,1504],{},[982,1502,1503],{},"crud.TranslationState"," is an embeddable struct, so a page, a fragment and a\ndomain record all answer the same three questions the same way:",[1050,1506,1508],{"className":1287,"code":1507,"language":1289,"meta":1055,"style":1055},"type TranslationState struct {\n    TranslationReady bool              \u002F\u002F has anyone said the source text is finished?\n    SourceHash       string            \u002F\u002F what did the translator last work from?\n    Retranslate      bool              \u002F\u002F one-shot \"do it again\"\n    Units            []TranslationUnit \u002F\u002F per-leaf: hash + which locales are current\n    ReviewRequired   bool              \u002F\u002F hold machine output back from the live record\n    Pending          []PendingTranslation\n}\n",[982,1509,1510,1515,1520,1525,1530,1535,1540,1545],{"__ignoreMap":1055},[1059,1511,1512],{"class":1061,"line":10},[1059,1513,1514],{},"type TranslationState struct {\n",[1059,1516,1517],{"class":1061,"line":16},[1059,1518,1519],{},"    TranslationReady bool              \u002F\u002F has anyone said the source text is finished?\n",[1059,1521,1522],{"class":1061,"line":22},[1059,1523,1524],{},"    SourceHash       string            \u002F\u002F what did the translator last work from?\n",[1059,1526,1527],{"class":1061,"line":51},[1059,1528,1529],{},"    Retranslate      bool              \u002F\u002F one-shot \"do it again\"\n",[1059,1531,1532],{"class":1061,"line":57},[1059,1533,1534],{},"    Units            []TranslationUnit \u002F\u002F per-leaf: hash + which locales are current\n",[1059,1536,1537],{"class":1061,"line":102},[1059,1538,1539],{},"    ReviewRequired   bool              \u002F\u002F hold machine output back from the live record\n",[1059,1541,1542],{"class":1061,"line":108},[1059,1543,1544],{},"    Pending          []PendingTranslation\n",[1059,1546,1547],{"class":1061,"line":114},[1059,1548,1306],{},[1097,1550,1552],{"id":1551},"staleness-is-per-unit-of-work-not-per-record","Staleness is per unit of work, not per record",[978,1554,1555,1558,1559,1562],{},[982,1556,1557],{},"SourceHash"," alone answers \"has anything moved\", which is the wrong question to\nbill against. A fragment holds a dozen independent blocks of prose; fixing a\ntypo in one moved the record hash, so ",[1004,1560,1561],{},"every"," block was retranslated into\nevery language. On an eleven-leaf fragment across 24 locales that was 263\ntranslations replacing text that was already correct.",[978,1564,1565,1566,1569],{},"The cost is the smaller half. Re-rolling a good translation can make it worse:\nasked three times for the same paragraph, a model returned three different\nfaults, one of which changed the meaning (\"every logged session\" became \"every\nlogged ",[1004,1567,1568],{},"set","\"). Retranslating what did not change is not a neutral waste — it\nis a way to lose work that was already right.",[978,1571,1572,1573,996],{},"So each unit — a leaf path for a fragment, a field for a page — records the\nhash it was translated from and which locales are current ",[1004,1574,1575],{},"at that hash",[1050,1577,1579],{"className":1287,"code":1578,"language":1289,"meta":1055,"style":1055},"st.MarkUnitTranslated(\"hero_lead\", hash, \"ru\", \"de\")\nst.UnitCurrent(\"hero_lead\", hash, \"ru\")   \u002F\u002F false once the English moves\nst.UnitNeedsWork(\"hero_lead\", hash, \"sk\") \u002F\u002F not current AND not awaiting review\n",[982,1580,1581,1586,1591],{"__ignoreMap":1055},[1059,1582,1583],{"class":1061,"line":10},[1059,1584,1585],{},"st.MarkUnitTranslated(\"hero_lead\", hash, \"ru\", \"de\")\n",[1059,1587,1588],{"class":1061,"line":16},[1059,1589,1590],{},"st.UnitCurrent(\"hero_lead\", hash, \"ru\")   \u002F\u002F false once the English moves\n",[1059,1592,1593],{"class":1061,"line":22},[1059,1594,1595],{},"st.UnitNeedsWork(\"hero_lead\", hash, \"sk\") \u002F\u002F not current AND not awaiting review\n",[978,1597,1598,1599,1602],{},"Partial success then has somewhere to live. Twenty locales landing and four\nfailing leaves twenty recorded and four to retry, where a record-level hash\nforced a choice between losing the four and redoing the twenty. A changed hash\n",[1004,1600,1601],{},"replaces"," the locale list rather than merging into it, or translations made\nfrom text that no longer exists would be marked current.",[978,1604,1605],{},"Records written before this field exists have no units, so existing text is\ntrusted and adopted on the next run that touches the leaf — otherwise the first\nrun after upgrading would retranslate the entire corpus.",[1097,1607,1609],{"id":1608},"review-gate-translated-but-not-served","Review gate — translated, but not served",[978,1611,1612,1613,1616,1617,1620],{},"Set ",[982,1614,1615],{},"ReviewRequired"," on anything whose wording is a commitment rather than copy.\n",[982,1618,1619],{},"legalpages"," sets it on every page it seeds: those state a GDPR one-month\nresponse deadline and cite Articles 11 and 12 DSA, and a machine rendering of\neither reaches readers in two dozen languages most teams cannot read.",[978,1622,1623,1624,1627],{},"Translations for such a record are produced and held in ",[982,1625,1626],{},"Pending","; the English\n(or an older approved translation) keeps rendering. Three admin endpoints work\nthe queue:",[1205,1629,1630,1643],{},[1208,1631,1632],{},[1211,1633,1634,1637,1640],{},[1214,1635,1636],{},"Method",[1214,1638,1639],{},"Path",[1214,1641,1642],{},"Purpose",[1224,1644,1645,1658,1677],{},[1211,1646,1647,1650,1655],{},[1229,1648,1649],{},"GET",[1229,1651,1652],{},[982,1653,1654],{},"\u002Fadmin\u002Fcontent\u002Ftranslations\u002Fpending",[1229,1656,1657],{},"What is waiting, with the English beside it and what readers currently see",[1211,1659,1660,1663,1668],{},[1229,1661,1662],{},"POST",[1229,1664,1665],{},[982,1666,1667],{},"\u002Fadmin\u002Fcontent\u002Ftranslations\u002Fapprove",[1229,1669,1670,1671,1171,1673,1676],{},"Publish one — optionally with edited ",[982,1672,1180],{},[982,1674,1675],{},"force"," to override staleness",[1211,1678,1679,1681,1686],{},[1229,1680,1662],{},[1229,1682,1683],{},[982,1684,1685],{},"\u002Fadmin\u002Fcontent\u002Ftranslations\u002Freject",[1229,1687,1688],{},"Discard, so the next run produces a fresh one",[978,1690,1691,1694],{},[1004,1692,1693],{},"Approve takes a text override on purpose."," The most valuable thing a reviewer\ndoes is fix a translation rather than reject it; without an edit field the only\nchoices are publish-it-wrong or spin the wheel again. Approving refuses by\ndefault when the English has moved since the translation was made — that is\ntext describing a superseded document.",[978,1696,1697,1700],{},[982,1698,1699],{},"UnitPending"," is what stops the queue costing money on a loop: a record awaiting\napproval is never \"current\", so without it every sweep retranslates it and\nreplaces the pending entry with an equivalent one, billing each time for a page\nnobody has looked at.",[978,1702,1703,1704,1707,1708,1711],{},"The admin surface is ",[1004,1705,1706],{},"Content → Translation review"," (",[982,1709,1710],{},"@redelay\u002Fjs-admin","\nconsumers get it from the project's content module).",[1050,1713,1715],{"className":1287,"code":1714,"language":1289,"meta":1055,"style":1055},"type Page struct {\n    crud.BaseModel        `bson:\",inline\"`\n    crud.TranslationState `bson:\",inline\"`\n    \u002F\u002F …\n}\n",[982,1716,1717,1722,1727,1732,1737],{"__ignoreMap":1055},[1059,1718,1719],{"class":1061,"line":10},[1059,1720,1721],{},"type Page struct {\n",[1059,1723,1724],{"class":1061,"line":16},[1059,1725,1726],{},"    crud.BaseModel        `bson:\",inline\"`\n",[1059,1728,1729],{"class":1061,"line":22},[1059,1730,1731],{},"    crud.TranslationState `bson:\",inline\"`\n",[1059,1733,1734],{"class":1061,"line":51},[1059,1735,1736],{},"    \u002F\u002F …\n",[1059,1738,1739],{"class":1061,"line":57},[1059,1740,1306],{},[978,1742,1743,1746],{},[1004,1744,1745],{},"Ready gate."," Off by default. A draft fanned out to 25 languages has to be\nredone the moment it is finished, so the default that costs nothing waits.\nBulk translators skip anything not marked ready — and should say so, because\n\"0 translated\" with no explanation is indistinguishable from a broken\ntranslator.",[978,1748,1749,1413,1752,1754,1755,1758],{},[1004,1750,1751],{},"Stale detection.",[982,1753,1557],{}," records the source the last run worked from.\n",[982,1756,1757],{},"IsStale(currentHash)"," is true when the English has changed since. Nobody has\nto remember to flag it.",[978,1760,1761,1764,1765,1769],{},[1004,1762,1763],{},"Retranslate."," For when the English has ",[1766,1767,1768],"em",{},"not"," changed but a translation is\nwrong. Cleared once a translator has acted.",[978,1771,1772,1773,1776,1777,1779],{},"Only ",[982,1774,1775],{},"MarkTranslated"," may write ",[982,1778,1557],{}," — an admin write that could set it\nwould let someone mark a page fresh with nothing translated, silently hiding\nevery stale locale.",[978,1781,1782,1413,1785,1788],{},[982,1783,1784],{},"crud.HashSource(parts...)",[1004,1786,1787],{},"sorts"," its inputs, so a hash computed over a Go\nmap is stable. A walk-order-dependent hash differs on every call and marks\neverything permanently stale, which reads as a broken indicator and gets\nignored.",[978,1790,1791,1792,1795,1796,1799,1800,1803],{},"Hash the ",[1004,1793,1794],{},"source"," fields only. A page hashes ",[982,1797,1798],{},"title[\"en\"]"," and ",[982,1801,1802],{},"body[\"en\"]","\nand deliberately excludes SEO: a reworded meta description should not trigger\n24 retranslations of a privacy policy.",[1036,1805],{},[1097,1807,1809],{"id":1808},"a-date-stamp-is-not-a-revision","A date stamp is not a revision",[978,1811,1812,1813,1815,1816,1819],{},"A module that seeds prose and drops translations when its English changes must\nnot let a rendered timestamp count as a change. ",[982,1814,1619],{}," opens every\ntemplate with ",[982,1817,1818],{},"Last updated {{.Updated}}",", filled from the clock at seed time,\nand compared whole bodies to decide whether the source had moved — so on the\nfirst boot of any new day, every page \"changed\", every translated body was\ndropped, and the document restamped itself as revised today.",[978,1821,1822],{},"Two failures for the price of one: 24 translations lost per page, and a legal\ndocument claiming a revision that never happened. Compare the prose with the\nvolatile part excluded, and skip the write entirely when it matches — that keeps\nthe stored date as well as the translations.",[1039,1824,1024],{"id":1825},"localized-fragments",[978,1827,1828,1829,1832,1833,1799,1836,1839,1840,1842,1843,1846,1847,1850],{},"A ",[982,1830,1831],{},"Page"," is localized at the type level — ",[982,1834,1835],{},"Title",[982,1837,1838],{},"Body"," are ",[982,1841,1396],{},".\nA ",[982,1844,1845],{},"Fragment"," cannot be: its ",[982,1848,1849],{},"Data"," is free-form JSON precisely so each consumer\ncan shape it, so nothing in the type says which leaves are prose and which are a\nURL, a CSS class or an icon name.",[978,1852,1853],{},"Localized leaves therefore carry an explicit marker:",[1050,1855,1859],{"className":1856,"code":1857,"language":1858,"meta":1055,"style":1055},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"heading\":  {\"_i18n\": {\"en\": \"What the app does\", \"pl\": \"Co robi aplikacja\"}},\n  \"cta_href\": \"\u002Flogin?mode=signup\"\n}\n","json",[982,1860,1861,1866,1936,1955],{"__ignoreMap":1055},[1059,1862,1863],{"class":1061,"line":10},[1059,1864,1865],{"class":1064},"{\n",[1059,1867,1868,1871,1875,1878,1880,1883,1885,1889,1891,1893,1896,1898,1902,1904,1906,1909,1912,1914,1917,1919,1922,1924,1926,1928,1931,1933],{"class":1061,"line":16},[1059,1869,1870],{"class":1064},"  \"",[1059,1872,1874],{"class":1873},"spNyl","heading",[1059,1876,1877],{"class":1064},"\"",[1059,1879,996],{"class":1064},[1059,1881,1882],{"class":1064},"  {",[1059,1884,1877],{"class":1064},[1059,1886,1888],{"class":1887},"sBMFI","_i18n",[1059,1890,1877],{"class":1064},[1059,1892,996],{"class":1064},[1059,1894,1895],{"class":1064}," {",[1059,1897,1877],{"class":1064},[1059,1899,1901],{"class":1900},"sbssI","en",[1059,1903,1877],{"class":1064},[1059,1905,996],{"class":1064},[1059,1907,1908],{"class":1064}," \"",[1059,1910,1911],{"class":1348},"What the app does",[1059,1913,1877],{"class":1064},[1059,1915,1916],{"class":1064},",",[1059,1918,1908],{"class":1064},[1059,1920,1921],{"class":1900},"pl",[1059,1923,1877],{"class":1064},[1059,1925,996],{"class":1064},[1059,1927,1908],{"class":1064},[1059,1929,1930],{"class":1348},"Co robi aplikacja",[1059,1932,1877],{"class":1064},[1059,1934,1935],{"class":1064},"}},\n",[1059,1937,1938,1940,1943,1945,1947,1949,1952],{"class":1061,"line":22},[1059,1939,1870],{"class":1064},[1059,1941,1942],{"class":1873},"cta_href",[1059,1944,1877],{"class":1064},[1059,1946,996],{"class":1064},[1059,1948,1908],{"class":1064},[1059,1950,1951],{"class":1348},"\u002Flogin?mode=signup",[1059,1953,1954],{"class":1064},"\"\n",[1059,1956,1957],{"class":1061,"line":51},[1059,1958,1306],{"class":1064},[978,1960,1961,1964],{},[982,1962,1963],{},"GET \u002Ffragments\u002F{key}"," projects that for the active locale:",[1050,1966,1968],{"className":1856,"code":1967,"language":1858,"meta":1055,"style":1055},"{\"heading\": \"Co robi aplikacja\", \"cta_href\": \"\u002Flogin?mode=signup\"}\n",[982,1969,1970],{"__ignoreMap":1055},[1059,1971,1972,1975,1977,1979,1981,1983,1985,1987,1989,1991,1993,1995,1997,1999,2001,2003,2005],{"class":1061,"line":10},[1059,1973,1974],{"class":1064},"{",[1059,1976,1877],{"class":1064},[1059,1978,1874],{"class":1873},[1059,1980,1877],{"class":1064},[1059,1982,996],{"class":1064},[1059,1984,1908],{"class":1064},[1059,1986,1930],{"class":1348},[1059,1988,1877],{"class":1064},[1059,1990,1916],{"class":1064},[1059,1992,1908],{"class":1064},[1059,1994,1942],{"class":1873},[1059,1996,1877],{"class":1064},[1059,1998,996],{"class":1064},[1059,2000,1908],{"class":1064},[1059,2002,1951],{"class":1348},[1059,2004,1877],{"class":1064},[1059,2006,1306],{"class":1064},[978,2008,2009,2012,2013,2016],{},[1004,2010,2011],{},"A marker, not a heuristic."," \"Any map whose keys look like locale codes\" reads\nfine until a fragment legitimately keys data ",[1766,2014,2015],{},"by"," language or country — a\nper-locale download link, an app-store URL per region — and then silently\ncollapses it to one string. It is also unstable over time: the same stored\ndocument changes meaning the day someone adds a locale whose code collides with\nan existing key. A marker cannot be triggered by accident.",[978,2018,2019,1153,2021,2024,2025,2028],{},[982,2020,1888],{},[982,2022,2023],{},"$i18n"," because Mongo rejects field names beginning with ",[982,2026,2027],{},"$",".",[978,2030,2031,2034,2035,2037,2038,2040],{},[1004,2032,2033],{},"The marker must be the only key in its map."," A map carrying ",[982,2036,1888],{}," alongside\nsiblings is malformed; honouring it would drop them with nothing to show for it.\nIt is treated as an ordinary map instead, so ",[982,2039,1888],{}," stays visible in the\noutput — the same choice rule 2 above makes for an unresolved token.",[978,2042,2043,2046,2047,2050],{},[1004,2044,2045],{},"Locale first, then tokens."," A translated legal line contains\n",[982,2048,2049],{},"[[ legalpages.entity_name ]]"," in every language — that is the whole point — so\nexpanding before picking would scan the locale map's siblings and leave the\nchosen text raw.",[978,2052,2053,2056],{},[1004,2054,2055],{},"Projection returns a copy."," The store hands back the document it holds and a\nfragment is read on every render, so projecting in place would freeze one\nreader's language for everyone after them.",[978,2058,2059],{},"Fallback is the same chain a page uses: requested locale → fallback locale →\nany non-empty value. A half-translated fragment renders English, not a blank.",[1036,2061],{},[1039,2063,2065],{"id":2064},"filling-the-translations","Filling the translations",[978,2067,2068],{},"Two commands, both opt-in subpackages — their parents sit on the request path\nand must not carry an AI provider for work most deployments never do:",[1050,2070,2074],{"className":2071,"code":2072,"language":2073,"meta":1055,"style":1055},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# CMS pages and content fragments.\nredelayctl content-translate --dry-run\nredelayctl content-translate --pages=about --locales=pl,de\nredelayctl content-translate --fragments=about-page --force\n\n# The i18n message catalogue.\nredelayctl i18n-translate --prefix=marketing.\nredelayctl i18n-translate --keys=a.b,c.d --overwrite\n","bash",[982,2075,2076,2081,2092,2104,2116,2122,2127,2137],{"__ignoreMap":1055},[1059,2077,2078],{"class":1061,"line":10},[1059,2079,2080],{"class":1383},"# CMS pages and content fragments.\n",[1059,2082,2083,2086,2089],{"class":1061,"line":16},[1059,2084,2085],{"class":1887},"redelayctl",[1059,2087,2088],{"class":1348}," content-translate",[1059,2090,2091],{"class":1348}," --dry-run\n",[1059,2093,2094,2096,2098,2101],{"class":1061,"line":22},[1059,2095,2085],{"class":1887},[1059,2097,2088],{"class":1348},[1059,2099,2100],{"class":1348}," --pages=about",[1059,2102,2103],{"class":1348}," --locales=pl,de\n",[1059,2105,2106,2108,2110,2113],{"class":1061,"line":51},[1059,2107,2085],{"class":1887},[1059,2109,2088],{"class":1348},[1059,2111,2112],{"class":1348}," --fragments=about-page",[1059,2114,2115],{"class":1348}," --force\n",[1059,2117,2118],{"class":1061,"line":57},[1059,2119,2121],{"emptyLinePlaceholder":2120},true,"\n",[1059,2123,2124],{"class":1061,"line":102},[1059,2125,2126],{"class":1383},"# The i18n message catalogue.\n",[1059,2128,2129,2131,2134],{"class":1061,"line":108},[1059,2130,2085],{"class":1887},[1059,2132,2133],{"class":1348}," i18n-translate",[1059,2135,2136],{"class":1348}," --prefix=marketing.\n",[1059,2138,2139,2141,2143,2146],{"class":1061,"line":114},[1059,2140,2085],{"class":1887},[1059,2142,2133],{"class":1348},[1059,2144,2145],{"class":1348}," --keys=a.b,c.d",[1059,2147,2148],{"class":1348}," --overwrite\n",[978,2150,2151,2152,2155,2156,2159,2160,2163,2164,2167],{},"Both fill ",[1004,2153,2154],{},"missing locales only"," unless told otherwise, so a run is resumable\nand cheap to repeat. Three independent reasons to redo a locale that already has\ntext: ",[982,2157,2158],{},"--force","\u002F",[982,2161,2162],{},"--overwrite",", the one-shot ",[982,2165,2166],{},"Retranslate"," flag, and a changed\nsource hash. Only the first is a flag; staleness is derived, so nobody has to\nremember it.",[1097,2169,2171],{"id":2170},"running-it-on-a-schedule","Running it on a schedule",[978,2173,2174,2175,2178,2179,2182],{},"The CLI is a person deciding to translate now. For unattended work, create a\nscheduler entry whose event type + action join to ",[982,2176,2177],{},"content.translate.sweep",";\nthe ",[982,2180,2181],{},"content\u002Ftranslate"," module consumes it and does exactly the work that is\noutstanding.",[978,2184,2185,2186,2189],{},"An ",[1004,2187,2188],{},"event"," rather than a ticker inside the module, for a reason that only\nappears in production: with several replicas a ticker runs in every one of\nthem, so N replicas means N times the model spend for the same work. The\nscheduler already solves that with leader election.",[1205,2191,2192,2204],{},[1208,2193,2194],{},[1211,2195,2196,2199,2202],{},[1214,2197,2198],{},"Variable",[1214,2200,2201],{},"Default",[1214,2203,1642],{},[1224,2205,2206,2225,2243],{},[1211,2207,2208,2213,2218],{},[1229,2209,2210],{},[982,2211,2212],{},"CONTENT_TRANSLATE_QUIET_MINUTES",[1229,2214,2215],{},[982,2216,2217],{},"10",[1229,2219,2220,2221,2224],{},"How long a record must sit untouched before the sweep will translate it. ",[982,2222,2223],{},"0"," disables",[1211,2226,2227,2232,2237],{},[1229,2228,2229],{},[982,2230,2231],{},"CONTENT_TRANSLATE_BUDGET",[1229,2233,2234],{},[982,2235,2236],{},"200",[1229,2238,2239,2240,2242],{},"Max (unit, locale) translations per sweep. ",[982,2241,2223],{}," = no cap",[1211,2244,2245,2250,2253],{},[1229,2246,2247],{},[982,2248,2249],{},"CONTENT_TRANSLATE_DOMAIN",[1229,2251,2252],{},"—",[1229,2254,2255],{},"One phrase describing what this deployment is, prepended to every translation hint",[978,2257,2258,2261],{},[1004,2259,2260],{},"The quiet period is the debounce",", and it costs nothing because the sweep is\nstate-driven: an editor saving six times in two minutes leaves one record with\none recent timestamp, and the run that picks it up translates the final wording\nonce. Reacting per save would pay for five drafts nobody reads — and on a\nreview-required record, queue five candidates a human must dismiss. A record\nwith no timestamp counts as settled, or anything seeded without that field\nwould be ineligible forever.",[978,2263,2264,2267],{},[1004,2265,2266],{},"The budget is a ceiling on a bad day, not a target."," A bulk import otherwise\nturns one cron tick into an unbounded bill with nobody watching. Work not done\nis not lost: nothing here is a queue that can drain incorrectly, so the next\nsweep sees the same state and continues.",[978,2269,2270],{},"A sweep that did nothing says which of the three reasons applies — waiting for\nedits to settle, stopped at the budget, or genuinely nothing to do. Printing\nsilence for all three is how an unattended job looks healthy while stuck.",[1097,2272,2274],{"id":2273},"tell-the-model-what-the-product-is","Tell the model what the product is",[978,2276,2277,2279],{},[982,2278,2249],{}," exists because every hint otherwise said some\nvariant of \"a block of prose on a web page\", which is true of every site ever\nwritten and leaves the model to guess the sense of any ambiguous word. On a\ntraining log it guessed wrong the first time it was asked: \"a set that did not\nsave\" came back as Russian «набор» — a set as in a collection of objects —\nwhere a lifter says «сет».",[978,2281,2282],{},"Name the terms, do not imply them. A general \"this is a strength-training app\"\ndid not move that word; a glossary naming «сет» and «тренировка» fixed it first\ntry. Readers do not see a mistranslation, they see a product that does not know\nits own vocabulary.",[978,2284,2285,2288,2289,2292,2293,2296,2297,2300],{},[982,2286,2287],{},"i18n-translate"," and the admin endpoint run ",[1004,2290,2291],{},"one shared loop","\n(",[982,2294,2295],{},"i18n.TranslateMissing"," over a ",[982,2298,2299],{},"Catalogue"," interface). Two copies would be two\nanswers to \"what counts as missing\" and would drift. They differ in exactly one\nrespect: the endpoint caps the number of keys because an HTTP request has a\ndeadline; the CLI passes zero, because a command that stopped at 40 could never\nfinish a larger catalogue.",[978,2302,2303,2310],{},[1004,2304,2305,2306,2309],{},"Raise ",[982,2307,2308],{},"CLI_TIMEOUT"," for long runs."," The framework caps a CLI at five minutes\nby default. Translating six legal pages into 24 languages is ~144 model calls at\nroughly ten seconds each; past the cap every remaining call fails instantly, and\nbefore this was reported properly it looked like the model rejecting everything.",[1050,2312,2314],{"className":2071,"code":2313,"language":2073,"meta":1055,"style":1055},"CLI_TIMEOUT=0 redelayctl content-translate     # no cap\n",[982,2315,2316],{"__ignoreMap":1055},[1059,2317,2318,2320,2323,2325,2328,2330],{"class":1061,"line":10},[1059,2319,2308],{"class":1074},[1059,2321,2322],{"class":1064},"=",[1059,2324,2223],{"class":1348},[1059,2326,2327],{"class":1887}," redelayctl",[1059,2329,2088],{"class":1348},[1059,2331,2332],{"class":1383},"     # no cap\n",[978,2334,2335,2338],{},[1004,2336,2337],{},"A failed locale is not \"nothing to do\"."," Both commands report attempted-and-\nfailed separately from complete, and neither records a source hash for work that\ndid not land — so a rerun retries exactly what is missing.",[1039,2340,2342],{"id":2341},"consuming-it-from-a-frontend","Consuming it from a frontend",[978,2344,2345],{},[1004,2346,2347,2348,1471,2351,2354],{},"Use ",[982,2349,2350],{},"useRequestFetch()",[982,2352,2353],{},"$fetch",", for anything locale-dependent.",[978,2356,2357,2358,2360,2361,2364,2365,2368],{},"On the server ",[982,2359,2353],{}," opens a new request carrying no cookies, so a ",[982,2362,2363],{},"lang","\ncookie never reaches the proxy that turns it into ",[982,2366,2367],{},"?lang="," — and every CMS page\nrenders in the default language however the reader set theirs, on the SSR pass\nthat crawlers also see.",[978,2370,2371],{},"This failure is invisible without looking for it, because the fallback chain\nworks: a request that carried no locale is indistinguishable from one whose\nlocale is untranslated. Both return a healthy 200 in English.",[1050,2373,2377],{"className":2374,"code":2375,"language":2376,"meta":1055,"style":1055},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const requestFetch = useRequestFetch()\nconst lang = useCookie\u003Cstring>('lang')       \u002F\u002F read in setup, not in the key fn\n\nconst { data } = await useAsyncData(\n  () => `page:${slug}:${lang.value || 'en'}`, \u002F\u002F locale in the cache key\n  () => requestFetch(`${apiBase}\u002Fpages\u002F${slug}`),\n)\n","ts",[982,2378,2379,2396,2431,2435,2460,2509,2544],{"__ignoreMap":1055},[1059,2380,2381,2384,2387,2389,2393],{"class":1061,"line":10},[1059,2382,2383],{"class":1873},"const",[1059,2385,2386],{"class":1074}," requestFetch ",[1059,2388,2322],{"class":1064},[1059,2390,2392],{"class":2391},"s2Zo4"," useRequestFetch",[1059,2394,2395],{"class":1074},"()\n",[1059,2397,2398,2400,2403,2405,2408,2410,2413,2415,2418,2421,2423,2425,2428],{"class":1061,"line":16},[1059,2399,2383],{"class":1873},[1059,2401,2402],{"class":1074}," lang ",[1059,2404,2322],{"class":1064},[1059,2406,2407],{"class":2391}," useCookie",[1059,2409,1065],{"class":1064},[1059,2411,2412],{"class":1887},"string",[1059,2414,1071],{"class":1064},[1059,2416,2417],{"class":1074},"(",[1059,2419,2420],{"class":1064},"'",[1059,2422,2363],{"class":1348},[1059,2424,2420],{"class":1064},[1059,2426,2427],{"class":1074},")       ",[1059,2429,2430],{"class":1383},"\u002F\u002F read in setup, not in the key fn\n",[1059,2432,2433],{"class":1061,"line":22},[1059,2434,2121],{"emptyLinePlaceholder":2120},[1059,2436,2437,2439,2441,2444,2447,2450,2454,2457],{"class":1061,"line":51},[1059,2438,2383],{"class":1873},[1059,2440,1895],{"class":1064},[1059,2442,2443],{"class":1074}," data ",[1059,2445,2446],{"class":1064},"}",[1059,2448,2449],{"class":1064}," =",[1059,2451,2453],{"class":2452},"s7zQu"," await",[1059,2455,2456],{"class":2391}," useAsyncData",[1059,2458,2459],{"class":1074},"(\n",[1059,2461,2462,2465,2468,2471,2474,2477,2480,2482,2484,2486,2488,2490,2493,2496,2499,2501,2504,2506],{"class":1061,"line":57},[1059,2463,2464],{"class":1064},"  ()",[1059,2466,2467],{"class":1873}," =>",[1059,2469,2470],{"class":1064}," `",[1059,2472,2473],{"class":1348},"page:",[1059,2475,2476],{"class":1064},"${",[1059,2478,2479],{"class":1074},"slug",[1059,2481,2446],{"class":1064},[1059,2483,996],{"class":1348},[1059,2485,2476],{"class":1064},[1059,2487,2363],{"class":1074},[1059,2489,2028],{"class":1064},[1059,2491,2492],{"class":1074},"value ",[1059,2494,2495],{"class":1064},"||",[1059,2497,2498],{"class":1064}," '",[1059,2500,1901],{"class":1348},[1059,2502,2503],{"class":1064},"'}`",[1059,2505,1916],{"class":1064},[1059,2507,2508],{"class":1383}," \u002F\u002F locale in the cache key\n",[1059,2510,2511,2513,2515,2518,2520,2523,2526,2528,2531,2533,2535,2538,2541],{"class":1061,"line":102},[1059,2512,2464],{"class":1064},[1059,2514,2467],{"class":1873},[1059,2516,2517],{"class":2391}," requestFetch",[1059,2519,2417],{"class":1074},[1059,2521,2522],{"class":1064},"`${",[1059,2524,2525],{"class":1074},"apiBase",[1059,2527,2446],{"class":1064},[1059,2529,2530],{"class":1348},"\u002Fpages\u002F",[1059,2532,2476],{"class":1064},[1059,2534,2479],{"class":1074},[1059,2536,2537],{"class":1064},"}`",[1059,2539,2540],{"class":1074},")",[1059,2542,2543],{"class":1064},",\n",[1059,2545,2546],{"class":1061,"line":108},[1059,2547,2548],{"class":1074},")\n",[978,2550,2551,2552,2555,2556,2559,2560,2563,2564,2567],{},"Two traps in those four lines. The locale belongs in the ",[1004,2553,2554],{},"cache key",", or the\nfirst language rendered is served to everyone afterwards. And ",[982,2557,2558],{},"useCookie"," must\nbe called in setup rather than inside the key function — ",[982,2561,2562],{},"useAsyncData"," invokes\nthat lazily, outside the component's setup context, where it throws\n",[982,2565,2566],{},"NUXT_E1001"," and takes the page down.",[1039,2569,2571,2572,2575],{"id":2570},"splitting-copy-between-t-and-content","Splitting copy between ",[982,2573,2574],{},"t()"," and content",[1205,2577,2578,2590],{},[1208,2579,2580],{},[1211,2581,2582,2584,2587],{},[1214,2583],{},[1214,2585,2586],{},"Goes in",[1214,2588,2589],{},"Why",[1224,2591,2592,2606],{},[1211,2593,2594,2597,2603],{},[1229,2595,2596],{},"Short labels — headings, buttons, nav",[1229,2598,2599,2600,2602],{},"i18n ",[982,2601,2574],{}," keys",[1229,2604,2605],{},"A translator gets a phrase and returns a phrase.",[1211,2607,2608,2611,2614],{},[1229,2609,2610],{},"Prose, especially with inline links",[1229,2612,2613],{},"CMS page or fragment",[1229,2615,2616,2617,2620,2621,2624],{},"A catalogue full of ",[982,2618,2619],{},"\u003Cp>…\u003Ca href>…\u003C\u002Fa>\u003C\u002Fp>"," is one nobody can translate safely: a dropped ",[982,2622,2623],{},"\u003C\u002Fa>"," breaks the page rather than the sentence.",[978,2626,2627,2628,2631,2632,985,2635,985,2638,985,2641,2644],{},"Give short keys a ",[1004,2629,2630],{},"context hint",". A page title is two words with no sentence\naround it, which is the worst case for a translator, and some are worse than\nthey look: English \"About\" is elliptical for \"About us\", a convention most\nlanguages do not share, so translated as a word it comes back as the bare\npreposition it literally is — ",[982,2633,2634],{},"О",[982,2636,2637],{},"O",[982,2639,2640],{},"Über",[982,2642,2643],{},"Про",". None of those is a title.",[978,2646,2647,2648,2651],{},"The frontend owns these hints in ",[982,2649,2650],{},"i18n-contexts.json","; the extractor posts them\nwith the keys, and the bulk translator feeds each note to the model:",[1050,2653,2655],{"className":1856,"code":2654,"language":1858,"meta":1055,"style":1055},"\"about.eyebrow\": \"Navigation link and page heading. English \\\"About\\\" is ELLIPTICAL for \\\"About us\\\". Render the natural page-title form, NEVER the bare preposition. Sample — PL: \\\"O NAS\\\" (not \\\"O\\\"); DE: \\\"ÜBER UNS\\\"; UA: \\\"ПРО НАС\\\".\"\n",[982,2656,2657],{"__ignoreMap":1055},[1059,2658,2659,2661,2664,2666,2669,2671,2674,2677,2680,2682,2685,2687,2690,2692,2695,2697,2700,2702,2705,2707,2709,2711,2714,2716,2719,2721,2724,2726,2729,2731,2733],{"class":1061,"line":10},[1059,2660,1877],{"class":1064},[1059,2662,2663],{"class":1348},"about.eyebrow",[1059,2665,1877],{"class":1064},[1059,2667,2668],{"class":1074},": ",[1059,2670,1877],{"class":1064},[1059,2672,2673],{"class":1348},"Navigation link and page heading. English ",[1059,2675,2676],{"class":1074},"\\\"",[1059,2678,2679],{"class":1348},"About",[1059,2681,2676],{"class":1074},[1059,2683,2684],{"class":1348}," is ELLIPTICAL for ",[1059,2686,2676],{"class":1074},[1059,2688,2689],{"class":1348},"About us",[1059,2691,2676],{"class":1074},[1059,2693,2694],{"class":1348},". Render the natural page-title form, NEVER the bare preposition. Sample — PL: ",[1059,2696,2676],{"class":1074},[1059,2698,2699],{"class":1348},"O NAS",[1059,2701,2676],{"class":1074},[1059,2703,2704],{"class":1348}," (not ",[1059,2706,2676],{"class":1074},[1059,2708,2637],{"class":1348},[1059,2710,2676],{"class":1074},[1059,2712,2713],{"class":1348},"); DE: ",[1059,2715,2676],{"class":1074},[1059,2717,2718],{"class":1348},"ÜBER UNS",[1059,2720,2676],{"class":1074},[1059,2722,2723],{"class":1348},"; UA: ",[1059,2725,2676],{"class":1074},[1059,2727,2728],{"class":1348},"ПРО НАС",[1059,2730,2676],{"class":1074},[1059,2732,2028],{"class":1348},[1059,2734,1954],{"class":1064},[1039,2736,2738],{"id":2737},"content-change-events","Content change events",[978,2740,2741,2744,2745,1799,2748,2751],{},[982,2742,2743],{},"content"," publishes ",[982,2746,2747],{},"content.page.changed",[982,2749,2750],{},"content.fragment.changed"," for\nanything that derives from a page or fragment: a search index that must not\nkeep serving the old wording, a cache, an audit trail. Optional — with no bus\nwired the module behaves exactly as before.",[978,2753,2754,2755,2758],{},"The payload carries ",[982,2756,2757],{},"source_changed",", so a consumer can ignore a position\nchange or a translation landing, and a reflowed paragraph that renders\nidentically wakes nobody.",[978,2760,2761,2764],{},[1004,2762,2763],{},"Translation deliberately does not consume these."," It works from stored\nhashes, so a lost event costs latency and never correctness — which is why the\nsweep is state-driven rather than queue-driven, and why a missed event is not a\nclass of bug this system can have.",[1039,2766,2768],{"id":2767},"editing-25-languages-in-the-admin","Editing 25 languages in the admin",[978,2770,2771],{},"A localized field renders one editor per locale, which is fine at two languages\nand unusable at twenty-five. Declare a focus instead:",[1050,2773,2777],{"className":2774,"code":2775,"language":2776,"meta":1055,"style":1055},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003CLocaleFocusBar :locales=\"locales\" \u002F>\n\u003CLocalizedField v-model=\"form.title\" :locales=\"locales\" \u002F>\n\u003CLocalizedRichText v-model=\"form.body\" :locales=\"locales\" \u002F>\n","vue",[982,2778,2779,2803,2841],{"__ignoreMap":1055},[1059,2780,2781,2783,2786,2789,2792,2794,2796,2798,2800],{"class":1061,"line":10},[1059,2782,1065],{"class":1064},[1059,2784,2785],{"class":1068},"LocaleFocusBar",[1059,2787,2788],{"class":1064}," :",[1059,2790,2791],{"class":1873},"locales",[1059,2793,2322],{"class":1064},[1059,2795,1877],{"class":1064},[1059,2797,2791],{"class":1074},[1059,2799,1877],{"class":1064},[1059,2801,2802],{"class":1064}," \u002F>\n",[1059,2804,2805,2807,2810,2813,2815,2817,2820,2822,2825,2827,2829,2831,2833,2835,2837,2839],{"class":1061,"line":16},[1059,2806,1065],{"class":1064},[1059,2808,2809],{"class":1068},"LocalizedField",[1059,2811,2812],{"class":1873}," v-model",[1059,2814,2322],{"class":1064},[1059,2816,1877],{"class":1064},[1059,2818,2819],{"class":1074},"form",[1059,2821,2028],{"class":1064},[1059,2823,2824],{"class":1074},"title",[1059,2826,1877],{"class":1064},[1059,2828,2788],{"class":1064},[1059,2830,2791],{"class":1873},[1059,2832,2322],{"class":1064},[1059,2834,1877],{"class":1064},[1059,2836,2791],{"class":1074},[1059,2838,1877],{"class":1064},[1059,2840,2802],{"class":1064},[1059,2842,2843,2845,2848,2850,2852,2854,2856,2858,2861,2863,2865,2867,2869,2871,2873,2875],{"class":1061,"line":22},[1059,2844,1065],{"class":1064},[1059,2846,2847],{"class":1068},"LocalizedRichText",[1059,2849,2812],{"class":1873},[1059,2851,2322],{"class":1064},[1059,2853,1877],{"class":1064},[1059,2855,2819],{"class":1074},[1059,2857,2028],{"class":1064},[1059,2859,2860],{"class":1074},"body",[1059,2862,1877],{"class":1064},[1059,2864,2788],{"class":1064},[1059,2866,2791],{"class":1873},[1059,2868,2322],{"class":1064},[1059,2870,1877],{"class":1064},[1059,2872,2791],{"class":1074},[1059,2874,1877],{"class":1064},[1059,2876,2802],{"class":1064},[978,2878,2879],{},"Every localized field below the bar renders only the focused language. The bar\ncarries a language dropdown and a chip per locale — green complete, amber\npartial, grey untouched — so the gaps are visible without clicking through the\nlist to find them.",[978,2881,2882],{},"Strictly opt-in: with no bar on the page there is no provider and fields render\nevery locale exactly as before. Fields register themselves with the bar, so\nper-locale coverage is computed across the whole record without the bar being\ntold the form's shape — a title translated while the body is not reads as\npartial, which is what it is.",[1039,2884,2886],{"id":2885},"what-the-translator-does-to-survive-a-real-model","What the translator does to survive a real model",[978,2888,2889,2890,2893,2894,2896],{},"The ",[982,2891,2892],{},"ai"," module's ",[982,2895,562],{}," grew these one failure at a time. Each is here\nbecause its absence produced a silent, expensive wrong answer.",[978,2898,2899,2902,2903,2906,2907,2910],{},[1004,2900,2901],{},"The token cap is sized from the source, not configured once."," A translation\nis the one completion whose output length is knowable up front — the source\ntext, once per locale — so a single ",[982,2904,2905],{},"AI_MAX_TOKENS"," is the wrong shape. At the\n2048 default, every locale of a 9 KB legal page came back truncated mid-JSON:\ncorrectly rejected by format validation, retried, bisected to a batch of one,\ntruncated identically, and finally dropped. Eighteen minutes and ~240 paid\ncalls produced nothing. The estimate assumes the expensive case, because\nCyrillic, Greek and CJK tokenize at roughly two characters per token where\nEnglish HTML manages four — a cap sized off the English source still truncates\nthe Russian reply. The configured value is a ",[1004,2908,2909],{},"floor",", never a ceiling.",[978,2912,2913,2916],{},[1004,2914,2915],{},"Batch size comes from an output budget, not a count."," Six locales of that\nsame page is a single ~28,000-token reply, near enough the ceiling that one\nslightly longer page loses all six at once. Short strings still batch\nsix-at-a-time; long documents step down to one locale per call.",[978,2918,2919,2922,2923,2926],{},[1004,2920,2921],{},"Retries carry the reason."," A format retry used to re-send a byte-identical\nprompt, which cannot help — the model already answered that question. A Slovak\npage failed three identical attempts on one paragraph whose ",[982,2924,2925],{},"\u003Cp>"," came back\nwithout its closing bracket. Each locale's rejection reason now goes into its\nnext attempt, and the first corrected retry succeeded.",[978,2928,2929,1413,2932,2935,2936,2939],{},[1004,2930,2931],{},"Rejections name the tags.",[982,2933,2934],{},"problems()"," reported \"structure mismatch\" for\nthe one check that fires on an HTML body. It now diffs the tag multisets as\ncounts (",[982,2937,2938],{},"dropped \u003C\u002Fp>×1, \u003Cp>×1","), which is what made the above diagnosable.",[978,2941,2942,2945,2946,2949,2950,2952,2953,2956],{},[1004,2943,2944],{},"Each call is bounded"," by ",[982,2947,2948],{},"AI_CALL_TIMEOUT"," (default 3m; ",[982,2951,2223],{}," disables). The\nbound belongs at the layer that makes calls: a caller can only bound the whole\noperation it asked for, and \"this page in 24 locales\" is many calls — too short\na limit kills the job, too long lets one stalled request hold it up. That was a\nfifteen-minute hang with no output. ",[982,2954,2955],{},"TRANSLATE_CALL_TIMEOUT"," still works and\nfeeds the same setting.",[978,2958,2959,2960,2962,2963,2028],{},"A related lesson worth keeping: ",[982,2961,2308],{}," defaults to five minutes, and a\nsix-page run that exceeds it reported \"no locale answered\" 144 times — blaming\nthe model for a deadline. Long runs want ",[982,2964,2965],{},"CLI_TIMEOUT=0",[2967,2968,2969],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":1055,"searchDepth":16,"depth":16,"links":2971},[2972,2979,2980,2985,2986,2990,2991,2993,2994,2995],{"id":1041,"depth":16,"text":1006,"children":2973},[2974,2975,2977,2978],{"id":1099,"depth":22,"text":1100},{"id":1148,"depth":22,"text":2976},"Why [[ ]] and not {{ }}",{"id":1202,"depth":22,"text":1203},{"id":1273,"depth":22,"text":1274},{"id":1322,"depth":16,"text":1012},{"id":1497,"depth":16,"text":1498,"children":2981},[2982,2983,2984],{"id":1551,"depth":22,"text":1552},{"id":1608,"depth":22,"text":1609},{"id":1808,"depth":22,"text":1809},{"id":1825,"depth":16,"text":1024},{"id":2064,"depth":16,"text":2065,"children":2987},[2988,2989],{"id":2170,"depth":22,"text":2171},{"id":2273,"depth":22,"text":2274},{"id":2341,"depth":16,"text":2342},{"id":2570,"depth":16,"text":2992},"Splitting copy between t() and content",{"id":2737,"depth":16,"text":2738},{"id":2767,"depth":16,"text":2768},{"id":2885,"depth":16,"text":2886},"Settings tokens in CMS prose, the shared translation lifecycle (ready \u002F stale \u002F retranslate), and localized fragment leaves.","md",null,{},{"order":282,"icon":283},{"title":279,"description":2996},"jI5OMFj8hLll1S0JS0oDEVlydm0Dipsby29Ky6D21NQ",[3004,3006],{"title":274,"path":275,"stem":276,"description":3005,"order":277,"icon":152,"children":-1},"A WebSocket hub — live connections grouped into rooms, with broadcast fan-out that stays correct across replicas via the framework Coordinator.",{"title":130,"path":285,"stem":286,"description":2998,"order":51,"children":-1},1790332226537]