feat(i18n): Hebrew locale + full RTL support

This commit is contained in:
Shuki Vaknin
2026-07-08 00:07:34 +02:00
committed by Linus Rath
parent 8904d724bb
commit 22418c17cf
113 changed files with 3958 additions and 494 deletions
+37 -37
View File
@@ -821,8 +821,8 @@ export function FileBrowser({
const SortIndicator = ({ column }: { column: SortKey }) => {
if (sortKey !== column) return null;
return sortDir === "asc"
? <ArrowUp className="w-3 h-3 inline ml-1" />
: <ArrowDown className="w-3 h-3 inline ml-1" />;
? <ArrowUp className="w-3 h-3 inline ms-1" />
: <ArrowDown className="w-3 h-3 inline ms-1" />;
};
// Keyboard shortcuts
@@ -942,7 +942,7 @@ export function FileBrowser({
<Button
variant="ghost"
size="icon"
className="h-8 w-8 -ml-2"
className="h-8 w-8 -ms-2"
onClick={() => setNarrowSidebarOpen((v) => !v)}
aria-label={t("open_folder_tree")}
>
@@ -982,7 +982,7 @@ export function FileBrowser({
className="h-8"
onClick={() => onBatchDownload([...selectedResources].filter(n => !resources.find(r => r.name === n)?.isDirectory))}
>
<Download className="w-4 h-4 mr-1" />
<Download className="w-4 h-4 me-1" />
{t("download")} ({[...selectedResources].filter(n => !resources.find(r => r.name === n)?.isDirectory).length})
</Button>
<Button
@@ -991,7 +991,7 @@ export function FileBrowser({
className="h-8 text-destructive hover:text-destructive"
onClick={() => onBatchDelete([...selectedResources])}
>
<Trash2 className="w-4 h-4 mr-1" />
<Trash2 className="w-4 h-4 me-1" />
{t("delete")} ({selectedResources.size})
</Button>
</>
@@ -1003,7 +1003,7 @@ export function FileBrowser({
className="h-8"
onClick={onPaste}
>
<Clipboard className="w-4 h-4 mr-1" />
<Clipboard className="w-4 h-4 me-1" />
{t("paste")} ({clipboard.names.length})
</Button>
)}
@@ -1157,7 +1157,7 @@ export function FileBrowser({
className="h-7 text-destructive hover:text-destructive shrink-0"
onClick={onRefresh}
>
<RefreshCw className="w-3.5 h-3.5 mr-1" />
<RefreshCw className="w-3.5 h-3.5 me-1" />
{t("retry")}
</Button>
</div>
@@ -1181,12 +1181,12 @@ export function FileBrowser({
<span className="truncate">
{t("uploading")} {uploadProgress.name}
{uploadProgress.totalFiles > 1 && (
<span className="text-muted-foreground ml-1">
<span className="text-muted-foreground ms-1">
({uploadProgress.current}/{uploadProgress.totalFiles})
</span>
)}
</span>
<span className="ml-auto tabular-nums shrink-0 flex items-center gap-2">
<span className="ms-auto tabular-nums shrink-0 flex items-center gap-2">
{uploadProgress.total > 0
? `${Math.round((uploadProgress.loaded / uploadProgress.total) * 100)}%`
: "…"}
@@ -1267,7 +1267,7 @@ export function FileBrowser({
)}
{/* Favorites & Recent sidebar (when layout is inline) */}
{folderLayout === "inline" && (favorites.length > 0 || recentFiles.length > 0) && (
<div className="w-48 border-r border-border bg-background overflow-y-auto shrink-0 hidden lg:block">
<div className="w-48 border-e border-border bg-background overflow-y-auto shrink-0 hidden lg:block">
{favorites.length > 0 && (
<div className="p-3">
<h4 className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-2 flex items-center gap-1">
@@ -1280,7 +1280,7 @@ export function FileBrowser({
key={fav}
onClick={() => onNavigate(fav)}
className={cn(
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm hover:bg-muted transition-colors text-left",
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm hover:bg-muted transition-colors text-start",
currentPath === fav && "bg-muted font-medium"
)}
>
@@ -1301,7 +1301,7 @@ export function FileBrowser({
{recentFiles.slice(0, 10).map((recent) => (
<button
key={recent.id}
className="w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm hover:bg-muted transition-colors text-start"
title={recent.name}
>
<File className="w-3.5 h-3.5 text-muted-foreground shrink-0" />
@@ -1322,14 +1322,14 @@ export function FileBrowser({
<table className="w-full text-sm">
<thead className="bg-muted/50 sticky top-0 z-10">
<tr className="border-b border-border">
<th className="text-left px-4 py-2 font-medium text-muted-foreground">
<th className="text-start px-4 py-2 font-medium text-muted-foreground">
<div className="flex items-center gap-3">
<div className="w-4 h-4" />
{t("name")}
</div>
</th>
<th className="text-left px-4 py-2 font-medium text-muted-foreground hidden md:table-cell w-24">{t("size")}</th>
<th className="text-left px-4 py-2 font-medium text-muted-foreground hidden lg:table-cell w-44">{t("modified")}</th>
<th className="text-start px-4 py-2 font-medium text-muted-foreground hidden md:table-cell w-24">{t("size")}</th>
<th className="text-start px-4 py-2 font-medium text-muted-foreground hidden lg:table-cell w-44">{t("modified")}</th>
<th className="w-10 px-2 py-2" />
</tr>
</thead>
@@ -1354,7 +1354,7 @@ export function FileBrowser({
key={`__account__:${acc.accountId}`}
onClick={() => onSelectAccount(acc.accountId)}
title={acc.email}
className="flex items-center gap-3 p-3 rounded-lg border border-border hover:bg-muted/50 transition-colors text-left min-w-0"
className="flex items-center gap-3 p-3 rounded-lg border border-border hover:bg-muted/50 transition-colors text-start min-w-0"
>
<Avatar
name={acc.label}
@@ -1514,7 +1514,7 @@ export function FileBrowser({
>
<thead className="bg-muted/50 sticky top-0 z-10">
<tr className="border-b border-border">
<th className="text-left px-4 py-2 font-medium text-muted-foreground">
<th className="text-start px-4 py-2 font-medium text-muted-foreground">
<div className="flex items-center gap-3">
<input
type="checkbox"
@@ -1530,13 +1530,13 @@ export function FileBrowser({
</button>
</div>
</th>
<th className="text-left px-4 py-2 font-medium text-muted-foreground hidden md:table-cell w-24">
<th className="text-start px-4 py-2 font-medium text-muted-foreground hidden md:table-cell w-24">
<button onClick={() => handleSortClick("size")} className="hover:text-foreground transition-colors">
{t("size")}
<SortIndicator column="size" />
</button>
</th>
<th className="text-left px-4 py-2 font-medium text-muted-foreground hidden lg:table-cell w-44">
<th className="text-start px-4 py-2 font-medium text-muted-foreground hidden lg:table-cell w-44">
<button onClick={() => handleSortClick("modified")} className="hover:text-foreground transition-colors">
{t("modified")}
<SortIndicator column="modified" />
@@ -1681,7 +1681,7 @@ export function FileBrowser({
>
{!resources.find(r => r.name === contextMenu.name)?.isDirectory && isPreviewable(contextMenu.name) && (
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
if (isImageFile(contextMenu.name)) {
onPreviewImage(contextMenu.name);
@@ -1697,7 +1697,7 @@ export function FileBrowser({
)}
{!resources.find(r => r.name === contextMenu.name)?.isDirectory && (
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
onDownload(contextMenu.name);
setContextMenu(null);
@@ -1708,7 +1708,7 @@ export function FileBrowser({
</button>
)}
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
onCut([contextMenu.name]);
setContextMenu(null);
@@ -1718,7 +1718,7 @@ export function FileBrowser({
{t("cut")}
</button>
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
onCopy([contextMenu.name]);
setContextMenu(null);
@@ -1729,7 +1729,7 @@ export function FileBrowser({
</button>
{clipboard && (
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
onPaste();
setContextMenu(null);
@@ -1741,7 +1741,7 @@ export function FileBrowser({
)}
{!resources.find(r => r.name === contextMenu.name)?.isDirectory && (
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
onDuplicate(contextMenu.name);
setContextMenu(null);
@@ -1753,7 +1753,7 @@ export function FileBrowser({
)}
{canShare(resources.find(r => r.name === contextMenu.name)) && (
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
const r = resources.find(res => res.name === contextMenu.name);
if (r) setShareTargetId(r.id);
@@ -1766,7 +1766,7 @@ export function FileBrowser({
)}
<div className="h-px bg-border my-1" />
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
onShowDetails(contextMenu.name);
setContextMenu(null);
@@ -1776,7 +1776,7 @@ export function FileBrowser({
{t("details")}
</button>
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
setRenameTarget(contextMenu.name);
setContextMenu(null);
@@ -1786,7 +1786,7 @@ export function FileBrowser({
{t("rename")}
</button>
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted text-destructive transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted text-destructive transition-colors text-start"
onClick={() => {
onDelete(contextMenu.name);
setContextMenu(null);
@@ -1808,7 +1808,7 @@ export function FileBrowser({
onClick={(e) => e.stopPropagation()}
>
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
setShowNewFolder(true);
setEmptyContextMenu(null);
@@ -1818,7 +1818,7 @@ export function FileBrowser({
{t("new_folder")}
</button>
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
setShowNewTextFile(true);
setEmptyContextMenu(null);
@@ -1828,7 +1828,7 @@ export function FileBrowser({
{t("new_text_file")}
</button>
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
fileInputRef.current?.click();
setEmptyContextMenu(null);
@@ -1838,7 +1838,7 @@ export function FileBrowser({
{t("upload")}
</button>
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
folderInputRef.current?.click();
setEmptyContextMenu(null);
@@ -1851,7 +1851,7 @@ export function FileBrowser({
<>
<div className="h-px bg-border my-1" />
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
onPaste();
setEmptyContextMenu(null);
@@ -1864,7 +1864,7 @@ export function FileBrowser({
)}
<div className="h-px bg-border my-1" />
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
onRefresh();
setEmptyContextMenu(null);
@@ -1895,7 +1895,7 @@ export function FileBrowser({
return (
<button
key={folder.id}
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-left"
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted transition-colors text-start"
onClick={() => {
onNavigate(folderPath, folder.id);
setBreadcrumbDropdown(null);
@@ -1926,7 +1926,7 @@ export function FileBrowser({
{/* Details sidebar */}
{showDetails && detailResource && (
<div role="complementary" aria-label={t("details")} className="w-64 border-l border-border bg-background p-4 overflow-y-auto shrink-0 hidden md:block">
<div role="complementary" aria-label={t("details")} className="w-64 border-s border-border bg-background p-4 overflow-y-auto shrink-0 hidden md:block">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium">{t("details")}</h3>
<button onClick={onToggleDetails} className="text-muted-foreground hover:text-foreground">