@@ -37,13 +37,13 @@ vi.mock('lucide-react', async (importOriginal) => ({
3737 Globe : ( ) => < span /> ,
3838} ) ) ;
3939
40- import { PLATFORM_LOCALE_TAGS } from '@objectstack/spec/system' ;
40+ import { PLATFORM_LOCALES , PLATFORM_LOCALE_TAGS } from '@objectstack/spec/system' ;
4141import { LocaleSwitcher , localeLabel } from '../LocaleSwitcher' ;
4242
4343/**
44- * The eight built-in native names an app shipping only `en`+`zh` must NOT show:
45- * every platform locale table language but English and Chinese, named as the
46- * menu names them (`pt-BR` by `Intl.DisplayNames`, objectstack#30511 ).
44+ * The eight built-in native names an app shipping only `en`+`zh-Hans ` must NOT
45+ * show: every platform locale table language but English and Chinese, named as
46+ * the menu names them (from the table's own labels ).
4747 */
4848const PHANTOM_LABELS = PLATFORM_LOCALE_TAGS
4949 . filter ( ( tag ) => tag !== 'en' && ! tag . startsWith ( 'zh-' ) )
@@ -75,12 +75,12 @@ beforeEach(() => {
7575
7676describe ( 'LocaleSwitcher — menu composed from the app locale list' , ( ) => {
7777 it ( 'offers an app-shipped locale outside the built-in ten, named by Intl.DisplayNames' , async ( ) => {
78- mountConsole ( { locales : async ( ) => [ 'en' , 'zh' , 'th' ] } ) ;
78+ mountConsole ( { locales : async ( ) => [ 'en' , 'zh-Hans ' , 'th' ] } ) ;
7979
80- // `th` has no built-in pack and no hand-written label ; CLDR names it.
80+ // `th` has no built-in pack and no platform locale table row ; CLDR names it.
8181 await waitFor ( ( ) => expect ( screen . getByText ( 'ไทย' ) ) . toBeInTheDocument ( ) ) ;
8282 expect ( screen . getByText ( 'English' ) ) . toBeInTheDocument ( ) ;
83- expect ( screen . getByText ( '中文 ' ) ) . toBeInTheDocument ( ) ;
83+ expect ( screen . getByText ( '简体中文 ' ) ) . toBeInTheDocument ( ) ;
8484 } ) ;
8585
8686 it ( 'offers an app-shipped locale supplied through config.resources' , async ( ) => {
@@ -106,10 +106,10 @@ describe('LocaleSwitcher — menu composed from the app locale list', () => {
106106 } ) ;
107107
108108 it ( 'shows ONLY what the app ships — the phantom eight are gone' , async ( ) => {
109- mountConsole ( { locales : async ( ) => [ 'en' , 'zh' ] } ) ;
109+ mountConsole ( { locales : async ( ) => [ 'en' , 'zh-Hans ' ] } ) ;
110110
111111 await waitFor ( ( ) => expect ( screen . getByText ( 'English' ) ) . toBeInTheDocument ( ) ) ;
112- expect ( screen . getByText ( '中文 ' ) ) . toBeInTheDocument ( ) ;
112+ expect ( screen . getByText ( '简体中文 ' ) ) . toBeInTheDocument ( ) ;
113113 for ( const phantom of PHANTOM_LABELS ) {
114114 expect ( screen . queryByText ( phantom ) ) . not . toBeInTheDocument ( ) ;
115115 }
@@ -154,7 +154,7 @@ describe('LocaleSwitcher — menu composed from the app locale list', () => {
154154 expect ( container . firstChild ) . toBeNull ( ) ;
155155 expect ( screen . queryByText ( 'English' ) ) . not . toBeInTheDocument ( ) ;
156156
157- answer ( [ 'en' , 'zh' ] ) ;
157+ answer ( [ 'en' , 'zh-Hans ' ] ) ;
158158
159159 await waitFor ( ( ) => expect ( screen . getByText ( 'English' ) ) . toBeInTheDocument ( ) ) ;
160160 expect ( screen . queryByText ( '日本語' ) ) . not . toBeInTheDocument ( ) ;
@@ -175,3 +175,40 @@ describe('LocaleSwitcher — menu composed from the app locale list', () => {
175175 }
176176 } ) ;
177177} ) ;
178+
179+ describe ( 'localeLabel — the built-in packs are named by the platform locale table' , ( ) => {
180+ it ( 'names every table tag with its row label' , ( ) => {
181+ for ( const row of PLATFORM_LOCALES ) {
182+ expect ( localeLabel ( row . tag ) , row . tag ) . toBe ( row . label ) ;
183+ }
184+ } ) ;
185+
186+ it ( 'names every table tag with its row label on a runtime without Intl.DisplayNames' , ( ) => {
187+ // The table label must not be whatever the runtime's CLDR happens to say:
188+ // with the runtime naming nothing, every row still reads its own label.
189+ const displayNames = Intl . DisplayNames ;
190+ Object . defineProperty ( Intl , 'DisplayNames' , {
191+ configurable : true ,
192+ writable : true ,
193+ value : undefined ,
194+ } ) ;
195+ try {
196+ for ( const row of PLATFORM_LOCALES ) {
197+ expect ( localeLabel ( row . tag ) , row . tag ) . toBe ( row . label ) ;
198+ }
199+ // An app-shipped code still degrades to the bare code, not a crash.
200+ expect ( localeLabel ( 'th' ) ) . toBe ( 'th' ) ;
201+ } finally {
202+ Object . defineProperty ( Intl , 'DisplayNames' , {
203+ configurable : true ,
204+ writable : true ,
205+ value : displayNames ,
206+ } ) ;
207+ }
208+ } ) ;
209+
210+ it ( 'cannot read a label off Object.prototype' , ( ) => {
211+ expect ( localeLabel ( 'constructor' ) ) . toBe ( 'constructor' ) ;
212+ expect ( localeLabel ( '__proto__' ) ) . toBe ( '__proto__' ) ;
213+ } ) ;
214+ } ) ;
0 commit comments