diff --git a/app/[locale]/languages/[language]/page.tsx b/app/[locale]/languages/[language]/page.tsx index 3a02a31..4e93529 100644 --- a/app/[locale]/languages/[language]/page.tsx +++ b/app/[locale]/languages/[language]/page.tsx @@ -15,18 +15,25 @@ interface LanguagesPageProps { // eslint-disable-next-line @typescript-eslint/no-unused-vars export default function LanguagesPage(props: LanguagesPageProps) { const t = useTranslations("LanguagesPage"); + const tl = useTranslations("Languages"); return ( <MainContent> - <InstantSearchView - queryArgsToMenuFields={{ language: "language" }} - // refinementDropdowns={{ language: `${t("all")} ${t("filter_by.language")}` }} - > + <InstantSearchView queryArgsToMenuFields={{ language: "language" }}> <SingleRefinementList allLabel={t("all languages")} attribute={"language"} + className="lowercase" refinementArgs={{ transformItems: (items) => { - return items; + return items + .map((item) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + item.label = tl(item.label as any); + return item; + }) + .sort((a, b) => { + return a.label.localeCompare(b.label); + }); }, }} /> diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index e952d7d..990eeb6 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -123,6 +123,7 @@ export default function LocaleLayout(props: LocaleLayoutProps): ReactNode { "BernhardCategories", "Error", "InstantSearch", + "Languages", "LanguagesPage", "PublicationCover", "SearchPage", diff --git a/app/[locale]/search/page.tsx b/app/[locale]/search/page.tsx index 33dcafc..078751c 100644 --- a/app/[locale]/search/page.tsx +++ b/app/[locale]/search/page.tsx @@ -1,12 +1,16 @@ "use client"; import { useTranslations } from "next-intl"; -import { Menu } from "react-instantsearch"; +import { Menu, type MenuProps } from "react-instantsearch"; import { InstantSearchView } from "@/components/instantsearch-view"; import { MainContent } from "@/components/main-content"; -function FilterMenu(props: { attribute: string }) { +function FilterMenu(props: { + attribute: string; + className?: string; + transformItems?: MenuProps["transformItems"]; +}) { const t = useTranslations("SearchPage"); return ( <> @@ -22,7 +26,7 @@ function FilterMenu(props: { attribute: string }) { classNames={{ count: 'before:content-["("] after:content-[")"]', disabledShowMore: "hidden", - label: "px-1", + label: `px-1 ${props.className ? props.className : ""}`, list: "text-[--color-link]", root: "py-2 text-right", selectedItem: "font-bold text-[--color-link-active]", @@ -31,6 +35,7 @@ function FilterMenu(props: { attribute: string }) { showMore={true} showMoreLimit={50} sortBy={["isRefined", "count", "name"]} + transformItems={props.transformItems} translations={{ showMoreButtonText({ isShowingMore }) { return t(isShowingMore ? "show less" : "show more"); @@ -42,6 +47,7 @@ function FilterMenu(props: { attribute: string }) { } export default function SearchPage() { + const tl = useTranslations("Languages"); return ( <MainContent className="mx-auto w-screen max-w-screen-lg p-6"> <InstantSearchView @@ -55,7 +61,17 @@ export default function SearchPage() { > <FilterMenu attribute="contains.work.short_title" /> - <FilterMenu attribute="language" /> + <FilterMenu + attribute="language" + className="lowercase" + transformItems={(items) => { + return items.map((item) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + item.label = tl(item.label as any); + return item; + }); + }} + /> { // FIXME when changing the query removes a refinement from the list, that refinement diff --git a/app/[locale]/translators/page.tsx b/app/[locale]/translators/page.tsx index fda4aa1..26523d3 100644 --- a/app/[locale]/translators/page.tsx +++ b/app/[locale]/translators/page.tsx @@ -46,6 +46,7 @@ function TranslatorsList() { export default function TranslatorsPage() { const t = useTranslations("TranslatorsPage"); + const tl = useTranslations("Languages"); return ( <MainContent className="mx-auto w-screen max-w-screen-lg p-6"> <InstantSearch @@ -56,7 +57,20 @@ export default function TranslatorsPage() { <SingleRefinementDropdown allLabel={t("all languages")} attribute={"language"} - refinementArgs={{ transformItems: undefined }} + className="lowercase" + refinementArgs={{ + transformItems: (items) => { + return items + .map((item) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + item.label = tl(item.label as any); + return item; + }) + .sort((a, b) => { + return a.label.localeCompare(b.label); + }); + }, + }} /> </div> <TranslatorsList /> diff --git a/components/language-link.tsx b/components/language-link.tsx index f512b3a..e520636 100644 --- a/components/language-link.tsx +++ b/components/language-link.tsx @@ -1,3 +1,5 @@ +import { useTranslations } from "next-intl"; + import { AppLink } from "./app-link"; interface LanguageLinkProps { @@ -5,5 +7,13 @@ interface LanguageLinkProps { } export function LanguageLink(props: LanguageLinkProps) { - return <AppLink href={`/languages?language=${props.language}`}>{props.language}</AppLink>; + const t = useTranslations("Languages"); + return ( + <AppLink className="lowercase" href={`/languages?language=${props.language}`}> + { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + t(props.language as any) + } + </AppLink> + ); } diff --git a/components/single-refinement-dropdown.tsx b/components/single-refinement-dropdown.tsx index b1d97a2..d67eb74 100644 --- a/components/single-refinement-dropdown.tsx +++ b/components/single-refinement-dropdown.tsx @@ -9,6 +9,7 @@ interface SingleRefinementDropdownProps { attribute: string; allLabel: string; refinementArgs?: Partial<UseMenuProps>; + className?: string; } const defaultTransformItems = (items: Array<RefinementListItem>) => { @@ -21,8 +22,6 @@ const defaultTransformItems = (items: Array<RefinementListItem>) => { // a refinement dropdown that is alphabetically ordered export function SingleRefinementDropdown(props: SingleRefinementDropdownProps) { - // const t = useTranslations("SearchPage"); - const { items, refine } = useMenu({ attribute: props.attribute, limit: 1000, @@ -39,19 +38,24 @@ export function SingleRefinementDropdown(props: SingleRefinementDropdownProps) { }} > <Label className="sr-only">sort order</Label> - <SelectTrigger> + <SelectTrigger className={props.className}> {items.find((i) => { return i.isRefined; - })?.value ?? props.allLabel} + })?.label ?? props.allLabel} </SelectTrigger> <SelectPopover> <SelectContent> - <SelectItem key={"all"} id={"all"} textValue={props.allLabel}> + <SelectItem key={"all"} className="lowercase" id={"all"} textValue={props.allLabel}> {props.allLabel} </SelectItem> {items.map((o) => { return ( - <SelectItem key={o.value} id={o.value} textValue={o.label}> + <SelectItem + key={o.value} + className={props.className} + id={o.value} + textValue={o.label} + > {o.label} </SelectItem> ); diff --git a/components/single-refinement-list.tsx b/components/single-refinement-list.tsx index 2badae1..d90d152 100644 --- a/components/single-refinement-list.tsx +++ b/components/single-refinement-list.tsx @@ -6,6 +6,7 @@ interface SingleRefinementListProps { attribute: string; allLabel?: string; refinementArgs?: Partial<UseMenuProps>; + className?: string; } const defaultTransformItems = (items: Array<RefinementListItem>) => { @@ -31,7 +32,7 @@ export function SingleRefinementList(props: SingleRefinementListProps) { <div className="px-2"> <label key="all" - className="block py-0.5 text-right focus-within:outline focus-within:outline-2" + className={`block py-0.5 text-right focus-within:outline focus-within:outline-2 ${props.className ? props.className : ""}`} > <input checked={items.every((i) => { @@ -63,7 +64,7 @@ export function SingleRefinementList(props: SingleRefinementListProps) { return ( <label key={item.label} - className="block py-1 text-right focus-within:outline focus-within:outline-2" + className={`block py-0.5 text-right focus-within:outline focus-within:outline-2 ${props.className ? props.className : ""}`} > <input checked={item.isRefined} diff --git a/messages/de.json b/messages/de.json index b2838e3..94d22ae 100644 --- a/messages/de.json +++ b/messages/de.json @@ -74,6 +74,50 @@ "table": "Detailansicht" } }, + "Languages": { + "sq": "Albanisch", + "ar": "Arabisch", + "az": "Aserbaidschanisch", + "eu": "Baskisch", + "bg": "Bulgarisch", + "ca": "Katalanisch", + "zh_hans": "Chinesisch (vereinfacht)", + "hr": "Kroatisch", + "cs": "Tschechisch", + "da": "Dänisch", + "nl": "Niederländisch", + "en": "Englisch", + "et": "Estnisch", + "fa": "Persisch", + "fi": "Finnisch", + "fr": "Französisch", + "gl": "Galicisch", + "ka": "Georgisch", + "el": "Griechisch", + "he": "Hebräisch", + "hu": "Ungarisch", + "is": "Isländisch", + "it": "Italienisch", + "ja": "Japanisch", + "ko": "Koreanisch", + "lt": "Litauisch", + "mk": "Mazedonisch", + "no": "Norwegisch", + "pl": "Polnisch", + "pt_pt": "Portugiesisch", + "pt_br": "Portugiesisch (Brasilien)", + "ro": "Rumänisch", + "ru": "Russisch", + "sr": "Serbisch", + "sk": "Slowakisch", + "sl": "Slowenisch", + "es": "Spanisch", + "sv": "Schwedish", + "tr": "Türkisch", + "uk": "Ukrainisch", + "ur": "Urdu", + "vi": "Vietnamesisch" + }, "LanguagesPage": { "all languages": "alle Sprachen" }, diff --git a/messages/en.json b/messages/en.json index 6ba7c3c..2576ea9 100644 --- a/messages/en.json +++ b/messages/en.json @@ -74,6 +74,50 @@ "table": "detail view" } }, + "Languages": { + "sq": "Albanian", + "ar": "Arabic", + "az": "Azerbaijani", + "eu": "Basque", + "bg": "Bulgarian", + "ca": "Catalan", + "zh_hans": "Chinese (Simplified)", + "hr": "Croatian", + "cs": "Czech", + "da": "Danish", + "nl": "Dutch", + "en": "English", + "et": "Estonian", + "fa": "Persian", + "fi": "Finnish", + "fr": "French", + "gl": "Galician", + "ka": "Georgian", + "el": "Greek", + "he": "Hebrew", + "hu": "Hungarian", + "is": "Icelandic", + "it": "Italian", + "ja": "Japanese", + "ko": "Korean", + "lt": "Lithuanian", + "mk": "Macedonian", + "no": "Norwegian", + "pl": "Polish", + "pt_pt": "Portuguese", + "pt_br": "Portuguese (Brazil)", + "ro": "Romanian", + "ru": "Russian", + "sr": "Serbian", + "sk": "Slovak", + "sl": "Slovenian", + "es": "Spanish", + "sv": "Swedish", + "tr": "Turkish", + "uk": "Ukrainian", + "ur": "Urdu", + "vi": "Vietnamese" + }, "LanguagesPage": { "all languages": "languages" }, diff --git a/scripts/3_to_typesense.py b/scripts/3_to_typesense.py index 0a9b92d..7128fda 100755 --- a/scripts/3_to_typesense.py +++ b/scripts/3_to_typesense.py @@ -147,12 +147,58 @@ def del_empty_strings(o, field_names): t["has_translators"] = len(t["translators"]) != 0 del_empty_strings(t, ["work_display_title"]) +languages = { + "albanian": "sq", + "arabic": "ar", + "azerbaijani": "az", + "basque": "eu", + "bulgarian": "bg", + "catalan": "ca", + "chinese (simpl.)": "zh_hans", + "croatian": "hr", + "czech": "cs", + "danish": "da", + "dutch": "nl", + "english": "en", + "estonian": "et", + "farsi": "fa", + "finnish": "fi", + "french": "fr", + "galician": "gl", + "georgian": "ka", + "greek": "el", + "hebrew": "he", + "hungarian": "hu", + "icelandic": "is", + "italian": "it", + "japanese": "ja", + "korean": "ko", + "lithuanian": "lt", + "macedonian": "mk", + "norwegian": "no", + "polish": "pl", + "portuguese": "pt_pt", + "portuguese (brazil)": "pt_br", + "romanian": "ro", + "russian": "ru", + "serbian": "sr", + "slovak": "sk", + "slovenian": "sl", + "spanish": "es", + "swedish": "sv", + "turkish": "tr", + "ukrainian": "uk", + "urdu": "ur", + "vietnamese": "vi", +} + for i, pub in enumerate(publications): pub["id"] = str(i + 1) if not w["short_title"]: w["short_title"] = w["title"] pub["contains"] = [translations[t_id - 1] for t_id in pub["contains"]] + pub["language"] = languages[pub["language"]] pub["images"] = ( [{"id": img} for img in pub["images"].split(" ")] if len(pub["images"]) else [] @@ -167,7 +213,7 @@ def del_empty_strings(o, field_names): else: publications[pid - 1]["later"] = [i + 1] - del_empty_strings(pub, ["parents", "publication_details"]) + del_empty_strings(pub, ["isbn", "parents", "publication_details"]) # trim data a little del pub["exemplar_suhrkamp_berlin"] @@ -203,7 +249,7 @@ def del_empty_strings(o, field_names): except ObjectNotFound: logging.info(f"collection '{collection_name}' does not exist yet, creating") schema = json.load(open("typesense-schema.json")) - schema["collection_name"] = collection_name + schema["name"] = collection_name create = client.collections.create(schema) r = client.collections[collection_name].retrieve()