Webatrice: improve language dropdown (#4589)

* useLocaleSort hook, translate language dropdown

* add pt-BR translation

* fix pt-BR flag

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
This commit is contained in:
Jeremy Letto 2022-03-06 20:12:27 -06:00 committed by GitHub
parent 21f7dd5eba
commit 533045445a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 314 additions and 278 deletions

File diff suppressed because one or more lines are too long

View file

@ -274,6 +274,12 @@
"YE": "Yemen", "YE": "Yemen",
"ZM": "Zambia", "ZM": "Zambia",
"ZW": "Zimbabwe" "ZW": "Zimbabwe"
},
"languages": {
"en-US": "English - US",
"fr": "French",
"nl": "Dutch",
"pt-BR": "Portuguese - Brazil"
} }
} }
} }

View file

@ -1,29 +1,24 @@
// eslint-disable-next-line import { useEffect, useState } from 'react';
import React, { useEffect, useState } from 'react';
import { Select, MenuItem } from '@material-ui/core'; import { Select, MenuItem } from '@material-ui/core';
import FormControl from '@material-ui/core/FormControl'; import FormControl from '@material-ui/core/FormControl';
import InputLabel from '@material-ui/core/InputLabel'; import InputLabel from '@material-ui/core/InputLabel';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useLocaleSort } from 'hooks';
import { Images } from 'images/Images'; import { Images } from 'images/Images';
import { CountryCode } from 'types'; import { countryCodes } from 'types';
import './CountryDropdown.css'; import './CountryDropdown.css';
const CountryDropdown = ({ input: { onChange } }) => { const CountryDropdown = ({ input: { onChange } }) => {
const [state, setState] = useState(''); const [value, setValue] = useState('');
const [sortedCountries, setSortedCountries] = useState([]); const { t } = useTranslation();
const { t, i18n } = useTranslation();
useEffect(() => onChange(state), [state]); useEffect(() => onChange(value), [value]);
useEffect(() => { const translateCountry = country => t(`Common.countries.${country}`);
const collator = new Intl.Collator(i18n.language); const sortedCountries = useLocaleSort(countryCodes, translateCountry);
setSortedCountries(Object.keys(CountryCode).sort((a, b) =>
collator.compare(t(`Common.countries.${a}`), t(`Common.countries.${b}`))
));
}, [i18n.language]);
return ( return (
<FormControl variant='outlined' className='CountryDropdown'> <FormControl variant='outlined' className='CountryDropdown'>
@ -33,9 +28,9 @@ const CountryDropdown = ({ input: { onChange } }) => {
labelId='CountryDropdown-label' labelId='CountryDropdown-label'
label='Country' label='Country'
margin='dense' margin='dense'
value={state} value={value}
fullWidth={true} fullWidth={true}
onChange={e => setState(e.target.value as string)} onChange={e => setValue(e.target.value as string)}
> >
<MenuItem value={''} key={-1}> <MenuItem value={''} key={-1}>
<div className="CountryDropdown-item"> <div className="CountryDropdown-item">
@ -48,7 +43,7 @@ const CountryDropdown = ({ input: { onChange } }) => {
<MenuItem value={country} key={index}> <MenuItem value={country} key={index}>
<div className="CountryDropdown-item"> <div className="CountryDropdown-item">
<img className="CountryDropdown-item__image" src={Images.Countries[country.toLowerCase()]} /> <img className="CountryDropdown-item__image" src={Images.Countries[country.toLowerCase()]} />
<span className="CountryDropdown-item__label">{t(`Common.countries.${country}`)}</span> <span className="CountryDropdown-item__label">{translateCountry(country)}</span>
</div> </div>
</MenuItem> </MenuItem>
)) ))

View file

@ -6,12 +6,12 @@ import FormControl from '@material-ui/core/FormControl';
import InputLabel from '@material-ui/core/InputLabel'; import InputLabel from '@material-ui/core/InputLabel';
import { Images } from 'images/Images'; import { Images } from 'images/Images';
import { Language, LanguageCountry } from 'types'; import { Language, LanguageCountry, LanguageNative } from 'types';
import './LanguageDropdown.css'; import './LanguageDropdown.css';
const LanguageDropdown = () => { const LanguageDropdown = () => {
const { i18n } = useTranslation(); const { t, i18n } = useTranslation();
const [language, setLanguage] = useState(i18n.resolvedLanguage); const [language, setLanguage] = useState(i18n.resolvedLanguage);
useEffect(() => { useEffect(() => {
@ -30,14 +30,20 @@ const LanguageDropdown = () => {
onChange={e => setLanguage(e.target.value as Language)} onChange={e => setLanguage(e.target.value as Language)}
> >
{ {
Object.keys(LanguageCountry).map((lang) => { Object.keys(Language).map((lang) => {
const country = LanguageCountry[lang]; const country = LanguageCountry[lang];
return ( return (
<MenuItem value={lang} key={lang}> <MenuItem value={lang} key={lang}>
<div className="LanguageDropdown-item"> <div className="LanguageDropdown-item">
<img className="LanguageDropdown-item__image" src={Images.Countries[country]} /> <img className="LanguageDropdown-item__image" src={Images.Countries[country]} />
<span className="LanguageDropdown-item__label">{lang}</span> <span className="LanguageDropdown-item__label">
{LanguageNative[lang]} {
LanguageNative[lang] !== t(`Common.languages.${lang}`) && (
<>({ t(`Common.languages.${lang}`) })</>
)
}
</span>
</div> </div>
</MenuItem> </MenuItem>
); );

View file

@ -1,4 +1,5 @@
export * from './useAutoConnect'; export * from './useAutoConnect';
export * from './useFireOnce'; export * from './useFireOnce';
export * from './useDebounce'; export * from './useDebounce';
export * from './useLocaleSort';
export * from './useReduxEffect'; export * from './useReduxEffect';

View file

@ -0,0 +1,18 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
export function useLocaleSort(arr: string[], valueGetter: (value: string) => string) {
const [state] = useState<string[]>(arr);
const [sorted, setSorted] = useState<string[]>([]);
const { i18n } = useTranslation();
useEffect(() => {
const collator = new Intl.Collator(i18n.language);
const sorter = (a, b) => collator.compare(valueGetter(a), valueGetter(b));
setSorted(state.sort(sorter));
}, [state, i18n.language]);
return sorted;
}

File diff suppressed because one or more lines are too long

View file

@ -17,9 +17,9 @@ i18n
.use(initReactI18next) .use(initReactI18next)
// for all options read: https://www.i18next.com/overview/configuration-options // for all options read: https://www.i18next.com/overview/configuration-options
.init({ .init({
fallbackLng: Language['en'], fallbackLng: Language['en-US'],
resources: { resources: {
[Language['en']]: { translation }, [Language['en-US']]: { translation },
}, },
partialBundledLanguages: true, partialBundledLanguages: true,

View file

@ -1,252 +1,252 @@
export enum CountryCode { export const countryCodes = [
'AF' = 'AF', 'AF',
'AX' = 'AX', 'AX',
'AL' = 'AL', 'AL',
'DZ' = 'DZ', 'DZ',
'AS' = 'AS', 'AS',
'AD' = 'AD', 'AD',
'AO' = 'AO', 'AO',
'AI' = 'AI', 'AI',
'AQ' = 'AQ', 'AQ',
'AG' = 'AG', 'AG',
'AR' = 'AR', 'AR',
'AM' = 'AM', 'AM',
'AW' = 'AW', 'AW',
'AU' = 'AU', 'AU',
'AT' = 'AT', 'AT',
'AZ' = 'AZ', 'AZ',
'BH' = 'BH', 'BH',
'BS' = 'BS', 'BS',
'BD' = 'BD', 'BD',
'BB' = 'BB', 'BB',
'BY' = 'BY', 'BY',
'BE' = 'BE', 'BE',
'BZ' = 'BZ', 'BZ',
'BJ' = 'BJ', 'BJ',
'BM' = 'BM', 'BM',
'BT' = 'BT', 'BT',
'BO' = 'BO', 'BO',
'BQ' = 'BQ', 'BQ',
'BA' = 'BA', 'BA',
'BW' = 'BW', 'BW',
'BV' = 'BV', 'BV',
'BR' = 'BR', 'BR',
'IO' = 'IO', 'IO',
'BN' = 'BN', 'BN',
'BG' = 'BG', 'BG',
'BF' = 'BF', 'BF',
'BI' = 'BI', 'BI',
'KH' = 'KH', 'KH',
'CM' = 'CM', 'CM',
'CA' = 'CA', 'CA',
'CV' = 'CV', 'CV',
'KY' = 'KY', 'KY',
'CF' = 'CF', 'CF',
'TD' = 'TD', 'TD',
'CL' = 'CL', 'CL',
'CN' = 'CN', 'CN',
'CX' = 'CX', 'CX',
'CC' = 'CC', 'CC',
'CO' = 'CO', 'CO',
'KM' = 'KM', 'KM',
'CG' = 'CG', 'CG',
'CD' = 'CD', 'CD',
'CK' = 'CK', 'CK',
'CR' = 'CR', 'CR',
'CI' = 'CI', 'CI',
'HR' = 'HR', 'HR',
'CU' = 'CU', 'CU',
'CW' = 'CW', 'CW',
'CY' = 'CY', 'CY',
'CZ' = 'CZ', 'CZ',
'DK' = 'DK', 'DK',
'DJ' = 'DJ', 'DJ',
'DM' = 'DM', 'DM',
'DO' = 'DO', 'DO',
'EC' = 'EC', 'EC',
'EG' = 'EG', 'EG',
'SV' = 'SV', 'SV',
'GQ' = 'GQ', 'GQ',
'ER' = 'ER', 'ER',
'EE' = 'EE', 'EE',
'ET' = 'ET', 'ET',
'EU' = 'EU', 'EU',
'FK' = 'FK', 'FK',
'FO' = 'FO', 'FO',
'FJ' = 'FJ', 'FJ',
'FI' = 'FI', 'FI',
'FR' = 'FR', 'FR',
'GF' = 'GF', 'GF',
'PF' = 'PF', 'PF',
'TF' = 'TF', 'TF',
'GA' = 'GA', 'GA',
'GM' = 'GM', 'GM',
'GE' = 'GE', 'GE',
'DE' = 'DE', 'DE',
'GH' = 'GH', 'GH',
'GI' = 'GI', 'GI',
'GR' = 'GR', 'GR',
'GL' = 'GL', 'GL',
'GD' = 'GD', 'GD',
'GP' = 'GP', 'GP',
'GU' = 'GU', 'GU',
'GT' = 'GT', 'GT',
'GG' = 'GG', 'GG',
'GN' = 'GN', 'GN',
'GW' = 'GW', 'GW',
'GY' = 'GY', 'GY',
'HT' = 'HT', 'HT',
'HM' = 'HM', 'HM',
'VA' = 'VA', 'VA',
'HN' = 'HN', 'HN',
'HK' = 'HK', 'HK',
'HU' = 'HU', 'HU',
'IS' = 'IS', 'IS',
'IN' = 'IN', 'IN',
'ID' = 'ID', 'ID',
'IR' = 'IR', 'IR',
'IQ' = 'IQ', 'IQ',
'IE' = 'IE', 'IE',
'IM' = 'IM', 'IM',
'IL' = 'IL', 'IL',
'IT' = 'IT', 'IT',
'JM' = 'JM', 'JM',
'JP' = 'JP', 'JP',
'JE' = 'JE', 'JE',
'JO' = 'JO', 'JO',
'KZ' = 'KZ', 'KZ',
'KE' = 'KE', 'KE',
'KI' = 'KI', 'KI',
'KP' = 'KP', 'KP',
'KR' = 'KR', 'KR',
'KW' = 'KW', 'KW',
'KG' = 'KG', 'KG',
'LA' = 'LA', 'LA',
'LV' = 'LV', 'LV',
'LB' = 'LB', 'LB',
'LS' = 'LS', 'LS',
'LR' = 'LR', 'LR',
'LY' = 'LY', 'LY',
'LI' = 'LI', 'LI',
'LT' = 'LT', 'LT',
'LU' = 'LU', 'LU',
'MO' = 'MO', 'MO',
'MK' = 'MK', 'MK',
'MG' = 'MG', 'MG',
'MW' = 'MW', 'MW',
'MY' = 'MY', 'MY',
'MV' = 'MV', 'MV',
'ML' = 'ML', 'ML',
'MT' = 'MT', 'MT',
'MH' = 'MH', 'MH',
'MQ' = 'MQ', 'MQ',
'MR' = 'MR', 'MR',
'MU' = 'MU', 'MU',
'YT' = 'YT', 'YT',
'MX' = 'MX', 'MX',
'FM' = 'FM', 'FM',
'MD' = 'MD', 'MD',
'MC' = 'MC', 'MC',
'MN' = 'MN', 'MN',
'ME' = 'ME', 'ME',
'MS' = 'MS', 'MS',
'MA' = 'MA', 'MA',
'MZ' = 'MZ', 'MZ',
'MM' = 'MM', 'MM',
'NA' = 'NA', 'NA',
'NR' = 'NR', 'NR',
'NP' = 'NP', 'NP',
'NL' = 'NL', 'NL',
'NC' = 'NC', 'NC',
'NZ' = 'NZ', 'NZ',
'NI' = 'NI', 'NI',
'NE' = 'NE', 'NE',
'NG' = 'NG', 'NG',
'NU' = 'NU', 'NU',
'NF' = 'NF', 'NF',
'MP' = 'MP', 'MP',
'NO' = 'NO', 'NO',
'OM' = 'OM', 'OM',
'PK' = 'PK', 'PK',
'PW' = 'PW', 'PW',
'PS' = 'PS', 'PS',
'PA' = 'PA', 'PA',
'PG' = 'PG', 'PG',
'PY' = 'PY', 'PY',
'PE' = 'PE', 'PE',
'PH' = 'PH', 'PH',
'PN' = 'PN', 'PN',
'PL' = 'PL', 'PL',
'PT' = 'PT', 'PT',
'PR' = 'PR', 'PR',
'QA' = 'QA', 'QA',
'RE' = 'RE', 'RE',
'RO' = 'RO', 'RO',
'RU' = 'RU', 'RU',
'RW' = 'RW', 'RW',
'BL' = 'BL', 'BL',
'SH' = 'SH', 'SH',
'KN' = 'KN', 'KN',
'LC' = 'LC', 'LC',
'MF' = 'MF', 'MF',
'PM' = 'PM', 'PM',
'VC' = 'VC', 'VC',
'WS' = 'WS', 'WS',
'SM' = 'SM', 'SM',
'ST' = 'ST', 'ST',
'SA' = 'SA', 'SA',
'SN' = 'SN', 'SN',
'RS' = 'RS', 'RS',
'SC' = 'SC', 'SC',
'SL' = 'SL', 'SL',
'SG' = 'SG', 'SG',
'SX' = 'SX', 'SX',
'SK' = 'SK', 'SK',
'SI' = 'SI', 'SI',
'SB' = 'SB', 'SB',
'SO' = 'SO', 'SO',
'ZA' = 'ZA', 'ZA',
'GS' = 'GS', 'GS',
'SS' = 'SS', 'SS',
'ES' = 'ES', 'ES',
'LK' = 'LK', 'LK',
'SD' = 'SD', 'SD',
'SR' = 'SR', 'SR',
'SJ' = 'SJ', 'SJ',
'SZ' = 'SZ', 'SZ',
'SE' = 'SE', 'SE',
'CH' = 'CH', 'CH',
'SY' = 'SY', 'SY',
'TW' = 'TW', 'TW',
'TJ' = 'TJ', 'TJ',
'TZ' = 'TZ', 'TZ',
'TH' = 'TH', 'TH',
'TL' = 'TL', 'TL',
'TG' = 'TG', 'TG',
'TK' = 'TK', 'TK',
'TO' = 'TO', 'TO',
'TT' = 'TT', 'TT',
'TN' = 'TN', 'TN',
'TR' = 'TR', 'TR',
'TM' = 'TM', 'TM',
'TC' = 'TC', 'TC',
'TV' = 'TV', 'TV',
'UG' = 'UG', 'UG',
'UA' = 'UA', 'UA',
'AE' = 'AE', 'AE',
'GB' = 'GB', 'GB',
'US' = 'US', 'US',
'UM' = 'UM', 'UM',
'UY' = 'UY', 'UY',
'UZ' = 'UZ', 'UZ',
'VU' = 'VU', 'VU',
'VE' = 'VE', 'VE',
'VN' = 'VN', 'VN',
'VG' = 'VG', 'VG',
'VI' = 'VI', 'VI',
'WF' = 'WF', 'WF',
'EH' = 'EH', 'EH',
'YE' = 'YE', 'YE',
'ZM' = 'ZM', 'ZM',
'ZW' = 'ZW', 'ZW',
}; ];

View file

@ -1,11 +1,20 @@
export enum Language { export enum Language {
'en' = 'en', 'en-US' = 'en-US',
'fr' = 'fr', 'fr' = 'fr',
'nl' = 'nl', 'nl' = 'nl',
'pt-BR' = 'pt-BR',
} }
export enum LanguageCountry { export enum LanguageCountry {
'en' = 'us', 'en-US' = 'us',
'fr' = 'fr', 'fr' = 'fr',
'nl' = 'nl', 'nl' = 'nl',
'pt-BR' = 'br'
}
export enum LanguageNative {
'en-US' = 'English - US',
'fr' = 'Français',
'nl' = 'Nederlands',
'pt-BR' = 'Portugues do Brasil',
} }