Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/web/features/profile/ProfileTagInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Autocomplete, {
import { CloseIcon, ExpandMoreIcon } from "components/Icons";
import { useTranslation } from "i18n";
import { PROFILE } from "i18n/namespaces";
import { getLocalizedListComparer } from "i18n/sorting";
import React, { useRef, useState } from "react";
import { ControllerRenderProps } from "react-hook-form";
import { theme } from "theme";
Expand Down Expand Up @@ -154,6 +155,7 @@ export default function ProfileTagInput({
inputFieldProps,
}: ProfileTagInputProps) {
const { t, i18n } = useTranslation(PROFILE);
const compareLocalizedLabels = getLocalizedListComparer(i18n.language);

// In case some value doesn't map to an option, add a fake option for it.
// For example if value is [en, xx] and options is { en: "English", fr: "French" },
Expand Down Expand Up @@ -272,7 +274,7 @@ export default function ProfileTagInput({
disablePortal
options={Object.entries(effectiveOptions)
.map(([key, label]) => ({ key, label }))
.sort((a, b) => a.label.localeCompare(b.label, i18n.language))}
.sort((a, b) => compareLocalizedLabels(a.label, b.label))}
renderOption={(props, option, { selected }) => {
const { key, ...rest } = props;

Expand Down
37 changes: 37 additions & 0 deletions app/web/i18n/sorting.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { getLocalizedListComparer } from "./sorting";

describe("getLocalizedListComparer", () => {
it("uses pinyin collation for zh-Hans when supported", () => {
const resolvedLocale = new Intl.Collator(
"zh-Hans-u-co-pinyin",
).resolvedOptions().locale;
Comment thread
tristanlabelle marked this conversation as resolved.
Outdated

if (resolvedLocale.includes("-pinyin")) {
expect(
["中国", "法国", "德国", "阿富汗"].sort(
getLocalizedListComparer("zh-Hans"),
),
).toEqual(["阿富汗", "德国", "法国", "中国"]);
}
});

it("uses stroke collation for zh-Hant when supported", () => {
const resolvedLocale = new Intl.Collator(
"zh-Hant-u-co-stroke",
).resolvedOptions().locale;

if (resolvedLocale.includes("-stroke")) {
expect(
["德國", "法國", "中國", "加拿大"].sort(
getLocalizedListComparer("zh-Hant"),
),
).toEqual(["中國", "加拿大", "法國", "德國"]);
}
});

it("sorts other locales normally", () => {
expect(
["Zulu", "Alpha", "Echo"].sort(getLocalizedListComparer("en")),
).toEqual(["Alpha", "Echo", "Zulu"]);
});
});
29 changes: 29 additions & 0 deletions app/web/i18n/sorting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
function getLocalizedListSortLocales(language: string): string[] {
if (language === "zh-Hans" || language.startsWith("zh-Hans-")) {
// Simplified Chinese lists are conventionally sorted by romanized pinyin.
return [
"zh-Hans-u-co-pinyin",
"zh-CN-u-co-pinyin",
"zh-u-co-pinyin",
language,
];
}

if (language === "zh-Hant" || language.startsWith("zh-Hant-")) {
// Traditional Chinese lists are conventionally sorted by character strokes.
return [
"zh-Hant-u-co-stroke",
"zh-TW-u-co-stroke",
"zh-u-co-stroke",
language,
];
}

return [language];
}

export function getLocalizedListComparer(
language: string,
): Intl.Collator["compare"] {
return new Intl.Collator(getLocalizedListSortLocales(language)).compare;
}
Loading