mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: translate the shell's resize, close, folder count and update labels, and announce a waiting update on the admin entry
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
// Shell labels come from the translations (keys in tests), and the admin
|
||||
// entry says that an update is waiting instead of a dot nobody hears.
|
||||
|
||||
vi.mock('../account-switcher', () => ({ AccountSwitcher: () => null }));
|
||||
vi.mock('@/components/plugins/plugin-slot', () => ({ PluginSlot: () => null }));
|
||||
vi.mock('@/components/keyboard-shortcuts-modal', () => ({ KeyboardShortcutsModal: () => null }));
|
||||
vi.mock('@/components/search/search-box', () => ({ SearchBox: () => null }));
|
||||
vi.mock('@/hooks/use-config', () => ({ useConfig: () => ({ appName: 'Mail' }) }));
|
||||
vi.mock('@/hooks/use-stalwart-admin', () => ({ useStalwartAdmin: () => true }));
|
||||
vi.mock('@/i18n/navigation', () => ({
|
||||
useRouter: () => ({ push: vi.fn(), back: vi.fn(), replace: vi.fn() }),
|
||||
usePathname: () => '/',
|
||||
Link: ({ children, href, ...rest }: { children: React.ReactNode; href: string }) => <a href={href} {...rest}>{children}</a>,
|
||||
}));
|
||||
|
||||
const { NavigationRail } = await import('../navigation-rail');
|
||||
const { MvTopBar } = await import('../mv-top-bar');
|
||||
const { ResizeHandle } = await import('../resize-handle');
|
||||
const { InlineAppView } = await import('../inline-app-view');
|
||||
const { useUpdateStore } = await import('@/stores/update-store');
|
||||
|
||||
beforeEach(() => {
|
||||
useUpdateStore.setState({
|
||||
startPolling: vi.fn(),
|
||||
status: { updateAvailable: true, severity: 'security', current: process.env.NEXT_PUBLIC_APP_VERSION ?? '', latest: '9.9.9' },
|
||||
} as never);
|
||||
});
|
||||
|
||||
describe('shell labels', () => {
|
||||
it('names the admin entry with the waiting update, in the rail, the tab bar and Mountain View', () => {
|
||||
const { unmount } = render(<NavigationRail collapsed />);
|
||||
expect(screen.getByRole('link', { name: 'admin, update_important' })).toBeInTheDocument();
|
||||
unmount();
|
||||
const bar = render(<NavigationRail orientation="horizontal" />);
|
||||
expect(screen.getByRole('link', { name: /update_important/ })).toBeInTheDocument();
|
||||
bar.unmount();
|
||||
render(<MvTopBar showMenuButton={false} />);
|
||||
expect(screen.getByRole('link', { name: 'admin, update_important' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('translates the resize handle and the inline app close button', () => {
|
||||
render(
|
||||
<>
|
||||
<ResizeHandle onResize={() => {}} />
|
||||
<InlineAppView apps={[]} activeAppId="x" onClose={() => {}} />
|
||||
</>
|
||||
);
|
||||
expect(screen.getByRole('separator', { name: 'resize' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'close' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { X } from '@/components/icons';
|
||||
import { IconButton } from '@/components/ui/icon-button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { InlineAppState } from '@/hooks/use-sidebar-apps';
|
||||
|
||||
@@ -12,6 +14,7 @@ interface InlineAppViewProps {
|
||||
}
|
||||
|
||||
export function InlineAppView({ apps, activeAppId, onClose, className }: InlineAppViewProps) {
|
||||
const tCommon = useTranslations('common');
|
||||
const activeApp = apps.find((a) => a.id === activeAppId);
|
||||
|
||||
return (
|
||||
@@ -19,13 +22,9 @@ export function InlineAppView({ apps, activeAppId, onClose, className }: InlineA
|
||||
{/* Header bar */}
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border bg-secondary/50 flex-shrink-0">
|
||||
<h3 className="text-sm font-medium truncate">{activeApp?.name}</h3>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1 rounded-md hover:bg-muted transition-colors text-muted-foreground hover:text-foreground"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
<IconButton label={tCommon("close")} onClick={onClose} className="text-muted-foreground">
|
||||
<X />
|
||||
</IconButton>
|
||||
</div>
|
||||
{/* Iframes - active one visible, rest hidden but alive */}
|
||||
<div className="flex-1 relative">
|
||||
|
||||
@@ -117,6 +117,7 @@ export function MvTopBar({
|
||||
}: MvTopBarProps) {
|
||||
const t = useTranslations("sidebar");
|
||||
const tSearch = useTranslations("advanced_search");
|
||||
const tShell = useTranslations("shell");
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const allInboxesReachable = useAllInboxesReachable();
|
||||
@@ -289,7 +290,7 @@ export function MvTopBar({
|
||||
href={`${getPathPrefix()}/admin`}
|
||||
className="relative grid place-items-center w-12 h-12 rounded-full text-muted-foreground hover:bg-muted hover:text-foreground transition-colors"
|
||||
title={t("admin")}
|
||||
aria-label={t("admin")}
|
||||
aria-label={hasUpdate ? `${t("admin")}, ${tShell(updateImportant ? "update_important" : "update_available")}` : t("admin")}
|
||||
>
|
||||
<Shield className="w-5 h-5" />
|
||||
{hasUpdate && (
|
||||
@@ -298,7 +299,7 @@ export function MvTopBar({
|
||||
"absolute top-3 end-3 w-2 h-2 rounded-full ring-2 ring-background",
|
||||
updateImportant ? "bg-red-500" : "bg-amber-500",
|
||||
)}
|
||||
aria-label={updateImportant ? "Important update available" : "Update available"}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</a>
|
||||
|
||||
@@ -253,6 +253,9 @@ export function NavigationRail({
|
||||
const startUpdatePolling = useUpdateStore((s) => s.startPolling);
|
||||
useEffect(() => { startUpdatePolling(); }, [startUpdatePolling]);
|
||||
const updateImportant = updateSeverity === 'security' || updateSeverity === 'deprecated';
|
||||
const tShell = useTranslations("shell");
|
||||
// Said with the admin entry; its dot alone is not announced.
|
||||
const updateLabel = hasUpdate ? tShell(updateImportant ? "update_important" : "update_available") : null;
|
||||
|
||||
// Account list for rail
|
||||
const accounts = useAccountStore((s) => s.accounts);
|
||||
@@ -501,11 +504,12 @@ export function NavigationRail({
|
||||
"absolute -top-0.5 -end-0.5 w-2 h-2 rounded-full ring-2 ring-background",
|
||||
updateImportant ? "bg-red-500" : "bg-amber-500",
|
||||
)}
|
||||
aria-label={updateImportant ? "Important update available" : "Update available"}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
<span className="text-[10px] font-medium leading-tight truncate max-w-full">{t("admin") || "Admin"}</span>
|
||||
{updateLabel && <span className="sr-only">{`, ${updateLabel}`}</span>}
|
||||
</a>
|
||||
)}
|
||||
|
||||
@@ -729,6 +733,7 @@ export function NavigationRail({
|
||||
adminActive ? "bg-primary/10 text-primary" : "text-muted-foreground hover:text-foreground hover:bg-muted",
|
||||
)}
|
||||
title={t("admin") || "Admin"}
|
||||
aria-label={updateLabel ? `${t("admin") || "Admin"}, ${updateLabel}` : undefined}
|
||||
aria-current={adminActive ? "page" : undefined}
|
||||
>
|
||||
<Shield className="w-[18px] h-[18px]" />
|
||||
@@ -738,7 +743,7 @@ export function NavigationRail({
|
||||
"absolute top-2 end-2 w-2 h-2 rounded-full ring-2 ring-background",
|
||||
updateImportant ? "bg-red-500" : "bg-amber-500",
|
||||
)}
|
||||
aria-label={updateImportant ? "Important update available" : "Update available"}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</a>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface ResizeHandleProps {
|
||||
@@ -17,6 +18,7 @@ interface ResizeHandleProps {
|
||||
const KEYBOARD_STEP = 10;
|
||||
|
||||
export function ResizeHandle({ onResizeStart, onResize, onResizeEnd, onDoubleClick, orientation = "vertical", className, testId }: ResizeHandleProps) {
|
||||
const t = useTranslations("shell");
|
||||
const handleRef = useRef<HTMLDivElement>(null);
|
||||
// Id of the pointer that started the drag, or null when idle. Only that
|
||||
// pointer may move or end the drag, so a second finger or button can't
|
||||
@@ -120,7 +122,7 @@ export function ResizeHandle({ onResizeStart, onResize, onResizeEnd, onDoubleCli
|
||||
ref={handleRef}
|
||||
role="separator"
|
||||
aria-orientation={isHorizontal ? "horizontal" : "vertical"}
|
||||
aria-label="Resize"
|
||||
aria-label={t("resize")}
|
||||
data-testid={testId}
|
||||
tabIndex={0}
|
||||
onPointerDown={handlePointerDown}
|
||||
|
||||
@@ -222,6 +222,7 @@ function SidebarRowCounts({
|
||||
isSelected: boolean;
|
||||
onUnreadClick?: () => void;
|
||||
}) {
|
||||
const tShell = useTranslations('shell');
|
||||
const showFolderTotalCount = useSettingsStore(s => s.showFolderTotalCount);
|
||||
const mountainView = useSettingsStore(s => s.interfaceLayout === 'mountain-view');
|
||||
// Per-folder override from the folder settings (#982).
|
||||
@@ -272,7 +273,7 @@ function SidebarRowCounts({
|
||||
}
|
||||
}}
|
||||
className={cn(unreadClass, "cursor-pointer hover:underline")}
|
||||
title={`${unreadCount} unread`}
|
||||
title={tShell("folder_unread", { count: unreadCount })}
|
||||
>
|
||||
{unreadCount}
|
||||
</span>
|
||||
@@ -284,7 +285,7 @@ function SidebarRowCounts({
|
||||
return (
|
||||
<span
|
||||
className="ms-2 flex-shrink-0 flex items-baseline gap-1"
|
||||
title={totalCount > 0 ? `${unreadCount} unread / ${totalCount} total` : `${unreadCount} unread`}
|
||||
title={totalCount > 0 ? tShell("folder_counts", { unread: unreadCount, total: totalCount }) : tShell("folder_unread", { count: unreadCount })}
|
||||
data-testid="folder-counts"
|
||||
data-unread={unreadCount}
|
||||
data-total={totalCount}
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "خيارات أخرى"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "تغيير الحجم",
|
||||
"update_available": "يتوفر تحديث",
|
||||
"update_important": "يتوفر تحديث مهم",
|
||||
"folder_unread": "{count} غير مقروءة",
|
||||
"folder_counts": "{unread} غير مقروءة، {total} إجمالًا"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "جارٍ تحميل الصفحة",
|
||||
"not_available_title": "هذه الصفحة غير متاحة",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Més opcions"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Canvia la mida",
|
||||
"update_available": "Hi ha una actualització",
|
||||
"update_important": "Hi ha una actualització important",
|
||||
"folder_unread": "{count} sense llegir",
|
||||
"folder_counts": "{unread} sense llegir, {total} en total"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "S'està carregant la pàgina",
|
||||
"not_available_title": "Aquesta pàgina no està disponible",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Další možnosti"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Změnit velikost",
|
||||
"update_available": "Je k dispozici aktualizace",
|
||||
"update_important": "Je k dispozici důležitá aktualizace",
|
||||
"folder_unread": "Nepřečteno: {count}",
|
||||
"folder_counts": "Nepřečteno: {unread}, celkem: {total}"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Načítání stránky",
|
||||
"not_available_title": "Tato stránka není k dispozici",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Flere muligheder"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Tilpas størrelse",
|
||||
"update_available": "Opdatering tilgængelig",
|
||||
"update_important": "Vigtig opdatering tilgængelig",
|
||||
"folder_unread": "{count} ulæste",
|
||||
"folder_counts": "{unread} ulæste, {total} i alt"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Indlæser side",
|
||||
"not_available_title": "Denne side er ikke tilgængelig",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Weitere Optionen"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Größe ändern",
|
||||
"update_available": "Update verfügbar",
|
||||
"update_important": "Wichtiges Update verfügbar",
|
||||
"folder_unread": "{count} ungelesen",
|
||||
"folder_counts": "{unread} ungelesen, {total} insgesamt"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Seite wird geladen",
|
||||
"not_available_title": "Diese Seite ist nicht verfügbar",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "More options"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Resize",
|
||||
"update_available": "Update available",
|
||||
"update_important": "Important update available",
|
||||
"folder_unread": "{count} unread",
|
||||
"folder_counts": "{unread} unread, {total} total"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Loading page",
|
||||
"not_available_title": "This page is not available",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Más opciones"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Cambiar tamaño",
|
||||
"update_available": "Actualización disponible",
|
||||
"update_important": "Actualización importante disponible",
|
||||
"folder_unread": "{count} sin leer",
|
||||
"folder_counts": "{unread} sin leer, {total} en total"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Cargando la página",
|
||||
"not_available_title": "Esta página no está disponible",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Rohkem valikuid"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Muuda suurust",
|
||||
"update_available": "Saadaval on värskendus",
|
||||
"update_important": "Saadaval on oluline värskendus",
|
||||
"folder_unread": "Lugemata: {count}",
|
||||
"folder_counts": "Lugemata: {unread}, kokku: {total}"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Lehe laadimine",
|
||||
"not_available_title": "See leht pole saadaval",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "گزینههای بیشتر"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "تغییر اندازه",
|
||||
"update_available": "بهروزرسانی موجود است",
|
||||
"update_important": "بهروزرسانی مهم موجود است",
|
||||
"folder_unread": "{count} خواندهنشده",
|
||||
"folder_counts": "{unread} خواندهنشده، {total} در مجموع"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "در حال بارگذاری صفحه",
|
||||
"not_available_title": "این صفحه در دسترس نیست",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Plus d'options"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Redimensionner",
|
||||
"update_available": "Mise à jour disponible",
|
||||
"update_important": "Mise à jour importante disponible",
|
||||
"folder_unread": "{count} non lus",
|
||||
"folder_counts": "{unread} non lus, {total} au total"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Chargement de la page",
|
||||
"not_available_title": "Cette page n'est pas disponible",
|
||||
|
||||
@@ -4581,6 +4581,13 @@
|
||||
"top_bar": {
|
||||
"more": "אפשרויות נוספות"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "שינוי גודל",
|
||||
"update_available": "יש עדכון",
|
||||
"update_important": "יש עדכון חשוב",
|
||||
"folder_unread": "{count} שלא נקראו",
|
||||
"folder_counts": "{unread} שלא נקראו, {total} בסך הכול"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "טוען את הדף",
|
||||
"not_available_title": "הדף הזה אינו זמין",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "További lehetőségek"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Átméretezés",
|
||||
"update_available": "Frissítés érhető el",
|
||||
"update_important": "Fontos frissítés érhető el",
|
||||
"folder_unread": "{count} olvasatlan",
|
||||
"folder_counts": "{unread} olvasatlan, összesen {total}"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Oldal betöltése",
|
||||
"not_available_title": "Ez az oldal nem érhető el",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Altre opzioni"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Ridimensiona",
|
||||
"update_available": "Aggiornamento disponibile",
|
||||
"update_important": "Aggiornamento importante disponibile",
|
||||
"folder_unread": "{count} non letti",
|
||||
"folder_counts": "{unread} non letti, {total} in totale"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Caricamento della pagina",
|
||||
"not_available_title": "Questa pagina non è disponibile",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "その他のオプション"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "サイズを変更",
|
||||
"update_available": "アップデートがあります",
|
||||
"update_important": "重要なアップデートがあります",
|
||||
"folder_unread": "未読 {count} 件",
|
||||
"folder_counts": "未読 {unread} 件 / 全 {total} 件"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "ページを読み込んでいます",
|
||||
"not_available_title": "このページは利用できません",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "추가 옵션"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "크기 조정",
|
||||
"update_available": "업데이트가 있습니다",
|
||||
"update_important": "중요한 업데이트가 있습니다",
|
||||
"folder_unread": "읽지 않음 {count}개",
|
||||
"folder_counts": "읽지 않음 {unread}개, 전체 {total}개"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "페이지를 불러오는 중",
|
||||
"not_available_title": "이 페이지를 사용할 수 없습니다",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Citas iespējas"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Mainīt izmēru",
|
||||
"update_available": "Pieejams atjauninājums",
|
||||
"update_important": "Pieejams svarīgs atjauninājums",
|
||||
"folder_unread": "Nelasīti: {count}",
|
||||
"folder_counts": "Nelasīti: {unread}, kopā: {total}"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Ielādē lapu",
|
||||
"not_available_title": "Šī lapa nav pieejama",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Бусад сонголт"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Хэмжээ өөрчлөх",
|
||||
"update_available": "Шинэчлэл гарсан",
|
||||
"update_important": "Чухал шинэчлэл гарсан",
|
||||
"folder_unread": "{count} уншаагүй",
|
||||
"folder_counts": "{unread} уншаагүй, нийт {total}"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Хуудсыг ачаалж байна",
|
||||
"not_available_title": "Энэ хуудас боломжгүй байна",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Flere alternativer"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Endre størrelse",
|
||||
"update_available": "Oppdatering tilgjengelig",
|
||||
"update_important": "Viktig oppdatering tilgjengelig",
|
||||
"folder_unread": "{count} uleste",
|
||||
"folder_counts": "{unread} uleste, {total} totalt"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Laster inn siden",
|
||||
"not_available_title": "Denne siden er ikke tilgjengelig",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Meer opties"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Formaat wijzigen",
|
||||
"update_available": "Update beschikbaar",
|
||||
"update_important": "Belangrijke update beschikbaar",
|
||||
"folder_unread": "{count} ongelezen",
|
||||
"folder_counts": "{unread} ongelezen, {total} totaal"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Pagina laden",
|
||||
"not_available_title": "Deze pagina is niet beschikbaar",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Więcej opcji"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Zmień rozmiar",
|
||||
"update_available": "Dostępna aktualizacja",
|
||||
"update_important": "Dostępna ważna aktualizacja",
|
||||
"folder_unread": "Nieprzeczytane: {count}",
|
||||
"folder_counts": "Nieprzeczytane: {unread}, łącznie: {total}"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Wczytywanie strony",
|
||||
"not_available_title": "Ta strona jest niedostępna",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Mais opções"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Redimensionar",
|
||||
"update_available": "Atualização disponível",
|
||||
"update_important": "Atualização importante disponível",
|
||||
"folder_unread": "{count} não lidas",
|
||||
"folder_counts": "{unread} não lidas, {total} no total"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Carregando a página",
|
||||
"not_available_title": "Esta página não está disponível",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Mai multe opțiuni"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Redimensionează",
|
||||
"update_available": "Actualizare disponibilă",
|
||||
"update_important": "Actualizare importantă disponibilă",
|
||||
"folder_unread": "{count} necitite",
|
||||
"folder_counts": "{unread} necitite, {total} în total"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Se încarcă pagina",
|
||||
"not_available_title": "Această pagină nu este disponibilă",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Другие действия"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Изменить размер",
|
||||
"update_available": "Доступно обновление",
|
||||
"update_important": "Доступно важное обновление",
|
||||
"folder_unread": "Непрочитанных: {count}",
|
||||
"folder_counts": "Непрочитанных: {unread}, всего: {total}"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Загрузка страницы",
|
||||
"not_available_title": "Эта страница недоступна",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Ďalšie možnosti"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Zmeniť veľkosť",
|
||||
"update_available": "K dispozícii je aktualizácia",
|
||||
"update_important": "K dispozícii je dôležitá aktualizácia",
|
||||
"folder_unread": "Neprečítané: {count}",
|
||||
"folder_counts": "Neprečítané: {unread}, spolu: {total}"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Načítava sa stránka",
|
||||
"not_available_title": "Táto stránka nie je k dispozícii",
|
||||
|
||||
@@ -41,6 +41,13 @@
|
||||
"top_bar": {
|
||||
"more": "ตัวเลือกเพิ่มเติม"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "ปรับขนาด",
|
||||
"update_available": "มีอัปเดต",
|
||||
"update_important": "มีอัปเดตสำคัญ",
|
||||
"folder_unread": "ยังไม่ได้อ่าน {count}",
|
||||
"folder_counts": "ยังไม่ได้อ่าน {unread} จากทั้งหมด {total}"
|
||||
},
|
||||
"date": {
|
||||
"today": "วันนี้",
|
||||
"yesterday": "เมื่อวานนี้",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Diğer seçenekler"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Yeniden boyutlandır",
|
||||
"update_available": "Güncelleme var",
|
||||
"update_important": "Önemli güncelleme var",
|
||||
"folder_unread": "{count} okunmamış",
|
||||
"folder_counts": "{unread} okunmamış, toplam {total}"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Sayfa yükleniyor",
|
||||
"not_available_title": "Bu sayfa kullanılamıyor",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "Інші дії"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "Змінити розмір",
|
||||
"update_available": "Доступне оновлення",
|
||||
"update_important": "Доступне важливе оновлення",
|
||||
"folder_unread": "Непрочитаних: {count}",
|
||||
"folder_counts": "Непрочитаних: {unread}, усього: {total}"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "Завантаження сторінки",
|
||||
"not_available_title": "Ця сторінка недоступна",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "更多選項"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "調整大小",
|
||||
"update_available": "有可用的更新",
|
||||
"update_important": "有重要更新",
|
||||
"folder_unread": "{count} 封未讀",
|
||||
"folder_counts": "{unread} 封未讀,共 {total} 封"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "正在載入頁面",
|
||||
"not_available_title": "此頁面無法使用",
|
||||
|
||||
@@ -4580,6 +4580,13 @@
|
||||
"top_bar": {
|
||||
"more": "更多选项"
|
||||
},
|
||||
"shell": {
|
||||
"resize": "调整大小",
|
||||
"update_available": "有可用更新",
|
||||
"update_important": "有重要更新",
|
||||
"folder_unread": "{count} 封未读",
|
||||
"folder_counts": "{unread} 封未读,共 {total} 封"
|
||||
},
|
||||
"plugin_pages": {
|
||||
"loading": "正在加载页面",
|
||||
"not_available_title": "此页面不可用",
|
||||
|
||||
Reference in New Issue
Block a user