fix(theme): navbar mobile sidebar should allow clicking dropdown parent link (#11053)

* try to repro + initial fix

* Use HTML button

* extract useItemCollapsible()

* Split DropdownNavbarItem into Mobile/Desktop subcomponents

* Add aria labels to button

* cleanup prop types

* aria label

* add translations

* cleanup types

* Split DefaultNavbarItem into Mobile/Desktop subcomponents

* revert change

* type fix
This commit is contained in:
Sébastien Lorber 2025-04-03 18:40:45 +02:00 committed by GitHub
parent a05629eeb5
commit 16e3002911
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 459 additions and 242 deletions

View file

@ -1181,19 +1181,40 @@ declare module '@theme/NavbarItem/DefaultNavbarItem' {
import type {ReactNode} from 'react';
import type {Props as NavbarNavLinkProps} from '@theme/NavbarItem/NavbarNavLink';
export type DesktopOrMobileNavBarItemProps = NavbarNavLinkProps & {
export type DefaultNavbarItemProps = NavbarNavLinkProps & {
readonly isDropdownItem?: boolean;
readonly className?: string;
readonly position?: 'left' | 'right';
};
export interface Props extends DesktopOrMobileNavBarItemProps {
// TODO Docusaurus v4, remove old type name
export type DesktopOrMobileNavBarItemProps = DefaultNavbarItemProps;
export interface Props extends DefaultNavbarItemProps {
readonly mobile?: boolean;
}
export default function DefaultNavbarItem(props: Props): ReactNode;
}
declare module '@theme/NavbarItem/DefaultNavbarItem/Mobile' {
import type {ReactNode} from 'react';
import type {DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
export interface Props extends DefaultNavbarItemProps {}
export default function DefaultNavbarItemMobile(props: Props): ReactNode;
}
declare module '@theme/NavbarItem/DefaultNavbarItem/Desktop' {
import type {ReactNode} from 'react';
import type {DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
export interface Props extends DefaultNavbarItemProps {}
export default function DefaultNavbarItemDesktop(props: Props): ReactNode;
}
declare module '@theme/NavbarItem/NavbarNavLink' {
import type {ReactNode} from 'react';
import type {Props as LinkProps} from '@docusaurus/Link';
@ -1216,19 +1237,40 @@ declare module '@theme/NavbarItem/DropdownNavbarItem' {
import type {Props as NavbarNavLinkProps} from '@theme/NavbarItem/NavbarNavLink';
import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem';
export type DesktopOrMobileNavBarItemProps = NavbarNavLinkProps & {
export type DropdownNavbarItemProps = NavbarNavLinkProps & {
readonly position?: 'left' | 'right';
readonly items: readonly LinkLikeNavbarItemProps[];
readonly className?: string;
};
export interface Props extends DesktopOrMobileNavBarItemProps {
// TODO Docusaurus v4, remove old type name
export type DesktopOrMobileNavBarItemProps = DropdownNavbarItemProps;
export interface Props extends DropdownNavbarItemProps {
readonly mobile?: boolean;
}
export default function DropdownNavbarItem(props: Props): ReactNode;
}
declare module '@theme/NavbarItem/DropdownNavbarItem/Mobile' {
import type {ReactNode} from 'react';
import type {DropdownNavbarItemProps} from '@theme/NavbarItem/DropdownNavbarItem';
export interface Props extends DropdownNavbarItemProps {}
export default function DropdownNavbarItemMobile(props: Props): ReactNode;
}
declare module '@theme/NavbarItem/DropdownNavbarItem/Desktop' {
import type {ReactNode} from 'react';
import type {DropdownNavbarItemProps} from '@theme/NavbarItem/DropdownNavbarItem';
export interface Props extends DropdownNavbarItemProps {}
export default function DropdownNavbarItemDesktop(props: Props): ReactNode;
}
declare module '@theme/NavbarItem/SearchNavbarItem' {
import type {ReactNode} from 'react';

View file

@ -1,66 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React, {type ReactNode} from 'react';
import clsx from 'clsx';
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
import type {
DesktopOrMobileNavBarItemProps,
Props,
} from '@theme/NavbarItem/DefaultNavbarItem';
function DefaultNavbarItemDesktop({
className,
isDropdownItem = false,
...props
}: DesktopOrMobileNavBarItemProps) {
const element = (
<NavbarNavLink
className={clsx(
isDropdownItem ? 'dropdown__link' : 'navbar__item navbar__link',
className,
)}
isDropdownLink={isDropdownItem}
{...props}
/>
);
if (isDropdownItem) {
return <li>{element}</li>;
}
return element;
}
function DefaultNavbarItemMobile({
className,
isDropdownItem,
...props
}: DesktopOrMobileNavBarItemProps) {
return (
<li className="menu__list-item">
<NavbarNavLink className={clsx('menu__link', className)} {...props} />
</li>
);
}
export default function DefaultNavbarItem({
mobile = false,
position, // Need to destructure position from props so that it doesn't get passed on.
...props
}: Props): ReactNode {
const Comp = mobile ? DefaultNavbarItemMobile : DefaultNavbarItemDesktop;
return (
<Comp
{...props}
activeClassName={
props.activeClassName ??
(mobile ? 'menu__link--active' : 'navbar__link--active')
}
/>
);
}

View file

@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React, {type ReactNode} from 'react';
import clsx from 'clsx';
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
import type {Props} from '@theme/NavbarItem/DefaultNavbarItem/Desktop';
export default function DefaultNavbarItemDesktop({
className,
isDropdownItem = false,
...props
}: Props): ReactNode {
const element = (
<NavbarNavLink
className={clsx(
isDropdownItem ? 'dropdown__link' : 'navbar__item navbar__link',
className,
)}
isDropdownLink={isDropdownItem}
{...props}
/>
);
if (isDropdownItem) {
return <li>{element}</li>;
}
return element;
}

View file

@ -0,0 +1,23 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React, {type ReactNode} from 'react';
import clsx from 'clsx';
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
import type {Props} from '@theme/NavbarItem/DefaultNavbarItem/Mobile';
export default function DefaultNavbarItemMobile({
className,
isDropdownItem,
...props
}: Props): ReactNode {
return (
<li className="menu__list-item">
<NavbarNavLink className={clsx('menu__link', className)} {...props} />
</li>
);
}

View file

@ -0,0 +1,28 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React, {type ReactNode} from 'react';
import DefaultNavbarItemMobile from '@theme/NavbarItem/DefaultNavbarItem/Mobile';
import DefaultNavbarItemDesktop from '@theme/NavbarItem/DefaultNavbarItem/Desktop';
import type {Props} from '@theme/NavbarItem/DefaultNavbarItem';
export default function DefaultNavbarItem({
mobile = false,
position, // Need to destructure position from props so that it doesn't get passed on.
...props
}: Props): ReactNode {
const Comp = mobile ? DefaultNavbarItemMobile : DefaultNavbarItemDesktop;
return (
<Comp
{...props}
activeClassName={
props.activeClassName ??
(mobile ? 'menu__link--active' : 'navbar__link--active')
}
/>
);
}

View file

@ -0,0 +1,86 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React, {useState, useRef, useEffect, type ReactNode} from 'react';
import clsx from 'clsx';
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
import NavbarItem from '@theme/NavbarItem';
import type {Props} from '@theme/NavbarItem/DropdownNavbarItem/Desktop';
export default function DropdownNavbarItemDesktop({
items,
position,
className,
onClick,
...props
}: Props): ReactNode {
const dropdownRef = useRef<HTMLDivElement>(null);
const [showDropdown, setShowDropdown] = useState(false);
useEffect(() => {
const handleClickOutside = (
event: MouseEvent | TouchEvent | FocusEvent,
) => {
if (
!dropdownRef.current ||
dropdownRef.current.contains(event.target as Node)
) {
return;
}
setShowDropdown(false);
};
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('touchstart', handleClickOutside);
document.addEventListener('focusin', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('touchstart', handleClickOutside);
document.removeEventListener('focusin', handleClickOutside);
};
}, [dropdownRef]);
return (
<div
ref={dropdownRef}
className={clsx('navbar__item', 'dropdown', 'dropdown--hoverable', {
'dropdown--right': position === 'right',
'dropdown--show': showDropdown,
})}>
<NavbarNavLink
aria-haspopup="true"
aria-expanded={showDropdown}
role="button"
// # hash permits to make the <a> tag focusable in case no link target
// See https://github.com/facebook/docusaurus/pull/6003
// There's probably a better solution though...
href={props.to ? undefined : '#'}
className={clsx('navbar__link', className)}
{...props}
onClick={props.to ? undefined : (e) => e.preventDefault()}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
setShowDropdown(!showDropdown);
}
}}>
{props.children ?? props.label}
</NavbarNavLink>
<ul className="dropdown__menu">
{items.map((childItemProps, i) => (
<NavbarItem
isDropdownItem
activeClassName="dropdown__link--active"
{...childItemProps}
key={i}
/>
))}
</ul>
</div>
);
}

View file

@ -0,0 +1,166 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React, {useEffect, type ReactNode, type ComponentProps} from 'react';
import clsx from 'clsx';
import {
isRegexpStringMatch,
useCollapsible,
Collapsible,
} from '@docusaurus/theme-common';
import {isSamePath, useLocalPathname} from '@docusaurus/theme-common/internal';
import {translate} from '@docusaurus/Translate';
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
import NavbarItem, {type LinkLikeNavbarItemProps} from '@theme/NavbarItem';
import type {Props} from '@theme/NavbarItem/DropdownNavbarItem/Mobile';
import styles from './styles.module.css';
function isItemActive(
item: LinkLikeNavbarItemProps,
localPathname: string,
): boolean {
if (isSamePath(item.to, localPathname)) {
return true;
}
if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) {
return true;
}
if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) {
return true;
}
return false;
}
function containsActiveItems(
items: readonly LinkLikeNavbarItemProps[],
localPathname: string,
): boolean {
return items.some((item) => isItemActive(item, localPathname));
}
function CollapseButton({
collapsed,
onClick,
}: {
collapsed: boolean;
onClick: ComponentProps<'button'>['onClick'];
}) {
return (
<button
aria-label={
collapsed
? translate({
id: 'theme.navbar.mobileDropdown.collapseButton.expandAriaLabel',
message: 'Expand the dropdown',
description:
'The ARIA label of the button to expand the mobile dropdown navbar item',
})
: translate({
id: 'theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel',
message: 'Collapse the dropdown',
description:
'The ARIA label of the button to collapse the mobile dropdown navbar item',
})
}
aria-expanded={!collapsed}
type="button"
className="clean-btn menu__caret"
onClick={onClick}
/>
);
}
function useItemCollapsible({active}: {active: boolean}) {
const {collapsed, toggleCollapsed, setCollapsed} = useCollapsible({
initialState: () => !active,
});
// Expand if any item active after a navigation
useEffect(() => {
if (active) {
setCollapsed(false);
}
}, [active, setCollapsed]);
return {
collapsed,
toggleCollapsed,
};
}
export default function DropdownNavbarItemMobile({
items,
className,
position, // Need to destructure position from props so that it doesn't get passed on.
onClick,
...props
}: Props): ReactNode {
const localPathname = useLocalPathname();
const isActive = isSamePath(props.to, localPathname);
const containsActive = containsActiveItems(items, localPathname);
const {collapsed, toggleCollapsed} = useItemCollapsible({
active: isActive || containsActive,
});
// # hash permits to make the <a> tag focusable in case no link target
// See https://github.com/facebook/docusaurus/pull/6003
// There's probably a better solution though...
const href = props.to ? undefined : '#';
return (
<li
className={clsx('menu__list-item', {
'menu__list-item--collapsed': collapsed,
})}>
<div
className={clsx('menu__list-item-collapsible', {
'menu__list-item-collapsible--active': isActive,
})}>
<NavbarNavLink
role="button"
className={clsx(
styles.dropdownNavbarItemMobile,
'menu__link menu__link--sublist',
className,
)}
href={href}
{...props}
onClick={(e) => {
// Prevent navigation when link is "#"
if (href === '#') {
e.preventDefault();
}
// Otherwise we let navigation eventually happen, and/or collapse
toggleCollapsed();
}}>
{props.children ?? props.label}
</NavbarNavLink>
<CollapseButton
collapsed={collapsed}
onClick={(e) => {
e.preventDefault();
toggleCollapsed();
}}
/>
</div>
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
{items.map((childItemProps, i) => (
<NavbarItem
mobile
isDropdownItem
onClick={onClick}
activeClassName="menu__link--active"
{...childItemProps}
key={i}
/>
))}
</Collapsible>
</li>
);
}

View file

@ -5,178 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/
import React, {useState, useRef, useEffect, type ReactNode} from 'react';
import clsx from 'clsx';
import {
isRegexpStringMatch,
useCollapsible,
Collapsible,
} from '@docusaurus/theme-common';
import {isSamePath, useLocalPathname} from '@docusaurus/theme-common/internal';
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
import NavbarItem, {type LinkLikeNavbarItemProps} from '@theme/NavbarItem';
import type {
DesktopOrMobileNavBarItemProps,
Props,
} from '@theme/NavbarItem/DropdownNavbarItem';
import styles from './styles.module.css';
function isItemActive(
item: LinkLikeNavbarItemProps,
localPathname: string,
): boolean {
if (isSamePath(item.to, localPathname)) {
return true;
}
if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) {
return true;
}
if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) {
return true;
}
return false;
}
function containsActiveItems(
items: readonly LinkLikeNavbarItemProps[],
localPathname: string,
): boolean {
return items.some((item) => isItemActive(item, localPathname));
}
function DropdownNavbarItemDesktop({
items,
position,
className,
onClick,
...props
}: DesktopOrMobileNavBarItemProps) {
const dropdownRef = useRef<HTMLDivElement>(null);
const [showDropdown, setShowDropdown] = useState(false);
useEffect(() => {
const handleClickOutside = (
event: MouseEvent | TouchEvent | FocusEvent,
) => {
if (
!dropdownRef.current ||
dropdownRef.current.contains(event.target as Node)
) {
return;
}
setShowDropdown(false);
};
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('touchstart', handleClickOutside);
document.addEventListener('focusin', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('touchstart', handleClickOutside);
document.removeEventListener('focusin', handleClickOutside);
};
}, [dropdownRef]);
return (
<div
ref={dropdownRef}
className={clsx('navbar__item', 'dropdown', 'dropdown--hoverable', {
'dropdown--right': position === 'right',
'dropdown--show': showDropdown,
})}>
<NavbarNavLink
aria-haspopup="true"
aria-expanded={showDropdown}
role="button"
// # hash permits to make the <a> tag focusable in case no link target
// See https://github.com/facebook/docusaurus/pull/6003
// There's probably a better solution though...
href={props.to ? undefined : '#'}
className={clsx('navbar__link', className)}
{...props}
onClick={props.to ? undefined : (e) => e.preventDefault()}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
setShowDropdown(!showDropdown);
}
}}>
{props.children ?? props.label}
</NavbarNavLink>
<ul className="dropdown__menu">
{items.map((childItemProps, i) => (
<NavbarItem
isDropdownItem
activeClassName="dropdown__link--active"
{...childItemProps}
key={i}
/>
))}
</ul>
</div>
);
}
function DropdownNavbarItemMobile({
items,
className,
position, // Need to destructure position from props so that it doesn't get passed on.
onClick,
...props
}: DesktopOrMobileNavBarItemProps) {
const localPathname = useLocalPathname();
const containsActive = containsActiveItems(items, localPathname);
const {collapsed, toggleCollapsed, setCollapsed} = useCollapsible({
initialState: () => !containsActive,
});
// Expand/collapse if any item active after a navigation
useEffect(() => {
if (containsActive) {
setCollapsed(!containsActive);
}
}, [localPathname, containsActive, setCollapsed]);
return (
<li
className={clsx('menu__list-item', {
'menu__list-item--collapsed': collapsed,
})}>
<NavbarNavLink
role="button"
className={clsx(
styles.dropdownNavbarItemMobile,
'menu__link menu__link--sublist menu__link--sublist-caret',
className,
)}
// # hash permits to make the <a> tag focusable in case no link target
// See https://github.com/facebook/docusaurus/pull/6003
// There's probably a better solution though...
href={props.to ? undefined : '#'}
{...props}
onClick={(e) => {
e.preventDefault();
toggleCollapsed();
}}>
{props.children ?? props.label}
</NavbarNavLink>
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
{items.map((childItemProps, i) => (
<NavbarItem
mobile
isDropdownItem
onClick={onClick}
activeClassName="menu__link--active"
{...childItemProps}
key={i}
/>
))}
</Collapsible>
</li>
);
}
import React, {type ReactNode} from 'react';
import DropdownNavbarItemMobile from '@theme/NavbarItem/DropdownNavbarItem/Mobile';
import DropdownNavbarItemDesktop from '@theme/NavbarItem/DropdownNavbarItem/Desktop';
import type {Props} from '@theme/NavbarItem/DropdownNavbarItem';
export default function DropdownNavbarItem({
mobile = false,

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " في {date}",
"theme.lastUpdated.byUser": " بواسطة {user}",
"theme.lastUpdated.lastUpdatedAtBy": "آخر تحديث{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "اللغات",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "→ العودة إلى القائمة الرئيسية",
"theme.navbar.mobileVersionsDropdown.label": "إصدارات",

View file

@ -144,6 +144,10 @@
"theme.lastUpdated.byUser___DESCRIPTION": "The words used to describe by who the page has been last updated",
"theme.lastUpdated.lastUpdatedAtBy": "Last updated{atDate}{byUser}",
"theme.lastUpdated.lastUpdatedAtBy___DESCRIPTION": "The sentence used to display when a page has been last updated, and by who",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel___DESCRIPTION": "The ARIA label of the button to collapse the mobile dropdown navbar item",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel___DESCRIPTION": "The ARIA label of the button to expand the mobile dropdown navbar item",
"theme.navbar.mobileLanguageDropdown.label": "Languages",
"theme.navbar.mobileLanguageDropdown.label___DESCRIPTION": "The label for the mobile language switcher dropdown",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Back to main menu",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " на {date}",
"theme.lastUpdated.byUser": " от {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Последно обновено{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Езици",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Назад към главното меню",
"theme.navbar.mobileVersionsDropdown.label": "Версии",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date} তারিখে",
"theme.lastUpdated.byUser": "{user} দ্বারা",
"theme.lastUpdated.lastUpdatedAtBy": "সর্বশেষ সংষ্করণ{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Languages",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← মেন মেনুতে যান",
"theme.navbar.mobileVersionsDropdown.label": "Versions",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date}",
"theme.lastUpdated.byUser": " od {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Naposledy aktualizováno{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Languages",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Zpět na hlavní menu",
"theme.navbar.mobileVersionsDropdown.label": "Versions",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " den {date}",
"theme.lastUpdated.byUser": " af {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Senest opdateret{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Languages",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Back to main menu",
"theme.navbar.mobileVersionsDropdown.label": "Versions",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " am {date}",
"theme.lastUpdated.byUser": " von {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Letztes Update{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Sprachen",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Zurück zum Hauptmenü",
"theme.navbar.mobileVersionsDropdown.label": "Versionen",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " en {date}",
"theme.lastUpdated.byUser": " por {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Última actualización{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Idiomas",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Volver al menú principal",
"theme.navbar.mobileVersionsDropdown.label": "Versiones",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date}",
"theme.lastUpdated.byUser": " {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Viimane uuendus{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Keeled",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Tagasi põhi menüüsse",
"theme.navbar.mobileVersionsDropdown.label": "Versioonid",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " در تاریخ {date}",
"theme.lastUpdated.byUser": " توسط {user}",
"theme.lastUpdated.lastUpdatedAtBy": "آخرین به روز رسانی{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Languages",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "→ بازگشت به منو اصلی",
"theme.navbar.mobileVersionsDropdown.label": "نسخه‌ها",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " noong {date}",
"theme.lastUpdated.byUser": " ni {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Huling inapdeyt{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Languages",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Back to main menu",
"theme.navbar.mobileVersionsDropdown.label": "Versions",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " le {date}",
"theme.lastUpdated.byUser": " par {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Dernière mise à jour{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Langues",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Retour au menu principal",
"theme.navbar.mobileVersionsDropdown.label": "Versions",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " בתאריך {date}",
"theme.lastUpdated.byUser": " על ידי {user}",
"theme.lastUpdated.lastUpdatedAtBy": "עודכן{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Languages",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← חזרה לתפריט הראשי",
"theme.navbar.mobileVersionsDropdown.label": "Versions",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date} पर",
"theme.lastUpdated.byUser": " {user} द्वारा",
"theme.lastUpdated.lastUpdatedAtBy": "आखरी अपडेट{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Languages",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← मुख्य मेनू में वापस जाएं",
"theme.navbar.mobileVersionsDropdown.label": "Versions",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date} napján",
"theme.lastUpdated.byUser": " {user} által",
"theme.lastUpdated.lastUpdatedAtBy": "Utolsó frissítés{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Nyelvek",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Vissza a főmenühöz",
"theme.navbar.mobileVersionsDropdown.label": "Verziók",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " pada {date}",
"theme.lastUpdated.byUser": " oleh {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Diperbaharui{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Bahasa",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Kembali ke menu utama",
"theme.navbar.mobileVersionsDropdown.label": "Versi",

