From 7f8f210fd6d9fa68c5cd37df358d486cd4570e32 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Thu, 12 Dec 2024 13:02:42 -0800 Subject: [PATCH] Detect dir of rtl or ltr based on BCP47 language codes (#239) --- package-lock.json | 2 +- src/components/Scroll/Annotation/Body.tsx | 3 +- src/lib/annotation-helpers.test.ts | 48 +++++++++++++++++++++++ src/lib/annotation-helpers.ts | 25 +++++++++++- 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f7741604..6432fcd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "react-error-boundary": "^4.1.2", "react-i18next": "^15.1.1", "sanitize-html": "^2.13.1", - "swiper": "^9.4.1", + "swiper": "^9.0.0", "uuid": "^9.0.1" }, "devDependencies": { diff --git a/src/components/Scroll/Annotation/Body.tsx b/src/components/Scroll/Annotation/Body.tsx index 39df072b..5c266908 100644 --- a/src/components/Scroll/Annotation/Body.tsx +++ b/src/components/Scroll/Annotation/Body.tsx @@ -4,6 +4,7 @@ import { EmbeddedResource } from "@iiif/presentation-3"; import { ScrollContext } from "src/context/scroll-context"; import { TextualBody } from "src/components/Scroll/Annotation/Body.styled"; import { getSearchResultSnippet } from "src/lib/search-helpers"; +import { getLanguageDirection } from "src/lib/annotation-helpers"; import useMarkdown from "@nulib/use-markdown"; const ScrollAnnotationBody = ({ @@ -62,7 +63,7 @@ const ScrollAnnotationBody = ({ } const id = [body.id, type].join("-"); - const isRtl = ["ar"].includes(String(body.language)); + const isRtl = getLanguageDirection(String(body.language)) === "RTL"; const dir = isRtl ? "rtl" : "ltr"; const fontSize = isRtl ? "1.3em" : "1em"; const lang = String(body.language); diff --git a/src/lib/annotation-helpers.test.ts b/src/lib/annotation-helpers.test.ts index 8f975cea..6d207b6e 100644 --- a/src/lib/annotation-helpers.test.ts +++ b/src/lib/annotation-helpers.test.ts @@ -1,8 +1,56 @@ import { + getLanguageDirection, parseAnnotationTarget, AnnotationTargetExtended, } from "./annotation-helpers"; +describe("getLanguageDirection", () => { + it("returns 'RTL' for Arabic", () => { + const result = getLanguageDirection("ar"); + expect(result).toEqual("RTL"); + }); + it("returns 'RTL' for Hebrew", () => { + const result = getLanguageDirection("he"); + expect(result).toEqual("RTL"); + }); + it("returns 'LTR' for English", () => { + const result = getLanguageDirection("en"); + expect(result).toEqual("LTR"); + }); + it("returns 'LTR' for French", () => { + const result = getLanguageDirection("fr"); + expect(result).toEqual("LTR"); + }); + it("returns 'RTL' for Persian", () => { + const result = getLanguageDirection("fa"); + expect(result).toEqual("RTL"); + }); + it("returns 'RTL' for Urdu", () => { + const result = getLanguageDirection("ur"); + expect(result).toEqual("RTL"); + }); + it("returns 'LTR' for Spanish", () => { + const result = getLanguageDirection("es"); + expect(result).toEqual("LTR"); + }); + it("returns 'LTR' for German", () => { + const result = getLanguageDirection("de"); + expect(result).toEqual("LTR"); + }); + it("returns 'RTL' for Kurdish", () => { + const result = getLanguageDirection("ku"); + expect(result).toEqual("RTL"); + }); + it("returns 'RTL' for Pashto", () => { + const result = getLanguageDirection("ps"); + expect(result).toEqual("RTL"); + }); + it("returns 'RTL' for Divehi", () => { + const result = getLanguageDirection("dv"); + expect(result).toEqual("RTL"); + }); +}); + describe("parseAnnotationTarget", () => { it("handles target strings with xywh", () => { const target = "http://example.com/canvas/1#xywh=100,200,300,400"; diff --git a/src/lib/annotation-helpers.ts b/src/lib/annotation-helpers.ts index 051fede1..a2f939cf 100644 --- a/src/lib/annotation-helpers.ts +++ b/src/lib/annotation-helpers.ts @@ -8,6 +8,29 @@ export type AnnotationTargetExtended = AnnotationTarget & { svg?: string; }; +const getLanguageDirection = (bcp47Code) => { + // Exhaustive list of RTL languages + const rtlLanguages = [ + "ar", + "fa", + "ur", + "ps", + "dv", + "sd", + "ug", + "ku", + "he", + "yi", + "jrb", + "jpr", + "nqo", + ]; + + // Get the base language from the BCP47 code + const baseLang = bcp47Code.split("-")[0]; // Extract the base language + return rtlLanguages.includes(baseLang) ? "RTL" : "LTR"; +}; + const parseAnnotationTarget = (target: AnnotationTargetExtended | string) => { let parsedTarget: ParsedAnnotationTarget = { id: typeof target === "string" ? target : target.source, @@ -98,4 +121,4 @@ function extractLanguages(annotations: AnnotationNormalized[]) { return Array.from(languages); } -export { extractLanguages, parseAnnotationTarget }; +export { getLanguageDirection, extractLanguages, parseAnnotationTarget };