diff --git a/packages/fxa-settings/src/components/DeviceInfoBlock/index.tsx b/packages/fxa-settings/src/components/DeviceInfoBlock/index.tsx index 06266db1168..0286007d885 100644 --- a/packages/fxa-settings/src/components/DeviceInfoBlock/index.tsx +++ b/packages/fxa-settings/src/components/DeviceInfoBlock/index.tsx @@ -19,38 +19,52 @@ export const DeviceInfoBlock = ({ remoteMetadata }: DeviceInfoBlockProps) => { city, region, country, + countryCode, } = remoteMetadata; + const currentLocale = document.documentElement.lang || 'en-US'; + let localizedCountry = country; + + if (countryCode && typeof Intl !== 'undefined' && Intl.DisplayNames) { + try { + const regionNames = new Intl.DisplayNames([currentLocale], { + type: 'region', + }); + localizedCountry = regionNames.of(countryCode) || country; + } catch (e) { + // Fallback to raw country name if Intl fails + } + } const LocalizedLocation = () => { if (city && region && country) { return ( {`${city}, ${region}, ${country} (estimated)`} + vars={{ city, region, country: localizedCountry }} + >{`${city}, ${region}, ${localizedCountry} (estimated)`} ); } if (region && country) { return ( {`${region}, ${country} (estimated)`} + vars={{ region, country: localizedCountry }} + >{`${region}, ${localizedCountry} (estimated)`} ); } if (city && country) { return ( {`${city}, ${country} (estimated)`} + vars={{ city, country: localizedCountry }} + >{`${city}, ${localizedCountry} (estimated)`} ); } if (country) { return ( {`${country} (estimated)`} + vars={{ country: localizedCountry }} + >{`${localizedCountry} (estimated)`} ); } return ( diff --git a/packages/fxa-settings/src/lib/types.ts b/packages/fxa-settings/src/lib/types.ts index 0faff1d7878..2a5b8be6cf4 100644 --- a/packages/fxa-settings/src/lib/types.ts +++ b/packages/fxa-settings/src/lib/types.ts @@ -69,6 +69,7 @@ export type RemoteMetadata = { deviceOS: string; ipAddress: string; country?: string; + countryCode?: string; region?: string; city?: string; }; diff --git a/packages/fxa-shared/connected-services/formatters.ts b/packages/fxa-shared/connected-services/formatters.ts index bb43d12d3eb..09d06e8bdc4 100644 --- a/packages/fxa-shared/connected-services/formatters.ts +++ b/packages/fxa-shared/connected-services/formatters.ts @@ -50,47 +50,18 @@ export class ClientFormatter implements IClientFormatter { client: AttachedClient, request: IClientFormatterRequest ) { - let language; if (!client.location) { client.location = {}; } else { const location = client.location; - try { - language = determineLocale( - request.app.acceptLanguage, - this.supportedLanguages - ); - - // For English, we can leave all the location components intact. - // For other languages, only return what we can translate - if (language[0] === 'e' || language[1] === 'n') { - client.location = { - city: location.city, - country: location.country, - state: location.state, - stateCode: location.stateCode, - }; - } else if (location.countryCode) { - const territoriesLang = - language === 'en-US' ? 'en-US-POSIX' : language; - const territories = require(`cldr-localenames-full/main/${territoriesLang}/territories.json`); - client.location = { - country: - territories.main[language].localeDisplayNames.territories[ - location.countryCode - ], - }; - } - } catch (err) { - const log = this.logProvider(); - log.debug('attached-clients.formatLocation.warning', { - err: err.message, - languages: request.app.acceptLanguage, - language, - location, - }); - client.location = {}; - } + // ClientFormatter should be locale-agnostic and return consistent identifiers across all requests. + client.location = { + city: location.city, + country: location.country, + countryCode: location.countryCode, + state: location.state, + stateCode: location.stateCode, + }; } return client; } diff --git a/packages/fxa-shared/connected-services/models/AttachedClient.ts b/packages/fxa-shared/connected-services/models/AttachedClient.ts index 0e10224a88a..dac8d76a236 100644 --- a/packages/fxa-shared/connected-services/models/AttachedClient.ts +++ b/packages/fxa-shared/connected-services/models/AttachedClient.ts @@ -21,6 +21,7 @@ export type AttachedClient = { lastAccessTimeFormatted?: string; approximateLastAccessTime?: number; approximateLastAccessTimeFormatted?: string; + countryCode?: string | null; }; export const attachedClientsDefaults: AttachedClient = { diff --git a/packages/fxa-shared/test/connected-services/formatters.ts b/packages/fxa-shared/test/connected-services/formatters.ts index 1cd600326e4..e74c47d5299 100644 --- a/packages/fxa-shared/test/connected-services/formatters.ts +++ b/packages/fxa-shared/test/connected-services/formatters.ts @@ -62,8 +62,7 @@ describe('connected-services/formatters', () => { assert.exists(client.location?.state); assert.exists(client.location?.stateCode); assert.exists(client.location?.country); - // Not set, is this a bug? - // assert.exists(client.location?.countryCode); + assert.exists(client.location?.countryCode); }); it('formats DE location', () => { @@ -71,15 +70,21 @@ describe('connected-services/formatters', () => { client.location = { city: 'Berlin', countryCode: 'DE', + country: 'Germany', + state: 'Berlin', + stateCode: 'BE' }; clientFormatter.formatLocation(client, request); + assert.exists(client.location?.city); + assert.exists(client.location?.state); + assert.exists(client.location?.stateCode); assert.exists(client.location?.country); - // Not set, is this a bug? - // assert.exists(client.location?.city); - // assert.exists(client.location?.state); - // assert.exists(client.location?.stateCode); - // assert.exists(client.location?.countryCode); + assert.exists(client.location?.countryCode); + + // Assert we are NOT localizing on the backend anymore + assert.strictEqual(client.location?.country, 'Germany'); // Should remain as input + assert.strictEqual(client.location?.countryCode, 'DE'); }); it('formats timestamps', () => {