Skip to content

Commit 1644174

Browse files
committed
fix: explicit country code handling for client-side localization
Refactor ClientFormatter to be locale-agnostic and return countryCode. Update DeviceInfoBlock to use Intl.DisplayNames. Update RemoteMetadata type.
1 parent 866e34d commit 1644174

File tree

4 files changed

+43
-52
lines changed

4 files changed

+43
-52
lines changed

packages/fxa-settings/src/components/DeviceInfoBlock/index.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,52 @@ export const DeviceInfoBlock = ({ remoteMetadata }: DeviceInfoBlockProps) => {
1919
city,
2020
region,
2121
country,
22+
countryCode,
2223
} = remoteMetadata;
24+
const currentLocale = document.documentElement.lang || 'en-US';
25+
let localizedCountry = country;
26+
27+
if (countryCode && typeof Intl !== 'undefined' && Intl.DisplayNames) {
28+
try {
29+
const regionNames = new Intl.DisplayNames([currentLocale], {
30+
type: 'region',
31+
});
32+
localizedCountry = regionNames.of(countryCode) || country;
33+
} catch (e) {
34+
// Fallback to raw country name if Intl fails
35+
}
36+
}
2337
const LocalizedLocation = () => {
2438
if (city && region && country) {
2539
return (
2640
<FtlMsg
2741
id="device-info-block-location-city-region-country"
28-
vars={{ city, region, country }}
29-
>{`${city}, ${region}, ${country} (estimated)`}</FtlMsg>
42+
vars={{ city, region, country: localizedCountry }}
43+
>{`${city}, ${region}, ${localizedCountry} (estimated)`}</FtlMsg>
3044
);
3145
}
3246
if (region && country) {
3347
return (
3448
<FtlMsg
3549
id="device-info-block-location-region-country"
36-
vars={{ region, country }}
37-
>{`${region}, ${country} (estimated)`}</FtlMsg>
50+
vars={{ region, country: localizedCountry }}
51+
>{`${region}, ${localizedCountry} (estimated)`}</FtlMsg>
3852
);
3953
}
4054
if (city && country) {
4155
return (
4256
<FtlMsg
4357
id="device-info-block-location-city-country"
44-
vars={{ city, country }}
45-
>{`${city}, ${country} (estimated)`}</FtlMsg>
58+
vars={{ city, country: localizedCountry }}
59+
>{`${city}, ${localizedCountry} (estimated)`}</FtlMsg>
4660
);
4761
}
4862
if (country) {
4963
return (
5064
<FtlMsg
5165
id="device-info-block-location-country"
52-
vars={{ country }}
53-
>{`${country} (estimated)`}</FtlMsg>
66+
vars={{ country: localizedCountry }}
67+
>{`${localizedCountry} (estimated)`}</FtlMsg>
5468
);
5569
}
5670
return (

packages/fxa-settings/src/lib/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export type RemoteMetadata = {
6969
deviceOS: string;
7070
ipAddress: string;
7171
country?: string;
72+
countryCode?: string;
7273
region?: string;
7374
city?: string;
7475
};

packages/fxa-shared/connected-services/formatters.ts

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -50,47 +50,18 @@ export class ClientFormatter implements IClientFormatter {
5050
client: AttachedClient,
5151
request: IClientFormatterRequest
5252
) {
53-
let language;
5453
if (!client.location) {
5554
client.location = {};
5655
} else {
5756
const location = client.location;
58-
try {
59-
language = determineLocale(
60-
request.app.acceptLanguage,
61-
this.supportedLanguages
62-
);
63-
64-
// For English, we can leave all the location components intact.
65-
// For other languages, only return what we can translate
66-
if (language[0] === 'e' || language[1] === 'n') {
67-
client.location = {
68-
city: location.city,
69-
country: location.country,
70-
state: location.state,
71-
stateCode: location.stateCode,
72-
};
73-
} else if (location.countryCode) {
74-
const territoriesLang =
75-
language === 'en-US' ? 'en-US-POSIX' : language;
76-
const territories = require(`cldr-localenames-full/main/${territoriesLang}/territories.json`);
77-
client.location = {
78-
country:
79-
territories.main[language].localeDisplayNames.territories[
80-
location.countryCode
81-
],
82-
};
83-
}
84-
} catch (err) {
85-
const log = this.logProvider();
86-
log.debug('attached-clients.formatLocation.warning', {
87-
err: err.message,
88-
languages: request.app.acceptLanguage,
89-
language,
90-
location,
91-
});
92-
client.location = {};
93-
}
57+
// ClientFormatter should be locale-agnostic and return consistent identifiers across all requests.
58+
client.location = {
59+
city: location.city,
60+
country: location.country,
61+
countryCode: location.countryCode,
62+
state: location.state,
63+
stateCode: location.stateCode,
64+
};
9465
}
9566
return client;
9667
}

packages/fxa-shared/test/connected-services/formatters.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,24 +62,29 @@ describe('connected-services/formatters', () => {
6262
assert.exists(client.location?.state);
6363
assert.exists(client.location?.stateCode);
6464
assert.exists(client.location?.country);
65-
// Not set, is this a bug?
66-
// assert.exists(client.location?.countryCode);
65+
assert.exists(client.location?.countryCode);
6766
});
6867

6968
it('formats DE location', () => {
7069
request.app.acceptLanguage = 'de-DE';
7170
client.location = {
7271
city: 'Berlin',
7372
countryCode: 'DE',
73+
country: 'Germany',
74+
state: 'Berlin',
75+
stateCode: 'BE',
7476
};
7577
clientFormatter.formatLocation(client, request);
7678

79+
assert.exists(client.location?.city);
80+
assert.exists(client.location?.state);
81+
assert.exists(client.location?.stateCode);
7782
assert.exists(client.location?.country);
78-
// Not set, is this a bug?
79-
// assert.exists(client.location?.city);
80-
// assert.exists(client.location?.state);
81-
// assert.exists(client.location?.stateCode);
82-
// assert.exists(client.location?.countryCode);
83+
assert.exists(client.location?.countryCode);
84+
85+
// Assert we are NOT localizing on the backend anymore
86+
assert.strictEqual(client.location?.country, 'Germany'); // Should remain as input
87+
assert.strictEqual(client.location?.countryCode, 'DE');
8388
});
8489

8590
it('formats timestamps', () => {

0 commit comments

Comments
 (0)