Skip to content

Commit 212ca43

Browse files
committed
refactor: improve translation in Document Viewer
1 parent 427356d commit 212ca43

File tree

2 files changed

+109
-88
lines changed

2 files changed

+109
-88
lines changed

src/components/DocumentViewer.tsx

Lines changed: 96 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,36 @@
1-
import { useState, useRef, useEffect } from "react";
2-
import { useIsMobile } from "@/hooks/use-mobile";
3-
import { Button } from "@/components/ui/button";
4-
import {
5-
ChevronRight,
6-
ChevronLeft,
7-
SkipBack,
8-
SkipForward,
9-
} from "lucide-react";
10-
import PageViewer from "./PageViewer";
1+
import { useEffect, useRef, useState } from 'react';
2+
import { useIsMobile } from '@/hooks/use-mobile';
3+
import { Button } from '@/components/ui/button';
4+
import { ChevronLeft, ChevronRight, SkipBack, SkipForward } from 'lucide-react';
5+
import PageViewer from './PageViewer';
116
import { useLanguage } from '@/contexts/LanguageContext';
127

138
interface DocumentViewerProps {
149
className?: string;
1510
}
1611

1712
export default function DocumentViewer({ className }: DocumentViewerProps) {
18-
const {t} = useLanguage();
13+
const { t, isRTL, currentLanguage } = useLanguage();
1914
const isMobile = useIsMobile();
2015
const totalPages = 604; // Total number of pages in the document
2116
const [currentPage, setCurrentPage] = useState<number>(1);
2217

23-
// ==== Improved "انتقال" Section state ====
24-
const [gotoPage, setGotoPage] = useState<string>("1");
18+
// Navigation state
19+
const [gotoPage, setGotoPage] = useState<string>('1');
2520
const [gotoError, setGotoError] = useState<string | null>(null);
2621
const inputRef = useRef<HTMLInputElement>(null);
2722

28-
// ---- Pagination logic ----
23+
// Calculate page increment based on view and RTL
2924
const pageIncrement = isMobile ? 1 : 2;
3025

26+
// Adjust page number when switching between mobile/desktop
3127
useEffect(() => {
3228
if (!isMobile && currentPage % 2 === 0) {
3329
setCurrentPage((prev) => prev - 1);
3430
}
3531
}, [isMobile, currentPage]);
3632

33+
// Navigation handlers with RTL consideration
3734
const goToNextPages = () => {
3835
setCurrentPage((prev) => Math.min(totalPages, prev + pageIncrement));
3936
};
@@ -47,20 +44,14 @@ export default function DocumentViewer({ className }: DocumentViewerProps) {
4744
};
4845

4946
const goToLastPage = () => {
50-
setCurrentPage(
51-
isMobile
52-
? totalPages
53-
: totalPages % 2 === 0
54-
? totalPages - 1
55-
: totalPages
56-
);
47+
const lastPage = isMobile ? totalPages : totalPages % 2 === 0 ? totalPages - 1 : totalPages;
48+
setCurrentPage(lastPage);
5749
};
5850

59-
// ==== Improved Go To Page Logic ====
6051
const handleGoto = () => {
6152
const page = parseInt(gotoPage, 10);
6253
if (isNaN(page) || page < 1 || page > totalPages) {
63-
setGotoError(`رقم الصفحة يجب أن يكون بين 1 و ${totalPages}`);
54+
setGotoError(t('pageNumberError', { min: 1, max: totalPages }));
6455
inputRef.current?.focus();
6556
} else {
6657
setCurrentPage(page);
@@ -70,142 +61,160 @@ export default function DocumentViewer({ className }: DocumentViewerProps) {
7061
}
7162
};
7263

73-
// Allow Enter key to trigger transition
7464
const handleGotoInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
75-
if (e.key === "Enter") {
65+
if (e.key === 'Enter') {
7666
e.preventDefault();
7767
handleGoto();
7868
}
7969
};
8070

81-
// Keep goto field in sync if user changes page elsewhere
8271
useEffect(() => {
8372
setGotoPage(String(currentPage));
8473
setGotoError(null);
8574
}, [currentPage]);
8675

76+
const getPageDisplay = () => {
77+
if (isMobile) {
78+
return [
79+
<PageViewer
80+
key={currentPage}
81+
pageNumber={currentPage}
82+
totalPages={totalPages}
83+
className="animate-fadeIn aspect-[3/4] w-full max-w-md"
84+
/>
85+
];
86+
}
87+
88+
return [
89+
<PageViewer
90+
key={currentPage + 1}
91+
pageNumber={currentPage + 1}
92+
totalPages={totalPages}
93+
className="animate-slideInRight aspect-[3/4] w-1/2 max-w-md"
94+
/>,
95+
<PageViewer
96+
key={currentPage}
97+
pageNumber={currentPage}
98+
totalPages={totalPages}
99+
className="animate-slideInLeft aspect-[3/4] w-1/2 max-w-md"
100+
/>
101+
];
102+
};
103+
104+
const getNavigationIcons = () => {
105+
return isRTL ? {
106+
first: <SkipForward className="h-4 w-4" />,
107+
previous: <ChevronRight className="h-4 w-4" />,
108+
next: <ChevronLeft className="h-4 w-4" />,
109+
last: <SkipBack className="h-4 w-4" />
110+
} : {
111+
first: <SkipBack className="h-4 w-4" />,
112+
previous: <ChevronLeft className="h-4 w-4" />,
113+
next: <ChevronRight className="h-4 w-4" />,
114+
last: <SkipForward className="h-4 w-4" />
115+
};
116+
};
117+
118+
const icons = getNavigationIcons();
119+
87120
return (
88-
<div className={className}>
121+
<div className={className} dir={isRTL ? 'rtl' : 'ltr'}>
89122
<div className="flex flex-col items-center space-y-8">
90123
{/* Pages container */}
91-
<div className="w-full flex flex-row-reverse items-center justify-center gap-1 md:gap-2 lg:gap-3">
92-
{isMobile ? (
93-
// Mobile view - one page
94-
<PageViewer
95-
pageNumber={currentPage}
96-
totalPages={totalPages}
97-
className="w-full aspect-[3/4] max-w-md animate-fadeIn"
98-
/>
99-
) : (
100-
// Desktop/tablet view - two pages
101-
<>
102-
{/* Left page (even) */}
103-
<PageViewer
104-
pageNumber={currentPage + 1}
105-
totalPages={totalPages}
106-
className="w-1/2 aspect-[3/4] max-w-md animate-slideInLeft"
107-
/>
108-
109-
{/* Right page (odd) */}
110-
<PageViewer
111-
pageNumber={currentPage}
112-
totalPages={totalPages}
113-
className="w-1/2 aspect-[3/4] max-w-md animate-slideInRight"
114-
/>
115-
</>
116-
)}
124+
<div className={`flex w-full items-center justify-center gap-1 md:gap-2 lg:gap-3 ${isRTL ? 'flex-row-reverse' : 'flex-row'}`}>
125+
{getPageDisplay()}
117126
</div>
118127

119128
{/* Navigation controls */}
120-
<div className="flex items-center justify-center space-x-2 rtl:space-x-reverse">
129+
<div className="flex items-center justify-center gap-2">
121130
<Button
122131
variant="outline"
123132
size="icon"
124133
onClick={goToFirstPage}
125134
disabled={currentPage === 1}
126-
title="الصفحة الأولى"
135+
title={t('firstPage')}
136+
aria-label={t('firstPage')}
127137
>
128-
<SkipForward className="h-4 w-4" />
138+
{icons.first}
129139
</Button>
130140

131141
<Button
132142
variant="outline"
133143
size="icon"
134144
onClick={goToPreviousPages}
135145
disabled={currentPage === 1}
136-
title="الصفحة السابقة"
146+
title={t('previousPage')}
147+
aria-label={t('previousPage')}
137148
>
138-
<ChevronRight className="h-4 w-4" />
149+
{icons.previous}
139150
</Button>
140151

141152
<div className="mx-4 text-sm font-medium">
142-
<span>{currentPage}</span>
153+
<span>{currentPage.toLocaleString(currentLanguage)}</span>
143154
{!isMobile && currentPage + 1 <= totalPages && (
144155
<>
145156
<span className="mx-1">-</span>
146-
<span>{currentPage + 1}</span>
157+
<span>{(currentPage + 1).toLocaleString(currentLanguage)}</span>
147158
</>
148159
)}
149-
<span className="mx-1 text-muted-foreground">من</span>
150-
<span>{totalPages}</span>
160+
<span className="mx-1 text-muted-foreground">{t('of')}</span>
161+
<span>{totalPages.toLocaleString(currentLanguage)}</span>
151162
</div>
152163

153164
<Button
154165
variant="outline"
155166
size="icon"
156167
onClick={goToNextPages}
157-
disabled={
158-
isMobile
159-
? currentPage === totalPages
160-
: currentPage + 1 >= totalPages
161-
}
162-
title="الصفحة التالية"
168+
disabled={isMobile ? currentPage === totalPages : currentPage + 1 >= totalPages}
169+
title={t('nextPage')}
170+
aria-label={t('nextPage')}
163171
>
164-
<ChevronLeft className="h-4 w-4" />
172+
{icons.next}
165173
</Button>
166174

167175
<Button
168176
variant="outline"
169177
size="icon"
170178
onClick={goToLastPage}
171-
disabled={
172-
isMobile
173-
? currentPage === totalPages
174-
: currentPage + 1 >= totalPages
175-
}
176-
title="الصفحة الأخيرة"
179+
disabled={isMobile ? currentPage === totalPages : currentPage + 1 >= totalPages}
180+
title={t('lastPage')}
181+
aria-label={t('lastPage')}
177182
>
178-
<SkipBack className="h-4 w-4" />
183+
{icons.last}
179184
</Button>
180185
</div>
181186

182-
{/* Improved Page number input */}
187+
{/* Page number input */}
183188
<div className="flex flex-col items-center gap-1">
184-
<label htmlFor="goto-input" className="text-right w-full text-xs md:text-sm font-bold mb-1 text-muted-foreground">
189+
<label
190+
htmlFor="goto-input"
191+
className="mb-1 w-full text-xs font-bold text-muted-foreground md:text-sm"
192+
style={{ textAlign: isRTL ? 'right' : 'left' }}
193+
>
185194
{t('goToPage')}
186195
</label>
187-
<div className="flex items-center space-x-2 rtl:space-x-reverse">
196+
<div className="flex items-center gap-2">
188197
<input
189198
id="goto-input"
190199
ref={inputRef}
191200
type="number"
192201
min={1}
193202
max={totalPages}
194203
value={gotoPage}
195-
onChange={(e) => setGotoPage(e.target.value.replace(/\D/, ""))}
204+
onChange={(e) => setGotoPage(e.target.value.replace(/\D/, ''))}
196205
onKeyDown={handleGotoInputKeyDown}
197-
className={`w-20 md:w-28 h-10 px-2 border rounded text-center focus:ring-2 focus:ring-primary transition-all`}
198-
aria-label="رقم الصفحة"
206+
className={`h-10 w-20 rounded border px-2 text-center transition-all focus:ring-2 focus:ring-primary md:w-28 ${
207+
isRTL ? 'text-right' : 'text-left'
208+
}`}
209+
aria-label={t('pageNumber')}
210+
dir="ltr" // Always LTR for numbers
199211
/>
200-
<Button
201-
variant="secondary"
202-
onClick={handleGoto}
203-
>
212+
<Button variant="secondary" onClick={handleGoto}>
204213
{t('goTo')}
205214
</Button>
206215
</div>
207216
{gotoError && (
208-
<div className="mt-2 text-xs text-red-500 animate-fadeIn" role="alert">
217+
<div className="animate-fadeIn mt-2 text-xs text-red-500" role="alert">
209218
{gotoError}
210219
</div>
211220
)}

src/contexts/LanguageContext/languageConfig.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,20 @@ export type Language = keyof typeof SUPPORTED_LANGUAGES;
1515
export const translations = {
1616
ar: {
1717
siteName: 'القرأن الكريم',
18-
siteSlogan: 'تصفح الآيات، وامنح قلبك يقينًا لا يزول',
18+
siteSlogan: 'نور للعقل، يقين للقلب',
1919
toggleLanguage: 'English',
2020
error: 'حدث خطأ في تحميل البيانات',
2121
pageNotFound: 'عذرًا! الصفحة غير موجودة',
2222
backHome: 'العودة إلى الصفحة الرئيسية',
2323
copyright: 'يقين للقرأن الكريم. جميع الحقوق محفوظة.',
2424
goToPage: 'اذهب إلى صفحة معينة',
2525
goTo: 'إنتقال',
26+
firstPage: 'الصفحة الأولى',
27+
previousPage: 'الصفحة السابقة',
28+
nextPage: 'الصفحة التالية',
29+
of: 'من',
30+
pageNumber: 'رقم الصفحة',
31+
pageNumberError: 'رقم الصفحة غير صحيح',
2632
},
2733
en: {
2834
siteName: 'Holy Quran',
@@ -34,6 +40,12 @@ export const translations = {
3440
copyright: 'Yaqiin Quran. All rights reserved.',
3541
goToPage: 'Go To Page',
3642
goTo: 'Go To',
43+
firstPage: 'First Page',
44+
previousPage: 'Previous Page',
45+
nextPage: 'Next Page',
46+
of: 'of',
47+
pageNumber: 'Page Number',
48+
pageNumberError: 'Invalid page number',
3749
}
3850
};
3951

0 commit comments

Comments
 (0)