[{"data":1,"prerenderedAt":4343},["ShallowReactive",2],{"navigation_docs":3,"-docs-reference-i18n":968,"-docs-reference-i18n-surround":4338},[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":268,"body":970,"description":4331,"extension":4332,"links":4333,"meta":4334,"navigation":4335,"path":269,"seo":4336,"stem":270,"__hash__":4337},"docs\u002Fdocs\u002F4.reference\u002F25.i18n.md",{"type":971,"value":972,"toc":4293},"minimark",[973,978,990,1014,1040,1051,1062,1066,1150,1155,1158,1166,1184,1198,1202,1205,1239,1242,1263,1266,1281,1285,1292,1298,1313,1328,1332,1431,1445,1449,1453,1503,1514,1534,1645,1652,1658,1672,1836,1847,1851,1861,1895,1967,1971,1988,1997,2001,2004,2010,2028,2035,2112,2124,2179,2202,2205,2437,2444,2448,2464,2488,2517,2532,2541,2557,2577,2597,2603,2652,2658,2669,2673,2688,2705,2730,2753,2762,2790,2826,2830,2836,2858,2967,2999,3031,3048,3078,3131,3135,3142,3152,3182,3197,3206,3226,3239,3243,3258,3262,3277,3360,3374,3398,3405,3416,3425,3431,3438,3540,3550,3574,3581,3592,3645,3652,3663,3707,3717,3721,3740,3750,3753,3810,3813,3852,3859,3865,3869,3977,3988,4001,4007,4011,4032,4036,4061,4087,4111,4118,4122,4125,4132,4264,4283,4286,4289],[974,975,977],"h1",{"id":976},"i18n-translation-and-localization","i18n — translation and localization",[979,980,981,985,986,989],"p",{},[982,983,984],"code",{},"github.com\u002Fredelay\u002Fgo-modules\u002Fi18n"," gives a Redelay app three things: it resolves the\nactive locale for every request, it serves a public key→value message catalog, and it\nexposes a ",[982,987,988],{},"Service"," that other modules use to translate strings with a locale fallback\nchain.",[991,992,997],"pre",{"className":993,"code":994,"language":995,"meta":996,"style":996},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com\u002Fredelay\u002Fgo-modules\u002Fi18n\n","bash","",[982,998,999],{"__ignoreMap":996},[1000,1001,1003,1007,1011],"span",{"class":1002,"line":10},"line",[1000,1004,1006],{"class":1005},"sBMFI","go",[1000,1008,1010],{"class":1009},"sfazB"," get",[1000,1012,1013],{"class":1009}," github.com\u002Fredelay\u002Fgo-modules\u002Fi18n\n",[991,1015,1018],{"className":1016,"code":1017,"language":1006,"meta":996,"style":996},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import (\n    _ \"github.com\u002Fredelay\u002Fgo-modules\u002Fi18n\"          \u002F\u002F module + locale middleware + GET \u002Fi18n\u002Fmessages\n    _ \"github.com\u002Fredelay\u002Fgo-modules\u002Fi18n\u002Fflowdsl\"  \u002F\u002F FlowDSL nodes: i18n\u002Ftranslate, i18n\u002Fresolve-locale\n)\n",[982,1019,1020,1025,1030,1035],{"__ignoreMap":996},[1000,1021,1022],{"class":1002,"line":10},[1000,1023,1024],{},"import (\n",[1000,1026,1027],{"class":1002,"line":16},[1000,1028,1029],{},"    _ \"github.com\u002Fredelay\u002Fgo-modules\u002Fi18n\"          \u002F\u002F module + locale middleware + GET \u002Fi18n\u002Fmessages\n",[1000,1031,1032],{"class":1002,"line":22},[1000,1033,1034],{},"    _ \"github.com\u002Fredelay\u002Fgo-modules\u002Fi18n\u002Fflowdsl\"  \u002F\u002F FlowDSL nodes: i18n\u002Ftranslate, i18n\u002Fresolve-locale\n",[1000,1036,1037],{"class":1002,"line":51},[1000,1038,1039],{},")\n",[979,1041,1042,1043,1046,1047,1050],{},"The admin submodule at ",[982,1044,1045],{},"i18n\u002Fadmin"," mounts translation CRUD under the admin prefix —\nimport it from ",[982,1048,1049],{},"cmd\u002Fadmin-api"," only.",[979,1052,1053,1054,1057,1058,1061],{},"Default locale is Polish (",[982,1055,1056],{},"pl",") with an English (",[982,1059,1060],{},"en",") fallback, inherited from the\nAsyncShop \u002F go.shop.pl deployment that first used the module. Override per deployment.",[1063,1064,28],"h2",{"id":1065},"concepts",[1067,1068,1069,1082],"table",{},[1070,1071,1072],"thead",{},[1073,1074,1075,1079],"tr",{},[1076,1077,1078],"th",{},"Term",[1076,1080,1081],{},"Meaning",[1083,1084,1085,1101,1122,1132],"tbody",{},[1073,1086,1087,1094],{},[1088,1089,1090],"td",{},[1091,1092,1093],"strong",{},"Message",[1088,1095,1096,1097,1100],{},"One translated string, uniquely keyed by ",[982,1098,1099],{},"(namespace, key, locale)",".",[1073,1102,1103,1108],{},[1088,1104,1105],{},[1091,1106,1107],{},"Namespace",[1088,1109,1110,1111,1114,1115,1114,1118,1121],{},"Groups related keys — ",[982,1112,1113],{},"ui",", ",[982,1116,1117],{},"checkout",[982,1119,1120],{},"emails",". The empty namespace is the default bucket.",[1073,1123,1124,1129],{},[1088,1125,1126],{},[1091,1127,1128],{},"Locale chain",[1088,1130,1131],{},"The ordered fallback list used for every lookup.",[1073,1133,1134,1139],{},[1088,1135,1136],{},[1091,1137,1138],{},"Catalog",[1088,1140,1141,1142,1145,1146,1149],{},"The merged ",[982,1143,1144],{},"key→value"," map for one ",[982,1147,1148],{},"(namespace, locale)",", fallback chain applied.",[1151,1152,1154],"h3",{"id":1153},"the-locale-chain","The locale chain",[979,1156,1157],{},"Every lookup walks a de-duplicated chain, most specific first:",[991,1159,1164],{"className":1160,"code":1162,"language":1163},[1161],"language-text","requested → its base → fallback → default\n   pl-PL   →   pl     →    en    →   pl\n","text",[982,1165,1162],{"__ignoreMap":996},[979,1167,1168,1169,1172,1173,1176,1177,1180,1181,1183],{},"The base is derived by splitting on ",[982,1170,1171],{},"-"," or ",[982,1174,1175],{},"_",", so ",[982,1178,1179],{},"pl-PL"," falls back to ",[982,1182,1056],{}," before\nreaching the configured fallback. This means a partially-translated regional variant\ndegrades to its language, then to English, rather than to raw keys.",[979,1185,1186,1189,1190,1193,1194,1197],{},[982,1187,1188],{},"TranslateLocale"," returns ",[1091,1191,1192],{},"the key itself"," when nothing in the chain matches. A missing\ntranslation renders as ",[982,1195,1196],{},"checkout.submit",", never as an empty string — the failure is\nvisible in the UI instead of silently blanking it.",[1151,1199,1201],{"id":1200},"request-locale-resolution","Request-locale resolution",[979,1203,1204],{},"The module registers middleware on every request. Resolution order:",[1206,1207,1208,1215,1221,1234],"ol",{},[1209,1210,1211,1214],"li",{},[982,1212,1213],{},"?lang="," query parameter",[1209,1216,1217,1220],{},[982,1218,1219],{},"lang"," cookie",[1209,1222,1223,1226,1227,1230,1231,1233],{},[982,1224,1225],{},"Accept-Language"," header — first tag only, quality markers stripped (",[982,1228,1229],{},"pl-PL,pl;q=0.9"," → ",[982,1232,1179],{},")",[1209,1235,1236],{},[982,1237,1238],{},"I18N_DEFAULT_LOCALE",[979,1240,1241],{},"The result lands in the request context. Other modules read it without importing any\nHTTP plumbing:",[991,1243,1245],{"className":1016,"code":1244,"language":1006,"meta":996,"style":996},"import \"github.com\u002Fredelay\u002Fgo-modules\u002Fi18n\"\n\nlocale := i18n.LocaleFromContext(ctx)  \u002F\u002F \"\" if the middleware did not run\n",[982,1246,1247,1252,1258],{"__ignoreMap":996},[1000,1248,1249],{"class":1002,"line":10},[1000,1250,1251],{},"import \"github.com\u002Fredelay\u002Fgo-modules\u002Fi18n\"\n",[1000,1253,1254],{"class":1002,"line":16},[1000,1255,1257],{"emptyLinePlaceholder":1256},true,"\n",[1000,1259,1260],{"class":1002,"line":22},[1000,1261,1262],{},"locale := i18n.LocaleFromContext(ctx)  \u002F\u002F \"\" if the middleware did not run\n",[979,1264,1265],{},"This is how catalog, content, and onepage modules project localized entity fields for the\nactive request.",[979,1267,1268,1269,1272,1273,1276,1277,1280],{},"i18n itself accepts ",[1091,1270,1271],{},"any"," locale string. A module that needs to constrain the set — the\nAsyncShop ",[982,1274,1275],{},"channels"," module restricts to a channel's ",[982,1278,1279],{},"SupportedLocales"," — wraps this\nmiddleware rather than modifying it.",[1151,1282,1284],{"id":1283},"interpolation","Interpolation",[979,1286,1287,1288,1291],{},"Values may carry Go-template placeholders, filled from the ",[982,1289,1290],{},"args"," map:",[991,1293,1296],{"className":1294,"code":1295,"language":1163},[1161],"greeting.hello = \"Cześć {{.Name}}!\"\n",[982,1297,1295],{"__ignoreMap":996},[991,1299,1301],{"className":1016,"code":1300,"language":1006,"meta":996,"style":996},"svc.Translate(ctx, \"ui\", \"greeting.hello\", map[string]any{\"Name\": \"Ada\"})\n\u002F\u002F → \"Cześć Ada!\"\n",[982,1302,1303,1308],{"__ignoreMap":996},[1000,1304,1305],{"class":1002,"line":10},[1000,1306,1307],{},"svc.Translate(ctx, \"ui\", \"greeting.hello\", map[string]any{\"Name\": \"Ada\"})\n",[1000,1309,1310],{"class":1002,"line":16},[1000,1311,1312],{},"\u002F\u002F → \"Cześć Ada!\"\n",[979,1314,1315,1316,1319,1320,1323,1324,1327],{},"Templates use ",[982,1317,1318],{},"missingkey=zero",". On any parse or execution error the ",[1091,1321,1322],{},"raw string is\nreturned unchanged"," — a malformed template degrades to visible source text rather than\nerroring the request. Strings without ",[982,1325,1326],{},"{{"," skip the template engine entirely.",[1063,1329,1331],{"id":1330},"configuration","Configuration",[1067,1333,1334,1347],{},[1070,1335,1336],{},[1073,1337,1338,1341,1344],{},[1076,1339,1340],{},"Key",[1076,1342,1343],{},"Default",[1076,1345,1346],{},"Purpose",[1083,1348,1349,1362,1376,1391,1414],{},[1073,1350,1351,1355,1359],{},[1088,1352,1353],{},[982,1354,1238],{},[1088,1356,1357],{},[982,1358,1056],{},[1088,1360,1361],{},"Locale when none resolves from the request",[1073,1363,1364,1369,1373],{},[1088,1365,1366],{},[982,1367,1368],{},"I18N_FALLBACK_LOCALE",[1088,1370,1371],{},[982,1372,1060],{},[1088,1374,1375],{},"Fills gaps before falling back to the default",[1073,1377,1378,1383,1388],{},[1088,1379,1380],{},[982,1381,1382],{},"I18N_COLLECTION",[1088,1384,1385],{},[982,1386,1387],{},"translations",[1088,1389,1390],{},"MongoDB collection for messages",[1073,1392,1393,1398,1403],{},[1088,1394,1395],{},[982,1396,1397],{},"I18N_AUTOREGISTER",[1088,1399,1400],{},[982,1401,1402],{},"true",[1088,1404,1405,1406,1409,1410,1413],{},"Enables the public ",[982,1407,1408],{},"POST \u002Fi18n\u002Freport-keys"," write-once endpoint. Set ",[982,1411,1412],{},"false"," to lock the catalog in production.",[1073,1415,1416,1421,1424],{},[1088,1417,1418],{},[982,1419,1420],{},"I18N_SOURCE_LOCALE",[1088,1422,1423],{},"(fallback locale)",[1088,1425,1426,1427,1430],{},"Language the frontend's inline default labels are written in — the locale new keys are stored under and the ",[982,1428,1429],{},"from"," language for bulk auto-translate.",[979,1432,1433,1436,1437,1440,1441,1444],{},[1091,1434,1435],{},"No database → module disabled."," The factory logs ",[982,1438,1439],{},"i18n: no database — module disabled","\nand returns a Module with a nil Service. Handlers then answer ",[982,1442,1443],{},"503 i18n not configured","\nrather than panicking, so an app can boot without Mongo.",[1063,1446,1448],{"id":1447},"http-surface","HTTP surface",[1151,1450,1452],{"id":1451},"public","Public",[1067,1454,1455,1467],{},[1070,1456,1457],{},[1073,1458,1459,1462,1465],{},[1076,1460,1461],{},"Method",[1076,1463,1464],{},"Path",[1076,1466,1346],{},[1083,1468,1469,1484],{},[1073,1470,1471,1476,1481],{},[1088,1472,1473],{},[982,1474,1475],{},"GET",[1088,1477,1478],{},[982,1479,1480],{},"\u002Fi18n\u002Fmessages",[1088,1482,1483],{},"Message catalog for a locale + namespace",[1073,1485,1486,1491,1496],{},[1088,1487,1488],{},[982,1489,1490],{},"POST",[1088,1492,1493],{},[982,1494,1495],{},"\u002Fi18n\u002Freport-keys",[1088,1497,1498,1499,1502],{},"Auto-register UI-label keys a frontend uses (text write-once; optional ",[982,1500,1501],{},"context"," note upserts)",[979,1504,1505,1506,1509,1510,1513],{},"Query params: ",[982,1507,1508],{},"locale"," (defaults to the resolved request locale, then the configured\ndefault) and ",[982,1511,1512],{},"ns"," (defaults to empty).",[991,1515,1517],{"className":993,"code":1516,"language":995,"meta":996,"style":996},"curl '\u002Fapi\u002Fv1\u002Fi18n\u002Fmessages?locale=pl&ns=ui'\n",[982,1518,1519],{"__ignoreMap":996},[1000,1520,1521,1524,1528,1531],{"class":1002,"line":10},[1000,1522,1523],{"class":1005},"curl",[1000,1525,1527],{"class":1526},"sMK4o"," '",[1000,1529,1530],{"class":1009},"\u002Fapi\u002Fv1\u002Fi18n\u002Fmessages?locale=pl&ns=ui",[1000,1532,1533],{"class":1526},"'\n",[991,1535,1539],{"className":1536,"code":1537,"language":1538,"meta":996,"style":996},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"locale\": \"pl\",\n  \"namespace\": \"ui\",\n  \"messages\": { \"checkout.submit\": \"Zamów\", \"cart.empty\": \"Koszyk jest pusty\" }\n}\n","json",[982,1540,1541,1546,1570,1589,1640],{"__ignoreMap":996},[1000,1542,1543],{"class":1002,"line":10},[1000,1544,1545],{"class":1526},"{\n",[1000,1547,1548,1551,1554,1557,1560,1563,1565,1567],{"class":1002,"line":16},[1000,1549,1550],{"class":1526},"  \"",[1000,1552,1508],{"class":1553},"spNyl",[1000,1555,1556],{"class":1526},"\"",[1000,1558,1559],{"class":1526},":",[1000,1561,1562],{"class":1526}," \"",[1000,1564,1056],{"class":1009},[1000,1566,1556],{"class":1526},[1000,1568,1569],{"class":1526},",\n",[1000,1571,1572,1574,1577,1579,1581,1583,1585,1587],{"class":1002,"line":22},[1000,1573,1550],{"class":1526},[1000,1575,1576],{"class":1553},"namespace",[1000,1578,1556],{"class":1526},[1000,1580,1559],{"class":1526},[1000,1582,1562],{"class":1526},[1000,1584,1113],{"class":1009},[1000,1586,1556],{"class":1526},[1000,1588,1569],{"class":1526},[1000,1590,1591,1593,1596,1598,1600,1603,1605,1607,1609,1611,1613,1616,1618,1621,1623,1626,1628,1630,1632,1635,1637],{"class":1002,"line":51},[1000,1592,1550],{"class":1526},[1000,1594,1595],{"class":1553},"messages",[1000,1597,1556],{"class":1526},[1000,1599,1559],{"class":1526},[1000,1601,1602],{"class":1526}," {",[1000,1604,1562],{"class":1526},[1000,1606,1196],{"class":1005},[1000,1608,1556],{"class":1526},[1000,1610,1559],{"class":1526},[1000,1612,1562],{"class":1526},[1000,1614,1615],{"class":1009},"Zamów",[1000,1617,1556],{"class":1526},[1000,1619,1620],{"class":1526},",",[1000,1622,1562],{"class":1526},[1000,1624,1625],{"class":1005},"cart.empty",[1000,1627,1556],{"class":1526},[1000,1629,1559],{"class":1526},[1000,1631,1562],{"class":1526},[1000,1633,1634],{"class":1009},"Koszyk jest pusty",[1000,1636,1556],{"class":1526},[1000,1638,1639],{"class":1526}," }\n",[1000,1641,1642],{"class":1002,"line":57},[1000,1643,1644],{"class":1526},"}\n",[979,1646,1647,1648,1651],{},"The response is the ",[1091,1649,1650],{},"merged"," catalog — the chain is walked least-specific first so the\nrequested locale overwrites fallback entries. A storefront fetches this once per locale\nand renders UI copy from it.",[1151,1653,1655,1656,1233],{"id":1654},"admin-i18nadmin","Admin (",[982,1657,1045],{},[979,1659,1660,1661,1664,1665,1668,1669,1100],{},"Mounted under ",[982,1662,1663],{},"\u002Fadmin\u002Fi18n",", gated by ",[982,1666,1667],{},"auth.RequireActiveUser"," +\n",[982,1670,1671],{},"auth.RequirePermission(\"admin:access\")",[1067,1673,1674,1684],{},[1070,1675,1676],{},[1073,1677,1678,1680,1682],{},[1076,1679,1461],{},[1076,1681,1464],{},[1076,1683,1346],{},[1083,1685,1686,1706,1720,1741,1755,1774,1788,1806],{},[1073,1687,1688,1692,1697],{},[1088,1689,1690],{},[982,1691,1475],{},[1088,1693,1694],{},[982,1695,1696],{},"\u002Fadmin\u002Fi18n\u002Fmessages",[1088,1698,1699,1700,1702,1703,1705],{},"List messages, optional ",[982,1701,1512],{}," \u002F ",[982,1704,1508],{}," filters",[1073,1707,1708,1713,1717],{},[1088,1709,1710],{},[982,1711,1712],{},"PUT",[1088,1714,1715],{},[982,1716,1696],{},[1088,1718,1719],{},"Create or update one message",[1073,1721,1722,1727,1731],{},[1088,1723,1724],{},[982,1725,1726],{},"DELETE",[1088,1728,1729],{},[982,1730,1696],{},[1088,1732,1733,1734,1737,1738,1740],{},"Delete one message (",[982,1735,1736],{},"key"," + ",[982,1739,1508],{}," required)",[1073,1742,1743,1747,1752],{},[1088,1744,1745],{},[982,1746,1475],{},[1088,1748,1749],{},[982,1750,1751],{},"\u002Fadmin\u002Fi18n\u002Flocales",[1088,1753,1754],{},"List locales (a project's supported languages)",[1073,1756,1757,1766,1771],{},[1088,1758,1759,1761,1762,1761,1764],{},[982,1760,1490],{},"\u002F",[982,1763,1712],{},[982,1765,1726],{},[1088,1767,1768],{},[982,1769,1770],{},"\u002Fadmin\u002Fi18n\u002Flocales[\u002F{id}]",[1088,1772,1773],{},"Manage the locale registry",[1073,1775,1776,1780,1785],{},[1088,1777,1778],{},[982,1779,1475],{},[1088,1781,1782],{},[982,1783,1784],{},"\u002Fadmin\u002Fi18n\u002Ftranslations",[1088,1786,1787],{},"List translation keys (each key with every locale)",[1073,1789,1790,1794,1799],{},[1088,1791,1792],{},[982,1793,1490],{},[1088,1795,1796],{},[982,1797,1798],{},"\u002Fadmin\u002Fi18n\u002Ftranslations\u002Ftranslate-missing",[1088,1800,1801,1802,1805],{},"Bulk auto-translate keys into every active locale via the ",[982,1803,1804],{},"ai"," module",[1073,1807,1808,1814,1828],{},[1088,1809,1810,1761,1812],{},[982,1811,1475],{},[982,1813,1490],{},[1088,1815,1816,1114,1819,1114,1822,1114,1825],{},[982,1817,1818],{},"\u002Fadmin\u002Fi18n\u002Ftranslations\u002Fpending",[982,1820,1821],{},"…\u002Fpending\u002Fapprove",[982,1823,1824],{},"…\u002Fpending\u002Freject",[982,1826,1827],{},"…\u002Freview",[1088,1829,1830,1831],{},"Review gate queue — see ",[1832,1833,1835],"a",{"href":1834},"#the-review-gate","the review gate",[979,1837,1838,1839,1842,1843,1846],{},"The admin surface backs the ",[1091,1840,1841],{},"Languages"," module in the ",[982,1844,1845],{},"@redelay\u002Fjs-admin"," base\nadmin (Locales manager + Translations catalog with a per-key AI-translate editor).",[1063,1848,1850],{"id":1849},"service-api","Service API",[979,1852,1853,1854,1857,1858,1559],{},"Obtain it from the module via ",[982,1855,1856],{},"Translations()",", or discover the module in ",[982,1859,1860],{},"Configure()",[991,1862,1864],{"className":1016,"code":1863,"language":1006,"meta":996,"style":996},"func (m *Module) Configure(registry *modules.Registry) error {\n    if mod := registry.Get(\"i18n\"); mod != nil {\n        if i, ok := mod.(*i18n.Module); ok { m.i18n = i }\n    }\n    return nil\n}\n",[982,1865,1866,1871,1876,1881,1886,1891],{"__ignoreMap":996},[1000,1867,1868],{"class":1002,"line":10},[1000,1869,1870],{},"func (m *Module) Configure(registry *modules.Registry) error {\n",[1000,1872,1873],{"class":1002,"line":16},[1000,1874,1875],{},"    if mod := registry.Get(\"i18n\"); mod != nil {\n",[1000,1877,1878],{"class":1002,"line":22},[1000,1879,1880],{},"        if i, ok := mod.(*i18n.Module); ok { m.i18n = i }\n",[1000,1882,1883],{"class":1002,"line":51},[1000,1884,1885],{},"    }\n",[1000,1887,1888],{"class":1002,"line":57},[1000,1889,1890],{},"    return nil\n",[1000,1892,1893],{"class":1002,"line":102},[1000,1894,1644],{},[1067,1896,1897,1905],{},[1070,1898,1899],{},[1073,1900,1901,1903],{},[1076,1902,1461],{},[1076,1904,1346],{},[1083,1906,1907,1917,1927,1937,1947,1957],{},[1073,1908,1909,1914],{},[1088,1910,1911],{},[982,1912,1913],{},"Translate(ctx, ns, key, args)",[1088,1915,1916],{},"Translate using the context locale",[1073,1918,1919,1924],{},[1088,1920,1921],{},[982,1922,1923],{},"TranslateLocale(ctx, ns, locale, key, args)",[1088,1925,1926],{},"Translate in an explicit locale",[1073,1928,1929,1934],{},[1088,1930,1931],{},[982,1932,1933],{},"Messages(ctx, ns, locale)",[1088,1935,1936],{},"Merged catalog for a namespace + locale",[1073,1938,1939,1944],{},[1088,1940,1941],{},[982,1942,1943],{},"Upsert(ctx, ns, key, locale, value)",[1088,1945,1946],{},"Write + invalidate the affected cache bucket",[1073,1948,1949,1954],{},[1088,1950,1951],{},[982,1952,1953],{},"Delete(ctx, ns, key, locale)",[1088,1955,1956],{},"Delete + invalidate",[1073,1958,1959,1964],{},[1088,1960,1961],{},[982,1962,1963],{},"InvalidateAll()",[1088,1965,1966],{},"Drop the whole cache",[1151,1968,1970],{"id":1969},"caching","Caching",[979,1972,1973,1974,1976,1977,1980,1981,1761,1984,1987],{},"The Service caches per ",[982,1975,1148],{}," bucket behind an ",[982,1978,1979],{},"RWMutex",", loading on miss.\nWrites through ",[982,1982,1983],{},"Upsert",[982,1985,1986],{},"Delete"," invalidate only the affected bucket.",[979,1989,1990,1993,1994,1996],{},[1091,1991,1992],{},"The cache is per-process."," A write on one instance does not invalidate peers. In a\nmulti-instance deployment, broadcast a change event and have each instance call\n",[982,1995,1963],{}," — that is what the method exists for.",[1063,1998,2000],{"id":1999},"storage","Storage",[979,2002,2003],{},"One Mongo collection, one unique index:",[991,2005,2008],{"className":2006,"code":2007,"language":1163},[1161],"i18n_ns_key_locale  →  (namespace, key, locale)  unique\n",[982,2009,2007],{"__ignoreMap":996},[979,2011,2012,2013,2016,2017,2019,2020,2023,2024,2027],{},"Created idempotently in ",[982,2014,2015],{},"Startup",". ",[982,2018,1983],{}," is a Mongo upsert against that triple, setting\n",[982,2021,2022],{},"updated_at"," on write and ",[982,2025,2026],{},"created_at"," only on insert.",[1063,2029,2031,2032,1233],{"id":2030},"flowdsl-nodes-i18nflowdsl","FlowDSL nodes (",[982,2033,2034],{},"i18n\u002Fflowdsl",[1067,2036,2037,2052],{},[1070,2038,2039],{},[1073,2040,2041,2044,2047,2050],{},[1076,2042,2043],{},"Node",[1076,2045,2046],{},"Kind",[1076,2048,2049],{},"Outputs",[1076,2051,1346],{},[1083,2053,2054,2075,2092],{},[1073,2055,2056,2061,2064,2072],{},[1088,2057,2058],{},[982,2059,2060],{},"i18n\u002Ftranslate",[1088,2062,2063],{},"transform",[1088,2065,2066,1114,2069],{},[982,2067,2068],{},"Translated",[982,2070,2071],{},"Error",[1088,2073,2074],{},"Resolve a key to a localized value (chain + interpolation)",[1073,2076,2077,2082,2084,2089],{},[1088,2078,2079],{},[982,2080,2081],{},"i18n\u002Fresolve-locale",[1088,2083,2063],{},[1088,2085,2086],{},[982,2087,2088],{},"Resolved",[1088,2090,2091],{},"Return the active locale from context or input",[1073,2093,2094,2099,2102,2105],{},[1088,2095,2096],{},[982,2097,2098],{},"i18n\u002Ffill-translations",[1088,2100,2101],{},"action",[1088,2103,2104],{},"—",[1088,2106,2107,2108],{},"Fill missing \u002F stale translations — see ",[1832,2109,2111],{"href":2110},"#i18nfill-translations-node","below",[979,2113,2114,2117,2118,2121,2122,1559],{},[1091,2115,2116],{},"Both nodes are configured by edge data, not by node settings"," — neither declares a\n",[982,2119,2120],{},"settings_schema",". Supply their inputs with an edge ",[982,2123,2063],{},[1067,2125,2126,2138],{},[1070,2127,2128],{},[1073,2129,2130,2132,2135],{},[1076,2131,2043],{},[1076,2133,2134],{},"Input fields",[1076,2136,2137],{},"Output payload",[1083,2139,2140,2163],{},[1073,2141,2142,2146,2158],{},[1088,2143,2144],{},[982,2145,2060],{},[1088,2147,2148,2150,2151,1114,2153,1114,2155,2157],{},[982,2149,1736],{}," (required), ",[982,2152,1512],{},[982,2154,1508],{},[982,2156,1290],{}," (map)",[1088,2159,2160],{},[982,2161,2162],{},"{value, locale, key}",[1073,2164,2165,2169,2174],{},[1088,2166,2167],{},[982,2168,2081],{},[1088,2170,2171,2173],{},[982,2172,1508],{}," (fallback only)",[1088,2175,2176],{},[982,2177,2178],{},"{locale}",[979,2180,2181,2183,2184,2016,2187,2189,2190,2192,2193,2195,2196,2199,2200,1100],{},[982,2182,1508],{}," is optional on both — when absent, each falls back to\n",[982,2185,2186],{},"i18n.LocaleFromContext(ctx)",[982,2188,2060],{}," emits on the ",[982,2191,2071],{}," port when ",[982,2194,1736],{}," is\nmissing; a key that simply has no translation is ",[1091,2197,2198],{},"not"," an error and returns the key\nitself on ",[982,2201,2068],{},[979,2203,2204],{},"Localizing a notification subject before sending:",[991,2206,2210],{"className":2207,"code":2208,"language":2209,"meta":996,"style":996},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","nodes:\n  - id: subject\n    nodeType: i18n\u002Ftranslate\n  - id: send\n    nodeType: redelay\u002Fnotifications-send\n\nedges:\n  - from: on_order_shipped\n    to:   subject\n    transform:\n      ns:     \"emails\"\n      key:    \"order.shipped.subject\"\n      locale: \"{{.payload.customer_locale}}\"\n      args:\n        Order: \"{{.payload.order_number}}\"\n  - from: subject\n    to:   send\n    when: \"output.name == 'Translated'\"\n    transform:\n      user_id: \"{{.payload.customer_id}}\"\n      title:   \"{{.value}}\"\n","yaml",[982,2211,2212,2221,2234,2244,2255,2264,2268,2275,2287,2297,2304,2319,2334,2348,2355,2369,2379,2388,2402,2408,2422],{"__ignoreMap":996},[1000,2213,2214,2218],{"class":1002,"line":10},[1000,2215,2217],{"class":2216},"swJcz","nodes",[1000,2219,2220],{"class":1526},":\n",[1000,2222,2223,2226,2229,2231],{"class":1002,"line":16},[1000,2224,2225],{"class":1526},"  -",[1000,2227,2228],{"class":2216}," id",[1000,2230,1559],{"class":1526},[1000,2232,2233],{"class":1009}," subject\n",[1000,2235,2236,2239,2241],{"class":1002,"line":22},[1000,2237,2238],{"class":2216},"    nodeType",[1000,2240,1559],{"class":1526},[1000,2242,2243],{"class":1009}," i18n\u002Ftranslate\n",[1000,2245,2246,2248,2250,2252],{"class":1002,"line":51},[1000,2247,2225],{"class":1526},[1000,2249,2228],{"class":2216},[1000,2251,1559],{"class":1526},[1000,2253,2254],{"class":1009}," send\n",[1000,2256,2257,2259,2261],{"class":1002,"line":57},[1000,2258,2238],{"class":2216},[1000,2260,1559],{"class":1526},[1000,2262,2263],{"class":1009}," redelay\u002Fnotifications-send\n",[1000,2265,2266],{"class":1002,"line":102},[1000,2267,1257],{"emptyLinePlaceholder":1256},[1000,2269,2270,2273],{"class":1002,"line":108},[1000,2271,2272],{"class":2216},"edges",[1000,2274,2220],{"class":1526},[1000,2276,2277,2279,2282,2284],{"class":1002,"line":114},[1000,2278,2225],{"class":1526},[1000,2280,2281],{"class":2216}," from",[1000,2283,1559],{"class":1526},[1000,2285,2286],{"class":1009}," on_order_shipped\n",[1000,2288,2289,2292,2294],{"class":1002,"line":120},[1000,2290,2291],{"class":2216},"    to",[1000,2293,1559],{"class":1526},[1000,2295,2296],{"class":1009},"   subject\n",[1000,2298,2299,2302],{"class":1002,"line":181},[1000,2300,2301],{"class":2216},"    transform",[1000,2303,2220],{"class":1526},[1000,2305,2306,2309,2311,2314,2316],{"class":1002,"line":186},[1000,2307,2308],{"class":2216},"      ns",[1000,2310,1559],{"class":1526},[1000,2312,2313],{"class":1526},"     \"",[1000,2315,1120],{"class":1009},[1000,2317,2318],{"class":1526},"\"\n",[1000,2320,2321,2324,2326,2329,2332],{"class":1002,"line":191},[1000,2322,2323],{"class":2216},"      key",[1000,2325,1559],{"class":1526},[1000,2327,2328],{"class":1526},"    \"",[1000,2330,2331],{"class":1009},"order.shipped.subject",[1000,2333,2318],{"class":1526},[1000,2335,2336,2339,2341,2343,2346],{"class":1002,"line":197},[1000,2337,2338],{"class":2216},"      locale",[1000,2340,1559],{"class":1526},[1000,2342,1562],{"class":1526},[1000,2344,2345],{"class":1009},"{{.payload.customer_locale}}",[1000,2347,2318],{"class":1526},[1000,2349,2350,2353],{"class":1002,"line":203},[1000,2351,2352],{"class":2216},"      args",[1000,2354,2220],{"class":1526},[1000,2356,2357,2360,2362,2364,2367],{"class":1002,"line":214},[1000,2358,2359],{"class":2216},"        Order",[1000,2361,1559],{"class":1526},[1000,2363,1562],{"class":1526},[1000,2365,2366],{"class":1009},"{{.payload.order_number}}",[1000,2368,2318],{"class":1526},[1000,2370,2371,2373,2375,2377],{"class":1002,"line":223},[1000,2372,2225],{"class":1526},[1000,2374,2281],{"class":2216},[1000,2376,1559],{"class":1526},[1000,2378,2233],{"class":1009},[1000,2380,2381,2383,2385],{"class":1002,"line":229},[1000,2382,2291],{"class":2216},[1000,2384,1559],{"class":1526},[1000,2386,2387],{"class":1009},"   send\n",[1000,2389,2390,2393,2395,2397,2400],{"class":1002,"line":234},[1000,2391,2392],{"class":2216},"    when",[1000,2394,1559],{"class":1526},[1000,2396,1562],{"class":1526},[1000,2398,2399],{"class":1009},"output.name == 'Translated'",[1000,2401,2318],{"class":1526},[1000,2403,2404,2406],{"class":1002,"line":240},[1000,2405,2301],{"class":2216},[1000,2407,2220],{"class":1526},[1000,2409,2410,2413,2415,2417,2420],{"class":1002,"line":246},[1000,2411,2412],{"class":2216},"      user_id",[1000,2414,1559],{"class":1526},[1000,2416,1562],{"class":1526},[1000,2418,2419],{"class":1009},"{{.payload.customer_id}}",[1000,2421,2318],{"class":1526},[1000,2423,2424,2427,2429,2432,2435],{"class":1002,"line":251},[1000,2425,2426],{"class":2216},"      title",[1000,2428,1559],{"class":1526},[1000,2430,2431],{"class":1526},"   \"",[1000,2433,2434],{"class":1009},"{{.value}}",[1000,2436,2318],{"class":1526},[2438,2439,2440,2441,2443],"callout",{"icon":11},"Because neither node declares a ",[982,2442,2120],{},", Studio renders them with an empty\nconfig panel — all wiring happens on the edges. Worth knowing before you go looking for\nnode settings that aren't there.",[1063,2445,2447],{"id":2446},"frontend-ui-label-i18n-customer-apps","Frontend UI-label i18n (customer apps)",[979,2449,2450,2451,2454,2455,1668,2458,1737,2461,1100],{},"The catalog also drives a customer app's ",[1091,2452,2453],{},"own UI chrome"," (buttons, headings,\nplaceholders, toasts) — not just backend-localized content. The pattern is\nprovider-agnostic and reusable across every Nuxt customer app on the framework;\nthe starter-kit skeleton ships it under ",[982,2456,2457],{},"app\u002Fplugins\u002Fi18n.ts",[982,2459,2460],{},"app\u002Fcomposables\u002FuseT.ts",[982,2462,2463],{},"scripts\u002Fi18n-extract.mjs",[979,2465,2466,2473,2474,2476,2477,2480,2481,2484,2485,1559],{},[1091,2467,2468,2469,2472],{},"1. A ",[982,2470,2471],{},"$t(key, default)"," helper."," A Nuxt plugin loads the catalog for the\nactive ",[982,2475,1219],{}," cookie from ",[982,2478,2479],{},"GET \u002Fi18n\u002Fmessages?locale=\u003Clang>"," ",[1091,2482,2483],{},"server-side"," (so\nlabels render translated on first paint, then hydrate), and provides a global\n",[982,2486,2487],{},"$t",[991,2489,2493],{"className":2490,"code":2491,"language":2492,"meta":996,"style":996},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Ch1>{{ $t('home.hero.title', 'Track Your Fitness Journey') }}\u003C\u002Fh1>\n","vue",[982,2494,2495],{"__ignoreMap":996},[1000,2496,2497,2500,2502,2505,2509,2512,2514],{"class":1002,"line":10},[1000,2498,2499],{"class":1526},"\u003C",[1000,2501,974],{"class":2216},[1000,2503,2504],{"class":1526},">",[1000,2506,2508],{"class":2507},"sTEyZ","{{ $t('home.hero.title', 'Track Your Fitness Journey') }}",[1000,2510,2511],{"class":1526},"\u003C\u002F",[1000,2513,974],{"class":2216},[1000,2515,2516],{"class":1526},">\n",[979,2518,2519,2521,2522,2525,2526,2529,2530,1559],{},[982,2520,2487],{}," returns the catalog value, else the ",[1091,2523,2524],{},"inline English default",", else the\nkey. The default is the source-of-truth text — no separate ",[982,2527,2528],{},"en.json"," file. Keep\ninterpolated variables in the template, outside ",[982,2531,2487],{},[991,2533,2535],{"className":2490,"code":2534,"language":2492,"meta":996,"style":996},"{{ $t('dashboard.greeting', 'Welcome back') }}, {{ user.name }}\n",[982,2536,2537],{"__ignoreMap":996},[1000,2538,2539],{"class":1002,"line":10},[1000,2540,2534],{"class":2507},[979,2542,2543,2544,2546,2547,2550,2551,1668,2554,2556],{},"Switching language flips the ",[982,2545,1219],{}," cookie and reloads, so the plugin re-runs\nwith the new catalog. The Nitro ",[982,2548,2549],{},"\u002Fapi\u002Fv1"," proxy forwards the cookie as ",[982,2552,2553],{},"?lang",[982,2555,1225],{},", so backend-localized content follows the same switch.",[979,2558,2559,2562,2563,2566,2567,2569,2570,2573,2574,2576],{},[1091,2560,2561],{},"2. Auto-detection of new keys."," In dev, any key the app renders that is ",[2564,2565,2198],"em",{},"\nin the catalog is batched and POSTed to ",[982,2568,1408],{}," (write-once —\nnever overwrites a curated key), so new labels appear in the admin ",[1091,2571,2572],{},"Translations","\ncatalog as the app is used. The write path is gated by ",[982,2575,1397],{}," and\nis off in production; there, a build-time extractor seeds keys deterministically:",[991,2578,2580],{"className":993,"code":2579,"language":995,"meta":996,"style":996},"npm run i18n:seed        # scan $t('key','default') sites → POST \u002Fi18n\u002Freport-keys\n",[982,2581,2582],{"__ignoreMap":996},[1000,2583,2584,2587,2590,2593],{"class":1002,"line":10},[1000,2585,2586],{"class":1005},"npm",[1000,2588,2589],{"class":1009}," run",[1000,2591,2592],{"class":1009}," i18n:seed",[1000,2594,2596],{"class":2595},"sHwdD","        # scan $t('key','default') sites → POST \u002Fi18n\u002Freport-keys\n",[979,2598,2599,2602],{},[1091,2600,2601],{},"3. Auto-translation."," Once keys exist (in English), an admin bulk-fills every\nactive locale from one action:",[991,2604,2606],{"className":993,"code":2605,"language":995,"meta":996,"style":996},"curl -X POST \u002Fapi\u002Fv1\u002Fadmin\u002Fi18n\u002Ftranslations\u002Ftranslate-missing \\\n  -H 'Authorization: Bearer \u003Cadmin>' -d '{}'\n# → {\"keysProcessed\":40,\"localesFilled\":960,...}  (re-run until keysProcessed is 0)\n",[982,2607,2608,2624,2647],{"__ignoreMap":996},[1000,2609,2610,2612,2615,2618,2621],{"class":1002,"line":10},[1000,2611,1523],{"class":1005},[1000,2613,2614],{"class":1009}," -X",[1000,2616,2617],{"class":1009}," POST",[1000,2619,2620],{"class":1009}," \u002Fapi\u002Fv1\u002Fadmin\u002Fi18n\u002Ftranslations\u002Ftranslate-missing",[1000,2622,2623],{"class":2507}," \\\n",[1000,2625,2626,2629,2631,2634,2637,2640,2642,2645],{"class":1002,"line":16},[1000,2627,2628],{"class":1009},"  -H",[1000,2630,1527],{"class":1526},[1000,2632,2633],{"class":1009},"Authorization: Bearer \u003Cadmin>",[1000,2635,2636],{"class":1526},"'",[1000,2638,2639],{"class":1009}," -d",[1000,2641,1527],{"class":1526},[1000,2643,2644],{"class":1009},"{}",[1000,2646,1533],{"class":1526},[1000,2648,2649],{"class":1002,"line":22},[1000,2650,2651],{"class":2595},"# → {\"keysProcessed\":40,\"localesFilled\":960,...}  (re-run until keysProcessed is 0)\n",[979,2653,2654,2655,2657],{},"It translates each key's source text into the locales it's missing via the ",[982,2656,1804],{},"\nmodule (Ollama\u002FOpenAI\u002F…), and is idempotent — already-filled locales are skipped.\nThe admin Translations catalog also offers per-key AI translation inline.",[979,2659,2660,2661,2668],{},"The end-to-end loop: ",[1091,2662,2663,2664,2667],{},"write ",[982,2665,2666],{},"$t('key','English')"," → key auto-registers → admin\ntranslate-missing → every locale filled → users see translated UI."," Adding a new\nlanguage is: create the locale in the admin, run translate-missing, done.",[1151,2670,2672],{"id":2671},"grammar-context-dont-translate-bare-words","Grammar & context — don't translate bare words",[979,2674,2675,2676,2679,2680,2683,2684,2687],{},"Translating an isolated word loses grammatical context. ",[982,2677,2678],{},"\"Days\""," alone is\nnominative (",[982,2681,2682],{},"Дни","), but after a number Russian needs the genitive (",[982,2685,2686],{},"5 дней",").\nTwo complementary fixes:",[979,2689,2690,2693,2694,2697,2698,2700,2701,2704],{},[1091,2691,2692],{},"Whole-phrase keys with placeholders (preferred)."," Put the variable ",[2564,2695,2696],{},"inside","\nthe string so the model translates the whole phrase and gets agreement right.\n",[982,2699,2487],{}," interpolates ",[982,2702,2703],{},"{token}"," params and the translate prompt preserves them:",[991,2706,2708],{"className":2490,"code":2707,"language":2492,"meta":996,"style":996},"\u003C!-- ✗ splits the number from the word — \"Days\" is translated in isolation -->\n{{ streak }} {{ $t('dashboard.days', 'Days') }}\n\u003C!-- ✓ one phrase — model sees the count, returns \"{count} дней\" -->\n{{ $t('dashboard.streakDays', '{count} Days', { count: streak }) }}\n",[982,2709,2710,2715,2720,2725],{"__ignoreMap":996},[1000,2711,2712],{"class":1002,"line":10},[1000,2713,2714],{"class":2595},"\u003C!-- ✗ splits the number from the word — \"Days\" is translated in isolation -->\n",[1000,2716,2717],{"class":1002,"line":16},[1000,2718,2719],{"class":2507},"{{ streak }} {{ $t('dashboard.days', 'Days') }}\n",[1000,2721,2722],{"class":1002,"line":22},[1000,2723,2724],{"class":2595},"\u003C!-- ✓ one phrase — model sees the count, returns \"{count} дней\" -->\n",[1000,2726,2727],{"class":1002,"line":51},[1000,2728,2729],{"class":2507},"{{ $t('dashboard.streakDays', '{count} Days', { count: streak }) }}\n",[979,2731,2732,2735,2736,2739,2740,2742,2743,2748,2749,2752],{},[1091,2733,2734],{},"ICU plural for full CLDR pluralization."," For languages with several plural\nforms (Russian ",[2564,2737,2738],{},"1 день \u002F 2 дня \u002F 5 дней",", Polish, Arabic's six forms), write an\nICU MessageFormat plural in the source; ",[982,2741,2487],{}," selects the branch with the\nbrowser-native ",[1091,2744,2745],{},[982,2746,2747],{},"Intl.PluralRules"," (full CLDR categories per locale — no\ndependency), replacing ",[982,2750,2751],{},"#"," with the count:",[991,2754,2756],{"className":2490,"code":2755,"language":2492,"meta":996,"style":996},"{{ $t('dashboard.streakDays', '{count, plural, one {# Day} other {# Days}}', { count: streak }) }}\n",[982,2757,2758],{"__ignoreMap":996},[1000,2759,2760],{"class":1002,"line":10},[1000,2761,2755],{"class":2507},[979,2763,2764,2765,2768,2769,2772,2773,2776,2777,2780,2781,2784,2785,1761,2787,2789],{},"The AI translate prompt recognises the ICU plural and emits the target\nlanguage's own categories, e.g. Russian\n",[982,2766,2767],{},"{count, plural, one {# день} few {# дня} many {# дней} other {# дней}}"," — so\n",[982,2770,2771],{},"Intl.PluralRules('ru').select(21) → 'one'"," renders ",[2564,2774,2775],{},"21 день"," correctly. ",[982,2778,2779],{},"=N","\nexact cases are supported (",[982,2782,2783],{},"=0 {No days}","). A locale whose value is plain text\n(no plural block) degrades to plain ",[982,2786,2703],{},[982,2788,2751],{}," substitution.",[979,2791,2792,2795,2796,2798,2799,2802,2803,2806,2807,2810,2811,2814,2815,2818,2819,2822,2823,2825],{},[1091,2793,2794],{},"Per-key context hint."," When a whole phrase isn't possible, each translation\nkey carries an optional ",[1091,2797,1501],{}," field (",[982,2800,2801],{},"Translation.Context",") injected into\nthe AI prompt. Set it in the admin editor — ",[2564,2804,2805],{},"\"appears as '{count} Days', a\ncount\""," — and hit the row's ",[1091,2808,2809],{},"Re-translate"," button (re-runs\n",[982,2812,2813],{},"translate-missing"," for that key with ",[982,2816,2817],{},"overwrite:true","). The ",[982,2820,2821],{},"ai\u002Ftranslate","\ncontract takes the same ",[982,2824,1501],{}," field, and the prompt instructs the model to\nhonour it for agreement, gender, and pluralisation.",[1151,2827,2829],{"id":2828},"how-to-declare-translation-context","How to declare translation context",[979,2831,2832,2833,2835],{},"Two places carry a ",[982,2834,1501],{}," hint — one for admin data fields, one for UI-label\nkeys. Both feed the same AI prompt.",[979,2837,2838,2841,2842,2844,2845,2848,2849,2851,2852,2854,2855,1559],{},[1091,2839,2840],{},"Admin localized data fields."," Declare ",[982,2843,1501],{}," per field in the module's\n",[982,2846,2847],{},"ResourceConfig"," form to disambiguate the AI ",[1091,2850,562],{}," button on\n",[982,2853,1845],{},"'s ",[982,2856,2857],{},"LocalizedField",[991,2859,2863],{"className":2860,"code":2861,"language":2862,"meta":996,"style":996},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F In a module's ResourceConfig form field (any module using LocalizedField):\n{ key: 'name', type: 'custom', component: 'LocalizedFieldAuto',\n  props: {\n    label: 'muscle group name',   \u002F\u002F used as fallback context if `context` is unset\n    context: 'A human-body muscle group name (e.g. Back, Chest) — the body part, not a direction\u002Fverb.'\n  } }\n","ts",[982,2864,2865,2870,2917,2927,2946,2960],{"__ignoreMap":996},[1000,2866,2867],{"class":1002,"line":10},[1000,2868,2869],{"class":2595},"\u002F\u002F In a module's ResourceConfig form field (any module using LocalizedField):\n",[1000,2871,2872,2875,2878,2880,2882,2885,2887,2889,2892,2894,2896,2899,2901,2903,2906,2908,2910,2913,2915],{"class":1002,"line":16},[1000,2873,2874],{"class":1526},"{",[1000,2876,2877],{"class":1005}," key",[1000,2879,1559],{"class":1526},[1000,2881,1527],{"class":1526},[1000,2883,2884],{"class":1009},"name",[1000,2886,2636],{"class":1526},[1000,2888,1620],{"class":1526},[1000,2890,2891],{"class":1005}," type",[1000,2893,1559],{"class":1526},[1000,2895,1527],{"class":1526},[1000,2897,2898],{"class":1009},"custom",[1000,2900,2636],{"class":1526},[1000,2902,1620],{"class":1526},[1000,2904,2905],{"class":1005}," component",[1000,2907,1559],{"class":1526},[1000,2909,1527],{"class":1526},[1000,2911,2912],{"class":1009},"LocalizedFieldAuto",[1000,2914,2636],{"class":1526},[1000,2916,1569],{"class":1526},[1000,2918,2919,2922,2924],{"class":1002,"line":22},[1000,2920,2921],{"class":1005},"  props",[1000,2923,1559],{"class":1526},[1000,2925,2926],{"class":1526}," {\n",[1000,2928,2929,2932,2934,2936,2939,2941,2943],{"class":1002,"line":51},[1000,2930,2931],{"class":1005},"    label",[1000,2933,1559],{"class":1526},[1000,2935,1527],{"class":1526},[1000,2937,2938],{"class":1009},"muscle group name",[1000,2940,2636],{"class":1526},[1000,2942,1620],{"class":1526},[1000,2944,2945],{"class":2595},"   \u002F\u002F used as fallback context if `context` is unset\n",[1000,2947,2948,2951,2953,2955,2958],{"class":1002,"line":57},[1000,2949,2950],{"class":1005},"    context",[1000,2952,1559],{"class":1526},[1000,2954,1527],{"class":1526},[1000,2956,2957],{"class":1009},"A human-body muscle group name (e.g. Back, Chest) — the body part, not a direction\u002Fverb.",[1000,2959,1533],{"class":1526},[1000,2961,2962,2965],{"class":1002,"line":102},[1000,2963,2964],{"class":1526},"  }",[1000,2966,1639],{"class":1526},[979,2968,2969,2970,2972,2973,2976,2977,2979,2980,2983,2984,2986,2987,2990,2991,2994,2995,2998],{},"The ",[982,2971,1501],{}," prop threads ",[982,2974,2975],{},"LocalizedField → AiFieldActions → useAiContent.translate → POST \u002Fadmin\u002Fai\u002Ftranslate"," (as the request body's\n",[982,2978,1501],{}," field), falling back to the field ",[982,2981,2982],{},"label"," when ",[982,2985,1501],{}," is unset.\nWithout it the muscle group ",[1091,2988,2989],{},"\"Back\""," translates to ",[2564,2992,2993],{},"Zurück \u002F Atrás \u002F Назад","\n(the direction); with it, ",[2564,2996,2997],{},"Rücken \u002F Espalda \u002F Спина"," (the body part).",[979,3000,3001,3004,3005,3007,3008,3011,3012,3014,3015,3018,3019,3021,3022,3024,3025,3027,3028,3030],{},[1091,3002,3003],{},"Per-key catalog context (UI-label keys)."," For ",[982,3006,2487],{}," keys, set the\n",[1091,3009,3010],{},"Translation context (AI hint)"," field on a key in the admin ",[1091,3013,2572],{},"\neditor (or ",[982,3016,3017],{},"POST \u002Fadmin\u002Fi18n\u002Ftranslations"," with a ",[982,3020,1501],{}," field), then hit the\nrow's ",[1091,3023,2809],{}," button. The hint persists on ",[982,3026,2801],{}," and is\ninjected into the prompt by both ",[982,3029,2813],{}," and per-row re-translate.",[979,3032,3033,3039,3040,3043,3044,3047],{},[1091,3034,3035,3036,3038],{},"Frontend-owned context (recommended for ",[982,3037,2487],{}," keys)."," Rather than curating the\nhint per key in the admin, a customer app can own it in source. Add\n",[982,3041,3042],{},"i18n-contexts.json"," next to the extractor — a map of a key (or a trailing-",[982,3045,3046],{},"*","\nprefix glob) to a free-form note; the most specific match wins:",[991,3049,3053],{"className":3050,"code":3051,"language":3052,"meta":996,"style":996},"language-jsonc shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F nuxt\u002Fi18n-contexts.json\n{\n  \"tracking.set*\":         \"Gym term: a SET is one group of reps. Not a kit\u002Fcollection, not the verb 'to set'. Sample — EN: \\\"Set {n}\\\"; RU: \\\"Сет {n}\\\".\",\n  \"dashboard.metric_volume\": \"Training VOLUME = total weight lifted across all sets (tonnage). Not sound\u002Fgeometric volume. Sample — EN: \\\"Volume\\\"; RU: \\\"Общий вес\\\".\"\n}\n","jsonc",[982,3054,3055,3060,3064,3069,3074],{"__ignoreMap":996},[1000,3056,3057],{"class":1002,"line":10},[1000,3058,3059],{},"\u002F\u002F nuxt\u002Fi18n-contexts.json\n",[1000,3061,3062],{"class":1002,"line":16},[1000,3063,1545],{},[1000,3065,3066],{"class":1002,"line":22},[1000,3067,3068],{},"  \"tracking.set*\":         \"Gym term: a SET is one group of reps. Not a kit\u002Fcollection, not the verb 'to set'. Sample — EN: \\\"Set {n}\\\"; RU: \\\"Сет {n}\\\".\",\n",[1000,3070,3071],{"class":1002,"line":51},[1000,3072,3073],{},"  \"dashboard.metric_volume\": \"Training VOLUME = total weight lifted across all sets (tonnage). Not sound\u002Fgeometric volume. Sample — EN: \\\"Volume\\\"; RU: \\\"Общий вес\\\".\"\n",[1000,3075,3076],{"class":1002,"line":57},[1000,3077,1644],{},[979,3079,3080,3081,3084,3085,3088,3089,3092,3093,3096,3097,3100,3101,3103,3104,3106,3107,3109,3110,3112,3113,3116,3117,3120,3121,3124,3125,3127,3128,3130],{},"Include ",[1091,3082,3083],{},"sample EN + target-language translations"," in the note for ambiguous\nterms — the model few-shots on them and renders the right domain sense in ",[2564,3086,3087],{},"every","\nlocale (fixes ",[982,3090,3091],{},"Set","→kit, ",[982,3094,3095],{},"Resume","→CV, ",[982,3098,3099],{},"Volume","→loudness, ",[982,3102,398],{},"→timber\u002Fjournal).\nThe extractor resolves each key's note and sends it as ",[982,3105,1501],{}," on\n",[982,3108,1408],{},", which — unlike the write-once ",[1091,3111,1163],{}," — ",[1091,3114,3115],{},"upserts"," the\nnote on new ",[2564,3118,3119],{},"and"," existing keys (",[982,3122,3123],{},"ReportKeysResponse.contextUpdated"," counts the\nsyncs). Re-run the extractor after editing the file, then ",[982,3126,2813],{},"\n(with ",[982,3129,2817],{}," to replace already-translated locales). The frontend\nregistry is then the source of truth; admin edits to a covered key's context are\noverwritten on the next extractor run.",[1151,3132,3134],{"id":3133},"sharing-keys-between-frontends","Sharing keys between frontends",[979,3136,3137,3138,3141],{},"A second surface — a native app, a watch app, a kiosk — usually renders strings\nthe web app already has. It should reuse those ",[1091,3139,3140],{},"keys"," rather than introduce\nparallel ones: the string is then translated once for both, and a wording change\nmoves both at the same time.",[979,3143,3144,3145,3148,3149,1559],{},"Report a shared key with your own ",[982,3146,3147],{},"module",". Modules ",[1091,3150,3151],{},"accumulate",[991,3153,3155],{"className":3050,"code":3154,"language":3052,"meta":996,"style":996},"\u002F\u002F the web app, at build time\n{\"key\": \"tracking.addSet\", \"text\": \"Add set\", \"module\": \"frontend\"}\n\u002F\u002F the watch app, later — same key, second module\n{\"key\": \"tracking.addSet\", \"text\": \"Add set\", \"module\": \"watch\"}\n\u002F\u002F stored: modules: [\"frontend\", \"watch\"]\n",[982,3156,3157,3162,3167,3172,3177],{"__ignoreMap":996},[1000,3158,3159],{"class":1002,"line":10},[1000,3160,3161],{},"\u002F\u002F the web app, at build time\n",[1000,3163,3164],{"class":1002,"line":16},[1000,3165,3166],{},"{\"key\": \"tracking.addSet\", \"text\": \"Add set\", \"module\": \"frontend\"}\n",[1000,3168,3169],{"class":1002,"line":22},[1000,3170,3171],{},"\u002F\u002F the watch app, later — same key, second module\n",[1000,3173,3174],{"class":1002,"line":51},[1000,3175,3176],{},"{\"key\": \"tracking.addSet\", \"text\": \"Add set\", \"module\": \"watch\"}\n",[1000,3178,3179],{"class":1002,"line":57},[1000,3180,3181],{},"\u002F\u002F stored: modules: [\"frontend\", \"watch\"]\n",[979,3183,3184,3185,3188,3189,3192,3193,3196],{},"This is what makes ",[982,3186,3187],{},"GET \u002Fi18n\u002Fmessages?locale=X&ns=watch"," return the keys a\nsurface ",[1091,3190,3191],{},"reuses"," as well as the ones it introduced. Modules used to be written\nonly when a key was ",[2564,3194,3195],{},"created",", so shared keys kept the first reporter's module\nalone and a namespaced fetch silently returned a fraction of what the second\nsurface renders — a catalogue that looks healthy and ships an app in English.\nAdding a module is idempotent, and it never touches translations.",[979,3198,3199,3205],{},[1091,3200,3201,3202,3204],{},"Do not send ",[982,3203,1501],{}," on a key you are reusing."," Unlike text, the note is\nupserted on every report, so two surfaces writing their own notes would\noverwrite each other on alternate deploys. Context belongs to whichever surface\nintroduced the key.",[979,3207,3208,2480,3214,3217,3218,3221,3222,3225],{},[1091,3209,3210,3213],{},[982,3211,3212],{},"ns="," returns raw translations, no fallback chain.",[982,3215,3216],{},"?locale=ru"," resolves a\nmissing key to the source text; ",[982,3219,3220],{},"?locale=ru&ns=watch"," omits it. For a UI that\nrenders live, the first is what you want. For anything ",[1091,3223,3224],{},"building a snapshot"," —\na bundled catalogue, a coverage report — use the namespaced form, or every\nlocale will appear fully translated because the source text was substituted in.",[979,3227,3228,2480,3231,3234,3235,3238],{},[1091,3229,3230],{},"Format integrity.",[982,3232,3233],{},"\u002Fadmin\u002Fai\u002Ftranslate"," validates every returned\ntranslation against the source's format signature — ",[982,3236,3237],{},"{placeholder}"," tokens, ICU\nplural structure, and HTML tags must survive. Corrupted locales are\nre-requested and, if still broken, omitted (the caller keeps the source) rather\nthan persisting markup that renders wrong. ICU-plural sources are translated one\nlocale per call so a single malformed value can't fail the whole batch.",[1063,3240,3242],{"id":3241},"automatic-translation-provenance-flows-and-the-review-gate","Automatic translation: provenance, flows and the review gate",[979,3244,3245,3246,3249,3250,3253,3254,3257],{},"A catalogue that fills itself needs three things a one-off bulk translate does\nnot: a record of ",[1091,3247,3248],{},"who wrote each translation"," (so automation never overwrites\na person), a ",[1091,3251,3252],{},"trigger"," that is not a person pressing a button, and a ",[1091,3255,3256],{},"check","\nbefore machine text reaches users. Each exists because its absence shipped a\ndefect.",[1151,3259,3261],{"id":3260},"provenance","Provenance",[979,3263,3264,3265,3267,3268,3270,3271,2016,3274,3276],{},"Every row carries ",[982,3266,3260],{}," beside ",[982,3269,1387],{}," — locale →\n",[982,3272,3273],{},"{source, by, at, source_hash, context_hash, review}",[982,3275,1387],{}," stays a\nplain map, so no reader changes.",[1067,3278,3279,3294],{},[1070,3280,3281],{},[1073,3282,3283,3288,3291],{},[1076,3284,3285],{},[982,3286,3287],{},"source",[1076,3289,3290],{},"Written by",[1076,3292,3293],{},"Automation may replace it?",[1083,3295,3296,3309,3325,3342],{},[1073,3297,3298,3303,3306],{},[1088,3299,3300],{},[982,3301,3302],{},"machine",[1088,3304,3305],{},"a translate run",[1088,3307,3308],{},"yes",[1073,3310,3311,3316,3319],{},[1088,3312,3313],{},[2564,3314,3315],{},"(absent)",[1088,3317,3318],{},"anything before provenance existed",[1088,3320,3321,3322],{},"only with ",[982,3323,3324],{},"overwrite",[1073,3326,3327,3332,3335],{},[1088,3328,3329],{},[982,3330,3331],{},"human",[1088,3333,3334],{},"an admin edit, an approved correction",[1088,3336,3321,3337,1737,3339],{},[982,3338,3324],{},[982,3340,3341],{},"includeProtected",[1073,3343,3344,3351,3354],{},[1088,3345,3346,1702,3348],{},[982,3347,982],{},[982,3349,3350],{},"seed",[1088,3352,3353],{},"a frontend default, a migration",[1088,3355,3321,3356,1737,3358],{},[982,3357,3324],{},[982,3359,3341],{},[979,3361,3362,3363,3366,3367,3369,3370,3373],{},"Every write ",[1091,3364,3365],{},"sets or clears"," the entry for the locale it touches in the same\nupdate, so a stale ",[982,3368,3331],{}," can never protect text no human wrote. An admin\nwhole-row save marks only the locales whose text ",[2564,3371,3372],{},"changed"," — a form submits all\n25 languages, and marking them all would protect 24 machine strings forever.",[979,3375,3376,1702,3379,3382,3383,3386,3387,3390,3391,3393,3394,3397],{},[982,3377,3378],{},"source_hash",[982,3380,3381],{},"context_hash"," fingerprint the source text and translator note a\nmachine translation was made from. When either changes, the translation is\n",[1091,3384,3385],{},"stale"," and a ",[982,3388,3389],{},"missing_and_stale"," run re-translates it — and only it.\nUnknown-origin text has no fingerprints and is never considered stale: fixing\nold text is an explicit ",[982,3392,3324],{},", scoped with ",[982,3395,3396],{},"locales"," to the languages that\nare actually wrong (fixing Polish «cal» must not re-roll 23 correct locales).",[1151,3399,3401,3404],{"id":3400},"i18nkeys_changed-the-work-signal",[982,3402,3403],{},"i18n.keys_changed"," — the work signal",[979,3406,3407,3408,3411,3412,3415],{},"Published when a translation ",[1091,3409,3410],{},"input"," changes: keys added, source-language\ntext changed, translator note changed (report-keys, ",[982,3413,3414],{},"RegisterMessages",", admin\ncreate\u002Fupdate). Never on a translation write — a flow that fills translations in\nresponse cannot wake itself.",[979,3417,3418,3419,2480,3421,3424],{},"It is deliberately ",[1091,3420,2198],{},[982,3422,3423],{},"i18n.updated",", which is the per-instance cache\nbroadcast: consuming that for work would translate the same keys once per\nreplica.",[1151,3426,3428,3430],{"id":3427},"i18nfill-translations-node",[982,3429,2098],{}," node",[979,3432,3433,3434,3437],{},"Runs the same loop as the admin endpoint and the CLI (needs ",[982,3435,3436],{},"go-modules\u002Fai",").",[1067,3439,3440,3451],{},[1070,3441,3442],{},[1073,3443,3444,3447,3449],{},[1076,3445,3446],{},"Setting",[1076,3448,1343],{},[1076,3450,1081],{},[1083,3452,3453,3467,3481,3499,3511,3526],{},[1073,3454,3455,3459,3461],{},[1088,3456,3457],{},[982,3458,3140],{},[1088,3460,2104],{},[1088,3462,3463,3464],{},"Keys to fill; otherwise the event's ",[982,3465,3466],{},"payload.keys",[1073,3468,3469,3474,3478],{},[1088,3470,3471],{},[982,3472,3473],{},"all_keys",[1088,3475,3476],{},[982,3477,1412],{},[1088,3479,3480],{},"Must be set for an empty key list to mean the whole catalogue",[1073,3482,3483,3488,3493],{},[1088,3484,3485],{},[982,3486,3487],{},"mode",[1088,3489,3490],{},[982,3491,3492],{},"missing",[1088,3494,3495,1172,3497],{},[982,3496,3492],{},[982,3498,3389],{},[1073,3500,3501,3505,3508],{},[1088,3502,3503],{},[982,3504,3396],{},[1088,3506,3507],{},"all active",[1088,3509,3510],{},"Restrict to these languages",[1073,3512,3513,3518,3523],{},[1088,3514,3515],{},[982,3516,3517],{},"limit",[1088,3519,3520],{},[982,3521,3522],{},"200",[1088,3524,3525],{},"Keys per run",[1073,3527,3528,3533,3537],{},[1088,3529,3530],{},[982,3531,3532],{},"review",[1088,3534,3535],{},[982,3536,1402],{},[1088,3538,3539],{},"Run the review gate before writing (below)",[979,3541,3542,3543,3546,3547,3549],{},"There is ",[1091,3544,3545],{},"no overwrite setting",", by design. The most an unattended flow does\nis re-translate stale ",[2564,3548,3302],{}," text.",[2438,3551,3553,3554,3557,3558,3561,3562,3565,3566,3569,3570,3573],{"icon":3552},"i-lucide-triangle-alert","Read node config by ",[1091,3555,3556],{},"kind",", not Go type. A published flow version comes back\nfrom Mongo, so arrays are ",[982,3559,3560],{},"primitive.A"," and small numbers ",[982,3563,3564],{},"int32",". A\n",[982,3567,3568],{},"case []any"," matched nothing: the first real run read ",[982,3571,3572],{},"locales: [pl, de]"," as\nempty and translated into all 25 languages while every literal-based unit test\npassed.",[1151,3575,3577,3580],{"id":3576},"i18nflowtemplates-auto-published-flows",[982,3578,3579],{},"i18n\u002Fflowtemplates"," — auto-published flows",[979,3582,3583,3584,3587,3588,3591],{},"Blank-import ",[982,3585,3586],{},"github.com\u002Fredelay\u002Fgo-modules\u002Fi18n\u002Fflowtemplates",". On startup it\npublishes, via ",[982,3589,3590],{},"EnsureTemplatePublished"," (idempotent by template id,\nre-published when the template changes, never clobbering an operator-edited\nflow):",[1067,3593,3594,3607],{},[1070,3595,3596],{},[1073,3597,3598,3601,3604],{},[1076,3599,3600],{},"Flow",[1076,3602,3603],{},"Trigger",[1076,3605,3606],{},"Fill",[1083,3608,3609,3626],{},[1073,3610,3611,3616,3620],{},[1088,3612,3613],{},[982,3614,3615],{},"i18n\u002Ftranslate-on-change",[1088,3617,3618],{},[982,3619,3403],{},[1088,3621,3622,3623,3625],{},"the event's keys, ",[982,3624,3389],{},", review on",[1073,3627,3628,3633,3638],{},[1088,3629,3630],{},[982,3631,3632],{},"i18n\u002Ftranslate-sweep",[1088,3634,3635],{},[982,3636,3637],{},"i18n.translate.sweep",[1088,3639,3640,1114,3642,3644],{},[982,3641,3473],{},[982,3643,3389],{},", 200 keys, review on",[979,3646,3647,3648,3651],{},"Two flows rather than one because a run walks one path. Each names its own\n",[982,3649,3650],{},"groupID",": a group derived from the flow id would differ between two replicas\npublishing on the same first boot, and every event would be translated twice.",[979,3653,3654,3655,3658,3659,3662],{},"It also seeds a scheduler entry, ",[982,3656,3657],{},"i18n-translate-sweep"," (stable id, UTC), that\npublishes the sweep event. It is ",[1091,3660,3661],{},"never retimed or re-enabled"," once present —\nan operator who paused it during a provider outage has decided something.",[1067,3664,3665,3676],{},[1070,3666,3667],{},[1073,3668,3669,3672,3674],{},[1076,3670,3671],{},"Env",[1076,3673,1343],{},[1076,3675,1346],{},[1083,3677,3678,3693],{},[1073,3679,3680,3685,3690],{},[1088,3681,3682],{},[982,3683,3684],{},"I18N_TRANSLATE_SWEEP_CRON",[1088,3686,3687],{},[982,3688,3689],{},"30 3 * * *",[1088,3691,3692],{},"Sweep schedule (empty = do not seed)",[1073,3694,3695,3700,3704],{},[1088,3696,3697],{},[982,3698,3699],{},"I18N_AUTO_TRANSLATE_DISABLED",[1088,3701,3702],{},[982,3703,1412],{},[1088,3705,3706],{},"Skip publishing the flows and seeding the schedule",[2438,3708,3709,3710,3713,3714,3716],{"icon":11},"flowexec subscribes event-source flows ",[1091,3711,3712],{},"at startup",". A flow published at\nruntime from Studio receives no events until the process restarts.\n",[982,3715,3590],{}," re-syncs subscribers itself, so these templates are\nunaffected.",[1151,3718,3720],{"id":3719},"the-review-gate","The review gate",[979,3722,3723,3724,3727,3728,3731,3732,3735,3736,3739],{},"Every defect found in one week of real catalogue data had the same shape:\nfluent, well-formed, in the right language — and wrong. Polish «cal» on a\ncalorie counter is the word for ",[2564,3725,3726],{},"inch","; «Zalogowano» for \"you logged 3 sets\" is\n",[2564,3729,3730],{},"logged in","; Turkish «km\u002Fs» is per ",[2564,3733,3734],{},"second","; Greek «ημερολ» abbreviates\n",[2564,3737,3738],{},"calendar",". No format or wrong-language check can see them.",[979,3741,3742,3745,3746,3749],{},[982,3743,3744],{},"ai.Service.ReviewTranslations"," asks a model, in one call per key, whether each\ncandidate's ",[1091,3747,3748],{},"meaning"," matches the source given the translator note — wrong\nsense, contradicts note, wrong unit, broken placeholder, untranslated. It is told\nexplicitly not to judge style: a reviewer that proposes improvements flags\neverything, and a gate that holds back everything gets turned off. A rejection\nwhose suggestion equals the candidate is discarded as self-contradicting.",[979,3751,3752],{},"What happens to each translation:",[1067,3754,3755,3768],{},[1070,3756,3757],{},[1073,3758,3759,3762,3765],{},[1076,3760,3761],{},"Verdict",[1076,3763,3764],{},"Key has a translator note",[1076,3766,3767],{},"Result",[1083,3769,3770,3780,3797],{},[1073,3771,3772,3775,3777],{},[1088,3773,3774],{},"passed, or no verdict",[1088,3776,2104],{},[1088,3778,3779],{},"live",[1073,3781,3782,3785,3787],{},[1088,3783,3784],{},"flagged",[1088,3786,3308],{},[1088,3788,3789,3792,3793,3796],{},[1091,3790,3791],{},"held"," in ",[982,3794,3795],{},"pending.\u003Clocale>"," — never served; users see the fallback",[1073,3798,3799,3801,3804],{},[1088,3800,3784],{},[1088,3802,3803],{},"no",[1088,3805,3806,3807],{},"live, flag recorded in ",[982,3808,3809],{},"provenance.\u003Clocale>.review",[979,3811,3812],{},"A reviewer outage never stops translation. The note\u002Fno-note split is measured,\nnot assumed — on 17 known-bad strings (15 real shipped defects) and 87 correct\ncatalogue strings:",[1067,3814,3815,3828],{},[1070,3816,3817],{},[1073,3818,3819,3822,3825],{},[1076,3820,3821],{},"Reviewed with",[1076,3823,3824],{},"Bad caught",[1076,3826,3827],{},"Correct wrongly flagged",[1083,3829,3830,3841],{},[1073,3831,3832,3835,3838],{},[1088,3833,3834],{},"the translator note",[1088,3836,3837],{},"16 \u002F 17",[1088,3839,3840],{},"2 \u002F 87",[1073,3842,3843,3846,3849],{},[1088,3844,3845],{},"no note",[1088,3847,3848],{},"14 \u002F 17",[1088,3850,3851],{},"7 \u002F 87",[979,3853,3854,3855,3858],{},"Without a note the reviewer confidently imposes its own terms (it claimed German\n«Satz» cannot mean a workout set), so holding on its word alone would fill the\nqueue with correct text. With a note, the figure is ",[1091,3856,3857],{},"optimistic",": many notes\nwere written after the defect was found and spell out the answer. The miss was\nexactly that case — Polish «cal» with a plain \"calories\" note. A note that names\nthe trap is what makes the gate catch it. Cost: ~640 prompt + ~80 completion\ntokens per key.",[979,3860,3861,3864],{},[1091,3862,3863],{},"Held and rejected entries block re-translation"," while their source and note\nfingerprints match, so a nightly sweep does not re-bill the same flagged answer.\nEditing the note or the source releases them. Any live write for the locale\nclears its pending entry.",[1151,3866,3868],{"id":3867},"admin-review-queue","Admin review queue",[1067,3870,3871,3881],{},[1070,3872,3873],{},[1073,3874,3875,3877,3879],{},[1076,3876,1461],{},[1076,3878,1464],{},[1076,3880,1346],{},[1083,3882,3883,3896,3919,3933,3955],{},[1073,3884,3885,3889,3893],{},[1088,3886,3887],{},[982,3888,1475],{},[1088,3890,3891],{},[982,3892,1818],{},[1088,3894,3895],{},"Rows with held or rejected translations",[1073,3897,3898,3902,3907],{},[1088,3899,3900],{},[982,3901,1490],{},[1088,3903,3904],{},[982,3905,3906],{},"\u002Fadmin\u002Fi18n\u002Ftranslations\u002Fpending\u002Fapprove",[1088,3908,3909,3912,3913,3916,3917],{},[982,3910,3911],{},"{key, locale, text?}"," — unchanged text goes live as machine (review ",[982,3914,3915],{},"approved","); edited text goes live as ",[1091,3918,3331],{},[1073,3920,3921,3925,3930],{},[1088,3922,3923],{},[982,3924,1490],{},[1088,3926,3927],{},[982,3928,3929],{},"\u002Fadmin\u002Fi18n\u002Ftranslations\u002Fpending\u002Freject",[1088,3931,3932],{},"Keep it out; blocks re-translation until inputs change",[1073,3934,3935,3939,3944],{},[1088,3936,3937],{},[982,3938,1490],{},[1088,3940,3941],{},[982,3942,3943],{},"\u002Fadmin\u002Fi18n\u002Ftranslations\u002Freview",[1088,3945,3946,3947,3950,3951,3954],{},"Read-only review of up to 40 keys; ",[982,3948,3949],{},"items[].candidates"," and ",[982,3952,3953],{},"items[].context"," override the stored values",[1073,3956,3957,3961,3965],{},[1088,3958,3959],{},[982,3960,1490],{},[1088,3962,3963],{},[982,3964,1798],{},[1088,3966,3967,3968,1114,3970,1114,3972,1114,3974,3976],{},"Also takes ",[982,3969,3324],{},[982,3971,3341],{},[982,3973,3396],{},[982,3975,3532],{}," (default off)",[979,3978,3979,3980,3983,3984,3987],{},"The override on ",[982,3981,3982],{},"\u002Freview"," exists to measure the reviewer honestly — test a\nknown-bad string without writing it, or with the note as it was ",[2564,3985,3986],{},"before"," the\ndefect was found.",[979,3989,3990,3992,3993,3996,3997,4000],{},[982,3991,1845],{}," ≥ v0.4.16 renders the queue at ",[1091,3994,3995],{},"Languages → Review","\n(",[982,3998,3999],{},"\u002Ftranslations\u002Freview","): the source, the note, what the reviewer objected to\nand its suggestion, an editable translation, and Approve \u002F Publish correction \u002F\nReject. Correcting beats re-rolling — a re-translation can come back worse.",[979,4002,4003,4004,1100],{},"The CLI mirrors it: ",[982,4005,4006],{},"i18n-translate --include-protected --review",[1063,4008,4010],{"id":4009},"related","Related",[4012,4013,4014,4022],"ul",{},[1209,4015,4016,3112,4018,4021],{},[1832,4017,279],{"href":280},[982,4019,4020],{},"[[ module.key ]]"," settings\ntokens, which are expanded in catalogue messages too (after parameter interpolation,\nso both syntaxes coexist in one string), plus the shared translation lifecycle",[1209,4023,4024,4027,4028,4031],{},[1832,4025,4026],{"href":145},"go-modules addons"," — the full addon module list,\nincluding ",[982,4029,4030],{},"content"," (localized pages and fragments, which resolve locale the same way)",[1063,4033,4035],{"id":4034},"a-locales-url-prefix-is-not-its-language","A locale's URL prefix is not its language",[979,4037,4038,4041,4042,4045,4046,4049,4050,4053,4054,4056,4057,4060],{},[982,4039,4040],{},"Locale.code"," is how a language appears in a URL. ",[982,4043,4044],{},"Locale.language"," is the\nBCP-47 tag it actually is. They are usually the same and sometimes must not be:\na project can reasonably route Ukrainian at ",[982,4047,4048],{},"\u002Fua",", because ",[982,4051,4052],{},"uk"," in a path reads\nas the United Kingdom to a human — while the language is ",[982,4055,4052],{},", and\n",[982,4058,4059],{},"hreflang=\"ua\""," is not a language at all.",[979,4062,4063,4064,1114,4067,1114,4070,1569,4073,4076,4077,3950,4080,2480,4083,4086],{},"Getting this wrong is quiet. Four of one project's prefixes (",[982,4065,4066],{},"cz",[982,4068,4069],{},"dk",[982,4071,4072],{},"gr",[982,4074,4075],{},"ua",") were codes Google discards outright, so those languages carried hreflang\nmarkup that did nothing. The other two were worse: ",[982,4078,4079],{},"ee",[982,4081,4082],{},"se",[1091,4084,4085],{},"are"," valid\nISO 639-1 codes — for Ewe and Northern Sami — so they parsed cleanly and\nadvertised the wrong language, and no validator flags it.",[979,4088,4089,4092,4093,4096,4097,1114,4100,1114,4103,4106,4107,4110],{},[982,4090,4091],{},"LanguageTag()"," is never empty: an unset ",[982,4094,4095],{},"Language"," falls back to an alias\ntable (",[982,4098,4099],{},"cz→cs",[982,4101,4102],{},"ee→et",[982,4104,4105],{},"se→sv",", …) and then to the code itself. The table is\na ",[1091,4108,4109],{},"default, not a rule"," — it lists only codes that are wrong by default, and\nany locale overrides it from admin.",[979,4112,4113,4114,4117],{},"Consumers should read ",[982,4115,4116],{},"language"," off the locale record and never carry their\nown mapping. Two copies of this mapping in one frontend, pointing opposite\nways, is how the wrong ones shipped.",[1063,4119,4121],{"id":4120},"making-the-admin-the-only-source-of-truth","Making the admin the only source of truth",[979,4123,4124],{},"A frontend that hardcodes its locale list needs a release every time a language\nis added. The one thing that usually forces it is the ROUTER, because route\npatterns are built at compile time.",[979,4126,4127,4128,4131],{},"Resist listing the codes there. Match the ",[1091,4129,4130],{},"shape"," of a prefix and validate\nmembership at runtime against the active-locale list:",[991,4133,4135],{"className":2860,"code":4134,"language":2862,"meta":996,"style":996},"\u002F\u002F Build time: a shape, not a list.\nconst LANG = ':lang([a-z]{2})'\n\n\u002F\u002F Runtime: the database decides what is actually served.\nif (!locales.value.some(l => l.code === lang)) {\n  throw createError({ statusCode: 404, statusMessage: 'Unknown language' })\n}\n",[982,4136,4137,4142,4160,4164,4169,4220,4260],{"__ignoreMap":996},[1000,4138,4139],{"class":1002,"line":10},[1000,4140,4141],{"class":2595},"\u002F\u002F Build time: a shape, not a list.\n",[1000,4143,4144,4147,4150,4153,4155,4158],{"class":1002,"line":16},[1000,4145,4146],{"class":1553},"const",[1000,4148,4149],{"class":2507}," LANG ",[1000,4151,4152],{"class":1526},"=",[1000,4154,1527],{"class":1526},[1000,4156,4157],{"class":1009},":lang([a-z]{2})",[1000,4159,1533],{"class":1526},[1000,4161,4162],{"class":1002,"line":22},[1000,4163,1257],{"emptyLinePlaceholder":1256},[1000,4165,4166],{"class":1002,"line":51},[1000,4167,4168],{"class":2595},"\u002F\u002F Runtime: the database decides what is actually served.\n",[1000,4170,4171,4175,4178,4181,4183,4185,4188,4190,4194,4197,4201,4204,4207,4209,4212,4215,4218],{"class":1002,"line":57},[1000,4172,4174],{"class":4173},"s7zQu","if",[1000,4176,4177],{"class":2507}," (",[1000,4179,4180],{"class":1526},"!",[1000,4182,3396],{"class":2507},[1000,4184,1100],{"class":1526},[1000,4186,4187],{"class":2507},"value",[1000,4189,1100],{"class":1526},[1000,4191,4193],{"class":4192},"s2Zo4","some",[1000,4195,4196],{"class":2507},"(",[1000,4198,4200],{"class":4199},"sHdIc","l",[1000,4202,4203],{"class":1553}," =>",[1000,4205,4206],{"class":2507}," l",[1000,4208,1100],{"class":1526},[1000,4210,4211],{"class":2507},"code ",[1000,4213,4214],{"class":1526},"===",[1000,4216,4217],{"class":2507}," lang)) ",[1000,4219,1545],{"class":1526},[1000,4221,4222,4225,4228,4230,4232,4235,4237,4241,4243,4246,4248,4250,4253,4255,4258],{"class":1002,"line":102},[1000,4223,4224],{"class":4173},"  throw",[1000,4226,4227],{"class":4192}," createError",[1000,4229,4196],{"class":2216},[1000,4231,2874],{"class":1526},[1000,4233,4234],{"class":2216}," statusCode",[1000,4236,1559],{"class":1526},[1000,4238,4240],{"class":4239},"sbssI"," 404",[1000,4242,1620],{"class":1526},[1000,4244,4245],{"class":2216}," statusMessage",[1000,4247,1559],{"class":1526},[1000,4249,1527],{"class":1526},[1000,4251,4252],{"class":1009},"Unknown language",[1000,4254,2636],{"class":1526},[1000,4256,4257],{"class":1526}," }",[1000,4259,1039],{"class":2216},[1000,4261,4262],{"class":1002,"line":108},[1000,4263,1644],{"class":1526},[979,4265,4266,4267,4270,4271,4274,4275,4278,4279,4282],{},"A constrained list does buy one real thing — ",[982,4268,4269],{},":lang(bg|cz|…)"," makes\n",[982,4272,4273],{},"\u002Fdashboard"," structurally incapable of being read as a language. Keep that\nguarantee with an assertion instead: fail the ",[1091,4276,4277],{},"build"," if any route's first\nsegment matches the locale shape, so adding a two-letter page like ",[982,4280,4281],{},"\u002Fai"," is a\nloud error rather than a page silently routed as a language.",[979,4284,4285],{},"With that in place, adding a locale in admin routes immediately, appears in the\nswitcher, the hreflang set and the sitemap; removing one 404s it and drops it\nfrom the sitemap. Both verified without a deploy.",[979,4287,4288],{},"The sitemap should fetch the active list too. Built from a compiled-in array it\nadvertises whatever the last release knew: a language added in admin is missing,\nand one removed is still being submitted.",[4290,4291,4292],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}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 .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}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}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":996,"searchDepth":16,"depth":16,"links":4294},[4295,4300,4301,4306,4309,4310,4312,4317,4328,4329,4330],{"id":1065,"depth":16,"text":28,"children":4296},[4297,4298,4299],{"id":1153,"depth":22,"text":1154},{"id":1200,"depth":22,"text":1201},{"id":1283,"depth":22,"text":1284},{"id":1330,"depth":16,"text":1331},{"id":1447,"depth":16,"text":1448,"children":4302},[4303,4304],{"id":1451,"depth":22,"text":1452},{"id":1654,"depth":22,"text":4305},"Admin (i18n\u002Fadmin)",{"id":1849,"depth":16,"text":1850,"children":4307},[4308],{"id":1969,"depth":22,"text":1970},{"id":1999,"depth":16,"text":2000},{"id":2030,"depth":16,"text":4311},"FlowDSL nodes (i18n\u002Fflowdsl)",{"id":2446,"depth":16,"text":2447,"children":4313},[4314,4315,4316],{"id":2671,"depth":22,"text":2672},{"id":2828,"depth":22,"text":2829},{"id":3133,"depth":22,"text":3134},{"id":3241,"depth":16,"text":3242,"children":4318},[4319,4320,4322,4324,4326,4327],{"id":3260,"depth":22,"text":3261},{"id":3400,"depth":22,"text":4321},"i18n.keys_changed — the work signal",{"id":3427,"depth":22,"text":4323},"i18n\u002Ffill-translations node",{"id":3576,"depth":22,"text":4325},"i18n\u002Fflowtemplates — auto-published flows",{"id":3719,"depth":22,"text":3720},{"id":3867,"depth":22,"text":3868},{"id":4009,"depth":16,"text":4010},{"id":4034,"depth":16,"text":4035},{"id":4120,"depth":16,"text":4121},"Request-locale resolution, a Mongo-backed message catalog with a locale fallback chain, admin CRUD, and FlowDSL nodes for translating inside flows.","md",null,{},{"order":271,"icon":272},{"title":268,"description":4331},"XlCCqLgzaQw5a3Jf3gU910eqF4VA_HNIVIAF2UgWl-Y",[4339,4341],{"title":264,"path":265,"stem":266,"description":4340,"children":-1},"The dispatch unit between modules and flows. Wraps every published\nflow with a stable ID, canary\u002FA-B variant routing, an enable\u002Fdisable\ntoggle, and a contract describing what the flow contributes to\n\u002Fopenapi.json + \u002Fasyncapi.json.\n",{"title":274,"path":275,"stem":276,"description":4342,"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.",1790332226537]