3030 </h3 >
3131 <p v-if =" peer.locationData.country"
3232 :class =" {'self': peer.type === 0 /* SELF */, 'connected': peer.connected}" >
33- {{ peer.locationData.city ? `${peer.locationData.city },` : '' }}
34- {{ peer.locationData.countryFull }}
33+ {{ getPeerCity( peer) ? `${getPeerCity( peer) },` : '' }}
34+ {{ getPeerCountry( peer) }}
3535 </p >
3636 </div >
3737 </template >
4141</template >
4242
4343<script lang="ts">
44+ import { shouldPolyfill as shouldPolyFillIntlDisplayNames } from ' @formatjs/intl-displaynames/should-polyfill' ;
4445import { defineComponent , onMounted , onUnmounted , ref , computed , watch } from ' @vue/composition-api' ;
4546import { Tooltip , HexagonIcon } from ' @nimiq/vue-components' ;
4647import { NetworkClient } from ' @nimiq/network-client' ;
4748import { getNetworkClient } from ' ../network' ;
48- import NetworkMap , { NodeHexagon , WIDTH , HEIGHT , SCALING_FACTOR , NodeType } from ' ../lib/NetworkMap' ;
49+ import NetworkMap , { NodeHexagon , WIDTH , HEIGHT , SCALING_FACTOR , Node , NodeType } from ' ../lib/NetworkMap' ;
50+ import { useSettingsStore } from ' ../stores/Settings' ;
51+
52+ type IntlDisplayNames = import (' @formatjs/intl-displaynames' ).DisplayNames ;
53+ type IntlDisplayNamesOptions = import (' @formatjs/intl-displaynames' ).DisplayNamesOptions ;
54+ // eslint-disable-next-line @typescript-eslint/no-namespace
55+ declare namespace Intl {
56+ let DisplayNames: undefined | {
57+ new (
58+ locales ? : string | string [],
59+ options ? : IntlDisplayNamesOptions ,
60+ ): IntlDisplayNames ,
61+
62+ readonly polyfilled? : true ,
63+ };
64+ }
65+
66+ const intlDisplayNamesReadyPromise = ! Intl .DisplayNames ?.polyfilled && shouldPolyFillIntlDisplayNames ()
67+ ? import (' @formatjs/intl-displaynames/polyfill' )
68+ : Promise .resolve ();
4969
5070export default defineComponent ({
5171 setup(props , context ) {
@@ -115,6 +135,40 @@ export default defineComponent({
115135 const ownXCoordinate = computed (() => ownNode .value ? ownNode .value .x : null );
116136 watch (ownXCoordinate , (x ) => x !== null && context .emit (' own-x-coordinate' , (x / 2 ) * SCALING_FACTOR ));
117137
138+ const { language } = useSettingsStore ();
139+ let i18nCountryName: IntlDisplayNames | null = null ;
140+
141+ watch (language , async () => {
142+ i18nCountryName = null ;
143+ // TODO polyfill can be removed in the future and i18nCountryName then changed to a non-async computed prop
144+ await intlDisplayNamesReadyPromise ;
145+ if (Intl .DisplayNames ! .polyfilled ) {
146+ // has to be imported after the polyfill is ready
147+ await import (
148+ /* webpackChunkName: "country-names-[request]" */
149+ /* webpackInclude: /\/\w{2}\.js$/ */
150+ ` @formatjs/intl-displaynames/locale-data/${language .value } ` );
151+ }
152+ i18nCountryName = new Intl .DisplayNames !(language .value , { type: ' region' });
153+ });
154+
155+ function getPeerCity(peer : Node ) {
156+ const fallbackCityName = peer .locationData .city ;
157+ const { i18nCityNames } = peer .locationData ;
158+ if (! i18nCityNames ) return fallbackCityName ;
159+ // Try to find a translation for current language
160+ const availableLanguage = i18nCityNames [language .value ]
161+ ? language .value
162+ : Object .keys (i18nCityNames ).find ((locale ) => locale .startsWith (language .value )); // accept zh-CH for zh
163+ return availableLanguage ? i18nCityNames [availableLanguage ] : fallbackCityName ;
164+ }
165+
166+ function getPeerCountry(peer : Node ) {
167+ return i18nCountryName && peer .locationData .country
168+ ? i18nCountryName .of (peer .locationData .country )
169+ : peer .locationData .country ;
170+ }
171+
118172 return {
119173 $container ,
120174 $network ,
@@ -123,6 +177,8 @@ export default defineComponent({
123177 scale ,
124178 width ,
125179 height ,
180+ getPeerCity ,
181+ getPeerCountry ,
126182 };
127183 },
128184 components: {
0 commit comments