i18n country names

This commit is contained in:
Minseo Lee 2024-08-28 16:44:29 +09:00
parent e9f657492d
commit adff1d7400
11 changed files with 47 additions and 83 deletions

View file

@ -1,23 +1,19 @@
import { useCallback } from 'react';
import ListTable from 'components/metrics/ListTable'; import ListTable from 'components/metrics/ListTable';
import { useLocale, useCountryNames, useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';
import { useIntl } from 'react-intl';
import classNames from 'classnames'; import classNames from 'classnames';
import styles from './RealtimeCountries.module.css'; import styles from './RealtimeCountries.module.css';
import TypeIcon from 'components/common/TypeIcon'; import TypeIcon from 'components/common/TypeIcon';
export function RealtimeCountries({ data }) { export function RealtimeCountries({ data }) {
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { locale } = useLocale(); const intl = useIntl();
const { countryNames } = useCountryNames(locale);
const renderCountryName = useCallback( const renderCountryName = ({ x: code }) => (
({ x: code }) => ( <span className={classNames(styles.row)}>
<span className={classNames(locale, styles.row)}> <TypeIcon type="country" value={code?.toLowerCase()} />
<TypeIcon type="country" value={code?.toLowerCase()} /> {intl.formatDisplayName(code, { type: 'region' })}
{countryNames[code]} </span>
</span>
),
[countryNames, locale],
); );
return ( return (

View file

@ -1,7 +1,7 @@
import useFormat from 'components//hooks/useFormat'; import useFormat from 'components//hooks/useFormat';
import Empty from 'components/common/Empty'; import Empty from 'components/common/Empty';
import FilterButtons from 'components/common/FilterButtons'; import FilterButtons from 'components/common/FilterButtons';
import { useCountryNames, useLocale, useMessages, useTimezone } from 'components/hooks'; import { useMessages, useTimezone } from 'components/hooks';
import Icons from 'components/icons'; import Icons from 'components/icons';
import { BROWSERS, OS_NAMES } from 'lib/constants'; import { BROWSERS, OS_NAMES } from 'lib/constants';
import { stringToColor } from 'lib/format'; import { stringToColor } from 'lib/format';
@ -31,9 +31,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) {
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const { formatMessage, labels, messages, FormattedMessage } = useMessages(); const { formatMessage, labels, messages, FormattedMessage } = useMessages();
const { formatValue } = useFormat(); const { formatValue } = useFormat();
const { locale } = useLocale();
const { formatTimezoneDate } = useTimezone(); const { formatTimezoneDate } = useTimezone();
const { countryNames } = useCountryNames(locale);
const [filter, setFilter] = useState(TYPE_ALL); const [filter, setFilter] = useState(TYPE_ALL);
const buttons = [ const buttons = [
@ -112,7 +110,12 @@ export function RealtimeLog({ data }: { data: RealtimeData }) {
<FormattedMessage <FormattedMessage
{...messages.visitorLog} {...messages.visitorLog}
values={{ values={{
country: <b>{countryNames[country] || formatMessage(labels.unknown)}</b>, country: (
<b>
{intl.formatDisplayName(country, { type: 'region' }) ||
formatMessage(labels.unknown)}
</b>
),
browser: <b>{BROWSERS[browser]}</b>, browser: <b>{BROWSERS[browser]}</b>,
os: <b>{OS_NAMES[os] || os}</b>, os: <b>{OS_NAMES[os] || os}</b>,
device: <b>{formatMessage(labels[device] || labels.unknown)}</b>, device: <b>{formatMessage(labels[device] || labels.unknown)}</b>,

View file

@ -1,4 +1,4 @@
import { useFormat, useLocale, useMessages, useRegionNames, useTimezone } from 'components/hooks'; import { useFormat, useMessages, useRegionNames, useTimezone } from 'components/hooks';
import TypeIcon from 'components/common/TypeIcon'; import TypeIcon from 'components/common/TypeIcon';
import { Icon, CopyIcon } from 'react-basics'; import { Icon, CopyIcon } from 'react-basics';
import Icons from 'components/icons'; import Icons from 'components/icons';
@ -7,11 +7,10 @@ import { useIntl } from 'react-intl';
export default function SessionInfo({ data }) { export default function SessionInfo({ data }) {
const intl = useIntl(); const intl = useIntl();
const { locale } = useLocale();
const { formatTimezoneDate } = useTimezone(); const { formatTimezoneDate } = useTimezone();
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { formatValue } = useFormat(); const { formatValue } = useFormat();
const { getRegionName } = useRegionNames(locale); const { getRegionName } = useRegionNames();
return ( return (
<div className={styles.info}> <div className={styles.info}>

View file

@ -28,7 +28,6 @@ export * from './queries/useWebsiteEvents';
export * from './queries/useWebsiteEventsSeries'; export * from './queries/useWebsiteEventsSeries';
export * from './queries/useWebsiteMetrics'; export * from './queries/useWebsiteMetrics';
export * from './queries/useWebsiteValues'; export * from './queries/useWebsiteValues';
export * from './useCountryNames';
export * from './useDateRange'; export * from './useDateRange';
export * from './useDocumentClick'; export * from './useDocumentClick';
export * from './useEscapeKey'; export * from './useEscapeKey';

View file

@ -1,34 +0,0 @@
import { useState, useEffect } from 'react';
import { httpGet } from 'next-basics';
import enUS from '../../../public/intl/country/en-US.json';
const countryNames = {
'en-US': enUS,
};
export function useCountryNames(locale: string) {
const [list, setList] = useState(countryNames[locale] || enUS);
async function loadData(locale: string) {
const { data } = await httpGet(`${process.env.basePath || ''}/intl/country/${locale}.json`);
if (data) {
countryNames[locale] = data;
setList(countryNames[locale]);
} else {
setList(enUS);
}
}
useEffect(() => {
if (!countryNames[locale]) {
loadData(locale);
} else {
setList(countryNames[locale]);
}
}, [locale]);
return { countryNames: list };
}
export default useCountryNames;

View file

@ -1,13 +1,11 @@
import useMessages from './useMessages'; import useMessages from './useMessages';
import { BROWSERS, OS_NAMES } from 'lib/constants'; import { BROWSERS, OS_NAMES } from 'lib/constants';
import useLocale from './useLocale'; import { useIntl } from 'react-intl';
import useCountryNames from './useCountryNames';
import regions from '../../../public/iso-3166-2.json'; import regions from '../../../public/iso-3166-2.json';
export function useFormat() { export function useFormat() {
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { locale } = useLocale(); const intl = useIntl();
const { countryNames } = useCountryNames(locale);
const formatOS = (value: string): string => { const formatOS = (value: string): string => {
return OS_NAMES[value] || value; return OS_NAMES[value] || value;
@ -22,16 +20,20 @@ export function useFormat() {
}; };
const formatCountry = (value: string): string => { const formatCountry = (value: string): string => {
return countryNames[value] || value; return intl.formatDisplayName(value, { type: 'region' }) || value;
}; };
const formatRegion = (value: string): string => { const formatRegion = (value: string): string => {
const [country] = value.split('-'); const [country] = value.split('-');
return regions[value] ? `${regions[value]}, ${countryNames[country]}` : value; return regions[value]
? `${regions[value]}, ${intl.formatDisplayName(country, { type: 'region' })}`
: value;
}; };
const formatCity = (value: string, country?: string): string => { const formatCity = (value: string, country?: string): string => {
return countryNames[country] ? `${value}, ${countryNames[country]}` : value; return intl.formatDisplayName(country, { type: 'region' })
? `${value}, ${intl.formatDisplayName(country, { type: 'region' })}`
: value;
}; };
const formatValue = (value: string, type: string, data?: { [key: string]: any }): string => { const formatValue = (value: string, type: string, data?: { [key: string]: any }): string => {

View file

@ -1,8 +1,8 @@
import useCountryNames from './useCountryNames'; import { useIntl } from 'react-intl';
import regions from '../../../public/iso-3166-2.json'; import regions from '../../../public/iso-3166-2.json';
export function useRegionNames(locale: string) { export function useRegionNames() {
const { countryNames } = useCountryNames(locale); const intl = useIntl();
const getRegionName = (regionCode: string, countryCode?: string) => { const getRegionName = (regionCode: string, countryCode?: string) => {
if (!countryCode) { if (!countryCode) {
@ -10,7 +10,9 @@ export function useRegionNames(locale: string) {
} }
const region = regionCode.includes('-') ? regionCode : `${countryCode}-${regionCode}`; const region = regionCode.includes('-') ? regionCode : `${countryCode}-${regionCode}`;
return regions[region] ? `${regions[region]}, ${countryNames[countryCode]}` : region; return regions[region]
? `${regions[region]}, ${intl.formatDisplayName(countryCode, { type: 'region' })}`
: region;
}; };
return { regionNames: regions, getRegionName }; return { regionNames: regions, getRegionName };

View file

@ -2,17 +2,15 @@ import MetricsTable, { MetricsTableProps } from './MetricsTable';
import { emptyFilter } from 'lib/filters'; import { emptyFilter } from 'lib/filters';
import FilterLink from 'components/common/FilterLink'; import FilterLink from 'components/common/FilterLink';
import TypeIcon from 'components/common/TypeIcon'; import TypeIcon from 'components/common/TypeIcon';
import { useLocale } from 'components/hooks'; import { useIntl } from 'react-intl';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';
import { useCountryNames } from 'components/hooks';
export function CitiesTable(props: MetricsTableProps) { export function CitiesTable(props: MetricsTableProps) {
const { locale } = useLocale(); const intl = useIntl();
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { countryNames } = useCountryNames(locale);
const renderLabel = (city: string, country: string) => { const renderLabel = (city: string, country: string) => {
const countryName = countryNames[country]; const countryName = intl.formatDisplayName(country, { type: 'region' });
return countryName ? `${city}, ${countryName}` : city; return countryName ? `${city}, ${countryName}` : city;
}; };

View file

@ -1,12 +1,11 @@
import FilterLink from 'components/common/FilterLink'; import FilterLink from 'components/common/FilterLink';
import { useCountryNames } from 'components/hooks'; import { useIntl } from 'react-intl';
import { useLocale, useMessages, useFormat } from 'components/hooks'; import { useMessages, useFormat } from 'components/hooks';
import MetricsTable, { MetricsTableProps } from './MetricsTable'; import MetricsTable, { MetricsTableProps } from './MetricsTable';
import TypeIcon from 'components/common/TypeIcon'; import TypeIcon from 'components/common/TypeIcon';
export function CountriesTable({ ...props }: MetricsTableProps) { export function CountriesTable({ ...props }: MetricsTableProps) {
const { locale } = useLocale(); const intl = useIntl();
const { countryNames } = useCountryNames(locale);
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { formatCountry } = useFormat(); const { formatCountry } = useFormat();
@ -14,8 +13,7 @@ export function CountriesTable({ ...props }: MetricsTableProps) {
return ( return (
<FilterLink <FilterLink
id="country" id="country"
className={locale} value={intl.formatDisplayName(code, { type: 'region' }) && code}
value={countryNames[code] && code}
label={formatCountry(code)} label={formatCountry(code)}
> >
<TypeIcon type="country" value={code?.toLowerCase()} /> <TypeIcon type="country" value={code?.toLowerCase()} />

View file

@ -1,17 +1,16 @@
import FilterLink from 'components/common/FilterLink'; import FilterLink from 'components/common/FilterLink';
import { emptyFilter } from 'lib/filters'; import { emptyFilter } from 'lib/filters';
import { useMessages, useLocale, useRegionNames } from 'components/hooks'; import { useMessages, useRegionNames } from 'components/hooks';
import MetricsTable, { MetricsTableProps } from './MetricsTable'; import MetricsTable, { MetricsTableProps } from './MetricsTable';
import TypeIcon from 'components/common/TypeIcon'; import TypeIcon from 'components/common/TypeIcon';
export function RegionsTable(props: MetricsTableProps) { export function RegionsTable(props: MetricsTableProps) {
const { locale } = useLocale();
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { getRegionName } = useRegionNames(locale); const { getRegionName } = useRegionNames();
const renderLink = ({ x: code, country }) => { const renderLink = ({ x: code, country }) => {
return ( return (
<FilterLink id="region" className={locale} value={code} label={getRegionName(code, country)}> <FilterLink id="region" value={code} label={getRegionName(code, country)}>
<TypeIcon type="country" value={country?.toLowerCase()} /> <TypeIcon type="country" value={country?.toLowerCase()} />
</FilterLink> </FilterLink>
); );

View file

@ -5,7 +5,7 @@ import { colord } from 'colord';
import HoverTooltip from 'components/common/HoverTooltip'; import HoverTooltip from 'components/common/HoverTooltip';
import { ISO_COUNTRIES, MAP_FILE } from 'lib/constants'; import { ISO_COUNTRIES, MAP_FILE } from 'lib/constants';
import { useDateRange, useTheme, useWebsiteMetrics } from 'components/hooks'; import { useDateRange, useTheme, useWebsiteMetrics } from 'components/hooks';
import { useCountryNames } from 'components/hooks'; import { useIntl } from 'react-intl';
import { useLocale } from 'components/hooks'; import { useLocale } from 'components/hooks';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';
import { formatLongNumber } from 'lib/format'; import { formatLongNumber } from 'lib/format';
@ -26,7 +26,7 @@ export function WorldMap({
const { theme, colors } = useTheme(); const { theme, colors } = useTheme();
const { locale } = useLocale(); const { locale } = useLocale();
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { countryNames } = useCountryNames(locale); const intl = useIntl();
const visitorsLabel = formatMessage(labels.visitors).toLocaleLowerCase(locale); const visitorsLabel = formatMessage(labels.visitors).toLocaleLowerCase(locale);
const { const {
dateRange: { startDate, endDate }, dateRange: { startDate, endDate },
@ -62,7 +62,9 @@ export function WorldMap({
if (code === 'AQ') return; if (code === 'AQ') return;
const country = metrics?.find(({ x }) => x === code); const country = metrics?.find(({ x }) => x === code);
setTooltipPopup( setTooltipPopup(
`${countryNames[code]}: ${formatLongNumber(country?.y || 0)} ${visitorsLabel}` as any, `${intl.formatDisplayName(code, { type: 'region' })}: ${formatLongNumber(
country?.y || 0,
)} ${visitorsLabel}` as any,
); );
}; };