diff --git a/.changeset/i18n-source-hash-rollout-eight-sets.md b/.changeset/i18n-source-hash-rollout-eight-sets.md new file mode 100644 index 0000000000..7f17e38131 --- /dev/null +++ b/.changeset/i18n-source-hash-rollout-eight-sets.md @@ -0,0 +1,75 @@ +--- +"@objectstack/cli": patch +"@objectstack/plugin-approvals": patch +"@objectstack/plugin-audit": patch +"@objectstack/plugin-security": patch +"@objectstack/plugin-sharing": patch +"@objectstack/plugin-webhooks": patch +"@objectstack/service-messaging": patch +"@objectstack/service-realtime": patch +"@objectstack/service-storage": patch +--- + +chore(i18n): roll the generated-leaf provenance companion out to the remaining bundle sets (#12559) + +`os i18n extract --source-hashes` (#11671, maintainer ruling #12069 Option A) +records, per generated translation leaf, the digest of the source revision that +leaf is **still a byte copy of** — the one signal that tells a stale fill from a +real translation once the source has moved and the two stopped being +distinguishable by value. It shipped opt-in, and exactly one of the nine i18n +bundle sets opted in. A landed detector, a changeset announcing it and a green +gate read together as *"generated translation staleness is now caught"*; for +eight of nine sets it was not, and the thing making it not caught was a single +absent flag in an extract config — invisible from all three of those surfaces. + +**All eight remaining sets now opt in** — `plugin-approvals`, `plugin-audit`, +`plugin-security`, `plugin-sharing`, `plugin-webhooks`, `service-messaging`, +`service-realtime`, `service-storage`. Each documents `source-hashes` in its +extract config and commits three `.source-hashes.generated.ts` +companions, produced by the same extract run as the bundles they sit beside +(`check:i18n` compares them byte-for-byte, so they cannot be written by hand). +`check:i18n` now reports 7 bundles per set where it reported 4, and 11 for +`platform-objects` where it reported 8. + +**Records count what is currently RECORDABLE, never what is covered.** A record +is written only for a leaf that *is* right now a byte copy of the current +source, so a fully translated locale starts with an empty table — which is the +instrument armed, not an instrument that measures nothing: the entry appears by +itself on the first extract after a leaf becomes a fill. Measured at this +commit, per set over its three translated locales: `service-messaging` 289, +`plugin-approvals` 61, `plugin-security` 33, `plugin-webhooks` 20, +`plugin-audit` 8, `service-storage` 7, `plugin-sharing` 1 (es-ES only; zh-CN and +ja-JP are fully translated and start empty), `service-realtime` 0 (all three +locales fully translated). **419 records written across the eight sets, 0 +stale.** + +**One extractor fix the rollout forced.** `--source-hashes` had one user, and +that user commits both generated sections, so the interaction with +`--no-metadata-forms` had never been exercised. The provenance table is computed +over every generated section the extractor builds; the eight sets here commit no +metadata-forms bundle, and their `metadataForms` subtree — absent from their +merge baseline — arrives as a fresh `--fill=default` copy of `en`, so every leaf +of it was recordable. First measured on `plugin-audit`: **763 records, of which +2 were its own objects and 761 were digests of the Studio metadata-form baseline +`@objectstack/platform-objects` owns.** Those records are unreadable in the +package holding them and would have rewritten all 24 companions on any unrelated +`*.form.ts` change in `packages/spec` — the cross-package coupling ADR-0029 D8 +and every `bundle-ownership.test.ts` keep out of committed bundles. The +companion now covers exactly the sections a run commits, decided by the same two +predicates that decide the bundle files. `platform-objects` commits both, so its +three committed companions are byte-for-byte unchanged. + +**Grade: `patch`, and behaviour on the day it lands is unchanged for every +leaf.** A record is written only where a leaf is currently a byte copy of the +**current** source, so every record written equals the current digest and none +of them can be stale; the mechanism cannot arrive red. No committed translation +bundle changed a byte, no public API moved, and no leaf's rendered text changed. +`narrowToCommittedSections` is new but internal to `@objectstack/cli` — the +package's entrypoint does not re-export the extractor utils. + +**What this does not do**, stated so the boundary is not inferred wrongly a +second time: these eight sets now *record* provenance. Reading it at serving +time is `withSourceFallback`, and that is still wired in +`@objectstack/platform-objects` alone — so a stale fill in one of the eight is +now recorded and reportable, but not yet substituted at runtime. Tracked +separately. diff --git a/packages/cli/src/commands/i18n/extract.ts b/packages/cli/src/commands/i18n/extract.ts index c838ea9269..185158f930 100644 --- a/packages/cli/src/commands/i18n/extract.ts +++ b/packages/cli/src/commands/i18n/extract.ts @@ -21,6 +21,7 @@ import { renderTranslationModule, renderSourceHashModule, parseSourceHashModule, + narrowToCommittedSections, type FillStrategy, } from '../../utils/i18n-extract.js'; @@ -198,6 +199,52 @@ export default class I18nExtract extends Command { const emitsMetadataForms = (locale: string): boolean => flags['metadata-forms'] && (metadataFormsCounts[locale] ?? 0) > 0; + /** + * The provenance table for one locale, narrowed to the sections this run + * actually COMMITS (#12559). + * + * `extractTranslations` computes the table over every generated section it + * built — `objects` and `metadataForms` both — because the rule that fills + * it (`collectFilledFromHashes`) is a statement about generated leaves, not + * about files. Which of those sections becomes a committed bundle is this + * layer's decision, and the two must agree: a record describes the leaf + * sitting in a bundle beside it, and a record for a leaf this package does + * not commit describes nothing that exists here. + * + * The mismatch is not hypothetical — it is what the eight-set rollout in + * #12559 measured on first contact. A package that owns only its own + * objects passes `--no-metadata-forms`, and the emitter's own note two + * blocks up says why: "without it, `--check` demands a baseline copy the + * package deliberately does not commit". Its `metadataForms` subtree is + * nonetheless built, and — having no entry in that package's merge + * baseline — arrives as a fresh `--fill=default` copy of `en`, so EVERY + * leaf of it satisfies `value === currentSource` and gets recorded. + * Measured on `plugin-audit`: 763 records, of which **2** were its own + * objects and 761 were digests of the Studio metadata-form baseline that + * `@objectstack/platform-objects` owns. Those records are unreadable here + * (no `metadataForms` bundle exists in this package for them to be about), + * and they would move all three of this package's companions every time an + * unrelated `*.form.ts` in `packages/spec` changed — the same cross-package + * coupling ADR-0029 D8 and each package's `bundle-ownership.test.ts` exist + * to keep out of its committed bundles. + * + * So the section list is decided by the SAME predicates that decide the + * bundle files, never by a second rule: `result.counts` for `objects` and + * {@link emitsMetadataForms} for `metadataForms`. A set that commits both — + * `platform-objects` is the one today — keeps every record it had. The + * narrowing itself is `narrowToCommittedSections`, a pure function in the + * extractor's utils so it can be pinned without driving oclif; this layer + * contributes only the two booleans it alone knows. + */ + const committedSourceHashes = (locale: string): Record | undefined => { + const table = result.sourceHashes[locale]; + if (!table) return undefined; + const committed: string[] = []; + if ((result.counts[locale] ?? 0) > 0) committed.push('objects'); + if (emitsMetadataForms(locale)) committed.push('metadataForms'); + return narrowToCommittedSections(table, committed); + }; + if (flags.json) { await emitJson({ totalExpected: result.totalExpected, @@ -275,7 +322,7 @@ export default class I18nExtract extends Command { // The provenance companion rides in the SAME list, so `--check` compares // it by the same byte-for-byte rule as the bundles it belongs to and can // never diverge from what a real extract writes. - const table = result.sourceHashes[locale]; + const table = committedSourceHashes(locale); if (flags['source-hashes'] && table) { emitted.push({ file: path.join(resolvedOutDir, `${locale}.source-hashes.generated.ts`), diff --git a/packages/cli/src/utils/i18n-extract.ts b/packages/cli/src/utils/i18n-extract.ts index 9ab57a4c45..b15473bb0d 100644 --- a/packages/cli/src/utils/i18n-extract.ts +++ b/packages/cli/src/utils/i18n-extract.ts @@ -1475,6 +1475,49 @@ export function renderSourceHashModule( return lines.join('\n'); } +/** + * Narrow a provenance table to the generated sections a run actually COMMITS + * (#12559). + * + * {@link extractTranslations} computes the table over every generated section it + * built — `objects` and `metadataForms` both — because the rule that fills it + * (`collectFilledFromHashes`) is a statement about generated leaves, not about + * files. Which of those sections becomes a committed bundle is the command + * layer's decision, and the two must agree: a record describes the leaf sitting + * in a bundle beside it, so a record for a section this package does not commit + * describes nothing that exists there. + * + * The mismatch is measured, not hypothetical. A package that owns only its own + * objects passes `--no-metadata-forms`, and the emitter's own note says why: + * "without it, `--check` demands a baseline copy the package deliberately does + * not commit". Its `metadataForms` subtree is built anyway, and — having no + * entry in that package's merge baseline — arrives as a fresh `--fill=default` + * copy of `en`, so EVERY leaf of it satisfies `value === currentSource` and is + * recorded. Measured on `@objectstack/plugin-audit` while rolling the companion + * out in #12559: 763 records, of which **2** were its own objects and 761 were + * digests of the Studio metadata-form baseline `@objectstack/platform-objects` + * owns — unreadable in that package (no `metadataForms` bundle exists there for + * them to be about) and re-written in all three of its companions every time an + * unrelated `*.form.ts` in `packages/spec` moved. That is the cross-package + * coupling ADR-0029 D8 and each package's `bundle-ownership.test.ts` keep out of + * the committed bundles; the companion is not exempt from it. + * + * A section is named by a leaf path's FIRST dotted segment — the same identity + * `collectGeneratedLeaves` seeds its walk with, so the two cannot disagree about + * what section a path belongs to. Callers pass the sections they are committing; + * this function invents none, so a set that commits both (`platform-objects` is + * the one today) keeps every record it had. + */ +export function narrowToCommittedSections( + hashes: Record, + committedSections: Iterable, +): Record { + const committed = new Set(committedSections); + return Object.fromEntries( + Object.entries(hashes).filter(([leafPath]) => committed.has(leafPath.split('.', 1)[0])), + ); +} + /** * Read a committed `.source-hashes.generated.ts` back into a table. * diff --git a/packages/cli/test/i18n-extract-source-hashes.test.ts b/packages/cli/test/i18n-extract-source-hashes.test.ts index e29caffa19..4e256d239f 100644 --- a/packages/cli/test/i18n-extract-source-hashes.test.ts +++ b/packages/cli/test/i18n-extract-source-hashes.test.ts @@ -15,6 +15,7 @@ import { extractTranslations, renderSourceHashModule, parseSourceHashModule, + narrowToCommittedSections, } from '../src/utils/i18n-extract.js'; const stack = (help: string) => ({ @@ -84,3 +85,62 @@ describe('the emitted module', () => { expect(parseSourceHashModule('export const x: Readonly> = { "a": 3 };')).toBeUndefined(); }); }); + +// --------------------------------------------------------------------------- +// The companion covers the sections the run COMMITS (#12559) +// --------------------------------------------------------------------------- +// +// `extractTranslations` fills the table over every generated section it built, +// `metadataForms` included, because the rule behind it is about generated +// leaves rather than about files. A package that owns only its own objects +// passes `--no-metadata-forms` and commits no metadata-forms bundle — and its +// `metadataForms` subtree, absent from its merge baseline, arrives as a fresh +// `--fill=default` copy of `en`, so every leaf of it is recordable. Unnarrowed, +// that is what the table carries: measured on `@objectstack/plugin-audit` during +// the #12559 rollout, 763 records of which 2 were its own objects and 761 were +// digests of the metadata-form baseline `@objectstack/platform-objects` owns. +// +// The narrowing is pinned here rather than mirrored from the command, because a +// mirror of an emit rule is a second contract — it agrees until the day one side +// changes, and nothing says so on that day. + +describe('the provenance table is narrowed to the committed sections (#12559)', () => { + const result = extractTranslations( + { objects: [{ name: 'account', label: 'Account', fields: { name: { label: 'Name' } } }] }, + { defaultLocale: 'en', locales: ['zh-CN'], fill: 'default' }, + ); + const table = result.sourceHashes['zh-CN']; + const sectionsIn = (t: Record) => [...new Set(Object.keys(t).map((k) => k.split('.', 1)[0]))].sort(); + + it('has both sections to narrow — otherwise every case below would pass vacuously', () => { + // The registry-driven metadata-form baseline is present for any stack, so + // this is the precondition that makes the two cases a measurement rather + // than an empty walk. + expect(sectionsIn(table)).toEqual(['metadataForms', 'objects']); + expect(Object.keys(table).length).toBeGreaterThan(100); + }); + + it('drops the metadata-forms records for a set that commits no metadata-forms bundle', () => { + const narrowed = narrowToCommittedSections(table, ['objects']); + expect(sectionsIn(narrowed)).toEqual(['objects']); + // Every surviving record is unchanged — this narrows the table, it does not + // recompute it. + for (const [path, digest] of Object.entries(narrowed)) expect(digest).toBe(table[path]); + }); + + it('keeps every record for a set that commits both, so the covered set is untouched', () => { + expect(narrowToCommittedSections(table, ['objects', 'metadataForms'])).toEqual(table); + }); + + it('names a section by a leaf path\'s first dotted segment, never by a prefix match', () => { + // `objects` must not be reached through a section that merely starts with + // it, and a nested key called `objects` must not be mistaken for the section. + const odd = { 'objectsExtra.a.label': 'aaaaaaaaaaaaaaaa', 'metadataForms.x.objects.b': 'bbbbbbbbbbbbbbbb' }; + expect(narrowToCommittedSections(odd, ['objects'])).toEqual({}); + expect(narrowToCommittedSections(odd, ['metadataForms'])).toEqual({ 'metadataForms.x.objects.b': 'bbbbbbbbbbbbbbbb' }); + }); + + it('commits nothing when no section is committed', () => { + expect(narrowToCommittedSections(table, [])).toEqual({}); + }); +}); diff --git a/packages/plugins/plugin-approvals/scripts/i18n-extract.config.ts b/packages/plugins/plugin-approvals/scripts/i18n-extract.config.ts index effd57c807..46112a080a 100644 --- a/packages/plugins/plugin-approvals/scripts/i18n-extract.config.ts +++ b/packages/plugins/plugin-approvals/scripts/i18n-extract.config.ts @@ -9,8 +9,32 @@ * * os i18n extract packages/plugins/plugin-approvals/scripts/i18n-extract.config.ts \ * --locales=zh-CN,ja-JP,es-ES --fill=default --objects-only --no-metadata-forms \ + * --source-hashes \ * --out=packages/plugins/plugin-approvals/src/translations * + * The `source-hashes` flag on the command above also emits + * `.source-hashes.generated.ts` — the provenance companion from + * maintainer ruling #12069 Option A (#11671), rolled out to every bundle set by + * #12559. Without it every generated leaf here is LEGACY-TRUSTED: a leaf filled + * from the source and then left behind when the source was revised is + * indistinguishable BY VALUE from a real translation, so it publishes a + * superseded draft under a green `check:i18n` forever, and + * `check:i18n-stale-fill` cannot see it either unless two locales happen to + * hold the same stale bytes. + * + * A record is written only for a leaf that IS currently a byte copy of the + * CURRENT source, so the companion arrives 0-stale by construction and only + * ever reports drift accruing afterwards. Records count the leaves currently + * RECORDABLE, never the leaves covered — a table with few entries, or with none + * at all where a locale is fully translated, is the instrument armed, and an + * entry appears by itself on the first extract after a leaf becomes a fill. + * + * The flag is named in prose WITHOUT its leading dashes on purpose: + * `flagsFromDocstring` (scripts/i18n-bundle-surface.mjs) harvests every + * recognised flag spelling out of this whole comment, so a second spelling + * would keep the opt-in switched on after someone deleted it from the command + * block — the one place that decides. + * * `--no-metadata-forms` because the Studio metadata-form baseline is owned by * `@objectstack/platform-objects` — this plugin translates only the objects it * owns, so it must not commit a second copy. Add `--check` to run the same diff --git a/packages/plugins/plugin-approvals/src/translations/es-ES.source-hashes.generated.ts b/packages/plugins/plugin-approvals/src/translations/es-ES.source-hashes.generated.ts new file mode 100644 index 0000000000..5cc4abc43f --- /dev/null +++ b/packages/plugins/plugin-approvals/src/translations/es-ES.source-hashes.generated.ts @@ -0,0 +1,49 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'es-ES'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const esESGeneratedSourceHashes: Readonly> = { + "objects.sys_approval_action.fields.actor_id.label": "b155813f8a7f06e3", + "objects.sys_approval_delegation._views.active.emptyState.message": "1095d61c017b6db2", + "objects.sys_approval_delegation._views.active.emptyState.title": "799d28e51d88963e", + "objects.sys_approval_delegation._views.active.label": "eee107cf4e466779", + "objects.sys_approval_delegation.description": "432b0435aa15f6b9", + "objects.sys_approval_delegation.fields.created_at.label": "1f02d416befb595b", + "objects.sys_approval_delegation.fields.delegate_id.help": "9cfc641f7b0b04f9", + "objects.sys_approval_delegation.fields.delegate_id.label": "afd6d8733dc5bc14", + "objects.sys_approval_delegation.fields.delegator_id.help": "c4686c5c9f24e0be", + "objects.sys_approval_delegation.fields.delegator_id.label": "f76b1f95f2fdabff", + "objects.sys_approval_delegation.fields.id.label": "3383564051b4b76d", + "objects.sys_approval_delegation.fields.organization_id.help": "f02982e88229d9ca", + "objects.sys_approval_delegation.fields.organization_id.label": "3e55836156e1c1de", + "objects.sys_approval_delegation.fields.reason.help": "712516f1461dff2b", + "objects.sys_approval_delegation.fields.reason.label": "ac399f03d7f2e64d", + "objects.sys_approval_delegation.fields.updated_at.label": "aba63dc2a9c79b8d", + "objects.sys_approval_delegation.fields.valid_from.help": "01386010c454bfbd", + "objects.sys_approval_delegation.fields.valid_from.label": "02eaf201e57c9eda", + "objects.sys_approval_delegation.fields.valid_until.help": "a48ea34e65933e9d", + "objects.sys_approval_delegation.fields.valid_until.label": "a68db5bd4fc72a13", + "objects.sys_approval_delegation.label": "4389c4fe5f5c9746", + "objects.sys_approval_delegation.pluralLabel": "f07fec3ffd74e6db", + "objects.sys_approval_request.fields.flow_node_id.help": "154aa23b4eee4cae", + "objects.sys_approval_request.fields.flow_node_id.label": "052ad568aa41227c", + "objects.sys_approval_request.fields.flow_run_id.help": "35c92818f5e11090", + "objects.sys_approval_request.fields.flow_run_id.label": "d0124e323da966f9", + "objects.sys_approval_request.fields.node_config_json.help": "7ee00ef9b3ca7e80", + "objects.sys_approval_request.fields.node_config_json.label": "e64eb4bf7d7ea033", +}; diff --git a/packages/plugins/plugin-approvals/src/translations/ja-JP.source-hashes.generated.ts b/packages/plugins/plugin-approvals/src/translations/ja-JP.source-hashes.generated.ts new file mode 100644 index 0000000000..1ae3cd1db9 --- /dev/null +++ b/packages/plugins/plugin-approvals/src/translations/ja-JP.source-hashes.generated.ts @@ -0,0 +1,48 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ja-JP'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const jaJPGeneratedSourceHashes: Readonly> = { + "objects.sys_approval_delegation._views.active.emptyState.message": "1095d61c017b6db2", + "objects.sys_approval_delegation._views.active.emptyState.title": "799d28e51d88963e", + "objects.sys_approval_delegation._views.active.label": "eee107cf4e466779", + "objects.sys_approval_delegation.description": "432b0435aa15f6b9", + "objects.sys_approval_delegation.fields.created_at.label": "1f02d416befb595b", + "objects.sys_approval_delegation.fields.delegate_id.help": "9cfc641f7b0b04f9", + "objects.sys_approval_delegation.fields.delegate_id.label": "afd6d8733dc5bc14", + "objects.sys_approval_delegation.fields.delegator_id.help": "c4686c5c9f24e0be", + "objects.sys_approval_delegation.fields.delegator_id.label": "f76b1f95f2fdabff", + "objects.sys_approval_delegation.fields.id.label": "3383564051b4b76d", + "objects.sys_approval_delegation.fields.organization_id.help": "f02982e88229d9ca", + "objects.sys_approval_delegation.fields.organization_id.label": "3e55836156e1c1de", + "objects.sys_approval_delegation.fields.reason.help": "712516f1461dff2b", + "objects.sys_approval_delegation.fields.reason.label": "ac399f03d7f2e64d", + "objects.sys_approval_delegation.fields.updated_at.label": "aba63dc2a9c79b8d", + "objects.sys_approval_delegation.fields.valid_from.help": "01386010c454bfbd", + "objects.sys_approval_delegation.fields.valid_from.label": "02eaf201e57c9eda", + "objects.sys_approval_delegation.fields.valid_until.help": "a48ea34e65933e9d", + "objects.sys_approval_delegation.fields.valid_until.label": "a68db5bd4fc72a13", + "objects.sys_approval_delegation.label": "4389c4fe5f5c9746", + "objects.sys_approval_delegation.pluralLabel": "f07fec3ffd74e6db", + "objects.sys_approval_request.fields.flow_node_id.help": "154aa23b4eee4cae", + "objects.sys_approval_request.fields.flow_node_id.label": "052ad568aa41227c", + "objects.sys_approval_request.fields.flow_run_id.help": "35c92818f5e11090", + "objects.sys_approval_request.fields.flow_run_id.label": "d0124e323da966f9", + "objects.sys_approval_request.fields.node_config_json.help": "7ee00ef9b3ca7e80", + "objects.sys_approval_request.fields.node_config_json.label": "e64eb4bf7d7ea033", +}; diff --git a/packages/plugins/plugin-approvals/src/translations/zh-CN.source-hashes.generated.ts b/packages/plugins/plugin-approvals/src/translations/zh-CN.source-hashes.generated.ts new file mode 100644 index 0000000000..db1ec71ba1 --- /dev/null +++ b/packages/plugins/plugin-approvals/src/translations/zh-CN.source-hashes.generated.ts @@ -0,0 +1,27 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'zh-CN'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const zhCNGeneratedSourceHashes: Readonly> = { + "objects.sys_approval_request.fields.flow_node_id.help": "154aa23b4eee4cae", + "objects.sys_approval_request.fields.flow_node_id.label": "052ad568aa41227c", + "objects.sys_approval_request.fields.flow_run_id.help": "35c92818f5e11090", + "objects.sys_approval_request.fields.flow_run_id.label": "d0124e323da966f9", + "objects.sys_approval_request.fields.node_config_json.help": "7ee00ef9b3ca7e80", + "objects.sys_approval_request.fields.node_config_json.label": "e64eb4bf7d7ea033", +}; diff --git a/packages/plugins/plugin-audit/scripts/i18n-extract.config.ts b/packages/plugins/plugin-audit/scripts/i18n-extract.config.ts index 40da549b70..479453ff92 100644 --- a/packages/plugins/plugin-audit/scripts/i18n-extract.config.ts +++ b/packages/plugins/plugin-audit/scripts/i18n-extract.config.ts @@ -9,7 +9,31 @@ * * os i18n extract packages/plugins/plugin-audit/scripts/i18n-extract.config.ts \ * --locales=zh-CN,ja-JP,es-ES --fill=default --objects-only --no-metadata-forms \ + * --source-hashes \ * --out=packages/plugins/plugin-audit/src/translations + * + * The `source-hashes` flag on the command above also emits + * `.source-hashes.generated.ts` — the provenance companion from + * maintainer ruling #12069 Option A (#11671), rolled out to every bundle set by + * #12559. Without it every generated leaf here is LEGACY-TRUSTED: a leaf filled + * from the source and then left behind when the source was revised is + * indistinguishable BY VALUE from a real translation, so it publishes a + * superseded draft under a green `check:i18n` forever, and + * `check:i18n-stale-fill` cannot see it either unless two locales happen to + * hold the same stale bytes. + * + * A record is written only for a leaf that IS currently a byte copy of the + * CURRENT source, so the companion arrives 0-stale by construction and only + * ever reports drift accruing afterwards. Records count the leaves currently + * RECORDABLE, never the leaves covered — a table with few entries, or with none + * at all where a locale is fully translated, is the instrument armed, and an + * entry appears by itself on the first extract after a leaf becomes a fill. + * + * The flag is named in prose WITHOUT its leading dashes on purpose: + * `flagsFromDocstring` (scripts/i18n-bundle-surface.mjs) harvests every + * recognised flag spelling out of this whole comment, so a second spelling + * would keep the opt-in switched on after someone deleted it from the command + * block — the one place that decides. */ import { defineStack, type ObjectStackDefinition } from '@objectstack/spec'; diff --git a/packages/plugins/plugin-audit/src/translations/es-ES.source-hashes.generated.ts b/packages/plugins/plugin-audit/src/translations/es-ES.source-hashes.generated.ts new file mode 100644 index 0000000000..e8114c4355 --- /dev/null +++ b/packages/plugins/plugin-audit/src/translations/es-ES.source-hashes.generated.ts @@ -0,0 +1,25 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'es-ES'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const esESGeneratedSourceHashes: Readonly> = { + "objects.sys_activity.fields.actor_id.label": "b155813f8a7f06e3", + "objects.sys_activity.fields.type.help": "bc8b10c89aacd494", + "objects.sys_activity.fields.url.label": "2203b0b9f72534f7", + "objects.sys_audit_log.fields.actor.label": "b155813f8a7f06e3", +}; diff --git a/packages/plugins/plugin-audit/src/translations/ja-JP.source-hashes.generated.ts b/packages/plugins/plugin-audit/src/translations/ja-JP.source-hashes.generated.ts new file mode 100644 index 0000000000..0ee7f8d206 --- /dev/null +++ b/packages/plugins/plugin-audit/src/translations/ja-JP.source-hashes.generated.ts @@ -0,0 +1,23 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ja-JP'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const jaJPGeneratedSourceHashes: Readonly> = { + "objects.sys_activity.fields.type.help": "bc8b10c89aacd494", + "objects.sys_activity.fields.url.label": "2203b0b9f72534f7", +}; diff --git a/packages/plugins/plugin-audit/src/translations/zh-CN.source-hashes.generated.ts b/packages/plugins/plugin-audit/src/translations/zh-CN.source-hashes.generated.ts new file mode 100644 index 0000000000..c99e66bb2b --- /dev/null +++ b/packages/plugins/plugin-audit/src/translations/zh-CN.source-hashes.generated.ts @@ -0,0 +1,23 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'zh-CN'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const zhCNGeneratedSourceHashes: Readonly> = { + "objects.sys_activity.fields.type.help": "bc8b10c89aacd494", + "objects.sys_activity.fields.url.label": "2203b0b9f72534f7", +}; diff --git a/packages/plugins/plugin-security/scripts/i18n-extract.config.ts b/packages/plugins/plugin-security/scripts/i18n-extract.config.ts index 3b1bfcd3aa..0a49255aa8 100644 --- a/packages/plugins/plugin-security/scripts/i18n-extract.config.ts +++ b/packages/plugins/plugin-security/scripts/i18n-extract.config.ts @@ -9,7 +9,31 @@ * * os i18n extract packages/plugins/plugin-security/scripts/i18n-extract.config.ts \ * --locales=zh-CN,ja-JP,es-ES --fill=default --objects-only --no-metadata-forms \ + * --source-hashes \ * --out=packages/plugins/plugin-security/src/translations + * + * The `source-hashes` flag on the command above also emits + * `.source-hashes.generated.ts` — the provenance companion from + * maintainer ruling #12069 Option A (#11671), rolled out to every bundle set by + * #12559. Without it every generated leaf here is LEGACY-TRUSTED: a leaf filled + * from the source and then left behind when the source was revised is + * indistinguishable BY VALUE from a real translation, so it publishes a + * superseded draft under a green `check:i18n` forever, and + * `check:i18n-stale-fill` cannot see it either unless two locales happen to + * hold the same stale bytes. + * + * A record is written only for a leaf that IS currently a byte copy of the + * CURRENT source, so the companion arrives 0-stale by construction and only + * ever reports drift accruing afterwards. Records count the leaves currently + * RECORDABLE, never the leaves covered — a table with few entries, or with none + * at all where a locale is fully translated, is the instrument armed, and an + * entry appears by itself on the first extract after a leaf becomes a fill. + * + * The flag is named in prose WITHOUT its leading dashes on purpose: + * `flagsFromDocstring` (scripts/i18n-bundle-surface.mjs) harvests every + * recognised flag spelling out of this whole comment, so a second spelling + * would keep the opt-in switched on after someone deleted it from the command + * block — the one place that decides. */ import { defineStack, type ObjectStackDefinition } from '@objectstack/spec'; diff --git a/packages/plugins/plugin-security/src/translations/es-ES.source-hashes.generated.ts b/packages/plugins/plugin-security/src/translations/es-ES.source-hashes.generated.ts new file mode 100644 index 0000000000..768fee7baf --- /dev/null +++ b/packages/plugins/plugin-security/src/translations/es-ES.source-hashes.generated.ts @@ -0,0 +1,32 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'es-ES'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const esESGeneratedSourceHashes: Readonly> = { + "objects.sys_permission_set._actions.discard_permission_set_overlay.confirmText": "02ce03bb6b6c58a2", + "objects.sys_permission_set._actions.discard_permission_set_overlay.label": "6523908dc7b796ed", + "objects.sys_permission_set._actions.discard_permission_set_overlay.successMessage": "938b7a7c989e5ff4", + "objects.sys_permission_set._views.drifted.label": "f7b9325c31edc552", + "objects.sys_permission_set.fields.drift_detail.help": "3572261f6d3bc8ab", + "objects.sys_permission_set.fields.drift_detail.label": "a84ebd221c37d10b", + "objects.sys_permission_set.fields.drift_status.help": "b16445c654ee6cec", + "objects.sys_permission_set.fields.drift_status.label": "7946a54138690b86", + "objects.sys_permission_set.fields.drift_status.options.other": "3ddcfb750014f293", + "objects.sys_permission_set.fields.drift_status.options.overlay_shadow": "7371472481b55b52", + "objects.sys_permission_set.fields.drift_status.options.provenance_skip": "fdd5f01e69fa0245", +}; diff --git a/packages/plugins/plugin-security/src/translations/ja-JP.source-hashes.generated.ts b/packages/plugins/plugin-security/src/translations/ja-JP.source-hashes.generated.ts new file mode 100644 index 0000000000..94c78ffffc --- /dev/null +++ b/packages/plugins/plugin-security/src/translations/ja-JP.source-hashes.generated.ts @@ -0,0 +1,32 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ja-JP'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const jaJPGeneratedSourceHashes: Readonly> = { + "objects.sys_permission_set._actions.discard_permission_set_overlay.confirmText": "02ce03bb6b6c58a2", + "objects.sys_permission_set._actions.discard_permission_set_overlay.label": "6523908dc7b796ed", + "objects.sys_permission_set._actions.discard_permission_set_overlay.successMessage": "938b7a7c989e5ff4", + "objects.sys_permission_set._views.drifted.label": "f7b9325c31edc552", + "objects.sys_permission_set.fields.drift_detail.help": "3572261f6d3bc8ab", + "objects.sys_permission_set.fields.drift_detail.label": "a84ebd221c37d10b", + "objects.sys_permission_set.fields.drift_status.help": "b16445c654ee6cec", + "objects.sys_permission_set.fields.drift_status.label": "7946a54138690b86", + "objects.sys_permission_set.fields.drift_status.options.other": "3ddcfb750014f293", + "objects.sys_permission_set.fields.drift_status.options.overlay_shadow": "7371472481b55b52", + "objects.sys_permission_set.fields.drift_status.options.provenance_skip": "fdd5f01e69fa0245", +}; diff --git a/packages/plugins/plugin-security/src/translations/zh-CN.source-hashes.generated.ts b/packages/plugins/plugin-security/src/translations/zh-CN.source-hashes.generated.ts new file mode 100644 index 0000000000..33418b78bf --- /dev/null +++ b/packages/plugins/plugin-security/src/translations/zh-CN.source-hashes.generated.ts @@ -0,0 +1,32 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'zh-CN'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const zhCNGeneratedSourceHashes: Readonly> = { + "objects.sys_permission_set._actions.discard_permission_set_overlay.confirmText": "02ce03bb6b6c58a2", + "objects.sys_permission_set._actions.discard_permission_set_overlay.label": "6523908dc7b796ed", + "objects.sys_permission_set._actions.discard_permission_set_overlay.successMessage": "938b7a7c989e5ff4", + "objects.sys_permission_set._views.drifted.label": "f7b9325c31edc552", + "objects.sys_permission_set.fields.drift_detail.help": "3572261f6d3bc8ab", + "objects.sys_permission_set.fields.drift_detail.label": "a84ebd221c37d10b", + "objects.sys_permission_set.fields.drift_status.help": "b16445c654ee6cec", + "objects.sys_permission_set.fields.drift_status.label": "7946a54138690b86", + "objects.sys_permission_set.fields.drift_status.options.other": "3ddcfb750014f293", + "objects.sys_permission_set.fields.drift_status.options.overlay_shadow": "7371472481b55b52", + "objects.sys_permission_set.fields.drift_status.options.provenance_skip": "fdd5f01e69fa0245", +}; diff --git a/packages/plugins/plugin-sharing/scripts/i18n-extract.config.ts b/packages/plugins/plugin-sharing/scripts/i18n-extract.config.ts index 0023b6f607..1524ec6fb7 100644 --- a/packages/plugins/plugin-sharing/scripts/i18n-extract.config.ts +++ b/packages/plugins/plugin-sharing/scripts/i18n-extract.config.ts @@ -9,7 +9,31 @@ * * os i18n extract packages/plugins/plugin-sharing/scripts/i18n-extract.config.ts \ * --locales=zh-CN,ja-JP,es-ES --fill=default --objects-only --no-metadata-forms \ + * --source-hashes \ * --out=packages/plugins/plugin-sharing/src/translations + * + * The `source-hashes` flag on the command above also emits + * `.source-hashes.generated.ts` — the provenance companion from + * maintainer ruling #12069 Option A (#11671), rolled out to every bundle set by + * #12559. Without it every generated leaf here is LEGACY-TRUSTED: a leaf filled + * from the source and then left behind when the source was revised is + * indistinguishable BY VALUE from a real translation, so it publishes a + * superseded draft under a green `check:i18n` forever, and + * `check:i18n-stale-fill` cannot see it either unless two locales happen to + * hold the same stale bytes. + * + * A record is written only for a leaf that IS currently a byte copy of the + * CURRENT source, so the companion arrives 0-stale by construction and only + * ever reports drift accruing afterwards. Records count the leaves currently + * RECORDABLE, never the leaves covered — a table with few entries, or with none + * at all where a locale is fully translated, is the instrument armed, and an + * entry appears by itself on the first extract after a leaf becomes a fill. + * + * The flag is named in prose WITHOUT its leading dashes on purpose: + * `flagsFromDocstring` (scripts/i18n-bundle-surface.mjs) harvests every + * recognised flag spelling out of this whole comment, so a second spelling + * would keep the opt-in switched on after someone deleted it from the command + * block — the one place that decides. */ import { defineStack, type ObjectStackDefinition } from '@objectstack/spec'; diff --git a/packages/plugins/plugin-sharing/src/translations/es-ES.source-hashes.generated.ts b/packages/plugins/plugin-sharing/src/translations/es-ES.source-hashes.generated.ts new file mode 100644 index 0000000000..c606d9bc74 --- /dev/null +++ b/packages/plugins/plugin-sharing/src/translations/es-ES.source-hashes.generated.ts @@ -0,0 +1,22 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'es-ES'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const esESGeneratedSourceHashes: Readonly> = { + "objects.sys_share_link.fields.token.label": "6e8de3393d2cee54", +}; diff --git a/packages/plugins/plugin-sharing/src/translations/ja-JP.source-hashes.generated.ts b/packages/plugins/plugin-sharing/src/translations/ja-JP.source-hashes.generated.ts new file mode 100644 index 0000000000..517826c13d --- /dev/null +++ b/packages/plugins/plugin-sharing/src/translations/ja-JP.source-hashes.generated.ts @@ -0,0 +1,21 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ja-JP'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const jaJPGeneratedSourceHashes: Readonly> = { +}; diff --git a/packages/plugins/plugin-sharing/src/translations/zh-CN.source-hashes.generated.ts b/packages/plugins/plugin-sharing/src/translations/zh-CN.source-hashes.generated.ts new file mode 100644 index 0000000000..f74e0d3d8a --- /dev/null +++ b/packages/plugins/plugin-sharing/src/translations/zh-CN.source-hashes.generated.ts @@ -0,0 +1,21 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'zh-CN'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const zhCNGeneratedSourceHashes: Readonly> = { +}; diff --git a/packages/plugins/plugin-webhooks/scripts/i18n-extract.config.ts b/packages/plugins/plugin-webhooks/scripts/i18n-extract.config.ts index a7ef6d2ebb..786d18e203 100644 --- a/packages/plugins/plugin-webhooks/scripts/i18n-extract.config.ts +++ b/packages/plugins/plugin-webhooks/scripts/i18n-extract.config.ts @@ -9,7 +9,31 @@ * * os i18n extract packages/plugins/plugin-webhooks/scripts/i18n-extract.config.ts \ * --locales=zh-CN,ja-JP,es-ES --fill=default --objects-only --no-metadata-forms \ + * --source-hashes \ * --out=packages/plugins/plugin-webhooks/src/translations + * + * The `source-hashes` flag on the command above also emits + * `.source-hashes.generated.ts` — the provenance companion from + * maintainer ruling #12069 Option A (#11671), rolled out to every bundle set by + * #12559. Without it every generated leaf here is LEGACY-TRUSTED: a leaf filled + * from the source and then left behind when the source was revised is + * indistinguishable BY VALUE from a real translation, so it publishes a + * superseded draft under a green `check:i18n` forever, and + * `check:i18n-stale-fill` cannot see it either unless two locales happen to + * hold the same stale bytes. + * + * A record is written only for a leaf that IS currently a byte copy of the + * CURRENT source, so the companion arrives 0-stale by construction and only + * ever reports drift accruing afterwards. Records count the leaves currently + * RECORDABLE, never the leaves covered — a table with few entries, or with none + * at all where a locale is fully translated, is the instrument armed, and an + * entry appears by itself on the first extract after a leaf becomes a fill. + * + * The flag is named in prose WITHOUT its leading dashes on purpose: + * `flagsFromDocstring` (scripts/i18n-bundle-surface.mjs) harvests every + * recognised flag spelling out of this whole comment, so a second spelling + * would keep the opt-in switched on after someone deleted it from the command + * block — the one place that decides. */ import { defineStack, type ObjectStackDefinition } from '@objectstack/spec'; diff --git a/packages/plugins/plugin-webhooks/src/translations/es-ES.source-hashes.generated.ts b/packages/plugins/plugin-webhooks/src/translations/es-ES.source-hashes.generated.ts new file mode 100644 index 0000000000..01e3ea3658 --- /dev/null +++ b/packages/plugins/plugin-webhooks/src/translations/es-ES.source-hashes.generated.ts @@ -0,0 +1,28 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'es-ES'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const esESGeneratedSourceHashes: Readonly> = { + "objects.sys_webhook.fields.method.options.delete": "f59e059ed51f9bfc", + "objects.sys_webhook.fields.method.options.get": "470848255971c548", + "objects.sys_webhook.fields.method.options.patch": "7de3603749decb30", + "objects.sys_webhook.fields.method.options.post": "004b24eab36d26a4", + "objects.sys_webhook.fields.method.options.put": "04d929ecae7aa435", + "objects.sys_webhook.label": "b608f1636b7e8184", + "objects.sys_webhook.pluralLabel": "8fdd81bc76459a6a", +}; diff --git a/packages/plugins/plugin-webhooks/src/translations/ja-JP.source-hashes.generated.ts b/packages/plugins/plugin-webhooks/src/translations/ja-JP.source-hashes.generated.ts new file mode 100644 index 0000000000..cfcaea9de7 --- /dev/null +++ b/packages/plugins/plugin-webhooks/src/translations/ja-JP.source-hashes.generated.ts @@ -0,0 +1,27 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ja-JP'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const jaJPGeneratedSourceHashes: Readonly> = { + "objects.sys_webhook.fields.id.label": "9a4c8ec2a2567b95", + "objects.sys_webhook.fields.method.options.delete": "f59e059ed51f9bfc", + "objects.sys_webhook.fields.method.options.get": "470848255971c548", + "objects.sys_webhook.fields.method.options.patch": "7de3603749decb30", + "objects.sys_webhook.fields.method.options.post": "004b24eab36d26a4", + "objects.sys_webhook.fields.method.options.put": "04d929ecae7aa435", +}; diff --git a/packages/plugins/plugin-webhooks/src/translations/zh-CN.source-hashes.generated.ts b/packages/plugins/plugin-webhooks/src/translations/zh-CN.source-hashes.generated.ts new file mode 100644 index 0000000000..3d2de3c7fe --- /dev/null +++ b/packages/plugins/plugin-webhooks/src/translations/zh-CN.source-hashes.generated.ts @@ -0,0 +1,28 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'zh-CN'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const zhCNGeneratedSourceHashes: Readonly> = { + "objects.sys_webhook.fields.id.label": "9a4c8ec2a2567b95", + "objects.sys_webhook.fields.method.options.delete": "f59e059ed51f9bfc", + "objects.sys_webhook.fields.method.options.get": "470848255971c548", + "objects.sys_webhook.fields.method.options.patch": "7de3603749decb30", + "objects.sys_webhook.fields.method.options.post": "004b24eab36d26a4", + "objects.sys_webhook.fields.method.options.put": "04d929ecae7aa435", + "objects.sys_webhook.label": "b608f1636b7e8184", +}; diff --git a/packages/services/service-messaging/scripts/i18n-extract.config.ts b/packages/services/service-messaging/scripts/i18n-extract.config.ts index 7e0992af5d..ba53299533 100644 --- a/packages/services/service-messaging/scripts/i18n-extract.config.ts +++ b/packages/services/service-messaging/scripts/i18n-extract.config.ts @@ -8,7 +8,31 @@ * * os i18n extract packages/services/service-messaging/scripts/i18n-extract.config.ts \ * --locales=zh-CN,ja-JP,es-ES --fill=default --objects-only --no-metadata-forms \ + * --source-hashes \ * --out=packages/services/service-messaging/src/translations + * + * The `source-hashes` flag on the command above also emits + * `.source-hashes.generated.ts` — the provenance companion from + * maintainer ruling #12069 Option A (#11671), rolled out to every bundle set by + * #12559. Without it every generated leaf here is LEGACY-TRUSTED: a leaf filled + * from the source and then left behind when the source was revised is + * indistinguishable BY VALUE from a real translation, so it publishes a + * superseded draft under a green `check:i18n` forever, and + * `check:i18n-stale-fill` cannot see it either unless two locales happen to + * hold the same stale bytes. + * + * A record is written only for a leaf that IS currently a byte copy of the + * CURRENT source, so the companion arrives 0-stale by construction and only + * ever reports drift accruing afterwards. Records count the leaves currently + * RECORDABLE, never the leaves covered — a table with few entries, or with none + * at all where a locale is fully translated, is the instrument armed, and an + * entry appears by itself on the first extract after a leaf becomes a fill. + * + * The flag is named in prose WITHOUT its leading dashes on purpose: + * `flagsFromDocstring` (scripts/i18n-bundle-surface.mjs) harvests every + * recognised flag spelling out of this whole comment, so a second spelling + * would keep the opt-in switched on after someone deleted it from the command + * block — the one place that decides. */ import { defineStack, type ObjectStackDefinition } from '@objectstack/spec'; diff --git a/packages/services/service-messaging/src/translations/es-ES.source-hashes.generated.ts b/packages/services/service-messaging/src/translations/es-ES.source-hashes.generated.ts new file mode 100644 index 0000000000..236b89382b --- /dev/null +++ b/packages/services/service-messaging/src/translations/es-ES.source-hashes.generated.ts @@ -0,0 +1,164 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'es-ES'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const esESGeneratedSourceHashes: Readonly> = { + "objects.sys_http_delivery._views.failures.label": "c4170eee981cbb84", + "objects.sys_http_delivery._views.pending.label": "23b532305c06373f", + "objects.sys_http_delivery._views.recent.label": "62d27bb9d0349c99", + "objects.sys_http_delivery.description": "1b5cc18e87fa69ae", + "objects.sys_http_delivery.fields.attempts.label": "096d2dbc7038a926", + "objects.sys_http_delivery.fields.claimed_at.label": "28fa2f052f552d6a", + "objects.sys_http_delivery.fields.claimed_by.label": "298232b57e56a870", + "objects.sys_http_delivery.fields.created_at.label": "1f02d416befb595b", + "objects.sys_http_delivery.fields.dedup_key.help": "5fbe8843e6268b38", + "objects.sys_http_delivery.fields.dedup_key.label": "33a740837f882c98", + "objects.sys_http_delivery.fields.error.help": "3edd3406757bedcf", + "objects.sys_http_delivery.fields.error.label": "786fed84bd8d5a32", + "objects.sys_http_delivery.fields.headers_json.help": "b14e8e640874e0a6", + "objects.sys_http_delivery.fields.headers_json.label": "e1b44aaae98059a7", + "objects.sys_http_delivery.fields.id.help": "12f58d17c3cdf5f8", + "objects.sys_http_delivery.fields.id.label": "05179727cb19255c", + "objects.sys_http_delivery.fields.label.help": "f6bd8da04c99c740", + "objects.sys_http_delivery.fields.label.label": "c680364257b11569", + "objects.sys_http_delivery.fields.last_attempted_at.label": "2123f8266be603b8", + "objects.sys_http_delivery.fields.method.label": "b92d456bcaaaa559", + "objects.sys_http_delivery.fields.next_retry_at.label": "a3be3a1ea3e87935", + "objects.sys_http_delivery.fields.partition_key.help": "3314028b4892f955", + "objects.sys_http_delivery.fields.partition_key.label": "ff6e8336a279020a", + "objects.sys_http_delivery.fields.payload_json.label": "f1d49f340a98d46e", + "objects.sys_http_delivery.fields.ref_id.help": "2ef42311bf901593", + "objects.sys_http_delivery.fields.ref_id.label": "86ca68af01f450a5", + "objects.sys_http_delivery.fields.response_body.label": "bf6a1ccfb062b374", + "objects.sys_http_delivery.fields.response_code.label": "be5ffdf6a1a0c48a", + "objects.sys_http_delivery.fields.signature.help": "73a692636ee3390f", + "objects.sys_http_delivery.fields.signature.label": "0d5e395be13b9fd8", + "objects.sys_http_delivery.fields.source.help": "c6046a29a9e8ef39", + "objects.sys_http_delivery.fields.source.label": "8fe786e8e29c8cec", + "objects.sys_http_delivery.fields.status.help": "e437ecf81dfb4715", + "objects.sys_http_delivery.fields.status.label": "64a420165247654b", + "objects.sys_http_delivery.fields.timeout_ms.label": "8e99c9f77556e013", + "objects.sys_http_delivery.fields.updated_at.label": "aba63dc2a9c79b8d", + "objects.sys_http_delivery.fields.url.help": "68b75de6ed1e3336", + "objects.sys_http_delivery.fields.url.label": "501585a180652bdf", + "objects.sys_http_delivery.label": "302f263363fe99ab", + "objects.sys_http_delivery.pluralLabel": "340f81dc6ea64987", + "objects.sys_notification_delivery.description": "6e93eafd2b3ec57a", + "objects.sys_notification_delivery.fields.attempts.label": "096d2dbc7038a926", + "objects.sys_notification_delivery.fields.channel.label": "979883fe73ee88cb", + "objects.sys_notification_delivery.fields.claimed_at.label": "28fa2f052f552d6a", + "objects.sys_notification_delivery.fields.claimed_by.help": "cb28f6419bc307e2", + "objects.sys_notification_delivery.fields.claimed_by.label": "298232b57e56a870", + "objects.sys_notification_delivery.fields.created_at.label": "1f02d416befb595b", + "objects.sys_notification_delivery.fields.digest_key.help": "6277161d780a5a7f", + "objects.sys_notification_delivery.fields.digest_key.label": "c67774ec9d8352cd", + "objects.sys_notification_delivery.fields.error.label": "786fed84bd8d5a32", + "objects.sys_notification_delivery.fields.id.label": "05179727cb19255c", + "objects.sys_notification_delivery.fields.last_attempted_at.label": "2123f8266be603b8", + "objects.sys_notification_delivery.fields.next_attempt_at.label": "d73e994fb1cd8d97", + "objects.sys_notification_delivery.fields.notification_id.help": "d3efe092d04d3e71", + "objects.sys_notification_delivery.fields.notification_id.label": "4eb344190c79dd4f", + "objects.sys_notification_delivery.fields.partition_key.label": "c3d981b1d6885418", + "objects.sys_notification_delivery.fields.payload.help": "310d32eb414a7fbe", + "objects.sys_notification_delivery.fields.payload.label": "4488e1328e37dcb2", + "objects.sys_notification_delivery.fields.recipient_id.label": "0fd8db52eea0eb8f", + "objects.sys_notification_delivery.fields.status.label": "64a420165247654b", + "objects.sys_notification_delivery.fields.status.options.dead": "4bc6e183e741bccc", + "objects.sys_notification_delivery.fields.status.options.failed": "83eaae562c4a1821", + "objects.sys_notification_delivery.fields.status.options.in_flight": "0adc2d8a7b42fc74", + "objects.sys_notification_delivery.fields.status.options.pending": "3c27125b8820d9b0", + "objects.sys_notification_delivery.fields.status.options.success": "e3a9faf0a124fb39", + "objects.sys_notification_delivery.fields.status.options.suppressed": "ffac12f09463c45b", + "objects.sys_notification_delivery.fields.topic.label": "819afdb3853e9d80", + "objects.sys_notification_delivery.fields.updated_at.label": "aba63dc2a9c79b8d", + "objects.sys_notification_delivery.label": "dab3eb6594ba3014", + "objects.sys_notification_delivery.pluralLabel": "654247db5abbd7d2", + "objects.sys_notification_preference.description": "560b825951c71086", + "objects.sys_notification_preference.fields.channel.help": "8c06b7c3f1a65fd7", + "objects.sys_notification_preference.fields.channel.label": "979883fe73ee88cb", + "objects.sys_notification_preference.fields.created_at.label": "1f02d416befb595b", + "objects.sys_notification_preference.fields.digest.help": "86c9da5370d5c2e3", + "objects.sys_notification_preference.fields.digest.label": "76e9b35f27134f0b", + "objects.sys_notification_preference.fields.digest.options.daily": "1dd810d2b5a02102", + "objects.sys_notification_preference.fields.digest.options.none": "f0ac498bc08c6383", + "objects.sys_notification_preference.fields.digest.options.weekly": "18ffbbec919e0d35", + "objects.sys_notification_preference.fields.enabled.help": "94e2727fd8c18296", + "objects.sys_notification_preference.fields.enabled.label": "41fb04f20e64dc4f", + "objects.sys_notification_preference.fields.id.label": "429cdf8675ce2833", + "objects.sys_notification_preference.fields.quiet_hours.help": "588a4adb4b5013e7", + "objects.sys_notification_preference.fields.quiet_hours.label": "fab43f24806d8f6f", + "objects.sys_notification_preference.fields.topic.help": "cf04f6b5ad2a5a33", + "objects.sys_notification_preference.fields.topic.label": "819afdb3853e9d80", + "objects.sys_notification_preference.fields.updated_at.label": "aba63dc2a9c79b8d", + "objects.sys_notification_preference.fields.user_id.help": "0ecc5b30fb3ec13c", + "objects.sys_notification_preference.fields.user_id.label": "6c0d22a931e235bd", + "objects.sys_notification_preference.label": "614ba2a098ea782c", + "objects.sys_notification_preference.pluralLabel": "8869f324c1bd545a", + "objects.sys_notification_receipt.description": "d07a68d191e3f76c", + "objects.sys_notification_receipt.fields.at.help": "f2dc84f60f311cb6", + "objects.sys_notification_receipt.fields.at.label": "dabd1da5bd8c1711", + "objects.sys_notification_receipt.fields.channel.help": "07a4fefa6d304c6c", + "objects.sys_notification_receipt.fields.channel.label": "979883fe73ee88cb", + "objects.sys_notification_receipt.fields.created_at.label": "1f02d416befb595b", + "objects.sys_notification_receipt.fields.delivery_id.help": "1172c7faf856a1c1", + "objects.sys_notification_receipt.fields.delivery_id.label": "5d66f7ce49251258", + "objects.sys_notification_receipt.fields.id.label": "35d9cd2a305993b9", + "objects.sys_notification_receipt.fields.notification_id.help": "d3efe092d04d3e71", + "objects.sys_notification_receipt.fields.notification_id.label": "4eb344190c79dd4f", + "objects.sys_notification_receipt.fields.state.label": "979534e5517c0efc", + "objects.sys_notification_receipt.fields.state.options.clicked": "74252f15f0defa81", + "objects.sys_notification_receipt.fields.state.options.delivered": "d356f1a0a5a39003", + "objects.sys_notification_receipt.fields.state.options.dismissed": "cbc3e8e878dc4685", + "objects.sys_notification_receipt.fields.state.options.read": "bd01091f9c8a6ddd", + "objects.sys_notification_receipt.fields.user_id.label": "0fd8db52eea0eb8f", + "objects.sys_notification_receipt.label": "c12ae2509f823fbf", + "objects.sys_notification_receipt.pluralLabel": "405256a600be0880", + "objects.sys_notification_subscription.description": "00de4abdcb0d43a8", + "objects.sys_notification_subscription.fields.created_at.label": "1f02d416befb595b", + "objects.sys_notification_subscription.fields.enabled.help": "37dd96827553974c", + "objects.sys_notification_subscription.fields.enabled.label": "41fb04f20e64dc4f", + "objects.sys_notification_subscription.fields.id.label": "9dd5d9fded88d363", + "objects.sys_notification_subscription.fields.principal.label": "e86ee0cdf997afed", + "objects.sys_notification_subscription.fields.topic.help": "ca33ba660ca4eb04", + "objects.sys_notification_subscription.fields.topic.label": "819afdb3853e9d80", + "objects.sys_notification_subscription.label": "cb96f976a7b36f34", + "objects.sys_notification_subscription.pluralLabel": "907cb60dda7b39c8", + "objects.sys_notification_template.description": "159a2c4fa16bf97c", + "objects.sys_notification_template.fields.body.help": "af87336e946106ce", + "objects.sys_notification_template.fields.body.label": "d1753a024d6a9bed", + "objects.sys_notification_template.fields.channel.help": "f71964058486f39e", + "objects.sys_notification_template.fields.channel.label": "979883fe73ee88cb", + "objects.sys_notification_template.fields.created_at.label": "1f02d416befb595b", + "objects.sys_notification_template.fields.format.label": "20d89df5129d933a", + "objects.sys_notification_template.fields.format.options.html": "071a232b3e2be262", + "objects.sys_notification_template.fields.format.options.markdown": "73da5c387f1a660d", + "objects.sys_notification_template.fields.format.options.mjml": "97e06d01b9117b1c", + "objects.sys_notification_template.fields.format.options.text": "434f11dd4c313183", + "objects.sys_notification_template.fields.id.label": "d2a8b4c89bfeddff", + "objects.sys_notification_template.fields.is_active.help": "c18ae718014145b7", + "objects.sys_notification_template.fields.is_active.label": "eee107cf4e466779", + "objects.sys_notification_template.fields.locale.help": "1d2b03117aa89de5", + "objects.sys_notification_template.fields.locale.label": "92b39430e88d3b51", + "objects.sys_notification_template.fields.subject.help": "9eef4993f2d0fbb8", + "objects.sys_notification_template.fields.subject.label": "92502991f5df9937", + "objects.sys_notification_template.fields.topic.label": "819afdb3853e9d80", + "objects.sys_notification_template.fields.updated_at.label": "aba63dc2a9c79b8d", + "objects.sys_notification_template.fields.version.label": "b74a37c0879ec50e", + "objects.sys_notification_template.label": "30ecafa4dac4b40d", + "objects.sys_notification_template.pluralLabel": "9e270c87ef2578df", +}; diff --git a/packages/services/service-messaging/src/translations/ja-JP.source-hashes.generated.ts b/packages/services/service-messaging/src/translations/ja-JP.source-hashes.generated.ts new file mode 100644 index 0000000000..b81556dd76 --- /dev/null +++ b/packages/services/service-messaging/src/translations/ja-JP.source-hashes.generated.ts @@ -0,0 +1,164 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ja-JP'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const jaJPGeneratedSourceHashes: Readonly> = { + "objects.sys_http_delivery._views.failures.label": "c4170eee981cbb84", + "objects.sys_http_delivery._views.pending.label": "23b532305c06373f", + "objects.sys_http_delivery._views.recent.label": "62d27bb9d0349c99", + "objects.sys_http_delivery.description": "1b5cc18e87fa69ae", + "objects.sys_http_delivery.fields.attempts.label": "096d2dbc7038a926", + "objects.sys_http_delivery.fields.claimed_at.label": "28fa2f052f552d6a", + "objects.sys_http_delivery.fields.claimed_by.label": "298232b57e56a870", + "objects.sys_http_delivery.fields.created_at.label": "1f02d416befb595b", + "objects.sys_http_delivery.fields.dedup_key.help": "5fbe8843e6268b38", + "objects.sys_http_delivery.fields.dedup_key.label": "33a740837f882c98", + "objects.sys_http_delivery.fields.error.help": "3edd3406757bedcf", + "objects.sys_http_delivery.fields.error.label": "786fed84bd8d5a32", + "objects.sys_http_delivery.fields.headers_json.help": "b14e8e640874e0a6", + "objects.sys_http_delivery.fields.headers_json.label": "e1b44aaae98059a7", + "objects.sys_http_delivery.fields.id.help": "12f58d17c3cdf5f8", + "objects.sys_http_delivery.fields.id.label": "05179727cb19255c", + "objects.sys_http_delivery.fields.label.help": "f6bd8da04c99c740", + "objects.sys_http_delivery.fields.label.label": "c680364257b11569", + "objects.sys_http_delivery.fields.last_attempted_at.label": "2123f8266be603b8", + "objects.sys_http_delivery.fields.method.label": "b92d456bcaaaa559", + "objects.sys_http_delivery.fields.next_retry_at.label": "a3be3a1ea3e87935", + "objects.sys_http_delivery.fields.partition_key.help": "3314028b4892f955", + "objects.sys_http_delivery.fields.partition_key.label": "ff6e8336a279020a", + "objects.sys_http_delivery.fields.payload_json.label": "f1d49f340a98d46e", + "objects.sys_http_delivery.fields.ref_id.help": "2ef42311bf901593", + "objects.sys_http_delivery.fields.ref_id.label": "86ca68af01f450a5", + "objects.sys_http_delivery.fields.response_body.label": "bf6a1ccfb062b374", + "objects.sys_http_delivery.fields.response_code.label": "be5ffdf6a1a0c48a", + "objects.sys_http_delivery.fields.signature.help": "73a692636ee3390f", + "objects.sys_http_delivery.fields.signature.label": "0d5e395be13b9fd8", + "objects.sys_http_delivery.fields.source.help": "c6046a29a9e8ef39", + "objects.sys_http_delivery.fields.source.label": "8fe786e8e29c8cec", + "objects.sys_http_delivery.fields.status.help": "e437ecf81dfb4715", + "objects.sys_http_delivery.fields.status.label": "64a420165247654b", + "objects.sys_http_delivery.fields.timeout_ms.label": "8e99c9f77556e013", + "objects.sys_http_delivery.fields.updated_at.label": "aba63dc2a9c79b8d", + "objects.sys_http_delivery.fields.url.help": "68b75de6ed1e3336", + "objects.sys_http_delivery.fields.url.label": "501585a180652bdf", + "objects.sys_http_delivery.label": "302f263363fe99ab", + "objects.sys_http_delivery.pluralLabel": "340f81dc6ea64987", + "objects.sys_notification_delivery.description": "6e93eafd2b3ec57a", + "objects.sys_notification_delivery.fields.attempts.label": "096d2dbc7038a926", + "objects.sys_notification_delivery.fields.channel.label": "979883fe73ee88cb", + "objects.sys_notification_delivery.fields.claimed_at.label": "28fa2f052f552d6a", + "objects.sys_notification_delivery.fields.claimed_by.help": "cb28f6419bc307e2", + "objects.sys_notification_delivery.fields.claimed_by.label": "298232b57e56a870", + "objects.sys_notification_delivery.fields.created_at.label": "1f02d416befb595b", + "objects.sys_notification_delivery.fields.digest_key.help": "6277161d780a5a7f", + "objects.sys_notification_delivery.fields.digest_key.label": "c67774ec9d8352cd", + "objects.sys_notification_delivery.fields.error.label": "786fed84bd8d5a32", + "objects.sys_notification_delivery.fields.id.label": "05179727cb19255c", + "objects.sys_notification_delivery.fields.last_attempted_at.label": "2123f8266be603b8", + "objects.sys_notification_delivery.fields.next_attempt_at.label": "d73e994fb1cd8d97", + "objects.sys_notification_delivery.fields.notification_id.help": "d3efe092d04d3e71", + "objects.sys_notification_delivery.fields.notification_id.label": "4eb344190c79dd4f", + "objects.sys_notification_delivery.fields.partition_key.label": "c3d981b1d6885418", + "objects.sys_notification_delivery.fields.payload.help": "310d32eb414a7fbe", + "objects.sys_notification_delivery.fields.payload.label": "4488e1328e37dcb2", + "objects.sys_notification_delivery.fields.recipient_id.label": "0fd8db52eea0eb8f", + "objects.sys_notification_delivery.fields.status.label": "64a420165247654b", + "objects.sys_notification_delivery.fields.status.options.dead": "4bc6e183e741bccc", + "objects.sys_notification_delivery.fields.status.options.failed": "83eaae562c4a1821", + "objects.sys_notification_delivery.fields.status.options.in_flight": "0adc2d8a7b42fc74", + "objects.sys_notification_delivery.fields.status.options.pending": "3c27125b8820d9b0", + "objects.sys_notification_delivery.fields.status.options.success": "e3a9faf0a124fb39", + "objects.sys_notification_delivery.fields.status.options.suppressed": "ffac12f09463c45b", + "objects.sys_notification_delivery.fields.topic.label": "819afdb3853e9d80", + "objects.sys_notification_delivery.fields.updated_at.label": "aba63dc2a9c79b8d", + "objects.sys_notification_delivery.label": "dab3eb6594ba3014", + "objects.sys_notification_delivery.pluralLabel": "654247db5abbd7d2", + "objects.sys_notification_preference.description": "560b825951c71086", + "objects.sys_notification_preference.fields.channel.help": "8c06b7c3f1a65fd7", + "objects.sys_notification_preference.fields.channel.label": "979883fe73ee88cb", + "objects.sys_notification_preference.fields.created_at.label": "1f02d416befb595b", + "objects.sys_notification_preference.fields.digest.help": "86c9da5370d5c2e3", + "objects.sys_notification_preference.fields.digest.label": "76e9b35f27134f0b", + "objects.sys_notification_preference.fields.digest.options.daily": "1dd810d2b5a02102", + "objects.sys_notification_preference.fields.digest.options.none": "f0ac498bc08c6383", + "objects.sys_notification_preference.fields.digest.options.weekly": "18ffbbec919e0d35", + "objects.sys_notification_preference.fields.enabled.help": "94e2727fd8c18296", + "objects.sys_notification_preference.fields.enabled.label": "41fb04f20e64dc4f", + "objects.sys_notification_preference.fields.id.label": "429cdf8675ce2833", + "objects.sys_notification_preference.fields.quiet_hours.help": "588a4adb4b5013e7", + "objects.sys_notification_preference.fields.quiet_hours.label": "fab43f24806d8f6f", + "objects.sys_notification_preference.fields.topic.help": "cf04f6b5ad2a5a33", + "objects.sys_notification_preference.fields.topic.label": "819afdb3853e9d80", + "objects.sys_notification_preference.fields.updated_at.label": "aba63dc2a9c79b8d", + "objects.sys_notification_preference.fields.user_id.help": "0ecc5b30fb3ec13c", + "objects.sys_notification_preference.fields.user_id.label": "6c0d22a931e235bd", + "objects.sys_notification_preference.label": "614ba2a098ea782c", + "objects.sys_notification_preference.pluralLabel": "8869f324c1bd545a", + "objects.sys_notification_receipt.description": "d07a68d191e3f76c", + "objects.sys_notification_receipt.fields.at.help": "f2dc84f60f311cb6", + "objects.sys_notification_receipt.fields.at.label": "dabd1da5bd8c1711", + "objects.sys_notification_receipt.fields.channel.help": "07a4fefa6d304c6c", + "objects.sys_notification_receipt.fields.channel.label": "979883fe73ee88cb", + "objects.sys_notification_receipt.fields.created_at.label": "1f02d416befb595b", + "objects.sys_notification_receipt.fields.delivery_id.help": "1172c7faf856a1c1", + "objects.sys_notification_receipt.fields.delivery_id.label": "5d66f7ce49251258", + "objects.sys_notification_receipt.fields.id.label": "35d9cd2a305993b9", + "objects.sys_notification_receipt.fields.notification_id.help": "d3efe092d04d3e71", + "objects.sys_notification_receipt.fields.notification_id.label": "4eb344190c79dd4f", + "objects.sys_notification_receipt.fields.state.label": "979534e5517c0efc", + "objects.sys_notification_receipt.fields.state.options.clicked": "74252f15f0defa81", + "objects.sys_notification_receipt.fields.state.options.delivered": "d356f1a0a5a39003", + "objects.sys_notification_receipt.fields.state.options.dismissed": "cbc3e8e878dc4685", + "objects.sys_notification_receipt.fields.state.options.read": "bd01091f9c8a6ddd", + "objects.sys_notification_receipt.fields.user_id.label": "0fd8db52eea0eb8f", + "objects.sys_notification_receipt.label": "c12ae2509f823fbf", + "objects.sys_notification_receipt.pluralLabel": "405256a600be0880", + "objects.sys_notification_subscription.description": "00de4abdcb0d43a8", + "objects.sys_notification_subscription.fields.created_at.label": "1f02d416befb595b", + "objects.sys_notification_subscription.fields.enabled.help": "37dd96827553974c", + "objects.sys_notification_subscription.fields.enabled.label": "41fb04f20e64dc4f", + "objects.sys_notification_subscription.fields.id.label": "9dd5d9fded88d363", + "objects.sys_notification_subscription.fields.principal.label": "e86ee0cdf997afed", + "objects.sys_notification_subscription.fields.topic.help": "ca33ba660ca4eb04", + "objects.sys_notification_subscription.fields.topic.label": "819afdb3853e9d80", + "objects.sys_notification_subscription.label": "cb96f976a7b36f34", + "objects.sys_notification_subscription.pluralLabel": "907cb60dda7b39c8", + "objects.sys_notification_template.description": "159a2c4fa16bf97c", + "objects.sys_notification_template.fields.body.help": "af87336e946106ce", + "objects.sys_notification_template.fields.body.label": "d1753a024d6a9bed", + "objects.sys_notification_template.fields.channel.help": "f71964058486f39e", + "objects.sys_notification_template.fields.channel.label": "979883fe73ee88cb", + "objects.sys_notification_template.fields.created_at.label": "1f02d416befb595b", + "objects.sys_notification_template.fields.format.label": "20d89df5129d933a", + "objects.sys_notification_template.fields.format.options.html": "071a232b3e2be262", + "objects.sys_notification_template.fields.format.options.markdown": "73da5c387f1a660d", + "objects.sys_notification_template.fields.format.options.mjml": "97e06d01b9117b1c", + "objects.sys_notification_template.fields.format.options.text": "434f11dd4c313183", + "objects.sys_notification_template.fields.id.label": "d2a8b4c89bfeddff", + "objects.sys_notification_template.fields.is_active.help": "c18ae718014145b7", + "objects.sys_notification_template.fields.is_active.label": "eee107cf4e466779", + "objects.sys_notification_template.fields.locale.help": "1d2b03117aa89de5", + "objects.sys_notification_template.fields.locale.label": "92b39430e88d3b51", + "objects.sys_notification_template.fields.subject.help": "9eef4993f2d0fbb8", + "objects.sys_notification_template.fields.subject.label": "92502991f5df9937", + "objects.sys_notification_template.fields.topic.label": "819afdb3853e9d80", + "objects.sys_notification_template.fields.updated_at.label": "aba63dc2a9c79b8d", + "objects.sys_notification_template.fields.version.label": "b74a37c0879ec50e", + "objects.sys_notification_template.label": "30ecafa4dac4b40d", + "objects.sys_notification_template.pluralLabel": "9e270c87ef2578df", +}; diff --git a/packages/services/service-messaging/src/translations/zh-CN.source-hashes.generated.ts b/packages/services/service-messaging/src/translations/zh-CN.source-hashes.generated.ts new file mode 100644 index 0000000000..8e3ba6ad75 --- /dev/null +++ b/packages/services/service-messaging/src/translations/zh-CN.source-hashes.generated.ts @@ -0,0 +1,24 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'zh-CN'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const zhCNGeneratedSourceHashes: Readonly> = { + "objects.sys_http_delivery.fields.error.help": "3edd3406757bedcf", + "objects.sys_http_delivery.fields.headers_json.help": "b14e8e640874e0a6", + "objects.sys_http_delivery.fields.status.help": "e437ecf81dfb4715", +}; diff --git a/packages/services/service-realtime/scripts/i18n-extract.config.ts b/packages/services/service-realtime/scripts/i18n-extract.config.ts index 5767b08cbe..c196fdfd5c 100644 --- a/packages/services/service-realtime/scripts/i18n-extract.config.ts +++ b/packages/services/service-realtime/scripts/i18n-extract.config.ts @@ -9,7 +9,31 @@ * * os i18n extract packages/services/service-realtime/scripts/i18n-extract.config.ts \ * --locales=zh-CN,ja-JP,es-ES --fill=default --objects-only --no-metadata-forms \ + * --source-hashes \ * --out=packages/services/service-realtime/src/translations + * + * The `source-hashes` flag on the command above also emits + * `.source-hashes.generated.ts` — the provenance companion from + * maintainer ruling #12069 Option A (#11671), rolled out to every bundle set by + * #12559. Without it every generated leaf here is LEGACY-TRUSTED: a leaf filled + * from the source and then left behind when the source was revised is + * indistinguishable BY VALUE from a real translation, so it publishes a + * superseded draft under a green `check:i18n` forever, and + * `check:i18n-stale-fill` cannot see it either unless two locales happen to + * hold the same stale bytes. + * + * A record is written only for a leaf that IS currently a byte copy of the + * CURRENT source, so the companion arrives 0-stale by construction and only + * ever reports drift accruing afterwards. Records count the leaves currently + * RECORDABLE, never the leaves covered — a table with few entries, or with none + * at all where a locale is fully translated, is the instrument armed, and an + * entry appears by itself on the first extract after a leaf becomes a fill. + * + * The flag is named in prose WITHOUT its leading dashes on purpose: + * `flagsFromDocstring` (scripts/i18n-bundle-surface.mjs) harvests every + * recognised flag spelling out of this whole comment, so a second spelling + * would keep the opt-in switched on after someone deleted it from the command + * block — the one place that decides. */ import { defineStack, type ObjectStackDefinition } from '@objectstack/spec'; diff --git a/packages/services/service-realtime/src/translations/es-ES.source-hashes.generated.ts b/packages/services/service-realtime/src/translations/es-ES.source-hashes.generated.ts new file mode 100644 index 0000000000..854c8b71cb --- /dev/null +++ b/packages/services/service-realtime/src/translations/es-ES.source-hashes.generated.ts @@ -0,0 +1,21 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'es-ES'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const esESGeneratedSourceHashes: Readonly> = { +}; diff --git a/packages/services/service-realtime/src/translations/ja-JP.source-hashes.generated.ts b/packages/services/service-realtime/src/translations/ja-JP.source-hashes.generated.ts new file mode 100644 index 0000000000..517826c13d --- /dev/null +++ b/packages/services/service-realtime/src/translations/ja-JP.source-hashes.generated.ts @@ -0,0 +1,21 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ja-JP'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const jaJPGeneratedSourceHashes: Readonly> = { +}; diff --git a/packages/services/service-realtime/src/translations/zh-CN.source-hashes.generated.ts b/packages/services/service-realtime/src/translations/zh-CN.source-hashes.generated.ts new file mode 100644 index 0000000000..f74e0d3d8a --- /dev/null +++ b/packages/services/service-realtime/src/translations/zh-CN.source-hashes.generated.ts @@ -0,0 +1,21 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'zh-CN'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const zhCNGeneratedSourceHashes: Readonly> = { +}; diff --git a/packages/services/service-storage/scripts/i18n-extract.config.ts b/packages/services/service-storage/scripts/i18n-extract.config.ts index f9fd66bc2e..aeea76ab3e 100644 --- a/packages/services/service-storage/scripts/i18n-extract.config.ts +++ b/packages/services/service-storage/scripts/i18n-extract.config.ts @@ -12,7 +12,31 @@ * * os i18n extract packages/services/service-storage/scripts/i18n-extract.config.ts \ * --locales=zh-CN,ja-JP,es-ES --fill=default --objects-only --no-metadata-forms \ + * --source-hashes \ * --out=packages/services/service-storage/src/translations + * + * The `source-hashes` flag on the command above also emits + * `.source-hashes.generated.ts` — the provenance companion from + * maintainer ruling #12069 Option A (#11671), rolled out to every bundle set by + * #12559. Without it every generated leaf here is LEGACY-TRUSTED: a leaf filled + * from the source and then left behind when the source was revised is + * indistinguishable BY VALUE from a real translation, so it publishes a + * superseded draft under a green `check:i18n` forever, and + * `check:i18n-stale-fill` cannot see it either unless two locales happen to + * hold the same stale bytes. + * + * A record is written only for a leaf that IS currently a byte copy of the + * CURRENT source, so the companion arrives 0-stale by construction and only + * ever reports drift accruing afterwards. Records count the leaves currently + * RECORDABLE, never the leaves covered — a table with few entries, or with none + * at all where a locale is fully translated, is the instrument armed, and an + * entry appears by itself on the first extract after a leaf becomes a fill. + * + * The flag is named in prose WITHOUT its leading dashes on purpose: + * `flagsFromDocstring` (scripts/i18n-bundle-surface.mjs) harvests every + * recognised flag spelling out of this whole comment, so a second spelling + * would keep the opt-in switched on after someone deleted it from the command + * block — the one place that decides. */ import { defineStack, type ObjectStackDefinition } from '@objectstack/spec'; diff --git a/packages/services/service-storage/src/translations/es-ES.source-hashes.generated.ts b/packages/services/service-storage/src/translations/es-ES.source-hashes.generated.ts new file mode 100644 index 0000000000..5039467236 --- /dev/null +++ b/packages/services/service-storage/src/translations/es-ES.source-hashes.generated.ts @@ -0,0 +1,25 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'es-ES'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const esESGeneratedSourceHashes: Readonly> = { + "objects.sys_file.fields.acl.label": "f08120d40bf5d975", + "objects.sys_file.fields.bucket.label": "3aa22e888d1f90c0", + "objects.sys_file.fields.etag.label": "a62e7fe0bac46fcb", + "objects.sys_upload_session.fields.bucket.label": "3aa22e888d1f90c0", +}; diff --git a/packages/services/service-storage/src/translations/ja-JP.source-hashes.generated.ts b/packages/services/service-storage/src/translations/ja-JP.source-hashes.generated.ts new file mode 100644 index 0000000000..f9869fcfb8 --- /dev/null +++ b/packages/services/service-storage/src/translations/ja-JP.source-hashes.generated.ts @@ -0,0 +1,23 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ja-JP'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const jaJPGeneratedSourceHashes: Readonly> = { + "objects.sys_file.fields.acl.label": "f08120d40bf5d975", + "objects.sys_file.fields.etag.label": "a62e7fe0bac46fcb", +}; diff --git a/packages/services/service-storage/src/translations/zh-CN.source-hashes.generated.ts b/packages/services/service-storage/src/translations/zh-CN.source-hashes.generated.ts new file mode 100644 index 0000000000..1a90cae2c0 --- /dev/null +++ b/packages/services/service-storage/src/translations/zh-CN.source-hashes.generated.ts @@ -0,0 +1,22 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'zh-CN'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (#11671, maintainer ruling #12069 Option A, extending #8765 Option B). + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const zhCNGeneratedSourceHashes: Readonly> = { + "objects.sys_file.fields.etag.label": "a62e7fe0bac46fcb", +};