@@ -27,30 +27,36 @@ interface Resolved { value: unknown; source?: string }
2727function fakeSettings ( values : Record < string , Resolved > ) {
2828 const listeners : Array < ( ) => void > = [ ] ;
2929 const actions = new Map < string , ( a : any ) => Promise < any > > ( ) ;
30- let liveStatus : ( ( ) => Promise < LiveMailTransportStatus | undefined > ) | undefined ;
30+ type LiveStatusFn = ( input ?: { locale ?: string } ) => Promise < LiveMailTransportStatus | undefined > ;
31+ let liveStatus : LiveStatusFn | undefined ;
3132 return {
3233 values,
3334 createClient : ( ) => ( { } ) ,
3435 getNamespace : async ( ) => ( { values } ) ,
3536 subscribe : ( _ns : string , cb : ( ) => void ) => { listeners . push ( cb ) ; } ,
3637 registerAction : ( ns : string , id : string , fn : ( a : any ) => Promise < any > ) => { actions . set ( `${ ns } /${ id } ` , fn ) ; } ,
37- registerLiveStatus : ( ns : string , fn : ( ) => Promise < LiveMailTransportStatus | undefined > ) => {
38+ registerLiveStatus : ( ns : string , fn : LiveStatusFn ) => {
3839 if ( ns === 'mail' ) liveStatus = fn ;
3940 } ,
4041 /** Patch the snapshot and emit settings:changed, as a page save does. */
4142 save ( patch : Record < string , Resolved > ) {
4243 Object . assign ( values , patch ) ;
4344 for ( const l of listeners ) l ( ) ;
4445 } ,
45- /** What the page states, as the settings GET route asks for it (waits for a running re-apply). */
46- statement : ( ) => liveStatus ! ( ) ,
46+ /**
47+ * What the page states, as the settings GET route asks for it (waits for a
48+ * running re-apply), in the caller's display `locale` when one is given.
49+ */
50+ statement : ( locale ?: string ) => liveStatus ! ( locale ? { locale } : undefined ) ,
4751 /**
4852 * The "Send test email" answer for the stored values — with nothing edited
49- * on the form, or with `form` as the unsaved form state the page posts.
53+ * on the form, or with `form` as the unsaved form state the page posts —
54+ * in the caller's display `locale` when one is given.
5055 */
51- sendTest : ( form ?: Record < string , unknown > ) => actions . get ( 'mail/test' ) ! ( {
56+ sendTest : ( form ?: Record < string , unknown > , locale ?: string ) => actions . get ( 'mail/test' ) ! ( {
5257 values : Object . fromEntries ( Object . entries ( values ) . map ( ( [ k , v ] ) => [ k , v . value ] ) ) ,
5358 payload : form ? { to : 'admin@example.test' , values : form } : { to : 'admin@example.test' } ,
59+ ...( locale ? { locale } : { } ) ,
5460 } ) ,
5561 } ;
5662}
@@ -226,3 +232,61 @@ describe('"Send test email" with provider "log" on the form, not saved, while SM
226232 expect ( answer . message ) . toMatch ( / ^ S e n t t e s t e m a i l t o a d m i n @ e x a m p l e \. t e s t v i a s m t p \( s m t p \. f o r m \. t e s t : 5 8 7 \) / ) ;
227233 } ) ;
228234} ) ;
235+
236+ // Setup → Email in the caller's display language. The page's statement and the
237+ // "Send test email" answer are both composed here, per request, so both take
238+ // the locale the settings routes read from the request (`Accept-Language`,
239+ // else `?locale`). English stays the answer for no locale and for a language
240+ // this plugin ships no copy for.
241+ describe ( 'Setup → Email answers in the caller\'s language (zh-CN)' , ( ) => {
242+ const HAN = / [ 一 - 鿿 ] / ;
243+ const SMTP_SAVED = {
244+ provider : { value : 'smtp' , source : 'global' } ,
245+ smtp_host : { value : 'smtp.saved.test' , source : 'global' } ,
246+ } ;
247+
248+ it ( 'the out-of-the-box statement says, in Chinese, that no mail is sent' , async ( ) => {
249+ const h = await boot ( { } ) ;
250+ const statement = await h . settings . statement ( 'zh-CN' ) ;
251+ expect ( statement ?. label ) . toBe ( '当前不会发出任何邮件' ) ;
252+ expect ( statement ?. bannerSeverity ) . toBe ( 'warning' ) ;
253+ expect ( statement ?. bannerText ) . toMatch ( HAN ) ;
254+ expect ( statement ?. bannerText ) . not . toMatch ( / l i v e t r a n s p o r t | s y s _ e m a i l / ) ;
255+ } ) ;
256+
257+ it ( 'a delivering transport is named in a Chinese statement' , async ( ) => {
258+ const h = await boot ( SMTP_SAVED ) ;
259+ const statement = await h . settings . statement ( 'zh-CN' ) ;
260+ expect ( statement ?. label ) . toBe ( '邮件通过 SMTP (smtp.saved.test:587) 投递' ) ;
261+ expect ( statement ?. bannerSeverity ) . toBe ( 'info' ) ;
262+ expect ( statement ?. bannerText ) . toMatch ( HAN ) ;
263+ } ) ;
264+
265+ it ( 'the test toast reports the send in Chinese' , async ( ) => {
266+ const h = await boot ( SMTP_SAVED ) ;
267+ nm . sendMail . mockClear ( ) ;
268+ const answer = await h . settings . sendTest (
269+ { provider : 'smtp' , smtp_host : 'smtp.form.test' , smtp_port : 587 } ,
270+ 'zh-CN' ,
271+ ) ;
272+ expect ( nm . sendMail ) . toHaveBeenCalledTimes ( 1 ) ;
273+ expect ( answer ) . toMatchObject ( { ok : true , severity : 'info' } ) ;
274+ expect ( answer . message ) . toMatch ( / ^ 测 试 邮 件 已 发 送 至 a d m i n @ e x a m p l e \. t e s t , 经 由 s m t p \( s m t p \. f o r m \. t e s t : 5 8 7 \) / ) ;
275+ expect ( answer . message ) . not . toMatch ( / S e n t t e s t e m a i l / ) ;
276+ } ) ;
277+
278+ it ( '"None (log only)" on the form: the warning is Chinese and names the live transport in Chinese' , async ( ) => {
279+ const h = await boot ( SMTP_SAVED ) ;
280+ nm . sendMail . mockClear ( ) ;
281+ const answer = await h . settings . sendTest ( { provider : 'log' } , 'zh-CN' ) ;
282+ expect ( nm . sendMail ) . not . toHaveBeenCalled ( ) ;
283+ expect ( answer ) . toMatchObject ( { ok : false , severity : 'warning' } ) ;
284+ expect ( answer . message ) . toMatch ( / ^ 没 有 投 递 任 何 邮 件 / ) ;
285+ expect ( answer . message ) . toContain ( '邮件通过 SMTP (smtp.saved.test:587) 投递' ) ;
286+ } ) ;
287+
288+ it ( 'CONTROL — a language with no copy answers in English' , async ( ) => {
289+ const h = await boot ( { } ) ;
290+ expect ( ( await h . settings . statement ( 'fr-FR' ) ) ?. label ) . toBe ( 'No mail leaves this machine' ) ;
291+ } ) ;
292+ } ) ;
0 commit comments