View file

@ -73,6 +73,8 @@
"theme.lastUpdated.atDate": " þann {date}",
"theme.lastUpdated.byUser": " af {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Seinast uppfært{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Tungumál",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Til baka á aðal valmynd",
"theme.navbar.mobileVersionsDropdown.label": "Útgáfur",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " il {date}",
"theme.lastUpdated.byUser": " da {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Ultimo aggiornamento{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Linguaggio",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Indietro al menu principale",
"theme.navbar.mobileVersionsDropdown.label": "Versioni",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": "{date}に",
"theme.lastUpdated.byUser": "{user}が",
"theme.lastUpdated.lastUpdatedAtBy": "{atDate}{byUser}最終更新",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "他の言語",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← メインメニューに戻る",
"theme.navbar.mobileVersionsDropdown.label": "他のバージョン",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date}에",
"theme.lastUpdated.byUser": " {user}가",
"theme.lastUpdated.lastUpdatedAtBy": "최종 수정: {atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "언어",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← 메인 메뉴로 돌아가기",
"theme.navbar.mobileVersionsDropdown.label": "버전",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " den {date}",
"theme.lastUpdated.byUser": " av {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Sist oppdatert{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Språk",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Tilbake til hovedmenyen",
"theme.navbar.mobileVersionsDropdown.label": "Versjoner",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " op {date}",
"theme.lastUpdated.byUser": " door {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Laatst bijgewerkt{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Talen",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Terug naar het hoofdmenu",
"theme.navbar.mobileVersionsDropdown.label": "Versies",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " dnia {date}",
"theme.lastUpdated.byUser": " przez {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Ostatnia aktualizacja{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Języki",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Wróć do menu głównego",
"theme.navbar.mobileVersionsDropdown.label": "Wersje",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " em {date}",
"theme.lastUpdated.byUser": " por {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Última atualização {atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Linguagens",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Voltar para o menu principal",
"theme.navbar.mobileVersionsDropdown.label": "Versões",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " a {date}",
"theme.lastUpdated.byUser": " por {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Última atualização{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Languages",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Back to main menu",
"theme.navbar.mobileVersionsDropdown.label": "Versions",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date}",
"theme.lastUpdated.byUser": " от {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Последнее обновление{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Языки",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Перейти к главному меню",
"theme.navbar.mobileVersionsDropdown.label": "Версии",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date}",
"theme.lastUpdated.byUser": " {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Nazadnje posodobil {byUser}, {atDate}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Jeziki",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Nazaj na glavni meni",
"theme.navbar.mobileVersionsDropdown.label": "Verzije",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " на {date}",
"theme.lastUpdated.byUser": " од {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Последња измена {atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Languages",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Назад на главни мени",
"theme.navbar.mobileVersionsDropdown.label": "Верзије",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " den {date}",
"theme.lastUpdated.byUser": " av {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Senast uppdaterad{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Språk",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Tillbaka till huvudmeny",
"theme.navbar.mobileVersionsDropdown.label": "Versioner",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date}",
"theme.lastUpdated.byUser": " {user} tarapyndan",
"theme.lastUpdated.lastUpdatedAtBy": "Soňky täzelenme{atDate}{byUser} tarapyndan",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Diller",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Esasy menýua geç",
"theme.navbar.mobileVersionsDropdown.label": "Wersiýalar",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date} tarihinde",
"theme.lastUpdated.byUser": " {user} tarafından",
"theme.lastUpdated.lastUpdatedAtBy": "En son {atDate} {byUser} güncellendi",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Diller",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Ana menüye dön",
"theme.navbar.mobileVersionsDropdown.label": "Versiyonlar",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " {date}",
"theme.lastUpdated.byUser": " від {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Останнє оновлення{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Мови",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Перейти до головного меню",
"theme.navbar.mobileVersionsDropdown.label": "Версії",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": " vào {date}",
"theme.lastUpdated.byUser": " bởi {user}",
"theme.lastUpdated.lastUpdatedAtBy": "Cập nhật lần cuối{atDate}{byUser}",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "Ngôn ngữ",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Trở lại menu chính",
"theme.navbar.mobileVersionsDropdown.label": "Phiên bản",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": "于 {date} ",
"theme.lastUpdated.byUser": "由 {user} ",
"theme.lastUpdated.lastUpdatedAtBy": "最后{byUser}{atDate}更新",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "选择语言",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← 回到主菜单",
"theme.navbar.mobileVersionsDropdown.label": "选择版本",

View file

@ -72,6 +72,8 @@
"theme.lastUpdated.atDate": "於 {date} ",
"theme.lastUpdated.byUser": "由 {user} ",
"theme.lastUpdated.lastUpdatedAtBy": "最後{byUser}{atDate}更新",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "選擇語言",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← 回到主選單",
"theme.navbar.mobileVersionsDropdown.label": "選擇版本",