diff --git a/fluxer_app/src/features/app/components/layout/GuildHeader.tsx b/fluxer_app/src/features/app/components/layout/GuildHeader.tsx index e5d5c5ad7..f9cbe664b 100644 --- a/fluxer_app/src/features/app/components/layout/GuildHeader.tsx +++ b/fluxer_app/src/features/app/components/layout/GuildHeader.tsx @@ -16,6 +16,7 @@ import {GuildHeaderBottomSheet} from '@app/features/guild/components/bottomsheet import {GuildBadge} from '@app/features/guild/components/GuildBadge'; import {GuildHeaderPopout} from '@app/features/guild/components/popouts/GuildHeaderPopout'; import type {Guild} from '@app/features/guild/models/Guild'; +import {useRecoveringBackgroundImageURL} from '@app/features/messaging/hooks/useImageRecovery'; import {GuildContextMenu} from '@app/features/ui/action_menu/GuildContextMenu'; import * as ContextMenuCommands from '@app/features/ui/commands/ContextMenuCommands'; import MobileLayout from '@app/features/ui/state/MobileLayout'; @@ -74,6 +75,7 @@ export const GuildHeader = observer(({guild, banner}: {guild: Guild; banner: Gui [guild], ); const guildNameRef = useRef(null); + const bannerImageURL = useRecoveringBackgroundImageURL(banner.imageUrl ?? ''); const badgeVisible = resolveGuildBadgeVisible(guild.features); const bannerPlacement = resolveSkeletonBannerPlacement(banner.hasBanner, banner.isDetached); useSkeletonLayoutReport(() => { @@ -98,7 +100,7 @@ export const GuildHeader = observer(({guild, banner}: {guild: Guild; banner: Gui style={{ height: banner.bannerHeight, y: banner.imageShift, - backgroundImage: `url(${banner.imageUrl})`, + backgroundImage: bannerImageURL == null ? undefined : `url(${bannerImageURL})`, }} data-flx="app.guild-header.banner-image" /> diff --git a/fluxer_app/src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx b/fluxer_app/src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx index 139fdb93b..b136b8249 100644 --- a/fluxer_app/src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx +++ b/fluxer_app/src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx @@ -29,6 +29,7 @@ import GuildReadState from '@app/features/guild/state/GuildReadState'; import {getInitialsFromName, truncateInitials} from '@app/features/guild/utils/GuildInitialsUtils'; import {MENTION_COUNT_ARIA_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors'; import {isKeyboardActivationKey} from '@app/features/input/utils/KeyboardUtils'; +import {useRecoveringBackgroundImageURL} from '@app/features/messaging/hooks/useImageRecovery'; import {useLocation} from '@app/features/platform/components/router/RouterReact'; import Theme from '@app/features/theme/state/Theme'; import {Edge} from '@app/features/ui/AxisOrientation'; @@ -766,8 +767,8 @@ interface MiniGuildIconProps { } const MiniGuildIcon = observer(({guild}: MiniGuildIconProps) => { - const iconURL = AvatarSourceUtils.getGuildIconURL(guild, false); - if (iconURL !== '') { + const iconURL = useRecoveringBackgroundImageURL(AvatarSourceUtils.getGuildIconURL(guild, false)); + if (iconURL != null) { return ( (null); const [bottomSheetOpen, setBottomSheetOpen] = useState(false); - useEffect(() => ImageCacheUtils.pinImage(iconURL), [iconURL]); + const paintableIconURL = useRecoveringBackgroundImageURL(iconURL); useEffect(() => { if (!isAnimatableIcon) return; if (!isHovering && !contextMenuOpen) return; @@ -121,7 +122,7 @@ export function useGuildListItemInteraction({ const handleCloseBottomSheet = useCallback(() => setBottomSheetOpen(false), []); const shouldPlayAnimated = isAnimatableIcon && (isHovering || contextMenuOpen) && loadedAnimatedURL === hoverIconURL; const displayedIconURL = resolveDisplayedGuildIconURL({hoverIconURL, iconURL, shouldPlayAnimated}); - const backgroundImage = resolveGuildIconBackgroundImage(iconURL, displayedIconURL); + const backgroundImage = resolveGuildIconBackgroundImage(paintableIconURL, displayedIconURL); return { hoverRef, isHovering, diff --git a/fluxer_app/src/features/auth/AccountAvatarUtils.ts b/fluxer_app/src/features/auth/AccountAvatarUtils.ts index 4ac72cbc2..d4729836d 100644 --- a/fluxer_app/src/features/auth/AccountAvatarUtils.ts +++ b/fluxer_app/src/features/auth/AccountAvatarUtils.ts @@ -1,5 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later +import {runtimeInstanceKey} from '@app/features/app/state/InstanceSnapshotStore'; import type {Account} from '@app/features/platform/state/AuthSession'; import * as AvatarUtils from '@app/features/user/utils/AvatarUtils'; import {getDefaultAvatarAssetURL} from '@app/features/user/utils/DefaultAvatars'; @@ -16,12 +17,25 @@ function defaultAccountAvatarURL(account: Account, size?: MediaProxyImageSize): function resolveAccountAvatarURL(account: Account, size?: MediaProxyImageSize): string { const avatar = account.userData?.avatar ?? null; - const mediaEndpoint = account.instance?.mediaEndpoint ?? null; - if (avatar == null || avatar.length === 0 || mediaEndpoint == null || mediaEndpoint.length === 0) { + const instance = account.instance ?? null; + const mediaEndpoint = instance?.mediaEndpoint ?? null; + const instanceKey = instance == null ? null : runtimeInstanceKey(instance); + if ( + avatar == null || + avatar.length === 0 || + mediaEndpoint == null || + mediaEndpoint.length === 0 || + instanceKey == null + ) { return defaultAccountAvatarURL(account, size); } try { - return AvatarUtils.getUserAvatarURLWithProxy({id: account.userId, avatar}, mediaEndpoint, false, size); + return AvatarUtils.getUserAvatarURLForInstance( + {id: account.userId, avatar}, + {mediaEndpoint, instanceKey}, + false, + size, + ); } catch { return defaultAccountAvatarURL(account, size); } diff --git a/fluxer_app/src/features/auth/state/AccountUserDataSync.ts b/fluxer_app/src/features/auth/state/AccountUserDataSync.ts new file mode 100644 index 000000000..d264cfc25 --- /dev/null +++ b/fluxer_app/src/features/auth/state/AccountUserDataSync.ts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import accountStorage from '@app/features/auth/state/AccountStorage'; +import type {UserData} from '@app/features/auth/state/AccountStorageContract'; +import Accounts from '@app/features/auth/state/Accounts'; +import {Logger} from '@app/features/platform/utils/AppLogger'; + +const logger = new Logger('AccountUserDataSync'); + +export interface AccountUserIdentity { + readonly username: string; + readonly discriminator: string; + readonly global_name?: string | null; + readonly email?: string | null; + readonly avatar?: string | null; +} + +export function syncAccountUserData(accountKey: string, user: AccountUserIdentity): void { + const previous = Accounts.getAccount(accountKey)?.userData; + const userData: UserData = { + username: user.username, + discriminator: user.discriminator, + globalName: user.global_name === undefined ? previous?.globalName : user.global_name, + email: user.email === undefined ? previous?.email : (user.email ?? undefined), + avatar: user.avatar ?? undefined, + }; + void accountStorage.updateAccountUserData(accountKey, userData).catch((error) => { + logger.warn(`Failed to persist account user data for ${accountKey}`, error); + }); + Accounts.updateAccountUserData(accountKey, userData); +} diff --git a/fluxer_app/src/features/gateway/events/GatewayReady.ts b/fluxer_app/src/features/gateway/events/GatewayReady.ts index 293aaa787..5499a80ab 100644 --- a/fluxer_app/src/features/gateway/events/GatewayReady.ts +++ b/fluxer_app/src/features/gateway/events/GatewayReady.ts @@ -2,8 +2,7 @@ import {startDomainMigrationTrigger} from '@app/features/app/domain_migration/DomainMigrationTrigger'; import Initialization from '@app/features/app/state/Initialization'; -import accountStorage from '@app/features/auth/state/AccountStorage'; -import Accounts from '@app/features/auth/state/Accounts'; +import {syncAccountUserData} from '@app/features/auth/state/AccountUserDataSync'; import Authentication from '@app/features/auth/state/Authentication'; import AuthSession from '@app/features/auth/state/AuthSession'; import ChannelPins from '@app/features/channel/state/ChannelPins'; @@ -152,18 +151,7 @@ function handleReadyInternal(data: ReadyPayload, context: GatewayHandlerContext, RtcRegions.setRegions(data.rtc_regions ?? []); Users.handleGatewayReady(accountKey, data.user); Users.cacheUsers(data.users ?? []); - const user = data.user; - const userData = { - username: user.username, - discriminator: user.discriminator, - globalName: user.global_name, - email: user.email ?? undefined, - avatar: user.avatar ?? undefined, - }; - void accountStorage.updateAccountUserData(accountKey, userData).catch((error) => { - logger.warn(`Failed to persist account user data for ${accountKey}`, error); - }); - Accounts.updateAccountUserData(accountKey, userData); + syncAccountUserData(accountKey, data.user); Authentication.handleGatewayReady({user: data.user}); Guilds.handleGatewayReady({guilds}); UserSettings.handleGatewayReady(data.user_settings); diff --git a/fluxer_app/src/features/guild/components/popouts/GuildIcon.tsx b/fluxer_app/src/features/guild/components/popouts/GuildIcon.tsx index 9c4ac75dc..f964fc966 100644 --- a/fluxer_app/src/features/guild/components/popouts/GuildIcon.tsx +++ b/fluxer_app/src/features/guild/components/popouts/GuildIcon.tsx @@ -4,6 +4,7 @@ import {useHover} from '@app/features/app/hooks/useHover'; import {useShouldAnimate} from '@app/features/app/hooks/useShouldAnimate'; import styles from '@app/features/guild/components/popouts/GuildIcon.module.css'; import {getGuildIconDisplayInitials, getInitialsLength} from '@app/features/guild/utils/GuildInitialsUtils'; +import {useRecoveringBackgroundImageURL} from '@app/features/messaging/hooks/useImageRecovery'; import * as ImageCacheUtils from '@app/features/messaging/utils/ImageCacheUtils'; import {remFromPx} from '@app/features/theme/layout/RemFromPx'; import * as AvatarUtils from '@app/features/user/utils/AvatarUtils'; @@ -52,8 +53,9 @@ export const GuildIcon = observer(function GuildIcon({ if (!animationAllowed || hoverIconUrl == null || isAnimatedLoaded) return; return ImageCacheUtils.loadImage(hoverIconUrl, () => setLoadedAnimatedUrl(hoverIconUrl)); }, [animationAllowed, hoverIconUrl, isAnimatedLoaded]); - const activeUrl = animationAllowed && isAnimatedLoaded ? hoverIconUrl : iconUrl; - const paintedUrl = activeUrl != null && activeUrl.length > 0 ? activeUrl : null; + const paintableIconUrl = useRecoveringBackgroundImageURL(iconUrl ?? ''); + const activeUrl = animationAllowed && isAnimatedLoaded ? hoverIconUrl : paintableIconUrl; + const paintedUrl = paintableIconUrl != null && activeUrl != null && activeUrl.length > 0 ? activeUrl : null; const styleVars: GuildIconStyleVars = {}; if (sizePx != null) { styleVars['--guild-icon-size'] = remFromPx(sizePx); diff --git a/fluxer_app/src/features/i18n/locales/ar/messages.po b/fluxer_app/src/features/i18n/locales/ar/messages.po index 9aed03cdb..6bb312eee 100644 --- a/fluxer_app/src/features/i18n/locales/ar/messages.po +++ b/fluxer_app/src/features/i18n/locales/ar/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, zero {# سنة} one {سنة واحدة} two { #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "حالة {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} دقيقة" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "مجلد {folderName}" @@ -5563,7 +5563,7 @@ msgstr "متوفر في تطبيق سطح المكتب." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "طي" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "طي {folderName}" @@ -8921,7 +8921,7 @@ msgstr "طي الرسائل غير المقروءة في {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "مطوي" @@ -15276,7 +15276,7 @@ msgstr "توسيع الرسائل غير المقروءة في {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "موسّعة" @@ -16217,7 +16217,7 @@ msgstr "صوت مركّز" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "مجلد" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "فتح المجتمع" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "فتح قائمة المجتمع لـ {guildName}" @@ -37248,7 +37248,7 @@ msgstr "الصور الرمزية في مؤشر الكتابة" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "مؤشر الكتابة" @@ -37967,7 +37967,7 @@ msgstr "تم إلغاء تثبيت المحادثة الجماعية" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "التفعيل بالصوت" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "نشاط صوتي" diff --git a/fluxer_app/src/features/i18n/locales/bg/messages.po b/fluxer_app/src/features/i18n/locales/bg/messages.po index f5aa70c72..cf46d91d9 100644 --- a/fluxer_app/src/features/i18n/locales/bg/messages.po +++ b/fluxer_app/src/features/i18n/locales/bg/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# година} other {# години}} #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Статус: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} минути" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Папка {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Налично в настолното приложение." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Свиване" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Свиване на {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Свиване на непрочетените съобщения за { #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "свито" @@ -15276,7 +15276,7 @@ msgstr "Разгъване на непрочетените съобщения з #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "разгънато" @@ -16217,7 +16217,7 @@ msgstr "Фокусиран глас" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Папка" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Отваряне на общността" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Отваряне на менюто на общността {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Аватари при писане" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Индикатор за писане" @@ -37967,7 +37967,7 @@ msgstr "Груповото DM е откачено" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Гласово активиране" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "гласова активност" diff --git a/fluxer_app/src/features/i18n/locales/cs/messages.po b/fluxer_app/src/features/i18n/locales/cs/messages.po index 79280dcd7..c0eb8c44f 100644 --- a/fluxer_app/src/features/i18n/locales/cs/messages.po +++ b/fluxer_app/src/features/i18n/locales/cs/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# rok} few {# roky} many {# roku} other #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Stav: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minut" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Složka {folderName}" @@ -5563,7 +5563,7 @@ msgstr "K dispozici v desktopové aplikaci." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Sbalit" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Sbalit složku {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Sbalit nepřečtené zprávy pro {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "sbaleno" @@ -15276,7 +15276,7 @@ msgstr "Rozbalit nepřečtené zprávy pro {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "rozbaleno" @@ -16217,7 +16217,7 @@ msgstr "Hlas v popředí" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Složka" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Otevřít komunitu" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Otevřít nabídku komunity {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatary při psaní" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Indikátor psaní" @@ -37967,7 +37967,7 @@ msgstr "Skupinová konverzace odepnuta" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Hlasová aktivace" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "hlasová aktivita" diff --git a/fluxer_app/src/features/i18n/locales/da/messages.po b/fluxer_app/src/features/i18n/locales/da/messages.po index c126c1b48..9d5e55d54 100644 --- a/fluxer_app/src/features/i18n/locales/da/messages.po +++ b/fluxer_app/src/features/i18n/locales/da/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# år} other {# år}} med {premiumProduc #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel}-status" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minutter" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Mappen {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Tilgængelig i computerappen." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Skjul" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Skjul {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Skjul ulæste beskeder for {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "skjult" @@ -15276,7 +15276,7 @@ msgstr "Udvid ulæste beskeder for {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "udvidet" @@ -16217,7 +16217,7 @@ msgstr "Fokuseret tale" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Mappe" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Åbn fællesskab" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Åbn fællesskabsmenu for {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Skrive-avatarer" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Skriveindikator" @@ -37967,7 +37967,7 @@ msgstr "Fastgørelse af gruppe-DM fjernet" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Taleaktivering" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "taleaktivitet" diff --git a/fluxer_app/src/features/i18n/locales/de/messages.po b/fluxer_app/src/features/i18n/locales/de/messages.po index ebc7c1c53..80ad77e5b 100644 --- a/fluxer_app/src/features/i18n/locales/de/messages.po +++ b/fluxer_app/src/features/i18n/locales/de/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# Jahr} other {# Jahre}} {premiumProduct #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Status: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} Minuten" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Ordner \"{folderName}\"" @@ -5563,7 +5563,7 @@ msgstr "Verfügbar in der Desktop-App." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Einklappen" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "{folderName} einklappen" @@ -8921,7 +8921,7 @@ msgstr "Ungelesene Nachrichten für {channelHeading} einklappen" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "eingeklappt" @@ -15276,7 +15276,7 @@ msgstr "Ungelesene Nachrichten für {channelHeading} einblenden" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "erweitert" @@ -16217,7 +16217,7 @@ msgstr "Fokussierter Sprachchat" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Ordner" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Community öffnen" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Community-Menü für {guildName} öffnen" @@ -37248,7 +37248,7 @@ msgstr "Tipp-Avatare" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Tipp-Indikator" @@ -37967,7 +37967,7 @@ msgstr "Anheftung der Gruppen-DM aufgehoben" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Sprachaktivierung" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "Sprachaktivität" diff --git a/fluxer_app/src/features/i18n/locales/el/messages.po b/fluxer_app/src/features/i18n/locales/el/messages.po index 5fe6dc9e7..a73b2c2ca 100644 --- a/fluxer_app/src/features/i18n/locales/el/messages.po +++ b/fluxer_app/src/features/i18n/locales/el/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# έτος} other {# έτη}} {premiumPr #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Κατάσταση {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} λεπτά" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Φάκελος {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Διαθέσιμο στην εφαρμογή για υπολογιστέ #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Σύμπτυξη" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Σύμπτυξη {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Σύμπτυξη μη αναγνωσμένων μηνυμάτων για #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "συμπτυγμένο" @@ -15276,7 +15276,7 @@ msgstr "Ανάπτυξη μη αναγνωσμένων μηνυμάτων για #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "αναπτυγμένο" @@ -16217,7 +16217,7 @@ msgstr "Εστιασμένη φωνή" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Φάκελος" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Άνοιγμα κοινότητας" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Άνοιγμα μενού κοινότητας για {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatar πληκτρολόγησης" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Ένδειξη πληκτρολόγησης" @@ -37967,7 +37967,7 @@ msgstr "Καταργήθηκε το καρφίτσωμα της ομαδικής #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Φωνητική ενεργοποίηση" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "φωνητική δραστηριότητα" diff --git a/fluxer_app/src/features/i18n/locales/en-GB/messages.po b/fluxer_app/src/features/i18n/locales/en-GB/messages.po index 89006c350..ad3461c7c 100644 --- a/fluxer_app/src/features/i18n/locales/en-GB/messages.po +++ b/fluxer_app/src/features/i18n/locales/en-GB/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# year} other {# years}} of {premiumProd #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel} status" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minutes" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "{folderName} folder" @@ -5564,7 +5564,7 @@ msgstr "Available in the desktop app." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8877,7 +8877,7 @@ msgid "Collapse" msgstr "Collapse" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Collapse {folderName}" @@ -8922,7 +8922,7 @@ msgstr "Collapse unread messages for {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "collapsed" @@ -15277,7 +15277,7 @@ msgstr "Expand unread messages for {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "expanded" @@ -16218,7 +16218,7 @@ msgstr "Focused voice" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Folder" @@ -25057,7 +25057,7 @@ msgid "Open community" msgstr "Open community" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Open community menu for {guildName}" @@ -37249,7 +37249,7 @@ msgstr "Typing avatars" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Typing indicator" @@ -37968,7 +37968,7 @@ msgstr "Unpinned group DM" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39823,7 +39823,7 @@ msgstr "Voice activation" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "voice activity" diff --git a/fluxer_app/src/features/i18n/locales/en-US/messages.po b/fluxer_app/src/features/i18n/locales/en-US/messages.po index 3714babd8..2407dfe94 100644 --- a/fluxer_app/src/features/i18n/locales/en-US/messages.po +++ b/fluxer_app/src/features/i18n/locales/en-US/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# year} other {# years}} of {premiumProd #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel} status" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minutes" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "{folderName} folder" @@ -5563,7 +5563,7 @@ msgstr "Available in the desktop app." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Collapse" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Collapse {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Collapse unread messages for {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "collapsed" @@ -15276,7 +15276,7 @@ msgstr "Expand unread messages for {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "expanded" @@ -16217,7 +16217,7 @@ msgstr "Focused voice" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Folder" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Open community" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Open community menu for {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Typing avatars" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Typing indicator" @@ -37967,7 +37967,7 @@ msgstr "Unpinned group DM" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Voice activation" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "voice activity" diff --git a/fluxer_app/src/features/i18n/locales/es-419/messages.po b/fluxer_app/src/features/i18n/locales/es-419/messages.po index 48129b017..948374dc3 100644 --- a/fluxer_app/src/features/i18n/locales/es-419/messages.po +++ b/fluxer_app/src/features/i18n/locales/es-419/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# año} other {# años}} de {premiumProd #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Estado: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minutos" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Carpeta {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Disponible en la aplicación de escritorio." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Contraer" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Contraer {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Contraer mensajes no leídos de {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "contraído" @@ -15276,7 +15276,7 @@ msgstr "Expandir mensajes no leídos de {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "expandido" @@ -16217,7 +16217,7 @@ msgstr "Voz enfocada" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Carpeta" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Abrir comunidad" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Abrir menú de la comunidad para {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatares de escritura" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Indicador de escritura" @@ -37967,7 +37967,7 @@ msgstr "MD de grupo desfijado" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Activación por voz" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "actividad de voz" diff --git a/fluxer_app/src/features/i18n/locales/es-ES/messages.po b/fluxer_app/src/features/i18n/locales/es-ES/messages.po index e51c81f5c..3c490fd63 100644 --- a/fluxer_app/src/features/i18n/locales/es-ES/messages.po +++ b/fluxer_app/src/features/i18n/locales/es-ES/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# año} other {# años}} de {premiumProd #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Estado: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minutos" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Carpeta {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Disponible en la aplicación de escritorio." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Contraer" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Contraer {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Contraer los mensajes no leídos de {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "contraído" @@ -15276,7 +15276,7 @@ msgstr "Expandir mensajes no leídos de {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "expandido" @@ -16217,7 +16217,7 @@ msgstr "Voz enfocada" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Carpeta" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Abrir comunidad" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Abrir menú de la comunidad para {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatares de escritura" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Indicador de escritura" @@ -37967,7 +37967,7 @@ msgstr "MD de grupo desfijado" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Activación por voz" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "actividad de voz" diff --git a/fluxer_app/src/features/i18n/locales/fi/messages.po b/fluxer_app/src/features/i18n/locales/fi/messages.po index a85fde363..a9c0a53b5 100644 --- a/fluxer_app/src/features/i18n/locales/fi/messages.po +++ b/fluxer_app/src/features/i18n/locales/fi/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# vuosi} other {# vuotta}} {premiumProdu #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel}-tila" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minuuttia" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Kansio: {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Saatavilla työpöytäsovelluksessa." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Tiivistä" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Tiivistä {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Tiivistä kohteen {channelHeading} lukemattomat viestit" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "tiivistetty" @@ -15276,7 +15276,7 @@ msgstr "Laajenna kanavan {channelHeading} lukemattomat viestit" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "laajennettu" @@ -16217,7 +16217,7 @@ msgstr "Kohdennettu ääni" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Kansio" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Avaa yhteisö" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Avaa yhteisön valikko ({guildName})" @@ -37248,7 +37248,7 @@ msgstr "Kirjoittajien avatarit" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Kirjoitusilmaisin" @@ -37967,7 +37967,7 @@ msgstr "Ryhmäkeskustelun kiinnitys poistettu" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Ääniaktivointi" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "puheaktiivisuus" diff --git a/fluxer_app/src/features/i18n/locales/fr/messages.po b/fluxer_app/src/features/i18n/locales/fr/messages.po index 68329c781..7484b6e9d 100644 --- a/fluxer_app/src/features/i18n/locales/fr/messages.po +++ b/fluxer_app/src/features/i18n/locales/fr/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# an} other {# ans}} de {premiumProductN #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Statut {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minutes" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Dossier {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Disponible dans l'application de bureau." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Réduire" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Réduire {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Réduire les messages non lus pour {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "réduit" @@ -15276,7 +15276,7 @@ msgstr "Afficher les messages non lus de {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "développé" @@ -16217,7 +16217,7 @@ msgstr "Optimisé pour la voix" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Dossier" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Ouvrir la communauté" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Ouvrir le menu de la communauté pour {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatars de saisie" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Indicateur de saisie" @@ -37967,7 +37967,7 @@ msgstr "MP de groupe désépinglé" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Activation vocale" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "activité vocale" diff --git a/fluxer_app/src/features/i18n/locales/he/messages.po b/fluxer_app/src/features/i18n/locales/he/messages.po index bbe225335..a289a308c 100644 --- a/fluxer_app/src/features/i18n/locales/he/messages.po +++ b/fluxer_app/src/features/i18n/locales/he/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# שנה} other {# שנים}} של {prem #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "סטטוס {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} דקות" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "תיקיית {folderName}" @@ -5563,7 +5563,7 @@ msgstr "זמין באפליקציית המחשב." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "כיווץ" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "כיווץ {folderName}" @@ -8921,7 +8921,7 @@ msgstr "כיווץ ההודעות שלא נקראו עבור {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "מכווץ" @@ -15276,7 +15276,7 @@ msgstr "הרחבת הודעות שלא נקראו עבור {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "מורחב" @@ -16217,7 +16217,7 @@ msgstr "קול ממוקד" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "תיקייה" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "פתח קהילה" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "פתח תפריט קהילה עבור {guildName}" @@ -37248,7 +37248,7 @@ msgstr "אווטרים בהקלדה" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "חיווי הקלדה" @@ -37967,7 +37967,7 @@ msgstr "צ'אט קבוצתי לא מוצמד" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "הפעלה קולית" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "פעילות קולית" diff --git a/fluxer_app/src/features/i18n/locales/hi/messages.po b/fluxer_app/src/features/i18n/locales/hi/messages.po index 501df9ea3..8f02462c4 100644 --- a/fluxer_app/src/features/i18n/locales/hi/messages.po +++ b/fluxer_app/src/features/i18n/locales/hi/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# साल} other {# साल}} का #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel} स्टेटस" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} मिनट" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "{folderName} फ़ोल्डर" @@ -5563,7 +5563,7 @@ msgstr "डेस्कटॉप ऐप में उपलब्ध है।" #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "छोटा करें" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "{folderName} को छोटा करें" @@ -8921,7 +8921,7 @@ msgstr "{channelHeading} के लिए न पढ़े गए मैसे #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "छोटा किया गया" @@ -15276,7 +15276,7 @@ msgstr "{channelHeading} के लिए न पढ़े गए मैसे #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "विस्तारित" @@ -16217,7 +16217,7 @@ msgstr "फ़ोकस्ड वॉइस" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "फ़ोल्डर" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "कम्युनिटी खोलें" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "{guildName} के लिए कम्युनिटी मेन्यू खोलें" @@ -37248,7 +37248,7 @@ msgstr "टाइपिंग अवतार" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "टाइपिंग इंडिकेटर" @@ -37967,7 +37967,7 @@ msgstr "ग्रुप DM अनपिन किया गया" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "वॉइस ऐक्टिवेशन" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "वॉइस गतिविधि" diff --git a/fluxer_app/src/features/i18n/locales/hr/messages.po b/fluxer_app/src/features/i18n/locales/hr/messages.po index bf2da3073..d001cd90e 100644 --- a/fluxer_app/src/features/i18n/locales/hr/messages.po +++ b/fluxer_app/src/features/i18n/locales/hr/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# godina} few {# godine} other {# godina #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Status: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minuta" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Mapa {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Dostupno u aplikaciji za stolna računala." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Sažmi" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Sažmi {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Sažmi nepročitane poruke za {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "sažeto" @@ -15276,7 +15276,7 @@ msgstr "Proširi nepročitane poruke za {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "prošireno" @@ -16217,7 +16217,7 @@ msgstr "Fokusirani glas" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Mapa" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Otvori zajednicu" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Otvori izbornik zajednice za {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatari koji tipkaju" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Indikator tipkanja" @@ -37967,7 +37967,7 @@ msgstr "Grupna izravna poruka je otkvačena" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Glasovna aktivacija" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "glasovna aktivnost" diff --git a/fluxer_app/src/features/i18n/locales/hu/messages.po b/fluxer_app/src/features/i18n/locales/hu/messages.po index 75f769ccf..b3608b063 100644 --- a/fluxer_app/src/features/i18n/locales/hu/messages.po +++ b/fluxer_app/src/features/i18n/locales/hu/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# év} other {# év}} {premiumProductNam #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel} állapot" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} perc" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "{folderName} mappa" @@ -5563,7 +5563,7 @@ msgstr "Elérhető az asztali alkalmazásban." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Összecsukás" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "{folderName} összecsukása" @@ -8921,7 +8921,7 @@ msgstr "{channelHeading} olvasatlan üzeneteinek összecsukása" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "összecsukva" @@ -15276,7 +15276,7 @@ msgstr "Olvasatlan üzenetek kibontása – {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "kibontva" @@ -16217,7 +16217,7 @@ msgstr "Beszédkiemelés" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Mappa" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Közösség megnyitása" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Közösségi menü megnyitása – {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Gépelésjelző profilképek" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Gépelésjelző" @@ -37967,7 +37967,7 @@ msgstr "Rögzítés feloldva (csoportos beszélgetés)" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Hangaktiválás" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "hangtevékenység" diff --git a/fluxer_app/src/features/i18n/locales/id/messages.po b/fluxer_app/src/features/i18n/locales/id/messages.po index d8e8f9c72..e8604b908 100644 --- a/fluxer_app/src/features/i18n/locales/id/messages.po +++ b/fluxer_app/src/features/i18n/locales/id/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# tahun} other {# tahun}} {premiumProduc #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Status {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} menit" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Folder {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Tersedia di aplikasi desktop." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Ciutkan" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Ciutkan {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Ciutkan pesan yang belum dibaca untuk {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "diciutkan" @@ -15276,7 +15276,7 @@ msgstr "Perluas pesan belum dibaca untuk {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "diperluas" @@ -16217,7 +16217,7 @@ msgstr "Suara terfokus" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Folder" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Buka komunitas" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Buka menu komunitas untuk {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatar mengetik" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Indikator mengetik" @@ -37967,7 +37967,7 @@ msgstr "Sematan DM grup dilepas" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Aktivasi suara" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "aktivitas suara" diff --git a/fluxer_app/src/features/i18n/locales/it/messages.po b/fluxer_app/src/features/i18n/locales/it/messages.po index 96b42a37c..e6c48f312 100644 --- a/fluxer_app/src/features/i18n/locales/it/messages.po +++ b/fluxer_app/src/features/i18n/locales/it/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# anno} other {# anni}} di {premiumProdu #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Stato {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minuti" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Cartella {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Disponibile nell'app desktop." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Comprimi" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Comprimi {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Comprimi i messaggi non letti per {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "chiuso" @@ -15276,7 +15276,7 @@ msgstr "Espandi i messaggi non letti per {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "espanso" @@ -16217,7 +16217,7 @@ msgstr "Voce isolata" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Cartella" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Apri community" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Apri il menu della community per {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatar durante la digitazione" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Indicatore di digitazione" @@ -37967,7 +37967,7 @@ msgstr "DM di gruppo non fissato" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Attivazione vocale" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "attività vocale" diff --git a/fluxer_app/src/features/i18n/locales/ja/messages.po b/fluxer_app/src/features/i18n/locales/ja/messages.po index b2cb6882e..f1e644eea 100644 --- a/fluxer_app/src/features/i18n/locales/ja/messages.po +++ b/fluxer_app/src/features/i18n/locales/ja/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {#年} other {#年}}分の{premiumProduct #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel}のステータス" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes}分" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "{folderName}フォルダー" @@ -5563,7 +5563,7 @@ msgstr "デスクトップアプリで利用できます。" #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "折りたたむ" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "{folderName}を折りたたむ" @@ -8921,7 +8921,7 @@ msgstr "{channelHeading}の未読メッセージを折りたたむ" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "折りたたまれています" @@ -15276,7 +15276,7 @@ msgstr "{channelHeading}の未読メッセージを展開" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "展開中" @@ -16217,7 +16217,7 @@ msgstr "音声にフォーカス" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "フォルダー" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "コミュニティを開く" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "{guildName}のコミュニティメニューを開く" @@ -37248,7 +37248,7 @@ msgstr "入力中のアバター" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "入力中インジケーター" @@ -37967,7 +37967,7 @@ msgstr "グループDMのピン留めを解除しました" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "音声検出" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "音声アクティビティ" diff --git a/fluxer_app/src/features/i18n/locales/ko/messages.po b/fluxer_app/src/features/i18n/locales/ko/messages.po index 539cab035..dfaa3df6e 100644 --- a/fluxer_app/src/features/i18n/locales/ko/messages.po +++ b/fluxer_app/src/features/i18n/locales/ko/messages.po @@ -1430,7 +1430,7 @@ msgstr "{premiumProductName} {durationQuantity, plural, one {#년} other {#년}} #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel} 상태" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes}분" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "{folderName} 폴더" @@ -5563,7 +5563,7 @@ msgstr "데스크톱 앱에서 사용 가능합니다." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "접기" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "{folderName} 접기" @@ -8921,7 +8921,7 @@ msgstr "{channelHeading}의 읽지 않은 메시지 접기" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "접힘" @@ -15276,7 +15276,7 @@ msgstr "{channelHeading}의 읽지 않은 메시지 펼치기" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "펼쳐짐" @@ -16217,7 +16217,7 @@ msgstr "음성 포커스" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "폴더" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "커뮤니티 열기" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "{guildName} 커뮤니티 메뉴 열기" @@ -37248,7 +37248,7 @@ msgstr "입력 중 아바타" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "입력 중 표시" @@ -37967,7 +37967,7 @@ msgstr "고정 해제된 그룹 DM" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "음성 활성화" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "음성 활동" diff --git a/fluxer_app/src/features/i18n/locales/lt/messages.po b/fluxer_app/src/features/i18n/locales/lt/messages.po index 0fc3ae6fd..ff3d07596 100644 --- a/fluxer_app/src/features/i18n/locales/lt/messages.po +++ b/fluxer_app/src/features/i18n/locales/lt/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# metai} few {# metai} other {# metų}} #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Būsena: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minučių" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Aplankas {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Pasiekiama darbalaukio programoje." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Sutraukti" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Sutraukti aplanką „{folderName}“" @@ -8921,7 +8921,7 @@ msgstr "Sutraukti neskaitytas žinutes kanale „{channelHeading}“" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "sutraukta" @@ -15276,7 +15276,7 @@ msgstr "Išskleisti neperskaitytas žinutes kanale „{channelHeading}“" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "išskleista" @@ -16217,7 +16217,7 @@ msgstr "Fokusuotas balsas" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Aplankas" @@ -25057,7 +25057,7 @@ msgid "Open community" msgstr "Atidaryti bendruomenę" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Atidaryti bendruomenės „{guildName}“ meniu" @@ -37249,7 +37249,7 @@ msgstr "Rašančiųjų avatarai" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Rašymo indikatorius" @@ -37968,7 +37968,7 @@ msgstr "Grupės DM atsegtas" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39822,7 +39822,7 @@ msgstr "Balso aktyvavimas" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "balso aktyvumas" diff --git a/fluxer_app/src/features/i18n/locales/nl/messages.po b/fluxer_app/src/features/i18n/locales/nl/messages.po index 3446b965a..93ee042ff 100644 --- a/fluxer_app/src/features/i18n/locales/nl/messages.po +++ b/fluxer_app/src/features/i18n/locales/nl/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# jaar} other {# jaar}} {premiumProductN #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Status: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minuten" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Map {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Beschikbaar in de desktopapp." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Inklappen" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Map {folderName} inklappen" @@ -8921,7 +8921,7 @@ msgstr "Ongelezen berichten voor {channelHeading} inklappen" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "ingeklapt" @@ -15276,7 +15276,7 @@ msgstr "Ongelezen berichten voor {channelHeading} uitvouwen" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "uitgeklapt" @@ -16217,7 +16217,7 @@ msgstr "Stemfocus" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Map" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Community openen" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Communitymenu openen voor {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Typende avatars" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Typindicator" @@ -37967,7 +37967,7 @@ msgstr "Groeps-DM losgemaakt" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Spraakactivering" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "spraakactiviteit" diff --git a/fluxer_app/src/features/i18n/locales/no/messages.po b/fluxer_app/src/features/i18n/locales/no/messages.po index d2d1ece46..b64d30e91 100644 --- a/fluxer_app/src/features/i18n/locales/no/messages.po +++ b/fluxer_app/src/features/i18n/locales/no/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# år} other {# år}} med {premiumProduc #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel}-status" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minutter" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Mappen {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Tilgjengelig i skrivebordsappen." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Skjul" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Skjul {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Skjul uleste meldinger for {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "sammenfoldet" @@ -15276,7 +15276,7 @@ msgstr "Vis uleste meldinger for {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "utvidet" @@ -16217,7 +16217,7 @@ msgstr "Tale i fokus" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Mappe" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Åpne fellesskap" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Åpne fellesskapsmeny for {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatarer i skriveindikatoren" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Skriveindikator" @@ -37967,7 +37967,7 @@ msgstr "Løsnet gruppechat" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Stemmeaktivering" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "taleaktivitet" diff --git a/fluxer_app/src/features/i18n/locales/pl/messages.po b/fluxer_app/src/features/i18n/locales/pl/messages.po index 3b5baaec5..265b93c37 100644 --- a/fluxer_app/src/features/i18n/locales/pl/messages.po +++ b/fluxer_app/src/features/i18n/locales/pl/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# rok} few {# lata} many {# lat} other { #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Status: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minut" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Folder {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Dostępne w aplikacji komputerowej." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Zwiń" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Zwiń folder \"{folderName}\"" @@ -8921,7 +8921,7 @@ msgstr "Zwiń nieprzeczytane wiadomości dla {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "zwinięte" @@ -15276,7 +15276,7 @@ msgstr "Rozwiń nieprzeczytane wiadomości dla {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "rozwinięte" @@ -16217,7 +16217,7 @@ msgstr "Wyrazisty głos" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Folder" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Otwórz społeczność" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Otwórz menu społeczności {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Awatary piszących osób" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Wskaźnik pisania" @@ -37967,7 +37967,7 @@ msgstr "Odpięto czat grupowy" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Aktywacja głosowa" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "aktywność głosowa" diff --git a/fluxer_app/src/features/i18n/locales/pt-BR/messages.po b/fluxer_app/src/features/i18n/locales/pt-BR/messages.po index 0bd0c13a5..2a0fe2a84 100644 --- a/fluxer_app/src/features/i18n/locales/pt-BR/messages.po +++ b/fluxer_app/src/features/i18n/locales/pt-BR/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, =0 {# anos} one {# ano} other {# anos}} de {p #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Status {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minutos" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Pasta {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Disponível no app para computador." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Recolher" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Recolher {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Recolher mensagens não lidas para {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "recolhido" @@ -15276,7 +15276,7 @@ msgstr "Expandir mensagens não lidas para {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "expandido" @@ -16217,7 +16217,7 @@ msgstr "Voz focada" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Pasta" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Abrir comunidade" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Abrir menu da comunidade {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatares no indicador de digitação" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Indicador de digitação" @@ -37967,7 +37967,7 @@ msgstr "Conversa em grupo desafixada" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Ativação por voz" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "atividade de voz" diff --git a/fluxer_app/src/features/i18n/locales/ro/messages.po b/fluxer_app/src/features/i18n/locales/ro/messages.po index 9228c781a..e26eeeffc 100644 --- a/fluxer_app/src/features/i18n/locales/ro/messages.po +++ b/fluxer_app/src/features/i18n/locales/ro/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# an} few {# ani} other {# de ani}} de { #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Stare: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minute" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Dosarul {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Disponibil în aplicația desktop." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Restrânge" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Restrânge {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Restrânge mesajele necitite pentru {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "restrâns" @@ -15276,7 +15276,7 @@ msgstr "Extinde mesajele necitite pentru {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "extins" @@ -16217,7 +16217,7 @@ msgstr "Voce focalizată" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Dosar" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Deschide comunitatea" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Deschide meniul comunității pentru {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatare în timpul tastării" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Indicator de tastare" @@ -37967,7 +37967,7 @@ msgstr "Fixarea DM-ului de grup a fost anulată" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Activare vocală" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "activitate vocală" diff --git a/fluxer_app/src/features/i18n/locales/ru/messages.po b/fluxer_app/src/features/i18n/locales/ru/messages.po index 025d3a100..83dc36cf9 100644 --- a/fluxer_app/src/features/i18n/locales/ru/messages.po +++ b/fluxer_app/src/features/i18n/locales/ru/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# год} few {# года} many {# ле #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Статус «{effectiveStatusLabel}»" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} минут" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Папка «{folderName}»" @@ -5563,7 +5563,7 @@ msgstr "Доступно в приложении для ПК." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Свернуть" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Свернуть «{folderName}»" @@ -8921,7 +8921,7 @@ msgstr "Свернуть непрочитанные сообщения для {c #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "свернуто" @@ -15276,7 +15276,7 @@ msgstr "Развернуть непрочитанные сообщения дл #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "развернуто" @@ -16217,7 +16217,7 @@ msgstr "Фокусировка на голосе" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Папка" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Открыть сообщество" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Открыть меню сообщества «{guildName}»" @@ -37248,7 +37248,7 @@ msgstr "Аватары при наборе текста" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Индикатор набора текста" @@ -37967,7 +37967,7 @@ msgstr "Групповой чат откреплен" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Голосовая активация" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "голосовая активность" diff --git a/fluxer_app/src/features/i18n/locales/sv-SE/messages.po b/fluxer_app/src/features/i18n/locales/sv-SE/messages.po index 3eab88562..bdf37df14 100644 --- a/fluxer_app/src/features/i18n/locales/sv-SE/messages.po +++ b/fluxer_app/src/features/i18n/locales/sv-SE/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# år} other {# år}} av {premiumProduct #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel}-status" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} minuter" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Mappen {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Tillgänglig i datorappen." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Fäll ihop" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Fäll ihop {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Fäll ihop olästa meddelanden för {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "hopfälld" @@ -15276,7 +15276,7 @@ msgstr "Visa olästa meddelanden för {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "utfälld" @@ -16217,7 +16217,7 @@ msgstr "Fokuserat röstläge" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Mapp" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Öppna community" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Öppna community-meny för {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Avatarer för personer som skriver" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Skrivindikator" @@ -37967,7 +37967,7 @@ msgstr "Grupp-DM-konversationen lossad" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Röstaktivering" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "röstaktivitet" diff --git a/fluxer_app/src/features/i18n/locales/th/messages.po b/fluxer_app/src/features/i18n/locales/th/messages.po index cccc46936..0c90aee69 100644 --- a/fluxer_app/src/features/i18n/locales/th/messages.po +++ b/fluxer_app/src/features/i18n/locales/th/messages.po @@ -1430,7 +1430,7 @@ msgstr "{premiumProductName} {durationQuantity, plural, one {# ปี} other {# #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "สถานะ{effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} นาที" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "โฟลเดอร์ {folderName}" @@ -5563,7 +5563,7 @@ msgstr "ใช้งานได้ในแอปเดสก์ท็อป" #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "ยุบ" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "ยุบ {folderName}" @@ -8921,7 +8921,7 @@ msgstr "ยุบข้อความที่ยังไม่ได้อ่ #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "ยุบแล้ว" @@ -15276,7 +15276,7 @@ msgstr "ขยายข้อความที่ยังไม่ได้อ #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "ขยายแล้ว" @@ -16217,7 +16217,7 @@ msgstr "เน้นเสียงพูด" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "โฟลเดอร์" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "เปิดคอมมูนิตี้" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "เปิดเมนูคอมมูนิตี้สำหรับ {guildName}" @@ -37248,7 +37248,7 @@ msgstr "รูปโปรไฟล์กำลังพิมพ์" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "ตัวบ่งชี้สถานะกำลังพิมพ์" @@ -37967,7 +37967,7 @@ msgstr "เลิกปักหมุดแชทกลุ่มแล้ว" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "การตรวจจับเสียงพูด" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "กำลังใช้เสียง" diff --git a/fluxer_app/src/features/i18n/locales/tr/messages.po b/fluxer_app/src/features/i18n/locales/tr/messages.po index 0b8d551a3..0b1dbcaa6 100644 --- a/fluxer_app/src/features/i18n/locales/tr/messages.po +++ b/fluxer_app/src/features/i18n/locales/tr/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# yıl} other {# yıl}} {premiumProductN #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel} durumu" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} dakika" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "{folderName} klasörü" @@ -5563,7 +5563,7 @@ msgstr "Masaüstü uygulamasında mevcut." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Daralt" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "{folderName} klasörünü daralt" @@ -8921,7 +8921,7 @@ msgstr "{channelHeading} için okunmamış mesajları daralt" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "daraltıldı" @@ -15276,7 +15276,7 @@ msgstr "{channelHeading} için okunmamış mesajları genişlet" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "genişletildi" @@ -16217,7 +16217,7 @@ msgstr "Odaklanmış ses" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Klasör" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Topluluğu aç" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "{guildName} için topluluk menüsünü aç" @@ -37248,7 +37248,7 @@ msgstr "Yazıyor avatarları" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Yazıyor göstergesi" @@ -37967,7 +37967,7 @@ msgstr "Grup DM'inin sabitlemesi kaldırıldı" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Sesle etkinleştirme" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "ses etkinliği" diff --git a/fluxer_app/src/features/i18n/locales/uk/messages.po b/fluxer_app/src/features/i18n/locales/uk/messages.po index 153cf67e4..0a39ddc88 100644 --- a/fluxer_app/src/features/i18n/locales/uk/messages.po +++ b/fluxer_app/src/features/i18n/locales/uk/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# рік} few {# роки} many {# ро #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Статус: {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} хвилин" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Папка «{folderName}»" @@ -5563,7 +5563,7 @@ msgstr "Доступно в десктопному застосунку." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Згорнути" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Згорнути {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Згорнути непрочитані повідомлення для #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "згорнуто" @@ -15276,7 +15276,7 @@ msgstr "Розгорнути непрочитані повідомлення д #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "розгорнуто" @@ -16217,7 +16217,7 @@ msgstr "Чіткий голос" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Папка" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Відкрити спільноту" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Відкрити меню спільноти для {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Аватари під час набору" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Індикатор набору тексту" @@ -37967,7 +37967,7 @@ msgstr "Відкріплений груповий чат" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Голосова активація" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "голосова активність" diff --git a/fluxer_app/src/features/i18n/locales/vi/messages.po b/fluxer_app/src/features/i18n/locales/vi/messages.po index 390e8a225..2e950414a 100644 --- a/fluxer_app/src/features/i18n/locales/vi/messages.po +++ b/fluxer_app/src/features/i18n/locales/vi/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# năm} other {# năm}} {premiumProductN #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "Trạng thái {effectiveStatusLabel}" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} phút" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "Thư mục {folderName}" @@ -5563,7 +5563,7 @@ msgstr "Có sẵn trong ứng dụng dành cho máy tính." #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "Thu gọn" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "Thu gọn {folderName}" @@ -8921,7 +8921,7 @@ msgstr "Thu gọn tin nhắn chưa đọc cho {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "đã thu gọn" @@ -15276,7 +15276,7 @@ msgstr "Mở rộng tin nhắn chưa đọc cho {channelHeading}" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "đã mở rộng" @@ -16217,7 +16217,7 @@ msgstr "Tập trung giọng nói" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "Thư mục" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "Mở cộng đồng" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "Mở menu cộng đồng cho {guildName}" @@ -37248,7 +37248,7 @@ msgstr "Ảnh đại diện khi đang nhập" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "Chỉ báo đang nhập" @@ -37967,7 +37967,7 @@ msgstr "Đã bỏ ghim tin nhắn nhóm" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "Kích hoạt giọng nói" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "hoạt động thoại" diff --git a/fluxer_app/src/features/i18n/locales/zh-CN/messages.po b/fluxer_app/src/features/i18n/locales/zh-CN/messages.po index 3703cae37..58dd120b3 100644 --- a/fluxer_app/src/features/i18n/locales/zh-CN/messages.po +++ b/fluxer_app/src/features/i18n/locales/zh-CN/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, other {# 年}}的 {premiumProductName}" #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel}状态" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} 分钟" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "{folderName} 文件夹" @@ -5563,7 +5563,7 @@ msgstr "可在桌面应用中使用。" #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "收起" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "收起“{folderName}”" @@ -8921,7 +8921,7 @@ msgstr "收起“{channelHeading}”的未读消息" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "已收起" @@ -15276,7 +15276,7 @@ msgstr "展开 {channelHeading} 的未读消息" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "已展开" @@ -16217,7 +16217,7 @@ msgstr "人声优化" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "文件夹" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "打开社区" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "打开 {guildName} 的社区菜单" @@ -37248,7 +37248,7 @@ msgstr "正在输入提示头像" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "正在输入" @@ -37967,7 +37967,7 @@ msgstr "已取消置顶群聊" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "语音激活" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "语音活动" diff --git a/fluxer_app/src/features/i18n/locales/zh-TW/messages.po b/fluxer_app/src/features/i18n/locales/zh-TW/messages.po index 3e2bfac34..900e3b76f 100644 --- a/fluxer_app/src/features/i18n/locales/zh-TW/messages.po +++ b/fluxer_app/src/features/i18n/locales/zh-TW/messages.po @@ -1430,7 +1430,7 @@ msgstr "{durationQuantity, plural, one {# 年} other {# 年}}的 {premiumProduct #. Accessible label for the avatar status badge; placeholder is the localized status name. #: src/features/app/components/shared/GroupDMAvatar.tsx:33 -#: src/features/ui/components/BaseAvatar.tsx:26 +#: src/features/ui/components/BaseAvatar.tsx:27 #: src/features/ui/components/StatusIndicator.tsx:12 msgid "{effectiveStatusLabel} status" msgstr "{effectiveStatusLabel}狀態" @@ -1476,7 +1476,7 @@ msgid "{fifteenMinutes} minutes" msgstr "{fifteenMinutes} 分鐘" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:77 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:78 msgid "{folderName} folder" msgstr "「{folderName}」資料夾" @@ -5563,7 +5563,7 @@ msgstr "可在桌面版應用程式中使用。" #. Accessible label for an avatar image. #. Form label for a webhook avatar image. #. Settings search entry label. Also used as a search synonym in the settings search bar. -#: src/features/ui/components/BaseAvatar.tsx:18 +#: src/features/ui/components/BaseAvatar.tsx:19 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:353 #: src/features/user/components/modals/tabs/my_profile_tab/AvatarUploader.tsx:401 #: src/features/user/components/settings_utils/search_index/MyProfileIndex.ts:13 @@ -8876,7 +8876,7 @@ msgid "Collapse" msgstr "收合" #. Short label in the sidebar navigation guild folder item. Preserve {folderName}; it is inserted by code. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:97 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:98 msgid "Collapse {folderName}" msgstr "收合「{folderName}」" @@ -8921,7 +8921,7 @@ msgstr "收合「{channelHeading}」的未讀訊息" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for a collapsed category. #: src/features/app/components/layout/ChannelItem.tsx:101 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:85 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:86 msgid "collapsed" msgstr "已收合" @@ -15276,7 +15276,7 @@ msgstr "展開「{channelHeading}」的未讀訊息" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase state label used inside channel-list accessible text for an expanded category. #: src/features/app/components/layout/ChannelItem.tsx:105 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:81 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:82 msgid "expanded" msgstr "已展開" @@ -16217,7 +16217,7 @@ msgstr "語音聚焦" #. Folder icon option in the community-folder settings modal: a generic folder shape. Short standalone label. #. Short label in the sidebar navigation guild folder item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:73 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:74 #: src/features/guild/components/modals/GuildFolderSettingsModal.tsx:40 msgid "Folder" msgstr "資料夾" @@ -25056,7 +25056,7 @@ msgid "Open community" msgstr "開啟社群" #. Short label in the app layout guild header. Preserve {guildName}; it is inserted by code. -#: src/features/app/components/layout/GuildHeader.tsx:33 +#: src/features/app/components/layout/GuildHeader.tsx:34 msgid "Open community menu for {guildName}" msgstr "開啟「{guildName}」社群選單" @@ -37248,7 +37248,7 @@ msgstr "正在輸入頭像" #. Settings search synonym. Used to match this term when the user types it in the settings search bar. #. Short label in the channel and chat typing. Keep it concise. #: src/features/channel/components/ChannelTyping.tsx:11 -#: src/features/ui/components/BaseAvatar.tsx:22 +#: src/features/ui/components/BaseAvatar.tsx:23 #: src/features/user/components/settings_utils/section_registry/AppearanceSections.ts:350 msgid "Typing indicator" msgstr "正在輸入" @@ -37967,7 +37967,7 @@ msgstr "已取消釘選群組私訊" #. Lowercase state label used inside channel-list accessible text when a channel has unread messages. #. Screen-reader fragment announcing that the DM list row has unread messages. Lowercase fragment. #: src/features/app/components/layout/ChannelItem.tsx:109 -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:89 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:90 #: src/features/app/components/layout/sidebar_nav/GuildListDMItem.tsx:58 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:27 #: src/features/channel/components/direct_message/DMListItem.tsx:71 @@ -39821,7 +39821,7 @@ msgstr "語音偵測" #. Lowercase screen-reader fragment in the sidebar navigation guild folder item. #. Lowercase screen-reader fragment in the sidebar navigation guild list item. -#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:93 +#: src/features/app/components/layout/sidebar_nav/GuildFolderItem.tsx:94 #: src/features/app/components/layout/sidebar_nav/UseGuildListItemState.ts:47 msgid "voice activity" msgstr "語音活動" diff --git a/fluxer_app/src/features/messaging/hooks/useImageRecovery.test.tsx b/fluxer_app/src/features/messaging/hooks/useImageRecovery.test.tsx new file mode 100644 index 000000000..8eac8acc2 --- /dev/null +++ b/fluxer_app/src/features/messaging/hooks/useImageRecovery.test.tsx @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +// @vitest-environment happy-dom + +import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest'; + +vi.mock('@lingui/core/macro', () => ({msg: (descriptor: unknown) => descriptor})); +vi.mock('@lingui/react/macro', () => ({ + useLingui: () => ({i18n: {_: (descriptor: {message?: string} | undefined) => descriptor?.message ?? ''}}), +})); +vi.mock('@app/features/accessibility/state/Accessibility', () => ({default: {useReducedMotion: false}})); +vi.mock('@app/features/ui/tooltip/Tooltip', () => ({Tooltip: ({children}: {children: unknown}) => children})); +vi.mock('@app/features/ui/focus_ring/FocusRing', () => ({default: ({children}: {children: unknown}) => children})); +vi.mock('@app/features/app/constants/AppConstants', () => ({getStatusTypeLabel: () => ''})); + +const AVATAR_URL = 'https://chat.example.com/media/avatars/1/abc.webp?size=128'; +const GUILD_ICON_URL = 'https://chat.example.com/media/icons/2/def.webp?size=96'; +const RESPONSE_DELAY_MS = 5; + +const network = {up: true, requests: [] as Array}; + +class SelfHostImage { + onload: (() => void) | null = null; + onerror: (() => void) | null = null; + decoding = 'auto'; + complete = false; + naturalWidth = 0; + naturalHeight = 0; + currentSrc = ''; + private source = ''; + + get src(): string { + return this.source; + } + + set src(next: string) { + this.source = next; + const answered = network.up; + network.requests.push(next); + setTimeout(() => { + this.complete = true; + if (!answered) { + this.onerror?.(); + return; + } + this.naturalWidth = 64; + this.naturalHeight = 64; + this.currentSrc = next; + this.onload?.(); + }, RESPONSE_DELAY_MS); + } + + getAttribute(): string { + return this.source; + } + + get ownerDocument(): Document { + return document; + } +} + +async function loadHarness() { + const React = await import('react'); + const {act} = React; + const {createRoot} = await import('react-dom/client'); + const {BaseAvatar} = await import('@app/features/ui/components/BaseAvatar'); + const {useRecoveringBackgroundImageURL} = await import('@app/features/messaging/hooks/useImageRecovery'); + const ImageCacheUtils = await import('@app/features/messaging/utils/ImageCacheUtils'); + ImageCacheUtils._clearForTests(); + const host = document.createElement('div'); + document.body.appendChild(host); + const root = createRoot(host); + function GuildRailIcon({url}: {url: string}) { + const paintable = useRecoveringBackgroundImageURL(url); + return React.createElement('div', { + 'data-testid': 'guild-icon', + style: paintable == null ? undefined : {backgroundImage: `url(${paintable})`}, + }); + } + const advance = async (ms: number, step = 1000) => { + for (let elapsed = 0; elapsed < ms; elapsed += step) { + await act(async () => { + await vi.advanceTimersByTimeAsync(Math.min(step, ms - elapsed)); + }); + } + }; + const avatarImage = () => host.querySelector('[data-flx="ui.base-avatar.image"]'); + const guildIcon = () => host.querySelector('[data-testid="guild-icon"]'); + const failAvatarElement = async () => { + await act(async () => { + avatarImage()?.dispatchEvent(new Event('error')); + }); + }; + const render = async (children: Array) => { + await act(async () => { + root.render(React.createElement(React.Fragment, null, ...children)); + }); + }; + const avatar = React.createElement(BaseAvatar, {key: 'avatar', size: 40, avatarUrl: AVATAR_URL}); + const rail = React.createElement(GuildRailIcon, {key: 'rail', url: GUILD_ICON_URL}); + return {act, root, advance, avatarImage, guildIcon, failAvatarElement, render, avatar, rail, ImageCacheUtils}; +} + +beforeEach(() => { + vi.useFakeTimers({toFake: ['setTimeout', 'clearTimeout', 'Date']}); + vi.stubGlobal('Image', SelfHostImage); + vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); + network.up = true; + network.requests.length = 0; +}); + +afterEach(() => { + vi.useRealTimers(); + vi.unstubAllGlobals(); + document.body.innerHTML = ''; +}); + +describe('self-hosted media that failed during an outage loads again on the same mount', () => { + test('a long-lived avatar and guild rail icon come back after a self-host restart without a remount', async () => { + const harness = await loadHarness(); + network.up = false; + await harness.render([harness.avatar, harness.rail]); + await harness.failAvatarElement(); + await harness.advance(30_000); + + expect(harness.avatarImage()).toBeNull(); + expect(harness.guildIcon()?.style.backgroundImage).toBe(''); + const requestsDuringOutage = network.requests.length; + + network.up = true; + await harness.advance(5 * 60_000); + + expect(harness.avatarImage()?.getAttribute('src')).toBe(AVATAR_URL); + expect(harness.guildIcon()?.style.backgroundImage).toContain(GUILD_ICON_URL); + expect(harness.ImageCacheUtils.hasFailedImage(AVATAR_URL)).toBe(false); + expect(network.requests.length).toBeGreaterThan(requestsDuringOutage); + await harness.act(async () => harness.root.unmount()); + }); + + test('retries back off during a long outage instead of hammering the self-host', async () => { + const harness = await loadHarness(); + network.up = false; + await harness.render([harness.avatar]); + await harness.failAvatarElement(); + await harness.advance(10 * 60_000, 5000); + + const lateWindowStart = network.requests.length; + await harness.advance(5 * 60_000, 5000); + const lateWindowRequests = network.requests.length - lateWindowStart; + + expect(lateWindowRequests).toBeGreaterThan(0); + expect(lateWindowRequests).toBeLessThanOrEqual(5 * 3 + 3); + await harness.act(async () => harness.root.unmount()); + }); + + test('an avatar mounted while its URL is cooling down starts blank and still recovers', async () => { + const harness = await loadHarness(); + network.up = false; + await harness.render([harness.avatar]); + await harness.failAvatarElement(); + expect(harness.ImageCacheUtils.hasFailedImage(AVATAR_URL)).toBe(true); + + await harness.render([]); + await harness.render([harness.avatar]); + expect(harness.avatarImage()).toBeNull(); + + network.up = true; + await harness.advance(70_000); + expect(harness.avatarImage()?.getAttribute('src')).toBe(AVATAR_URL); + await harness.act(async () => harness.root.unmount()); + }); + + test('coming back online retries at once instead of waiting out the backoff', async () => { + const harness = await loadHarness(); + network.up = false; + await harness.render([harness.avatar]); + await harness.failAvatarElement(); + await harness.advance(10 * 60_000, 5000); + expect(harness.avatarImage()).toBeNull(); + + network.up = true; + await harness.act(async () => { + window.dispatchEvent(new Event('online')); + }); + await harness.advance(100, 10); + expect(harness.avatarImage()?.getAttribute('src')).toBe(AVATAR_URL); + await harness.act(async () => harness.root.unmount()); + }); + + test('refocusing the window during an outage does not retry in a burst', async () => { + const harness = await loadHarness(); + network.up = false; + await harness.render([harness.avatar]); + await harness.failAvatarElement(); + await harness.advance(10 * 60_000, 5000); + const before = network.requests.length; + for (let i = 0; i < 20; i++) { + await harness.act(async () => { + window.dispatchEvent(new Event('focus')); + }); + await harness.advance(500, 10); + } + expect(network.requests.length - before).toBeLessThanOrEqual(3); + await harness.act(async () => harness.root.unmount()); + }); + + test('an unmounted avatar stops retrying', async () => { + const harness = await loadHarness(); + network.up = false; + await harness.render([harness.avatar]); + await harness.failAvatarElement(); + await harness.render([]); + await harness.advance(60_000, 5000); + const afterUnmount = network.requests.length; + await harness.advance(10 * 60_000, 5000); + expect(network.requests.length).toBe(afterUnmount); + await harness.act(async () => harness.root.unmount()); + }); +}); diff --git a/fluxer_app/src/features/messaging/hooks/useImageRecovery.ts b/fluxer_app/src/features/messaging/hooks/useImageRecovery.ts new file mode 100644 index 000000000..ad5b8b4aa --- /dev/null +++ b/fluxer_app/src/features/messaging/hooks/useImageRecovery.ts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import * as ImageCacheUtils from '@app/features/messaging/utils/ImageCacheUtils'; +import {useCallback, useEffect, useState} from 'react'; + +export interface ImageRecoveryState { + readonly failed: boolean; + readonly reportError: () => void; +} + +export function useImageRecovery(src: string): ImageRecoveryState { + const [failedSrc, setFailedSrc] = useState(() => (ImageCacheUtils.hasFailedImage(src) ? src : null)); + const failed = src.length > 0 && failedSrc === src; + useEffect(() => { + if (ImageCacheUtils.hasFailedImage(src)) setFailedSrc(src); + }, [src]); + useEffect(() => { + if (!failed) return; + return ImageCacheUtils.awaitImage(src, () => { + setFailedSrc((current) => (current === src ? null : current)); + }); + }, [failed, src]); + const reportError = useCallback(() => { + ImageCacheUtils.reportImageError(src); + setFailedSrc(src); + }, [src]); + return {failed, reportError}; +} + +export function useRecoveringBackgroundImageURL(src: string): string | null { + const {failed, reportError} = useImageRecovery(src); + useEffect(() => { + if (failed || src.length === 0) return; + return ImageCacheUtils.loadImage(src, () => {}, reportError); + }, [failed, reportError, src]); + return failed || src.length === 0 ? null : src; +} diff --git a/fluxer_app/src/features/messaging/utils/DesktopResourceUrl.ts b/fluxer_app/src/features/messaging/utils/DesktopResourceUrl.ts index 196b12b2c..58231978e 100644 --- a/fluxer_app/src/features/messaging/utils/DesktopResourceUrl.ts +++ b/fluxer_app/src/features/messaging/utils/DesktopResourceUrl.ts @@ -21,7 +21,6 @@ const UPLOAD_RELAY_MULTIPART_PARAMETERS: ReadonlySet = new Set(['uploadI const UPLOAD_RELAY_BASE_PATH_PATTERN = /(?:^|\/)relay(?:\/|$)/u; const UPLOAD_RELAY_BASE_SUFFIX_PATTERN = /\/v1\/relay$/u; const TRAILING_SLASHES_PATTERN = /\/+$/u; -const LOOPBACK_IPV4_PATTERN = /^127(?:\.\d{1,3}){3}$/u; class DesktopUploadRelayUrlError extends Error { constructor(message: string) { @@ -87,12 +86,11 @@ function parseDisplayTarget(value: string): URL | null { function isDirectlyDisplayableTarget(target: URL): boolean { if (target.protocol === 'https:') return true; - return target.protocol === 'http:' && isLoopbackHostname(target.hostname); + return target.protocol === 'http:' && isDocumentAllowedCleartextHost(target.hostname); } -function isLoopbackHostname(hostname: string): boolean { - if (hostname === 'localhost' || hostname.endsWith('.localhost') || hostname === '[::1]') return true; - return LOOPBACK_IPV4_PATTERN.test(hostname); +function isDocumentAllowedCleartextHost(hostname: string): boolean { + return hostname === 'localhost' || hostname.endsWith('.localhost') || hostname === '127.0.0.1'; } export function updateDesktopLocalResourceURLTarget(value: string, updateTarget: (target: URL) => boolean): string { diff --git a/fluxer_app/src/features/messaging/utils/ImageCacheUtils.ts b/fluxer_app/src/features/messaging/utils/ImageCacheUtils.ts index 90ad7a7d4..9febfc32d 100644 --- a/fluxer_app/src/features/messaging/utils/ImageCacheUtils.ts +++ b/fluxer_app/src/features/messaging/utils/ImageCacheUtils.ts @@ -21,6 +21,7 @@ interface ImageCacheEntry { image: HTMLImageElement | null; subscribers: Set; failedAttempts: number; + failedCycles: number; retryDelayMs: number; failedUntil: number; loadTimeoutId: number; @@ -35,7 +36,20 @@ const IMAGE_LOAD_TIMEOUT_MS = 30_000; const IMAGE_RETRY_ATTEMPT_LIMIT = 2; const IMAGE_RETRY_INITIAL_DELAY_MS = 1000; const IMAGE_RETRY_MAX_DELAY_MS = IMAGE_RETRY_INITIAL_DELAY_MS * 10; +const IMAGE_FAILURE_INITIAL_COOLDOWN_MS = 2000; const IMAGE_FAILURE_COOLDOWN_MS = 60_000; +const IMAGE_RECOVERY_ATTENTION_WAKE_INTERVAL_MS = 15_000; + +interface ImageRecovery { + readonly src: string; + readonly waiters: Set<() => void>; + timeoutId: number; + cancelLoad: (() => void) | null; +} + +const recoveries = new Map(); +let recoveryWakeListenersInstalled = false; +let lastAttentionWakeAt = Number.NEGATIVE_INFINITY; const imageCache = new LRUCache({ max: MAX_CACHE_ENTRIES, @@ -71,6 +85,12 @@ const ownsCacheKey = (entry: ImageCacheEntry): boolean => imageCache.peek(entry. const isCoolingDown = (entry: ImageCacheEntry): boolean => entry.failedUntil > Date.now(); +const isLoadInFlight = (entry: ImageCacheEntry): boolean => + entry.image != null || entry.retryTimeoutId !== 0 || entry.connectivityListener != null; + +const failureCooldownMs = (failedCycles: number): number => + Math.min(IMAGE_FAILURE_COOLDOWN_MS, IMAGE_FAILURE_INITIAL_COOLDOWN_MS * 2 ** Math.max(0, failedCycles - 1)); + function clearRetryState(entry: ImageCacheEntry): void { if (entry.retryTimeoutId !== 0) { window.clearTimeout(entry.retryTimeoutId); @@ -94,14 +114,11 @@ function detachImageLoad(entry: ImageCacheEntry): void { } } -function notifySubscribers(entry: ImageCacheEntry, loaded: boolean): void { - const subscribers = [...entry.subscribers]; - entry.subscribers.clear(); +function runCallbacks(callbacks: ReadonlyArray<() => void>): void { const failures: Array = []; - for (const subscriber of subscribers) { + for (const callback of callbacks) { try { - if (loaded) subscriber.onLoad(); - else if (subscriber.onError) subscriber.onError(); + callback(); } catch (error) { failures.push(error); } @@ -109,6 +126,12 @@ function notifySubscribers(entry: ImageCacheEntry, loaded: boolean): void { if (failures.length > 0) throw new AggregateError(failures, 'Image load callbacks failed'); } +function notifySubscribers(entry: ImageCacheEntry, loaded: boolean): void { + const subscribers = [...entry.subscribers]; + entry.subscribers.clear(); + runCallbacks(subscribers.map((subscriber) => (loaded ? subscriber.onLoad : (subscriber.onError ?? (() => {}))))); +} + function abandonEntry(entry: ImageCacheEntry): void { clearRetryState(entry); detachImageLoad(entry); @@ -122,8 +145,9 @@ function dropEntry(entry: ImageCacheEntry): void { function failEntry(entry: ImageCacheEntry): void { entry.failedAttempts = 0; + entry.failedCycles += 1; entry.retryDelayMs = IMAGE_RETRY_INITIAL_DELAY_MS; - entry.failedUntil = Date.now() + IMAGE_FAILURE_COOLDOWN_MS; + entry.failedUntil = Date.now() + failureCooldownMs(entry.failedCycles); abandonEntry(entry); } @@ -135,9 +159,14 @@ function settleLoaded(entry: ImageCacheEntry, image: HTMLImageElement): void { entry.width = image.naturalWidth; entry.height = image.naturalHeight; entry.failedAttempts = 0; + entry.failedCycles = 0; entry.retryDelayMs = IMAGE_RETRY_INITIAL_DELAY_MS; entry.failedUntil = 0; - notifySubscribers(entry, true); + try { + notifySubscribers(entry, true); + } finally { + resolveRecovery(entry.src); + } } function retryDelayForAttempt(entry: ImageCacheEntry): number { @@ -200,6 +229,7 @@ function createEntry(src: string): ImageCacheEntry { image: null, subscribers: new Set(), failedAttempts: 0, + failedCycles: 0, retryDelayMs: IMAGE_RETRY_INITIAL_DELAY_MS, failedUntil: 0, loadTimeoutId: 0, @@ -260,7 +290,7 @@ export function loadImage(src: string | null | undefined, onLoad: () => void, on const entry = cached ?? createEntry(src); const subscriber: ImageSubscriber = {onLoad, onError}; entry.subscribers.add(subscriber); - if (cached == null || cached.failedUntil !== 0) { + if (!isLoadInFlight(entry)) { entry.failedUntil = 0; startImageLoad(entry); } @@ -269,6 +299,125 @@ export function loadImage(src: string | null | undefined, onLoad: () => void, on }; } +export function reportImageError(src: string | null | undefined): void { + if (!acceptsImageSource(src)) return; + const entry = imageCache.get(src) ?? createEntry(src); + if (isLoadInFlight(entry) || isCoolingDown(entry)) return; + entry.loaded = false; + entry.width = 0; + entry.height = 0; + failEntry(entry); +} + +export function awaitImage(src: string | null | undefined, onLoad: () => void): () => void { + if (!acceptsImageSource(src)) return () => {}; + if (hasImage(src)) { + onLoad(); + return () => {}; + } + const recovery = recoveries.get(src) ?? createRecovery(src); + const waiter = (): void => onLoad(); + recovery.waiters.add(waiter); + if (recovery.timeoutId === 0 && recovery.cancelLoad == null) attemptRecovery(recovery); + return () => { + recovery.waiters.delete(waiter); + if (recovery.waiters.size === 0) stopRecovery(recovery); + }; +} + +function createRecovery(src: string): ImageRecovery { + installRecoveryWakeListeners(); + const recovery: ImageRecovery = {src, waiters: new Set(), timeoutId: 0, cancelLoad: null}; + recoveries.set(src, recovery); + return recovery; +} + +function clearRecoveryWork(recovery: ImageRecovery): void { + if (recovery.timeoutId !== 0) { + window.clearTimeout(recovery.timeoutId); + recovery.timeoutId = 0; + } + const cancelLoad = recovery.cancelLoad; + recovery.cancelLoad = null; + cancelLoad?.(); +} + +function stopRecovery(recovery: ImageRecovery): void { + clearRecoveryWork(recovery); + if (recoveries.get(recovery.src) === recovery) recoveries.delete(recovery.src); +} + +function resolveRecovery(src: string): void { + const recovery = recoveries.get(src); + if (recovery == null) return; + const waiters = [...recovery.waiters]; + recovery.waiters.clear(); + stopRecovery(recovery); + runCallbacks(waiters); +} + +function scheduleRecovery(recovery: ImageRecovery): void { + if (recoveries.get(recovery.src) !== recovery) return; + const entry = imageCache.peek(recovery.src); + const delay = entry != null && isCoolingDown(entry) ? entry.failedUntil - Date.now() : IMAGE_RETRY_INITIAL_DELAY_MS; + recovery.timeoutId = window.setTimeout(() => { + recovery.timeoutId = 0; + attemptRecovery(recovery); + }, delay); +} + +function attemptRecovery(recovery: ImageRecovery): void { + clearRecoveryWork(recovery); + if (hasImage(recovery.src)) { + resolveRecovery(recovery.src); + return; + } + const entry = imageCache.peek(recovery.src); + if (entry != null && isCoolingDown(entry)) { + scheduleRecovery(recovery); + return; + } + let settled = false; + const cancelLoad = loadImage( + recovery.src, + () => { + settled = true; + }, + () => { + settled = true; + recovery.cancelLoad = null; + scheduleRecovery(recovery); + }, + ); + if (!settled) recovery.cancelLoad = cancelLoad; +} + +function wakeRecoveries(): void { + for (const recovery of [...recoveries.values()]) { + if (recovery.cancelLoad != null) continue; + const entry = imageCache.peek(recovery.src); + if (entry != null && !isLoadInFlight(entry)) entry.failedUntil = 0; + attemptRecovery(recovery); + } +} + +function wakeRecoveriesOnAttention(): void { + const now = Date.now(); + if (now - lastAttentionWakeAt < IMAGE_RECOVERY_ATTENTION_WAKE_INTERVAL_MS) return; + lastAttentionWakeAt = now; + wakeRecoveries(); +} + +function installRecoveryWakeListeners(): void { + if (recoveryWakeListenersInstalled || typeof window === 'undefined') return; + recoveryWakeListenersInstalled = true; + window.addEventListener('online', wakeRecoveries); + window.addEventListener('focus', wakeRecoveriesOnAttention); + document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'visible') wakeRecoveriesOnAttention(); + }); +} + export function pinImage(src: string | null | undefined): () => void { return loadImage(src, () => {}); } @@ -278,5 +427,7 @@ export function warmImage(src: string | null | undefined): void { } export function _clearForTests(): void { + for (const recovery of [...recoveries.values()]) stopRecovery(recovery); imageCache.clear(); + lastAttentionWakeAt = Number.NEGATIVE_INFINITY; } diff --git a/fluxer_app/src/features/ui/components/BaseAvatar.tsx b/fluxer_app/src/features/ui/components/BaseAvatar.tsx index 7a2518e34..72443d516 100644 --- a/fluxer_app/src/features/ui/components/BaseAvatar.tsx +++ b/fluxer_app/src/features/ui/components/BaseAvatar.tsx @@ -2,6 +2,7 @@ import Accessibility from '@app/features/accessibility/state/Accessibility'; import {getStatusTypeLabel} from '@app/features/app/constants/AppConstants'; +import {useImageRecovery} from '@app/features/messaging/hooks/useImageRecovery'; import * as ImageCacheUtils from '@app/features/messaging/utils/ImageCacheUtils'; import {remFromPx} from '@app/features/theme/layout/RemFromPx'; import {type AvatarStatusLayout, getAvatarStatusLayout} from '@app/features/ui/components/AvatarStatusLayout'; @@ -13,7 +14,7 @@ import type {StatusType} from '@fluxer/constants/src/StatusConstants'; import {normalizeStatus, StatusTypes} from '@fluxer/constants/src/StatusConstants'; import {msg} from '@lingui/core/macro'; import {useLingui} from '@lingui/react/macro'; -import React, {useCallback, useEffect, useId, useMemo, useState} from 'react'; +import React, {useCallback, useId, useMemo, useState} from 'react'; const AVATAR_DESCRIPTOR = msg({ message: 'Avatar', @@ -29,7 +30,6 @@ const STATUS_DESCRIPTOR = msg({ }); const DEFAULT_CUSTOM_STATUS_BADGE_SCALE = 1.25; const DEFAULT_CUSTOM_STATUS_BADGE_CUTOUT_PADDING_SCALE = 1.2; -const AVATAR_IMAGE_RETRY_LIMIT = 3; const PLACEHOLDER_FAST_FADE_WINDOW_MS = 200; type AvatarCSSProperties = React.CSSProperties & { @@ -175,32 +175,12 @@ export const BaseAvatar = React.forwardRef( const maskIsMobileOnline = shouldShowCustomStatusBadge ? false : isMobileOnline; const reducedMotion = Accessibility.useReducedMotion; const candidateUrl = animatedMediaPlaybackEnabled ? hoverAvatarUrl || '' : avatarUrl; - const [imgError, setImgError] = useState(() => ImageCacheUtils.hasFailedImage(candidateUrl)); - const [imgRetryCount, setImgRetryCount] = useState(0); + const {failed: imgError, reportError: handleImageError} = useImageRecovery(candidateUrl); const [wasCachedAtMount] = useState(() => ImageCacheUtils.hasImage(candidateUrl)); const [mountedAt] = useState(() => Date.now()); const [paintedImage, setPaintedImage] = useState(() => wasCachedAtMount ? {url: candidateUrl, fade: 'instant'} : null, ); - useEffect(() => { - setImgError(ImageCacheUtils.hasFailedImage(candidateUrl)); - setImgRetryCount(0); - }, [candidateUrl]); - useEffect(() => { - if (!imgError || !candidateUrl || imgRetryCount >= AVATAR_IMAGE_RETRY_LIMIT) { - return; - } - let active = true; - const cancelImageRetry = ImageCacheUtils.loadImage(candidateUrl, () => { - if (!active) return; - setImgRetryCount((count) => count + 1); - setImgError(false); - }); - return () => { - active = false; - cancelImageRetry(); - }; - }, [candidateUrl, imgError, imgRetryCount]); const handleImageRef = useCallback( (node: HTMLImageElement | null) => { if (node == null) return; @@ -227,9 +207,6 @@ export const BaseAvatar = React.forwardRef( }, [mountedAt, onImageLoaded], ); - const handleImageError = useCallback(() => { - setImgError(true); - }, []); const showFallback = !candidateUrl || imgError; const paintedCandidate = paintedImage?.url === candidateUrl ? paintedImage : null; const shouldMountPlaceholder = showSkeleton && !wasCachedAtMount; diff --git a/fluxer_app/src/features/user/events/UserUpdate.ts b/fluxer_app/src/features/user/events/UserUpdate.ts index 7aedebad7..be7cf3cd0 100644 --- a/fluxer_app/src/features/user/events/UserUpdate.ts +++ b/fluxer_app/src/features/user/events/UserUpdate.ts @@ -1,5 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later +import {syncAccountUserData} from '@app/features/auth/state/AccountUserDataSync'; import type {GatewayHandlerContext} from '@app/features/gateway/events/EventRouter'; import GuildVerification from '@app/features/guild/state/GuildVerification'; import Messages from '@app/features/messaging/state/MessagingMessages'; @@ -12,13 +13,18 @@ export interface UserUpdatePayload { id: string; username: string; discriminator: string; + global_name?: string | null; + email?: string | null; avatar: string | null; flags: number; is_staff?: boolean; } -export function handleUserUpdate(data: UserUpdatePayload, _context: GatewayHandlerContext): void { +export function handleUserUpdate(data: UserUpdatePayload, context: GatewayHandlerContext): void { Users.handleUserUpdate(data as User); + if (context.accountKey !== null && data.id === context.expectedUserId) { + syncAccountUserData(context.accountKey, data); + } Messages.handleUserUpdate({user: {id: data.id}}); Permission.handleUserUpdate(data.id); QuickSwitcher.recomputeIfOpen(); diff --git a/fluxer_app/src/features/user/utils/AvatarUtils.ts b/fluxer_app/src/features/user/utils/AvatarUtils.ts index 8d47ec0ab..3f02274ba 100644 --- a/fluxer_app/src/features/user/utils/AvatarUtils.ts +++ b/fluxer_app/src/features/user/utils/AvatarUtils.ts @@ -4,6 +4,7 @@ import RuntimeConfig from '@app/features/app/state/RuntimeConfig'; import DeveloperOptions from '@app/features/devtools/state/DeveloperOptions'; import { buildMediaProxyURL, + buildMediaProxyURLForInstance, LARGEST_MEDIA_PROXY_IMAGE_SIZE, MEDIA_PROXY_IMAGE_SIZE_LADDER, snapMediaProxyImageSize, @@ -99,6 +100,7 @@ type MediaURLParams = { format: string; animated?: boolean; endpoint?: string; + instanceKey?: string; }; const parseMediaHashForRequest = (value: string, animated = false) => { @@ -109,7 +111,7 @@ const parseMediaHashForRequest = (value: string, animated = false) => { animated: shouldAnimate ? true : undefined, }; }; -const getMediaURL = ({path, id, hash, size, format, animated, endpoint}: MediaURLParams) => { +const getMediaURL = ({path, id, hash, size, format, animated, endpoint, instanceKey}: MediaURLParams) => { if (DeveloperOptions.forceRenderPlaceholders) { return ''; } @@ -120,7 +122,10 @@ const getMediaURL = ({path, id, hash, size, format, animated, endpoint}: MediaUR const basePath = `${path}/${id}/${hash}.${format}`; const url = size ? setPathQueryParams(basePath, {size}) : basePath; const proxyOptions = animated === undefined ? undefined : {animated}; - return buildMediaProxyURL(`${normalizeEndpoint(baseEndpoint)}/${url}`, proxyOptions); + const target = `${normalizeEndpoint(baseEndpoint)}/${url}`; + return instanceKey === undefined + ? buildMediaProxyURL(target, proxyOptions) + : buildMediaProxyURLForInstance(target, instanceKey, proxyOptions); }; type GuildMemberMediaURLParams = { @@ -252,6 +257,8 @@ export function getUserBannerURL({id, banner}: BannerOptions, animated = false, }); } +const mediaURLCacheScope = (): string => `${RuntimeConfig.apiEndpoint} ${RuntimeConfig.mediaEndpoint}`; + const SOUNDBOARD_SOUND_URL_CACHE = new Map(); const SOUNDBOARD_SOUND_URL_CACHE_LIMIT = 4096; @@ -259,7 +266,7 @@ export function getSoundboardSoundURL(soundId: string): string { if (DeveloperOptions.forceRenderPlaceholders) { return ''; } - const key = `${RuntimeConfig.mediaEndpoint}:${soundId}`; + const key = `${mediaURLCacheScope()}:${soundId}`; const cached = SOUNDBOARD_SOUND_URL_CACHE.get(key); if (cached !== undefined) return cached; const result = mediaUrl(`${SOUNDBOARD_SOUND_PATH_PREFIX}/${soundId}`); @@ -423,16 +430,17 @@ export function getGuildMemberBannerURL({ return ''; } -export function getUserAvatarURLWithProxy( - options: AvatarOptions, - endpoint: string, +export interface AvatarMediaInstance { + readonly mediaEndpoint: string; + readonly instanceKey: string; +} + +export function getUserAvatarURLForInstance( + {id, avatar}: AvatarOptions, + instance: AvatarMediaInstance, animated = false, size: MediaProxyImageSize = MEDIA_PROXY_AVATAR_SIZE_DEFAULT, ) { - if (!endpoint) { - return getUserAvatarURL(options, animated, size); - } - const {id, avatar} = options; if (!avatar) { return getDefaultAvatarURLForIndex(getDefaultAvatarIndex(id)); } @@ -443,7 +451,8 @@ export function getUserAvatarURLWithProxy( hash, size: snapIconImageSize(size), animated: shouldAnimate, - endpoint, + endpoint: instance.mediaEndpoint, + instanceKey: instance.instanceKey, }); } @@ -499,7 +508,7 @@ export function getEmojiURL({id, animated, isAnimatable}: {id: string; animated? return ''; } const animatedFlag = isAnimatable === false ? false : animated === true; - const key = `${RuntimeConfig.mediaEndpoint}:${animatedFlag ? 'a' : 's'}:${id}`; + const key = `${mediaURLCacheScope()}:${animatedFlag ? 'a' : 's'}:${id}`; const cached = EMOJI_URL_CACHE.get(key); if (cached !== undefined) return cached; const result = mediaUrl(`emojis/${id}.webp`, {animated: animatedFlag}); @@ -529,7 +538,7 @@ export function getStickerURL({ } const animatedFlag = isAnimatable === false ? false : animated === true; const safeSize: StickerSize = size === 320 ? 320 : 160; - const key = `${RuntimeConfig.mediaEndpoint}:${animatedFlag ? 'a' : 's'}:${safeSize}:${id}`; + const key = `${mediaURLCacheScope()}:${animatedFlag ? 'a' : 's'}:${safeSize}:${id}`; const cached = STICKER_URL_CACHE.get(key); if (cached !== undefined) return cached; const result = mediaUrl(setPathQueryParams(`stickers/${id}.webp`, {size: safeSize}), {animated: animatedFlag}); diff --git a/fluxer_desktop/src/main/DesktopOutboundHTTP.test.mjs b/fluxer_desktop/src/main/DesktopOutboundHTTP.test.mjs index 34af111c5..1e8bfbc93 100644 --- a/fluxer_desktop/src/main/DesktopOutboundHTTP.test.mjs +++ b/fluxer_desktop/src/main/DesktopOutboundHTTP.test.mjs @@ -508,7 +508,7 @@ describe('DesktopOutboundHTTP origin anchoring', () => { } }); - test('a released binding refuses a re-resolution that changes the address scope', async () => { + test('a released cleartext binding that re-resolves to a public address never sends cleartext there', async () => { const {port, server} = await startServer({}); await new Promise((resolve) => server.close(resolve)); let rebound = false; @@ -528,7 +528,67 @@ describe('DesktopOutboundHTTP origin anchoring', () => { }; await assert.rejects(client.fetch(request), /ECONNREFUSED/); rebound = true; - await assert.rejects(client.fetch(request), /resolved to both public and non-public addresses/); + await assert.rejects(client.fetch(request), /requires https for the publicly routable origin/); + outboundHTTP.cleanup(); + }); + + test('a split-horizon origin that moves from its LAN address to a public one is resolved again instead of failing until restart', async () => { + const {port, server} = await startServer({}); + await new Promise((resolve) => server.close(resolve)); + let rebound = false; + let resolutions = 0; + const outboundHTTP = new DesktopOutboundHTTP({ + resolveHostAddresses: async () => { + resolutions += 1; + return rebound ? ['93.184.216.34'] : ['127.0.0.1']; + }, + }); + const request = (signal) => ({ + body: null, + expectedOrigin: `https://home.example:${port}`, + headers: null, + method: 'GET', + originTrust: DesktopOriginTrust.BOUND, + serviceName: 'test', + signal, + timeoutMs: 2000, + url: `https://home.example:${port}/ping`, + }); + await assert.rejects(outboundHTTP.request(request(null)), /ECONNREFUSED/); + rebound = true; + const left = new AbortController(); + left.abort(); + await assert.rejects(outboundHTTP.request(request(left.signal)), /was aborted by its caller/); + assert.equal(resolutions, 2); + outboundHTTP.cleanup(); + }); + + test('an address that swallows connections is released when the request gives up before connecting', async () => { + let resolutions = 0; + const outboundHTTP = new DesktopOutboundHTTP({ + resolveHostAddresses: async () => { + resolutions += 1; + return ['10.255.255.1']; + }, + }); + const request = { + body: null, + expectedOrigin: 'http://blackhole.example:9', + headers: null, + method: 'GET', + originTrust: DesktopOriginTrust.BOUND, + serviceName: 'test', + signal: null, + timeoutMs: 200, + url: 'http://blackhole.example:9/ping', + }; + await assert.rejects(outboundHTTP.request(request)); + await assert.rejects(outboundHTTP.request(request)); + assert.equal( + resolutions, + 2, + 'a binding whose address never answers stays pinned for the process lifetime when a timeout does not release it', + ); outboundHTTP.cleanup(); }); diff --git a/fluxer_desktop/src/main/DesktopOutboundHTTP.ts b/fluxer_desktop/src/main/DesktopOutboundHTTP.ts index bf50bafb2..4df23f28e 100644 --- a/fluxer_desktop/src/main/DesktopOutboundHTTP.ts +++ b/fluxer_desktop/src/main/DesktopOutboundHTTP.ts @@ -231,13 +231,6 @@ class DesktopOutboundHTTPOriginNotRegisteredError extends Error { } } -class DesktopOutboundHTTPMixedAddressScopeError extends Error { - public constructor(origin: string) { - super(`Desktop outbound HTTP origin ${origin} resolved to both public and non-public addresses`); - this.name = 'DesktopOutboundHTTPMixedAddressScopeError'; - } -} - class DesktopOutboundHTTPEmptyResolutionError extends Error { public constructor(origin: string) { super(`Desktop outbound HTTP origin ${origin} resolved to no usable address`); @@ -640,15 +633,6 @@ function requireScope(binding: DesktopOriginAddressBinding, requirement: Desktop } } -function requireStableScope( - previous: DesktopOriginAddressBinding | undefined, - next: DesktopOriginAddressBinding, -): void { - if (previous != null && previous.scope !== next.scope) { - throw new DesktopOutboundHTTPMixedAddressScopeError(next.origin); - } -} - function isStaleKeepAliveSocketError(error: unknown): boolean { if (error == null || typeof error !== 'object') { return false; @@ -1168,7 +1152,6 @@ export class DesktopOutboundHTTP { const record: DesktopPendingOriginBinding = { operation: this.resolveBinding(origin).then((binding) => { this.requireAdmission(); - requireStableScope(existing, binding); requireScope(binding, record.requirement); this.bindings.set(origin, binding); return binding; @@ -1271,13 +1254,21 @@ export class DesktopOutboundHTTP { let attemptsRemaining = isReplayableRequest(request) ? DESKTOP_OUTBOUND_HTTP_STALE_SOCKET_ATTEMPTS : 0; let active: http.ClientRequest | null = null; let terminated = false; + const releaseUnconnectedBinding = (): void => { + const socket = active?.socket; + if (socket == null || socket.connecting) { + this.markBindingUnreachable(binding); + } + }; const timeout = setTimeout(() => { terminated = true; + releaseUnconnectedBinding(); active?.destroy(new DesktopOutboundHTTPRequestTimeoutError(target.toString(), request.timeoutMs)); }, request.timeoutMs); timeout.unref(); const onAbort = (): void => { terminated = true; + releaseUnconnectedBinding(); active?.destroy(new DesktopOutboundHTTPRequestAbortedError(target.toString())); }; request.signal?.addEventListener('abort', onAbort, {once: true}); diff --git a/fluxer_desktop/src/main/DesktopRuntimeDiscovery.ts b/fluxer_desktop/src/main/DesktopRuntimeDiscovery.ts index c2a67cf40..3b23773e9 100644 --- a/fluxer_desktop/src/main/DesktopRuntimeDiscovery.ts +++ b/fluxer_desktop/src/main/DesktopRuntimeDiscovery.ts @@ -120,6 +120,25 @@ export async function resolveDesktopRuntimePlan({ return resolved.plan; } +export async function restoreRememberedRuntimePlan(instanceKey: string): Promise { + let apiEndpoint: string; + try { + apiEndpoint = requireDesktopRuntimeAPIEndpoint(instanceKey); + } catch { + return null; + } + const remembered = await readLastServedDiscovery(apiEndpoint); + if (remembered === null || remembered.plan.instanceKey !== instanceKey) { + return null; + } + await getDesktopOutboundHTTP().registerAnchoredOrigins({ + anchorOrigin: requireDesktopHTTPOrigin(new URL(apiEndpoint).origin), + origins: runtimePlanTrustedHTTPOrigins(remembered.plan), + unresolvedAnchorRequirement: remembered.anchorRequirement, + }); + return remembered.plan; +} + export function desktopRuntimeInitialInput(): string | null { return getLaunchInstanceEndpointOverride(); } diff --git a/fluxer_desktop/src/main/DesktopRuntimeDiscoveryUnreachable.test.mjs b/fluxer_desktop/src/main/DesktopRuntimeDiscoveryUnreachable.test.mjs index 7496f9552..350321c29 100644 --- a/fluxer_desktop/src/main/DesktopRuntimeDiscoveryUnreachable.test.mjs +++ b/fluxer_desktop/src/main/DesktopRuntimeDiscoveryUnreachable.test.mjs @@ -53,7 +53,9 @@ installTestModuleStub( }`, ); -const {resolveDesktopRuntimePlan} = await import('@electron/main/DesktopRuntimeDiscovery'); +const {resolveDesktopRuntimePlan, restoreRememberedRuntimePlan} = await import( + '@electron/main/DesktopRuntimeDiscovery' +); const {DEPLOYED_OFFICIAL_DOCUMENT} = await import('@fluxer/instance_bootstrap/src/__tests__/DiscoveryFixtures'); const {DESKTOP_RUNTIME_DISCOVERY_UNREACHABLE_ERROR_NAME} = await import( '@fluxer/desktop_ipc/src/LocalAppRuntimeContract' @@ -183,3 +185,33 @@ describe('desktop discovery while the instance is unreachable', () => { assert.equal(markers.size, 0); }); }); + +describe('a media or API route for a saved instance after a cold start', () => { + beforeEach(() => { + markers.clear(); + storage.refuseWrites = false; + network.fetched.length = 0; + network.resolvable = true; + network.registrations.length = 0; + network.respond = served(DEPLOYED_OFFICIAL_DOCUMENT); + }); + + test('restores the plan of an instance this app was served before without fetching discovery', async () => { + const online = await resolve(); + network.fetched.length = 0; + network.registrations.length = 0; + const restored = await restoreRememberedRuntimePlan(online.instanceKey); + assert.equal(restored?.instanceKey, online.instanceKey); + assert.deepEqual(restored?.endpoints, online.endpoints); + assert.deepEqual(network.fetched, []); + assert.equal(network.registrations.length, 1); + assert.equal(network.registrations[0].unresolvedAnchorRequirement, 'public'); + }); + + test('never invents a plan for an instance this app was not served', async () => { + assert.equal(await restoreRememberedRuntimePlan('https://unknown.example'), null); + assert.equal(await restoreRememberedRuntimePlan('not a url'), null); + assert.deepEqual(network.fetched, []); + assert.deepEqual(network.registrations, []); + }); +}); diff --git a/fluxer_desktop/src/main/LocalAppAPIProxyRequestHandler.ts b/fluxer_desktop/src/main/LocalAppAPIProxyRequestHandler.ts index d3261c614..845db6552 100644 --- a/fluxer_desktop/src/main/LocalAppAPIProxyRequestHandler.ts +++ b/fluxer_desktop/src/main/LocalAppAPIProxyRequestHandler.ts @@ -30,7 +30,7 @@ import {LOCAL_APP_API_PATH_PREFIX} from '@fluxer/desktop_ipc/src/LocalAppRouteCo const log = createChildLogger('LocalAppAPIProxy'); interface LocalAppAPIProxyRuntimePlans { - findPlanForRoute(runtimeKey: string): LocalAppRuntimePlan | null; + planForRoute(runtimeKey: string): Promise; } interface DesktopLocalAppAPIProxyRequestHandlerDependencies { @@ -62,7 +62,7 @@ export class DesktopLocalAppAPIProxyRequestHandler { if (route == null) { return localAppProxyNotFoundResponse('Unknown local app API runtime'); } - const plan = this.runtimePlans.findPlanForRoute(route.runtimeKey); + const plan = await this.runtimePlans.planForRoute(route.runtimeKey); if (plan == null) { return localAppProxyNotFoundResponse('Unknown local app API runtime'); } diff --git a/fluxer_desktop/src/main/LocalAppProtocol.ts b/fluxer_desktop/src/main/LocalAppProtocol.ts index e13fa9173..89d1310cb 100644 --- a/fluxer_desktop/src/main/LocalAppProtocol.ts +++ b/fluxer_desktop/src/main/LocalAppProtocol.ts @@ -3,12 +3,13 @@ import {DESKTOP_APP_SCHEME} from '@electron/common/Constants'; import {createChildLogger} from '@electron/common/Logger'; import {getDesktopOutboundHTTP} from '@electron/main/DesktopOutboundHTTP'; +import {restoreRememberedRuntimePlan} from '@electron/main/DesktopRuntimeDiscovery'; import {recordGatewayOriginAnchor} from '@electron/main/GatewayOriginRegistry'; import {readStagedHarvestLocalStorageSync} from '@electron/main/LegacyOriginHarvestStore'; import type {LocalAppIndexContext} from '@electron/main/LocalAppFileRequestHandler'; import {DesktopLocalAppFiles} from '@electron/main/LocalAppFileResolver'; import {getDesktopLocalAppAuthorization} from '@electron/main/LocalAppProtocolAuthorization'; -import {DesktopLocalAppProxyClient} from '@electron/main/LocalAppProxyClient'; +import {DesktopLocalAppProxyClient, LocalAppProxyTraffic} from '@electron/main/LocalAppProxyClient'; import {DesktopLocalAppRequestHandler} from '@electron/main/LocalAppRequestHandler'; import { DesktopLocalAppRuntimePlans, @@ -57,6 +58,7 @@ export class DesktopLocalAppProtocol { private readonly shutdownController = new AbortController(); private readonly activeOperations = new Set>(); private readonly runtimePlans = new DesktopLocalAppRuntimePlans(); + private readonly planRestorations = new Map>(); private readonly files = new DesktopLocalAppFiles(); private readonly requestHandler: DesktopLocalAppRequestHandler; private readonly stopObservingCommittedModuleFiles: () => void; @@ -69,8 +71,15 @@ export class DesktopLocalAppProtocol { authorization: getDesktopLocalAppAuthorization(), files: this.files, indexContext: () => this.getIndexContext(), - proxyClient: new DesktopLocalAppProxyClient({outboundHTTP: getDesktopOutboundHTTP()}), - runtimePlans: this.runtimePlans, + apiProxyClient: new DesktopLocalAppProxyClient({ + outboundHTTP: getDesktopOutboundHTTP(), + traffic: LocalAppProxyTraffic.API, + }), + resourceProxyClient: new DesktopLocalAppProxyClient({ + outboundHTTP: getDesktopOutboundHTTP(), + traffic: LocalAppProxyTraffic.REMOTE_RESOURCE, + }), + runtimePlans: {planForRoute: (runtimeKey) => this.planForRoute(runtimeKey)}, shutdownSignal: this.shutdownController.signal, }); } @@ -126,6 +135,40 @@ export class DesktopLocalAppProtocol { return this.runtimePlans.findPlanForRoute(runtimeKey); } + public planForRoute(runtimeKey: string): Promise { + const known = this.runtimePlans.findPlanForRoute(runtimeKey); + if (known != null) { + return Promise.resolve(known); + } + let restoration = this.planRestorations.get(runtimeKey); + if (restoration == null) { + restoration = this.restoreRuntimePlan(runtimeKey).finally(() => { + this.planRestorations.delete(runtimeKey); + }); + this.planRestorations.set(runtimeKey, restoration); + } + return restoration; + } + + private async restoreRuntimePlan(runtimeKey: string): Promise { + let plan: LocalAppRuntimePlan | null; + try { + plan = await restoreRememberedRuntimePlan(runtimeKey); + } catch (error) { + log.warn('Failed to restore a remembered local app runtime plan', {instanceKey: runtimeKey, error}); + return null; + } + if (plan == null || !this.acceptingRequests) { + return null; + } + const known = this.runtimePlans.findPlanForRoute(runtimeKey); + if (known != null) { + return known; + } + this.cacheRuntimePlan(plan); + return plan; + } + public cacheRuntimePlan(plan: LocalAppRuntimePlan): void { this.requireAdmission(); this.requireRuntimeOriginsRegistered(plan); diff --git a/fluxer_desktop/src/main/LocalAppProxyClient.ts b/fluxer_desktop/src/main/LocalAppProxyClient.ts index fa6c8f813..4c7f78ba1 100644 --- a/fluxer_desktop/src/main/LocalAppProxyClient.ts +++ b/fluxer_desktop/src/main/LocalAppProxyClient.ts @@ -19,7 +19,7 @@ const LOCAL_APP_PROXY_DEADLINE_MS = 5 * 60 * 1000; const LOCAL_APP_PROXY_REQUEST_BODY_MAX_BYTES = 100 * 1024 * 1024; export const LOCAL_APP_PROXY_ACCEPT_ENCODING = 'gzip, br'; -const LOCAL_APP_PROXY_SERVICE_NAME = 'desktop_local_app_proxy'; +const LOCAL_APP_PROXY_RESOURCE_RESPONSE_HEADERS_DEADLINE_MS = 30_000; const LOCAL_APP_PROXY_MAX_CONCURRENT_REQUESTS = 32; const LOCAL_APP_PROXY_MAX_QUEUED_REQUESTS = 4096; const UPLOAD_PROGRESS_MIN_BYTES = 64 * 1024; @@ -28,6 +28,20 @@ const MINIMUM_RESPONSE_STATUS = 200; const MAXIMUM_RESPONSE_STATUS = 599; const NULL_BODY_STATUSES: ReadonlySet = new Set([204, 205, 304]); +export const LocalAppProxyTraffic = Object.freeze({ + API: 'desktop_local_app_proxy', + REMOTE_RESOURCE: 'desktop_local_app_resource_proxy', +} as const); + +export type LocalAppProxyTraffic = (typeof LocalAppProxyTraffic)[keyof typeof LocalAppProxyTraffic]; + +class LocalAppProxyResponseHeadersDeadlineError extends Error { + public constructor(timeoutMs: number) { + super(`Local app proxy upstream sent no response headers within ${timeoutMs} ms`); + this.name = 'LocalAppProxyResponseHeadersDeadlineError'; + } +} + class LocalAppProxyQueueOverflowError extends Error { public constructor() { super('Local app proxy has too many queued requests'); @@ -78,6 +92,11 @@ class LocalAppProxyResponseBodyLease { class LocalAppProxyConcurrencyGate { private active = 0; private readonly waiting: Array<{readonly admit: () => void}> = []; + private readonly newestFirst: boolean; + + public constructor(newestFirst: boolean) { + this.newestFirst = newestFirst; + } public acquire(signal: AbortSignal): Promise { signal.throwIfAborted(); @@ -109,7 +128,7 @@ class LocalAppProxyConcurrencyGate { } private release(): void { - const next = this.waiting.shift(); + const next = this.newestFirst ? this.waiting.pop() : this.waiting.shift(); if (next != null) { next.admit(); return; @@ -120,6 +139,7 @@ class LocalAppProxyConcurrencyGate { interface DesktopLocalAppProxyClientDependencies { readonly outboundHTTP: DesktopOutboundHTTP; + readonly traffic: LocalAppProxyTraffic; } interface LocalAppProxyFetchRequest { @@ -206,23 +226,49 @@ function emitLocalAppUploadProgress(progress: DesktopLocalAppUploadProgress): vo export class DesktopLocalAppProxyClient { private readonly outboundHTTP: DesktopOutboundHTTP; + private readonly traffic: LocalAppProxyTraffic; + private readonly concurrency: LocalAppProxyConcurrencyGate; + private readonly responseHeadersDeadlineMs: number | null; public constructor(dependencies: DesktopLocalAppProxyClientDependencies) { this.outboundHTTP = dependencies.outboundHTTP; + this.traffic = dependencies.traffic; + const remoteResource = dependencies.traffic === LocalAppProxyTraffic.REMOTE_RESOURCE; + this.concurrency = new LocalAppProxyConcurrencyGate(remoteResource); + this.responseHeadersDeadlineMs = remoteResource ? LOCAL_APP_PROXY_RESOURCE_RESPONSE_HEADERS_DEADLINE_MS : null; } - private readonly concurrency = new LocalAppProxyConcurrencyGate(); - public async fetch(request: LocalAppProxyFetchRequest): Promise { const permit = await this.concurrency.acquire(request.signal); try { - return await this.runFetch(request, permit); + return await this.runFetchWithinHeadersDeadline(request, permit); } catch (error) { permit.release(); throw error; } } + private async runFetchWithinHeadersDeadline( + request: LocalAppProxyFetchRequest, + permit: LocalAppProxyConcurrencyPermit, + ): Promise { + const deadlineMs = this.responseHeadersDeadlineMs; + if (deadlineMs == null || requestHasBody(request.method)) { + return await this.runFetch(request, permit); + } + const deadline = new AbortController(); + const timer = setTimeout( + () => deadline.abort(new LocalAppProxyResponseHeadersDeadlineError(deadlineMs)), + deadlineMs, + ); + timer.unref(); + try { + return await this.runFetch({...request, signal: AbortSignal.any([request.signal, deadline.signal])}, permit); + } finally { + clearTimeout(timer); + } + } + private async runFetch( request: LocalAppProxyFetchRequest, permit: LocalAppProxyConcurrencyPermit, @@ -236,7 +282,7 @@ export class DesktopLocalAppProxyClient { maximumRequestBodyBytes: LOCAL_APP_PROXY_REQUEST_BODY_MAX_BYTES, method: request.method, originTrust: DesktopOriginTrust.REGISTERED, - serviceName: LOCAL_APP_PROXY_SERVICE_NAME, + serviceName: this.traffic, signal: request.signal, timeoutMs: LOCAL_APP_PROXY_DEADLINE_MS, url: target.href, diff --git a/fluxer_desktop/src/main/LocalAppProxyConcurrency.test.mjs b/fluxer_desktop/src/main/LocalAppProxyConcurrency.test.mjs index 1644db4cd..e9871273b 100644 --- a/fluxer_desktop/src/main/LocalAppProxyConcurrency.test.mjs +++ b/fluxer_desktop/src/main/LocalAppProxyConcurrency.test.mjs @@ -3,12 +3,12 @@ import assert from 'node:assert/strict'; import {Buffer} from 'node:buffer'; import {Readable} from 'node:stream'; -import {describe, test} from 'node:test'; +import {describe, mock, test} from 'node:test'; import {installElectronStub} from './LocalAppTestSupport.test.mjs'; installElectronStub(); -const {DesktopLocalAppProxyClient} = await import('./LocalAppProxyClient.ts'); +const {DesktopLocalAppProxyClient, LocalAppProxyTraffic} = await import('./LocalAppProxyClient.ts'); const PROXY_MAX_CONCURRENT_REQUESTS = 32; const PROXY_MAX_QUEUED_REQUESTS = 4096; @@ -93,7 +93,7 @@ function readAvatar(client, index, signal) { describe('the local app proxy bounds its own concurrency instead of overrunning outbound HTTP', () => { test('every avatar of a fully rendered chat resolves rather than tripping the outbound capacity cap', async () => { const {outboundHTTP, state} = createOutboundHTTP(); - const client = new DesktopLocalAppProxyClient({outboundHTTP}); + const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE}); const controller = new AbortController(); const fetches = []; @@ -113,7 +113,7 @@ describe('the local app proxy bounds its own concurrency instead of overrunning test('no more requests are in flight at any instant than the proxy concurrency limit', async () => { const {outboundHTTP, state} = createOutboundHTTP(); - const client = new DesktopLocalAppProxyClient({outboundHTTP}); + const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE}); const controller = new AbortController(); const fetches = []; @@ -130,7 +130,7 @@ describe('the local app proxy bounds its own concurrency instead of overrunning test('an aborted waiter rejects with the abort reason and leaves its queue slot to the next waiter', async () => { const {outboundHTTP, state} = createOutboundHTTP(); - const client = new DesktopLocalAppProxyClient({outboundHTTP}); + const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE}); const held = new AbortController(); const holding = []; @@ -161,7 +161,7 @@ describe('the local app proxy bounds its own concurrency instead of overrunning test('the queue refuses a request past its bound rather than growing without limit', async () => { const {outboundHTTP, state} = createOutboundHTTP(); - const client = new DesktopLocalAppProxyClient({outboundHTTP}); + const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE}); const controller = new AbortController(); const accepted = []; @@ -178,3 +178,114 @@ describe('the local app proxy bounds its own concurrency instead of overrunning assert.equal(settled.length, PROXY_MAX_CONCURRENT_REQUESTS + PROXY_MAX_QUEUED_REQUESTS); }); }); + +const RESOURCE_HEADERS_DEADLINE_MS = 30_000; + +function createSilentUpstream() { + const state = {started: [], aborted: 0}; + const outboundHTTP = { + request: (request) => + new Promise((resolve, reject) => { + const entry = { + url: request.url, + serviceName: request.serviceName, + answer: () => + resolve({ + headers: {'content-type': 'image/webp'}, + message: Readable.from([Buffer.from('avatar-bytes')]), + status: 200, + statusText: 'OK', + url: new URL(request.url), + }), + }; + state.started.push(entry); + request.signal.addEventListener( + 'abort', + () => { + state.aborted += 1; + reject(request.signal.reason); + }, + {once: true}, + ); + }), + }; + return {outboundHTTP, state}; +} + +function apiRequest(signal) { + return {...avatarRequest(0, signal), targetURL: 'https://api.fluxer.app/v1/users/@me'}; +} + +describe('media that a slow or silent self-host never answers cannot wedge the local app proxy', () => { + test('avatars dropped while their upstream sends no headers free their permits at the headers deadline', async () => { + mock.timers.enable({apis: ['setTimeout']}); + try { + const {outboundHTTP, state} = createSilentUpstream(); + const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE}); + const renderer = new AbortController(); + const dropped = []; + for (let index = 0; index < PROXY_MAX_CONCURRENT_REQUESTS; index += 1) { + const fetch = client.fetch(avatarRequest(index, renderer.signal)); + fetch.catch(() => {}); + dropped.push(fetch); + } + await tick(); + const fresh = client.fetch(avatarRequest(1000, renderer.signal)); + await tick(); + assert.equal(state.started.length, PROXY_MAX_CONCURRENT_REQUESTS); + + mock.timers.tick(RESOURCE_HEADERS_DEADLINE_MS); + await Promise.allSettled(dropped); + await tick(); + + assert.equal(state.aborted, PROXY_MAX_CONCURRENT_REQUESTS); + assert.equal(state.started.length, PROXY_MAX_CONCURRENT_REQUESTS + 1); + state.started.at(-1).answer(); + const response = await fresh; + assert.equal(response.status, 200); + assert.equal(await response.text(), 'avatar-bytes'); + } finally { + mock.timers.reset(); + } + }); + + test('API requests keep flowing while every media permit is held by a silent upstream', async () => { + const {outboundHTTP, state} = createSilentUpstream(); + const media = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE}); + const api = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.API}); + const renderer = new AbortController(); + for (let index = 0; index < PROXY_MAX_CONCURRENT_REQUESTS * 2; index += 1) { + media.fetch(avatarRequest(index, renderer.signal)).catch(() => {}); + } + await tick(); + const apiCall = api.fetch(apiRequest(renderer.signal)); + await tick(); + const started = state.started.at(-1); + assert.equal(started.url, 'https://api.fluxer.app/v1/users/@me'); + assert.equal(started.serviceName, LocalAppProxyTraffic.API); + started.answer(); + assert.equal((await apiCall).status, 200); + renderer.abort(); + }); + + test('the newest queued media request is admitted first so what is on screen loads before what scrolled away', async () => { + const {outboundHTTP, state} = createSilentUpstream(); + const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE}); + const renderer = new AbortController(); + const fetches = []; + for (let index = 0; index < PROXY_MAX_CONCURRENT_REQUESTS + 3; index += 1) { + const fetch = client.fetch(avatarRequest(index, renderer.signal)); + fetch.catch(() => {}); + fetches.push(fetch); + } + await tick(); + state.started[0].answer(); + await (await fetches[0]).text(); + await tick(); + assert.equal( + state.started.at(-1).url, + `https://media.fluxer.app/avatars/${PROXY_MAX_CONCURRENT_REQUESTS + 2}.webp`, + ); + renderer.abort(); + }); +}); diff --git a/fluxer_desktop/src/main/LocalAppRemoteResourceRequestHandler.ts b/fluxer_desktop/src/main/LocalAppRemoteResourceRequestHandler.ts index 226508a85..1d9183c4b 100644 --- a/fluxer_desktop/src/main/LocalAppRemoteResourceRequestHandler.ts +++ b/fluxer_desktop/src/main/LocalAppRemoteResourceRequestHandler.ts @@ -24,7 +24,7 @@ import {LOCAL_APP_REMOTE_PROXY_PATH_PREFIX} from '@fluxer/desktop_ipc/src/LocalA const log = createChildLogger('LocalAppRemoteResourceProxy'); interface LocalAppRemoteResourceRuntimePlans { - findPlanForRoute(runtimeKey: string): LocalAppRuntimePlan | null; + planForRoute(runtimeKey: string): Promise; } interface DesktopLocalAppRemoteResourceRequestHandlerDependencies { @@ -69,7 +69,7 @@ export class DesktopLocalAppRemoteResourceRequestHandler { headers: LOCAL_APP_PROXY_TEXT_RESPONSE_HEADERS, }); } - const plan = this.runtimePlans.findPlanForRoute(route.runtimeKey); + const plan = await this.runtimePlans.planForRoute(route.runtimeKey); if (plan == null) { return localAppProxyNotFoundResponse('Unknown local app resource runtime'); } diff --git a/fluxer_desktop/src/main/LocalAppRequestHandler.test.mjs b/fluxer_desktop/src/main/LocalAppRequestHandler.test.mjs index 2afe83117..9c144530d 100644 --- a/fluxer_desktop/src/main/LocalAppRequestHandler.test.mjs +++ b/fluxer_desktop/src/main/LocalAppRequestHandler.test.mjs @@ -68,8 +68,9 @@ function createHandler(options = {}) { }, files: new DesktopLocalAppFiles({rendererRoot}), indexContext: () => ({prebootTheme: null}), - proxyClient: createProxyClient(), - runtimePlans: {findPlanForRoute: (key) => (key === INSTANCE_KEY ? PLAN : null)}, + apiProxyClient: createProxyClient(), + resourceProxyClient: createProxyClient(), + runtimePlans: {planForRoute: async (key) => (key === INSTANCE_KEY ? PLAN : null)}, shutdownSignal: new AbortController().signal, ...options, }); diff --git a/fluxer_desktop/src/main/LocalAppRequestHandler.ts b/fluxer_desktop/src/main/LocalAppRequestHandler.ts index 5cae56cbb..386b83108 100644 --- a/fluxer_desktop/src/main/LocalAppRequestHandler.ts +++ b/fluxer_desktop/src/main/LocalAppRequestHandler.ts @@ -30,14 +30,15 @@ const BLOCKED_PROXY_DOCUMENT_DESTINATIONS: ReadonlySet = new Set([ ]); interface LocalAppRequestRuntimePlans { - findPlanForRoute(runtimeKey: string): LocalAppRuntimePlan | null; + planForRoute(runtimeKey: string): Promise; } interface DesktopLocalAppRequestHandlerDependencies { readonly authorization: DesktopLocalAppAuthorization; readonly files: DesktopLocalAppFiles; readonly indexContext: () => LocalAppIndexContext; - readonly proxyClient: DesktopLocalAppProxyClient; + readonly apiProxyClient: DesktopLocalAppProxyClient; + readonly resourceProxyClient: DesktopLocalAppProxyClient; readonly runtimePlans: LocalAppRequestRuntimePlans; readonly shutdownSignal: AbortSignal; } @@ -67,11 +68,11 @@ export class DesktopLocalAppRequestHandler { this.authorization = dependencies.authorization; this.shutdownSignal = dependencies.shutdownSignal; this.apiProxyRequestHandler = new DesktopLocalAppAPIProxyRequestHandler({ - proxyClient: dependencies.proxyClient, + proxyClient: dependencies.apiProxyClient, runtimePlans: dependencies.runtimePlans, }); this.remoteResourceRequestHandler = new DesktopLocalAppRemoteResourceRequestHandler({ - proxyClient: dependencies.proxyClient, + proxyClient: dependencies.resourceProxyClient, runtimePlans: dependencies.runtimePlans, }); this.fileRequestHandler = new DesktopLocalAppFileRequestHandler({