mirror of
https://github.com/temetro/temetro.git
synced 2026-08-31 12:58:08 +00:00
frontend: add Somali, Arabic (RTL) & German languages
Add three UI locales (so/ar/de) with full ~1,660-key translations alongside en/fr, selectable in Settings → Profile. Arabic gets full right-to-left support: - config.ts registers the locales and exports a `dirFor` helper; an inline <head> script in layout.tsx sets <html dir/lang> before first paint (no RTL flash), and i18n-provider keeps them in sync on language change. - ~160 physical direction utilities converted to logical (ms/me/ps/pe/ start/end/text-start/text-end); directional chevrons/arrows get rtl:rotate-180; chat-bubble align variants fixed to logical. - IBM Plex Sans Arabic appended to the sans/heading font stacks for per-character Arabic fallback. - Language persists to the backend user_settings and re-applies on sign-in so it roams across devices (localStorage stays the offline source of truth). - New scripts/check-locales.mjs (npm run check-locales) enforces key/placeholder parity and Arabic CLDR plural completeness. Bump to 0.3.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,7 +35,7 @@ function Row({ label, value }: { label: string; value: ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<span className="text-muted-foreground text-sm">{label}</span>
|
||||
<span className="text-right text-foreground text-sm">{value}</span>
|
||||
<span className="text-end text-foreground text-sm">{value}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -68,7 +68,7 @@ export function InventoryDetailDialog({
|
||||
</span>
|
||||
<span className="min-w-0 truncate">{item.name}</span>
|
||||
<Badge
|
||||
className="ml-auto shrink-0"
|
||||
className="ms-auto shrink-0"
|
||||
variant={availabilityVariant[availability]}
|
||||
>
|
||||
{t(`inventory.availability.${availability}`)}
|
||||
|
||||
@@ -67,7 +67,7 @@ function ItemRow({
|
||||
const descriptor = [item.strength, item.form].filter(Boolean).join(" · ");
|
||||
return (
|
||||
<div
|
||||
className="flex w-full cursor-pointer items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50"
|
||||
className="flex w-full cursor-pointer items-center gap-3 px-4 py-3 text-start transition-colors hover:bg-accent/50"
|
||||
onClick={onOpen}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
@@ -205,9 +205,9 @@ export function InventoryView() {
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative">
|
||||
<Search className="-translate-y-1/2 absolute top-1/2 left-3 size-4 text-muted-foreground" />
|
||||
<Search className="-translate-y-1/2 absolute top-1/2 start-3 size-4 text-muted-foreground" />
|
||||
<Input
|
||||
className="w-full pl-9 sm:w-64"
|
||||
className="w-full ps-9 sm:w-64"
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
placeholder={t("inventory.searchPlaceholder")}
|
||||
value={query}
|
||||
|
||||
@@ -330,9 +330,9 @@ export function PharmacyView() {
|
||||
<p className="text-muted-foreground text-sm">{t("pharmacy.subtitle")}</p>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<Search className="-translate-y-1/2 absolute top-1/2 left-3 size-4 text-muted-foreground" />
|
||||
<Search className="-translate-y-1/2 absolute top-1/2 start-3 size-4 text-muted-foreground" />
|
||||
<Input
|
||||
className="w-full pl-9 sm:w-64"
|
||||
className="w-full ps-9 sm:w-64"
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
placeholder={t("pharmacy.searchPlaceholder")}
|
||||
value={query}
|
||||
|
||||
Reference in New Issue
Block a user