Use Intl for names of languages (#11427)
* Use Intl for names of languages * Tweak Intl language style from "American English" -> "US English" * Update tests * Fix tests * Consolidate languageHandler-test files * Improve coverage * Consistent casing for languages in dropdown * Update LanguageDropdown.tsx * Delint & update snapshot * Fix tests * Improve coverage `of` will fallback to the given code with fallback=code (default)
This commit is contained in:
parent
3684c77cfe
commit
4de315fb6c
15 changed files with 304 additions and 193 deletions
|
@ -70,7 +70,7 @@ export default class CountryDropdown extends React.Component<IProps, IState> {
|
|||
const locale = new Intl.Locale(navigator.language ?? navigator.languages[0]);
|
||||
const code = locale.region ?? locale.language ?? locale.baseName;
|
||||
const displayNames = new Intl.DisplayNames(["en"], { type: "region" });
|
||||
const displayName = displayNames.of(code)?.toUpperCase();
|
||||
const displayName = displayNames.of(code)!.toUpperCase();
|
||||
defaultCountry = COUNTRIES.find(
|
||||
(c) => c.iso2 === code.toUpperCase() || c.name.toUpperCase() === displayName,
|
||||
);
|
||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React, { ReactElement } from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
import * as languageHandler from "../../../languageHandler";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
@ -24,9 +25,10 @@ import Spinner from "./Spinner";
|
|||
import Dropdown from "./Dropdown";
|
||||
import { NonEmptyArray } from "../../../@types/common";
|
||||
|
||||
type Languages = Awaited<ReturnType<typeof languageHandler.getAllLanguagesFromJson>>;
|
||||
type Languages = Awaited<ReturnType<typeof languageHandler.getAllLanguagesWithLabels>>;
|
||||
|
||||
function languageMatchesSearchQuery(query: string, language: Languages[0]): boolean {
|
||||
if (language.labelInTargetLanguage.toUpperCase().includes(query.toUpperCase())) return true;
|
||||
if (language.label.toUpperCase().includes(query.toUpperCase())) return true;
|
||||
if (language.value.toUpperCase() === query.toUpperCase()) return true;
|
||||
return false;
|
||||
|
@ -56,23 +58,30 @@ export default class LanguageDropdown extends React.Component<IProps, IState> {
|
|||
|
||||
public componentDidMount(): void {
|
||||
languageHandler
|
||||
.getAllLanguagesFromJson()
|
||||
.getAllLanguagesWithLabels()
|
||||
.then((langs) => {
|
||||
langs.sort(function (a, b) {
|
||||
if (a.label < b.label) return -1;
|
||||
if (a.label > b.label) return 1;
|
||||
if (a.labelInTargetLanguage < b.labelInTargetLanguage) return -1;
|
||||
if (a.labelInTargetLanguage > b.labelInTargetLanguage) return 1;
|
||||
return 0;
|
||||
});
|
||||
this.setState({ langs });
|
||||
})
|
||||
.catch(() => {
|
||||
this.setState({ langs: [{ value: "en", label: "English" }] });
|
||||
this.setState({
|
||||
langs: [
|
||||
{
|
||||
value: "en",
|
||||
label: "English",
|
||||
labelInTargetLanguage: "English",
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
if (!this.props.value) {
|
||||
// If no value is given, we start with the first
|
||||
// country selected, but our parent component
|
||||
// doesn't know this, therefore we do this.
|
||||
// If no value is given, we start with the first country selected,
|
||||
// but our parent component doesn't know this, therefore we do this.
|
||||
const language = languageHandler.getUserLanguage();
|
||||
this.props.onOptionChange(language);
|
||||
}
|
||||
|
@ -89,7 +98,7 @@ export default class LanguageDropdown extends React.Component<IProps, IState> {
|
|||
return <Spinner />;
|
||||
}
|
||||
|
||||
let displayedLanguages: Awaited<ReturnType<typeof languageHandler.getAllLanguagesFromJson>>;
|
||||
let displayedLanguages: Awaited<ReturnType<typeof languageHandler.getAllLanguagesWithLabels>>;
|
||||
if (this.state.searchQuery) {
|
||||
displayedLanguages = this.state.langs.filter((lang) => {
|
||||
return languageMatchesSearchQuery(this.state.searchQuery, lang);
|
||||
|
@ -99,7 +108,7 @@ export default class LanguageDropdown extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
const options = displayedLanguages.map((language) => {
|
||||
return <div key={language.value}>{language.label}</div>;
|
||||
return <div key={language.value}>{language.labelInTargetLanguage}</div>;
|
||||
}) as NonEmptyArray<ReactElement & { key: string }>;
|
||||
|
||||
// default value here too, otherwise we need to handle null / undefined
|
||||
|
@ -116,7 +125,7 @@ export default class LanguageDropdown extends React.Component<IProps, IState> {
|
|||
return (
|
||||
<Dropdown
|
||||
id="mx_LanguageDropdown"
|
||||
className={this.props.className}
|
||||
className={classNames("mx_LanguageDropdown", this.props.className)}
|
||||
onOptionChange={this.props.onOptionChange}
|
||||
onSearchChange={this.onSearchChange}
|
||||
searchEnabled={true}
|
||||
|
|
|
@ -19,12 +19,14 @@ import React, { ReactElement } from "react";
|
|||
import Dropdown from "../../views/elements/Dropdown";
|
||||
import PlatformPeg from "../../../PlatformPeg";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { _t, getUserLanguage } from "../../../languageHandler";
|
||||
import Spinner from "./Spinner";
|
||||
import * as languageHandler from "../../../languageHandler";
|
||||
import { NonEmptyArray } from "../../../@types/common";
|
||||
|
||||
type Languages = Awaited<ReturnType<typeof languageHandler.getAllLanguagesFromJson>>;
|
||||
type Languages = {
|
||||
value: string;
|
||||
label: string; // translated
|
||||
}[];
|
||||
function languageMatchesSearchQuery(query: string, language: Languages[0]): boolean {
|
||||
if (language.label.toUpperCase().includes(query.toUpperCase())) return true;
|
||||
if (language.value.toUpperCase() === query.toUpperCase()) return true;
|
||||
|
@ -58,6 +60,7 @@ export default class SpellCheckLanguagesDropdown extends React.Component<
|
|||
public componentDidMount(): void {
|
||||
const plaf = PlatformPeg.get();
|
||||
if (plaf) {
|
||||
const languageNames = new Intl.DisplayNames([getUserLanguage()], { type: "language", style: "short" });
|
||||
plaf.getAvailableSpellCheckLanguages()
|
||||
?.then((languages) => {
|
||||
languages.sort(function (a, b) {
|
||||
|
@ -68,7 +71,7 @@ export default class SpellCheckLanguagesDropdown extends React.Component<
|
|||
const langs: Languages = [];
|
||||
languages.forEach((language) => {
|
||||
langs.push({
|
||||
label: language,
|
||||
label: languageNames.of(language)!,
|
||||
value: language,
|
||||
});
|
||||
});
|
||||
|
@ -79,7 +82,7 @@ export default class SpellCheckLanguagesDropdown extends React.Component<
|
|||
languages: [
|
||||
{
|
||||
value: "en",
|
||||
label: "English",
|
||||
label: languageNames.of("en")!,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue