@@ -103,6 +103,7 @@ import { createMemoryI18n } from '@objectstack/core';
103103import { installAuditWriters } from './audit-writers.js' ;
104104import { zhCNObjects } from './translations/zh-CN.objects.generated.js' ;
105105import { singlePostureDefaultOrganization } from './__tests__/single-posture-tenancy.js' ;
106+ import { summaryAsRead } from './__tests__/summary-as-read.js' ;
106107
107108// ---------------------------------------------------------------------------
108109// Fixtures
@@ -347,7 +348,16 @@ async function boot(opts: { locale?: string; i18n?: unknown } = {}) {
347348 getI18n : ( ) => opts . i18n as any ,
348349 getLocale : async ( ) => opts . locale ,
349350 } ) ;
350- return { engine, ...stub } ;
351+ // [#26167] The summaries as a reader in `opts.locale` is served them: the
352+ // row stores the English sentence and its code, and the field and option
353+ // labels are localized per READER (`summary-as-read.ts`).
354+ const readerSummaries = ( ) =>
355+ Array . from ( stub . storeFor ( 'sys_activity' ) . values ( ) ) . map ( ( r ) => summaryAsRead ( r , opts . i18n , opts . locale , engine ) ) ;
356+ const readerSummary = ( ) => {
357+ const all = readerSummaries ( ) ;
358+ return all [ all . length - 1 ] ;
359+ } ;
360+ return { engine, readerSummaries, readerSummary, ...stub } ;
351361}
352362
353363const summariesOf = ( storeFor : ( o : string ) => Map < string , Record < string , unknown > > ) =>
@@ -387,40 +397,42 @@ const setStage = (engine: any, id: string, stage: string) =>
387397
388398describe ( '[#7289] tracked-change branch — select option labels resolve through the bundle' , ( ) => {
389399 it ( 'localizes the option label on BOTH sides of the diff (the reported string)' , async ( ) => {
390- const { engine, storeFor } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
400+ const { engine, storeFor, readerSummary } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
391401 await seed ( engine ) ;
392402
393403 await setStage ( engine , 'deal_1' , 'closed_won' ) ;
394404
395405 // Was `阶段: Proposal → Closed Won` — localized frame, authored values.
396- expect ( lastSummary ( storeFor ) ) . toBe ( '阶段: 提案中 → 已赢单' ) ;
406+ // [#26167] Stored in English with the option VALUES; read in zh-CN.
407+ expect ( lastSummary ( storeFor ) ) . toBe ( 'Stage: Proposal → Closed Won' ) ;
408+ expect ( readerSummary ( ) ) . toBe ( '阶段: 提案中 → 已赢单' ) ;
397409 } ) ;
398410
399411 it ( 'falls back to the AUTHORED option label per VALUE, not per field' , async ( ) => {
400- const { engine, storeFor } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
412+ const { engine, readerSummary } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
401413 await seed ( engine ) ;
402414
403415 // `stage.options.on_hold` is absent from the bundle; `proposal` is present.
404416 // A mixed assertion on purpose: a single-sided one would survive deleting
405417 // the translate limb, which is the trap #7291 measured on its raw-id case.
406418 await setStage ( engine , 'deal_1' , 'on_hold' ) ;
407419
408- expect ( lastSummary ( storeFor ) ) . toBe ( '阶段: 提案中 → On Hold' ) ;
420+ expect ( readerSummary ( ) ) . toBe ( '阶段: 提案中 → On Hold' ) ;
409421 } ) ;
410422
411423 it ( 'localizes the BARE-STRING option shape, whose authored label is the value' , async ( ) => {
412- const { engine, storeFor } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
424+ const { engine, readerSummary } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
413425 await seed ( engine ) ;
414426
415427 // `options: ['p1', 'p2']` → the authored label chain has only `ov` in it.
416428 // `p1` is in the bundle, `p2` is not, so both directions are pinned here.
417429 await engine . update ( 'biz_deal' , { priority : 'p1' } , { where : { id : 'deal_1' } } as any ) ;
418430
419- expect ( lastSummary ( storeFor ) ) . toBe ( '优先级: p2 → 最高' ) ;
431+ expect ( readerSummary ( ) ) . toBe ( '优先级: p2 → 最高' ) ;
420432 } ) ;
421433
422434 it ( 'renders String(value) for a value matching NO declared option, bundle key or not' , async ( ) => {
423- const { engine, storeFor } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
435+ const { engine, readerSummary } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
424436 await seed ( engine ) ;
425437
426438 // The bundle DOES declare `channel.options.phone_call = '电话'`, and
@@ -431,11 +443,11 @@ describe('[#7289] tracked-change branch — select option labels resolve through
431443 // by the translate limb being dead.
432444 await engine . update ( 'biz_deal' , { channel : 'phone_call' } , { where : { id : 'deal_1' } } as any ) ;
433445
434- expect ( lastSummary ( storeFor ) ) . toBe ( '渠道: 网页 → phone_call' ) ;
446+ expect ( readerSummary ( ) ) . toBe ( '渠道: 网页 → phone_call' ) ;
435447 } ) ;
436448
437449 it ( 'leaves the reference branch (#7230 / #7291) rendering titles beside the options' , async ( ) => {
438- const { engine, storeFor } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
450+ const { engine, readerSummary } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
439451 await seed ( engine ) ;
440452
441453 await engine . update (
@@ -446,7 +458,7 @@ describe('[#7289] tracked-change branch — select option labels resolve through
446458
447459 // Option label from the bundle, record title from the read: the two
448460 // resolutions are independent and both must be present in one summary.
449- expect ( lastSummary ( storeFor ) ) . toBe ( '阶段: 提案中 → 已赢单; 负责人: ∅ → 张伟' ) ;
461+ expect ( readerSummary ( ) ) . toBe ( '阶段: 提案中 → 已赢单; 负责人: ∅ → 张伟' ) ;
450462 } ) ;
451463
452464 it ( 'keeps the authored option label when no i18n service is resolvable' , async ( ) => {
@@ -474,7 +486,7 @@ describe('[#7289] tracked-change branch — select option labels resolve through
474486
475487describe ( '[#7289] fired-milestone branch — option labels stay AUTHORED, on purpose' , ( ) => {
476488 it ( 'renders the authored option label inside the untranslated author sentence' , async ( ) => {
477- const { engine, storeFor } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
489+ const { engine, storeFor, readerSummaries } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
478490 await seed ( engine ) ;
479491
480492 // Two writes, and the first one is the CONTROL: it proves the locale is
@@ -486,13 +498,15 @@ describe('[#7289] fired-milestone branch — option labels stay AUTHORED, on pur
486498 await setStage ( engine , 'deal_2' , 'archived' ) ;
487499
488500 const all = summariesOf ( storeFor ) ;
489- expect ( all [ all . length - 2 ] ) . toBe ( '阶段: 提案中 → 已赢单' ) ;
501+ expect ( readerSummaries ( ) [ all . length - 2 ] ) . toBe ( '阶段: 提案中 → 已赢单' ) ;
490502
491503 // `{stage}` → the AUTHORED `Archived`, although the bundle declares
492504 // `stage.options.archived = '已归档'`. `{owner}` → `张伟`, because #7290's
493505 // title resolution is locale-INDEPENDENT data, not a translation. The
494506 // author's sentence and the author's option label stay in one language.
495507 expect ( all [ all . length - 1 ] ) . toBe ( 'Deal moved to Archived by 张伟' ) ;
508+ // [#26167] …and a zh-CN READER is served it as written: no summary code.
509+ expect ( readerSummaries ( ) [ all . length - 1 ] ) . toBe ( 'Deal moved to Archived by 张伟' ) ;
496510 } ) ;
497511
498512 // ⚠️ Status-quo pin, not coverage of this change: the locale is OFF here, so
@@ -517,7 +531,7 @@ describe('[#7289] fired-milestone branch — option labels stay AUTHORED, on pur
517531
518532describe ( '[#7289] localizing an option label is a bundle lookup and adds no reads' , ( ) => {
519533 it ( 'a tracked option-only change pays ZERO reads, on a row that HOLDS references' , async ( ) => {
520- const { engine, reads, storeFor } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
534+ const { engine, reads, readerSummary } = await boot ( { locale : 'zh-CN' , i18n : makeI18n ( ) } ) ;
521535 await seed ( engine ) ;
522536 const before = { user : reads . findOn [ 'sys_user' ] ?? 0 , acct : reads . findOn [ 'crm_account' ] ?? 0 } ;
523537
@@ -530,7 +544,7 @@ describe('[#7289] localizing an option label is a bundle lookup and adds no read
530544
531545 expect ( ( reads . findOn [ 'sys_user' ] ?? 0 ) - before . user ) . toBe ( 0 ) ;
532546 expect ( ( reads . findOn [ 'crm_account' ] ?? 0 ) - before . acct ) . toBe ( 0 ) ;
533- expect ( lastSummary ( storeFor ) ) . toBe ( '阶段: 提案中 → 已赢单' ) ;
547+ expect ( readerSummary ( ) ) . toBe ( '阶段: 提案中 → 已赢单' ) ;
534548 } ) ;
535549
536550 // ⚠️ Retained as a forward guard, not as coverage of this change: a create
0 commit comments