mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-09 20:22:11 +09:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47b5c3d4f0 | ||
|
|
d9bfca66d6 | ||
|
|
ded51b65d3 | ||
|
|
ddc8837d4f | ||
|
|
df16957c95 | ||
|
|
f38b19d4bd | ||
|
|
f7cd4f2c74 | ||
|
|
a078392888 | ||
|
|
3060f23f8c | ||
|
|
6a5f0d4d29 | ||
|
|
91d989dc7c | ||
|
|
7c82804df6 |
+1
@@ -332,6 +332,7 @@ export const GuildListItemPresentation = forwardRef<
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
data-guild-list-focus-item="true"
|
||||
data-guild-id={guild.id}
|
||||
onLongPress={onLongPress}
|
||||
disabled={false}
|
||||
data-flx="app.sidebar-nav.guild-list-item-presentation.guild-list-item.click"
|
||||
|
||||
@@ -5,7 +5,7 @@ import FocusRing from '@app/features/ui/focus_ring/FocusRing';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
import {useLingui} from '@lingui/react/macro';
|
||||
import {clsx} from 'clsx';
|
||||
import type {CSSProperties, FC, ReactNode} from 'react';
|
||||
import type {FC, ReactNode} from 'react';
|
||||
|
||||
const REVEAL_SPOILER_DESCRIPTOR = msg({
|
||||
message: 'Reveal spoiler',
|
||||
@@ -23,24 +23,14 @@ interface SpoilerOverlayProps {
|
||||
label?: string;
|
||||
inline?: boolean;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
export const SpoilerOverlay: FC<SpoilerOverlayProps> = ({
|
||||
hidden,
|
||||
onReveal,
|
||||
children,
|
||||
label,
|
||||
inline,
|
||||
className,
|
||||
style,
|
||||
}) => {
|
||||
export const SpoilerOverlay: FC<SpoilerOverlayProps> = ({hidden, onReveal, children, label, inline, className}) => {
|
||||
const {i18n} = useLingui();
|
||||
const ariaLabel = label ?? i18n._(REVEAL_SPOILER_DESCRIPTOR);
|
||||
return (
|
||||
<div
|
||||
className={clsx(styles.container, inline && styles.inline, hidden && styles.hidden, className)}
|
||||
style={style}
|
||||
data-flx="app.spoiler-overlay.container"
|
||||
>
|
||||
<div className={styles.content} aria-hidden={hidden} data-flx="app.spoiler-overlay.content">
|
||||
|
||||
@@ -28,6 +28,8 @@ export const SUPPORT_EMAIL = '[email protected]';
|
||||
export const SUPPORT_EMAIL_MAILTO = `mailto:${SUPPORT_EMAIL}`;
|
||||
export const I18N_EMAIL = '[email protected]';
|
||||
export const I18N_EMAIL_MAILTO = `mailto:${I18N_EMAIL}`;
|
||||
export const I18N_WEBLATE_DOMAIN = 'weblate.fluxer.tools';
|
||||
export const I18N_WEBLATE_URL = `https://${I18N_WEBLATE_DOMAIN}`;
|
||||
export const EXAMPLE_DOMAIN = 'example.com';
|
||||
export const EXAMPLE_URL = `https://${EXAMPLE_DOMAIN}`;
|
||||
export const EXAMPLE_CALLBACK_URL = `${EXAMPLE_URL}/callback`;
|
||||
|
||||
@@ -28,6 +28,10 @@ import {
|
||||
SearchIndexingState,
|
||||
} from '@app/features/channel/components/channel_search_results/SearchResultsStateViews';
|
||||
import type {MessageGroupRenderWrapperProps} from '@app/features/channel/components/MessageGroup';
|
||||
import {
|
||||
buildSearchResultGroups,
|
||||
buildSearchResultGroupsByMessageId,
|
||||
} from '@app/features/channel/components/SearchResultGrouping';
|
||||
import {SearchResultMessageList} from '@app/features/channel/components/SearchResultMessageList';
|
||||
import {areSegmentsEqual} from '@app/features/channel/components/SearchResultsUtils';
|
||||
import {DEFAULT_SCOPE_VALUE, getScopeOptionsForChannel} from '@app/features/channel/components/SearchScopeOptions';
|
||||
@@ -134,35 +138,27 @@ export const ChannelSearchResults = observer(
|
||||
() => new Map(successChannels.map((searchChannel) => [searchChannel.id, searchChannel])),
|
||||
[successChannels],
|
||||
);
|
||||
const messagesByChannel = useMemo(() => {
|
||||
const grouped = new Map<string, Array<Message>>();
|
||||
for (const message of successResults) {
|
||||
if (!grouped.has(message.channelId)) {
|
||||
grouped.set(message.channelId, []);
|
||||
}
|
||||
grouped.get(message.channelId)!.push(message);
|
||||
}
|
||||
return grouped;
|
||||
}, [successResults]);
|
||||
const resultGroups = useMemo(() => buildSearchResultGroups(successResults), [successResults]);
|
||||
const resultGroupsByMessageId = useMemo(() => buildSearchResultGroupsByMessageId(resultGroups), [resultGroups]);
|
||||
const onCopySelectedMessages = useMessageSelectionCopyForMessages<HTMLDivElement>(successResults);
|
||||
const spammerOverrideVersion = LocalUserSpamOverride.version;
|
||||
const collapsedMessageVisibility = useMemo(
|
||||
() => ({
|
||||
isMessageRevealed: (message: Message) => {
|
||||
const channelMessages = messagesByChannel.get(message.channelId);
|
||||
if (!channelMessages) return false;
|
||||
const resultGroup = resultGroupsByMessageId.get(message.id);
|
||||
if (!resultGroup) return false;
|
||||
const messageChannel = searchChannelsById.get(message.channelId) ?? Channels.getChannel(message.channelId);
|
||||
if (!messageChannel) return false;
|
||||
const groupKey = getCollapsedMessageGroupKey({
|
||||
channel: messageChannel,
|
||||
messages: channelMessages,
|
||||
messages: resultGroup.messages,
|
||||
messageId: message.id,
|
||||
treatSpam: true,
|
||||
});
|
||||
return groupKey != null && revealedGroupKeys.has(groupKey);
|
||||
},
|
||||
}),
|
||||
[messagesByChannel, revealedGroupKeys, searchChannelsById, spammerOverrideVersion],
|
||||
[resultGroupsByMessageId, revealedGroupKeys, searchChannelsById, spammerOverrideVersion],
|
||||
);
|
||||
const handleCollapsedGroupRevealChange = useCallback((groupKey: string, revealed: boolean) => {
|
||||
setRevealedGroupKeys((current) => {
|
||||
@@ -606,9 +602,9 @@ export const ChannelSearchResults = observer(
|
||||
data-message-selection-root="true"
|
||||
data-flx="channel.channel-search-results.render-content.results-scroller"
|
||||
>
|
||||
{Array.from(messagesByChannel.entries()).map(([resultChannelId, messages]) => {
|
||||
{resultGroups.map((resultGroup) => {
|
||||
const renderData = getSearchResultChannelRenderData(
|
||||
resultChannelId,
|
||||
resultGroup.channelId,
|
||||
searchChannelsById,
|
||||
(activeScope ?? DEFAULT_SCOPE_VALUE) as MessageSearchScope,
|
||||
);
|
||||
@@ -649,7 +645,7 @@ export const ChannelSearchResults = observer(
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<React.Fragment key={resultChannelId}>
|
||||
<React.Fragment key={resultGroup.key}>
|
||||
<MessageContextPrefix
|
||||
channel={messageChannel}
|
||||
showGuildMeta={showGuildMeta}
|
||||
@@ -660,7 +656,7 @@ export const ChannelSearchResults = observer(
|
||||
/>
|
||||
<SearchResultMessageList
|
||||
channel={messageChannel}
|
||||
messages={messages}
|
||||
messages={resultGroup.messages}
|
||||
revealedGroupKeys={revealedGroupKeys}
|
||||
onGroupRevealChange={handleCollapsedGroupRevealChange}
|
||||
collapsedGroupClassName={styles.collapsedMessageGroup}
|
||||
@@ -725,7 +721,7 @@ export const ChannelSearchResults = observer(
|
||||
handlePaginationJump,
|
||||
onCopySelectedMessages,
|
||||
collapsedMessageVisibility,
|
||||
messagesByChannel,
|
||||
resultGroups,
|
||||
searchChannelsById,
|
||||
revealedGroupKeys,
|
||||
handleCollapsedGroupRevealChange,
|
||||
|
||||
@@ -1375,7 +1375,10 @@ export const LexicalChannelTextareaContent = observer(
|
||||
showStickersButton={showStickersButton}
|
||||
showEmojiButton={showEmojiButton}
|
||||
showMessageSendButton={showMessageSendButton}
|
||||
showVoiceMessageButton={false}
|
||||
canRecordVoice={canAttachFilesInChannel(channel)}
|
||||
isEditingMessage={isEditingMessageInComposer || editingMessageId != null}
|
||||
hasPendingSticker={hasPendingSticker}
|
||||
voiceTooltipAnchorRef={contentAreaRef}
|
||||
expressionPickerOpen={expressionPickerOpen}
|
||||
selectedTab={selectedTab}
|
||||
isMobile={mobileLayout.enabled}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
import type {MessageAttachment} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
|
||||
|
||||
export const ATTACHMENT_CARD_WIDTH = 400;
|
||||
|
||||
const IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp', 'image/avif'];
|
||||
const VIDEO_TYPES = ['video/mp4', 'video/webm', 'video/quicktime'];
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
margin-top: var(--message-container-gap, 0.25rem);
|
||||
}
|
||||
|
||||
.urlEmbedSpoiler {
|
||||
div.urlEmbedSpoiler {
|
||||
width: 100%;
|
||||
max-width: 22.5rem;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,6 @@ const SpoileredUrlEmbed = observer(function SpoileredUrlEmbed({
|
||||
hidden={hidden}
|
||||
onReveal={reveal}
|
||||
className={styles.urlEmbedSpoiler}
|
||||
style={{width: '100%', maxWidth: 360}}
|
||||
data-flx="channel.message-attachments.spoilered-url-embed"
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {ATTACHMENT_CARD_WIDTH} from '@app/features/channel/components/MessageAttachmentUtils';
|
||||
import styles from '@app/features/channel/components/MessageUploadProgress.module.css';
|
||||
import * as MessageCommands from '@app/features/messaging/commands/MessageCommands';
|
||||
import {useMessageUpload} from '@app/features/messaging/hooks/useCloudUpload';
|
||||
@@ -77,8 +78,8 @@ export const MessageUploadProgress = observer(({attachment, message}: MessageUpl
|
||||
}
|
||||
: {
|
||||
display: 'grid',
|
||||
width: '400px',
|
||||
maxWidth: '400px',
|
||||
width: remFromPx(ATTACHMENT_CARD_WIDTH),
|
||||
maxWidth: remFromPx(ATTACHMENT_CARD_WIDTH),
|
||||
};
|
||||
return (
|
||||
<div style={containerStyles} data-flx="channel.message-upload-progress.div">
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {Message} from '@app/features/messaging/models/MessagingMessage';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
import {
|
||||
buildSearchResultGroups,
|
||||
buildSearchResultGroupsByMessageId,
|
||||
countSearchResultChannels,
|
||||
} from './SearchResultGrouping';
|
||||
|
||||
const message = (id: string, channelId: string): Message => ({id, channelId}) as Message;
|
||||
|
||||
describe('SearchResultGrouping', () => {
|
||||
it('keeps the order the server returned instead of bucketing per channel', () => {
|
||||
const groups = buildSearchResultGroups([
|
||||
message('500', 'general'),
|
||||
message('400', 'random'),
|
||||
message('300', 'general'),
|
||||
]);
|
||||
expect(groups.map((group) => group.messages.map((result) => result.id))).toEqual([['500'], ['400'], ['300']]);
|
||||
expect(groups.map((group) => group.channelId)).toEqual(['general', 'random', 'general']);
|
||||
});
|
||||
|
||||
it('merges consecutive results from the same channel into one group', () => {
|
||||
const groups = buildSearchResultGroups([
|
||||
message('500', 'general'),
|
||||
message('490', 'general'),
|
||||
message('400', 'random'),
|
||||
]);
|
||||
expect(groups).toHaveLength(2);
|
||||
expect(groups[0].messages.map((result) => result.id)).toEqual(['500', '490']);
|
||||
expect(groups[1].messages.map((result) => result.id)).toEqual(['400']);
|
||||
});
|
||||
|
||||
it('gives every group a unique key even when a channel appears more than once', () => {
|
||||
const groups = buildSearchResultGroups([
|
||||
message('500', 'general'),
|
||||
message('400', 'random'),
|
||||
message('300', 'general'),
|
||||
]);
|
||||
expect(new Set(groups.map((group) => group.key)).size).toBe(3);
|
||||
});
|
||||
|
||||
it('indexes each message to the group it renders in', () => {
|
||||
const groups = buildSearchResultGroups([
|
||||
message('500', 'general'),
|
||||
message('400', 'random'),
|
||||
message('300', 'general'),
|
||||
]);
|
||||
const groupsByMessageId = buildSearchResultGroupsByMessageId(groups);
|
||||
expect(groupsByMessageId.get('500')).toBe(groups[0]);
|
||||
expect(groupsByMessageId.get('400')).toBe(groups[1]);
|
||||
expect(groupsByMessageId.get('300')).toBe(groups[2]);
|
||||
});
|
||||
|
||||
it('counts distinct channels rather than groups', () => {
|
||||
const groups = buildSearchResultGroups([
|
||||
message('500', 'general'),
|
||||
message('400', 'random'),
|
||||
message('300', 'general'),
|
||||
]);
|
||||
expect(countSearchResultChannels(groups)).toBe(2);
|
||||
expect(countSearchResultChannels(buildSearchResultGroups([]))).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {Message} from '@app/features/messaging/models/MessagingMessage';
|
||||
|
||||
export interface SearchResultGroup {
|
||||
key: string;
|
||||
channelId: string;
|
||||
messages: Array<Message>;
|
||||
}
|
||||
|
||||
export const buildSearchResultGroups = (messages: Array<Message>): Array<SearchResultGroup> => {
|
||||
const groups: Array<SearchResultGroup> = [];
|
||||
let currentGroup: SearchResultGroup | null = null;
|
||||
for (const message of messages) {
|
||||
if (!currentGroup || currentGroup.channelId !== message.channelId) {
|
||||
currentGroup = {key: `${message.channelId}-${message.id}`, channelId: message.channelId, messages: []};
|
||||
groups.push(currentGroup);
|
||||
}
|
||||
currentGroup.messages.push(message);
|
||||
}
|
||||
return groups;
|
||||
};
|
||||
|
||||
export const buildSearchResultGroupsByMessageId = (
|
||||
groups: Array<SearchResultGroup>,
|
||||
): Map<string, SearchResultGroup> => {
|
||||
const groupsByMessageId = new Map<string, SearchResultGroup>();
|
||||
for (const group of groups) {
|
||||
for (const message of group.messages) {
|
||||
groupsByMessageId.set(message.id, group);
|
||||
}
|
||||
}
|
||||
return groupsByMessageId;
|
||||
};
|
||||
|
||||
export const countSearchResultChannels = (groups: Array<SearchResultGroup>): number => {
|
||||
return new Set(groups.map((group) => group.channelId)).size;
|
||||
};
|
||||
@@ -7,6 +7,34 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.voiceButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: var(--textarea-button-height);
|
||||
height: var(--textarea-button-height);
|
||||
padding: 0;
|
||||
color: var(--text-primary-muted);
|
||||
border-radius: var(--radius-md);
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.voiceButton:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.voiceButtonIcon {
|
||||
width: var(--textarea-button-icon-size);
|
||||
height: var(--textarea-button-icon-size);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.holdTooltip {
|
||||
background-color: var(--brand-primary);
|
||||
color: var(--text-on-brand-primary);
|
||||
@@ -33,6 +61,10 @@
|
||||
}
|
||||
|
||||
.overlayInner {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 0.375rem;
|
||||
padding: 0.5rem;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
@@ -133,6 +165,7 @@
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import Accessibility from '@app/features/accessibility/state/Accessibility';
|
||||
import {LimitResolver} from '@app/features/app/utils/LimitResolverAdapter';
|
||||
import layoutStyles from '@app/features/channel/components/textarea/MobileTextareaLayout.module.css';
|
||||
import styles from '@app/features/channel/components/VoiceMessageRecorder.module.css';
|
||||
import {Logger} from '@app/features/platform/utils/AppLogger';
|
||||
import {usePortalHost} from '@app/features/ui/overlay/PortalHostContext';
|
||||
@@ -529,7 +528,7 @@ export default function VoiceMessageRecorder({channelId, disabled, tooltipAnchor
|
||||
>
|
||||
<motion.button
|
||||
type="button"
|
||||
className={layoutStyles.mobileVoiceButton}
|
||||
className={styles.voiceButton}
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerUp={handlePointerUp}
|
||||
onPointerMove={handlePointerMove}
|
||||
@@ -541,7 +540,7 @@ export default function VoiceMessageRecorder({channelId, disabled, tooltipAnchor
|
||||
data-flx="channel.voice-message-recorder.button.pointer-down"
|
||||
>
|
||||
<MicrophoneIcon
|
||||
className={layoutStyles.mobileRightButtonIcon}
|
||||
className={styles.voiceButtonIcon}
|
||||
data-flx="channel.voice-message-recorder.microphone-icon"
|
||||
/>
|
||||
</motion.button>
|
||||
@@ -578,10 +577,7 @@ export default function VoiceMessageRecorder({channelId, disabled, tooltipAnchor
|
||||
transition={resolvedOverlayMotion.transition}
|
||||
data-flx="channel.voice-message-recorder.recording-overlay"
|
||||
>
|
||||
<div
|
||||
className={clsx(styles.overlayInner, layoutStyles.mobileTextareaWrapper)}
|
||||
data-flx="channel.voice-message-recorder.overlay-inner"
|
||||
>
|
||||
<div className={styles.overlayInner} data-flx="channel.voice-message-recorder.overlay-inner">
|
||||
<button
|
||||
type="button"
|
||||
className={clsx(styles.iconButton, styles.trashButton)}
|
||||
@@ -610,7 +606,7 @@ export default function VoiceMessageRecorder({channelId, disabled, tooltipAnchor
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(styles.lockIndicatorWrapper, layoutStyles.mobileRightButtonContainer)}
|
||||
className={styles.lockIndicatorWrapper}
|
||||
data-flx="channel.voice-message-recorder.lock-indicator-wrapper"
|
||||
>
|
||||
<button
|
||||
|
||||
+20
-19
@@ -8,6 +8,11 @@ import {Message as MessageComponent} from '@app/features/channel/components/Chan
|
||||
import {CollapsedMessageVisibilityProvider} from '@app/features/channel/components/CollapsedMessageVisibilityContext';
|
||||
import {MessageActionBottomSheet} from '@app/features/channel/components/MessageActionBottomSheet';
|
||||
import type {MessageGroupRenderWrapperProps} from '@app/features/channel/components/MessageGroup';
|
||||
import {
|
||||
buildSearchResultGroups,
|
||||
buildSearchResultGroupsByMessageId,
|
||||
countSearchResultChannels,
|
||||
} from '@app/features/channel/components/SearchResultGrouping';
|
||||
import {SearchResultMessageList} from '@app/features/channel/components/SearchResultMessageList';
|
||||
import {type ChannelSearchFilters, useChannelSearch} from '@app/features/channel/hooks/useChannelSearch';
|
||||
import type {Channel} from '@app/features/channel/models/Channel';
|
||||
@@ -216,22 +221,17 @@ export const ChannelSearchBottomSheet: React.FC<ChannelSearchBottomSheetProps> =
|
||||
() => new Map(successChannels.map((searchChannel) => [searchChannel.id, searchChannel])),
|
||||
[successChannels],
|
||||
);
|
||||
const messagesByChannel = React.useMemo(() => {
|
||||
const grouped = new Map<string, Array<Message>>();
|
||||
for (const message of successResults) {
|
||||
if (!grouped.has(message.channelId)) {
|
||||
grouped.set(message.channelId, []);
|
||||
}
|
||||
grouped.get(message.channelId)!.push(message);
|
||||
}
|
||||
return grouped;
|
||||
}, [successResults]);
|
||||
const resultGroups = React.useMemo(() => buildSearchResultGroups(successResults), [successResults]);
|
||||
const resultGroupsByMessageId = React.useMemo(
|
||||
() => buildSearchResultGroupsByMessageId(resultGroups),
|
||||
[resultGroups],
|
||||
);
|
||||
const spammerOverrideVersion = LocalUserSpamOverride.version;
|
||||
const collapsedMessageVisibility = React.useMemo(
|
||||
() => ({
|
||||
isMessageRevealed: (message: Message) => {
|
||||
const channelMessages = messagesByChannel.get(message.channelId);
|
||||
if (!channelMessages) {
|
||||
const resultGroup = resultGroupsByMessageId.get(message.id);
|
||||
if (!resultGroup) {
|
||||
return false;
|
||||
}
|
||||
const messageChannel = searchChannelsById.get(message.channelId) ?? Channels.getChannel(message.channelId);
|
||||
@@ -240,14 +240,14 @@ export const ChannelSearchBottomSheet: React.FC<ChannelSearchBottomSheetProps> =
|
||||
}
|
||||
const groupKey = getCollapsedMessageGroupKey({
|
||||
channel: messageChannel,
|
||||
messages: channelMessages,
|
||||
messages: resultGroup.messages,
|
||||
messageId: message.id,
|
||||
treatSpam: true,
|
||||
});
|
||||
return groupKey != null && revealedGroupKeys.has(groupKey);
|
||||
},
|
||||
}),
|
||||
[messagesByChannel, revealedGroupKeys, searchChannelsById, spammerOverrideVersion],
|
||||
[resultGroupsByMessageId, revealedGroupKeys, searchChannelsById, spammerOverrideVersion],
|
||||
);
|
||||
const handleCollapsedGroupRevealChange = useCallback((groupKey: string, revealed: boolean) => {
|
||||
setRevealedGroupKeys((current) => {
|
||||
@@ -598,7 +598,7 @@ export const ChannelSearchBottomSheet: React.FC<ChannelSearchBottomSheetProps> =
|
||||
);
|
||||
}
|
||||
const totalPages = Math.max(1, Math.ceil(total / hitsPerPage));
|
||||
const hasMultipleChannels = messagesByChannel.size > 1;
|
||||
const hasMultipleChannels = countSearchResultChannels(resultGroups) > 1;
|
||||
return (
|
||||
<>
|
||||
<CollapsedMessageVisibilityProvider
|
||||
@@ -613,8 +613,9 @@ export const ChannelSearchBottomSheet: React.FC<ChannelSearchBottomSheetProps> =
|
||||
data-message-selection-root="true"
|
||||
data-flx="channel.channel-search-bottom-sheet.render-content.results-scroller"
|
||||
>
|
||||
{Array.from(messagesByChannel.entries()).map(([channelId, messages]) => {
|
||||
const messageChannel = searchChannelsById.get(channelId) ?? Channels.getChannel(channelId);
|
||||
{resultGroups.map((resultGroup) => {
|
||||
const messageChannel =
|
||||
searchChannelsById.get(resultGroup.channelId) ?? Channels.getChannel(resultGroup.channelId);
|
||||
if (!messageChannel) {
|
||||
return null;
|
||||
}
|
||||
@@ -648,7 +649,7 @@ export const ChannelSearchBottomSheet: React.FC<ChannelSearchBottomSheetProps> =
|
||||
</LongPressable>
|
||||
);
|
||||
return (
|
||||
<React.Fragment key={channelId}>
|
||||
<React.Fragment key={resultGroup.key}>
|
||||
{hasMultipleChannels && (
|
||||
<div
|
||||
className={styles.channelSection}
|
||||
@@ -667,7 +668,7 @@ export const ChannelSearchBottomSheet: React.FC<ChannelSearchBottomSheetProps> =
|
||||
)}
|
||||
<SearchResultMessageList
|
||||
channel={messageChannel}
|
||||
messages={messages}
|
||||
messages={resultGroup.messages}
|
||||
revealedGroupKeys={revealedGroupKeys}
|
||||
onGroupRevealChange={handleCollapsedGroupRevealChange}
|
||||
collapsedGroupClassName={styles.collapsedMessageGroup}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {
|
||||
BLUESKY_EMBED_MEDIA_FALLBACK_OUTER_WIDTH,
|
||||
BLUESKY_EMBED_MIN_CONTENT_WIDTH,
|
||||
calculateBlueskyMediaContainerWidth,
|
||||
calculateBlueskyOuterMaxWidth,
|
||||
EMBED_TEXT_OUTER_WIDTH,
|
||||
formatResponsiveEmbedWidth,
|
||||
} from '@app/features/channel/components/embeds/EmbedRenderUtils';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
describe('formatResponsiveEmbedWidth', () => {
|
||||
it('caps in rem so the width tracks the root font size at any zoom', () => {
|
||||
expect(formatResponsiveEmbedWidth(EMBED_TEXT_OUTER_WIDTH)).toBe('min(100%, 32.25rem)');
|
||||
expect(formatResponsiveEmbedWidth(BLUESKY_EMBED_MEDIA_FALLBACK_OUTER_WIDTH)).toBe('min(100%, 27rem)');
|
||||
});
|
||||
|
||||
it('rounds to whole pixels before converting', () => {
|
||||
expect(formatResponsiveEmbedWidth(400.4)).toBe('min(100%, 25rem)');
|
||||
});
|
||||
|
||||
it('clamps non-positive and non-finite widths', () => {
|
||||
expect(formatResponsiveEmbedWidth(0)).toBe('min(100%, 0rem)');
|
||||
expect(formatResponsiveEmbedWidth(Number.NaN)).toBe('min(100%, 0rem)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateBlueskyOuterMaxWidth', () => {
|
||||
it('falls back to the shared text width when there is no media', () => {
|
||||
expect(calculateBlueskyOuterMaxWidth({hasMedia: false, chromeWidth: 29})).toBe(EMBED_TEXT_OUTER_WIDTH);
|
||||
});
|
||||
|
||||
it('adds chrome around the media container width', () => {
|
||||
expect(calculateBlueskyOuterMaxWidth({mediaWidth: 480, hasMedia: true, chromeWidth: 29})).toBe(509);
|
||||
});
|
||||
|
||||
it('never goes below the minimum content width', () => {
|
||||
expect(calculateBlueskyMediaContainerWidth(120)).toBe(BLUESKY_EMBED_MIN_CONTENT_WIDTH);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {remFromPx} from '@app/features/theme/layout/RemFromPx';
|
||||
import {MessageEmbedTypes} from '@fluxer/constants/src/ChannelConstants';
|
||||
import type {MessageEmbed} from '@fluxer/schema/src/domains/message/EmbedSchemas';
|
||||
|
||||
@@ -28,7 +29,7 @@ export function isMediaOnlyEmbed(embed: MediaOnlyEmbedFields): boolean {
|
||||
|
||||
export const BLUESKY_EMBED_MIN_CONTENT_WIDTH = 320;
|
||||
export const BLUESKY_EMBED_MEDIA_FALLBACK_OUTER_WIDTH = 432;
|
||||
export const BLUESKY_EMBED_TEXT_OUTER_WIDTH = 516;
|
||||
export const EMBED_TEXT_OUTER_WIDTH = 516;
|
||||
const normalizePositiveWidth = (width: number): number => {
|
||||
if (!Number.isFinite(width) || width <= 0) {
|
||||
return 0;
|
||||
@@ -55,9 +56,9 @@ export function calculateBlueskyOuterMaxWidth({
|
||||
if (mediaContainerWidth !== undefined) {
|
||||
return mediaContainerWidth + normalizePositiveWidth(chromeWidth);
|
||||
}
|
||||
return hasMedia ? BLUESKY_EMBED_MEDIA_FALLBACK_OUTER_WIDTH : BLUESKY_EMBED_TEXT_OUTER_WIDTH;
|
||||
return hasMedia ? BLUESKY_EMBED_MEDIA_FALLBACK_OUTER_WIDTH : EMBED_TEXT_OUTER_WIDTH;
|
||||
}
|
||||
|
||||
export function formatResponsiveEmbedWidth(width: number): string {
|
||||
return `min(100%, ${normalizePositiveWidth(width)}px)`;
|
||||
return `min(100%, ${remFromPx(normalizePositiveWidth(width))})`;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import {TextualAttachmentPreview} from '@app/features/channel/components/embeds/attachments/TextualAttachmentPreview';
|
||||
import {splitFilename} from '@app/features/channel/components/embeds/EmbedUtils';
|
||||
import {canDeleteAttachmentUtil} from '@app/features/channel/components/MessageActionUtils';
|
||||
import {ATTACHMENT_CARD_WIDTH} from '@app/features/channel/components/MessageAttachmentUtils';
|
||||
import {useMaybeMessageViewContext} from '@app/features/channel/components/MessageViewContext';
|
||||
import {DELETE_ATTACHMENT_DESCRIPTOR, DOWNLOAD_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
|
||||
import {useDeleteAttachment} from '@app/features/messaging/hooks/useDeleteAttachment';
|
||||
@@ -181,8 +182,8 @@ export const AttachmentFile = observer(({attachment, message, isPreview}: Attach
|
||||
containerStyles = {
|
||||
display: 'grid',
|
||||
width: '100%',
|
||||
maxWidth: '400px',
|
||||
minWidth: 'min(400px, 100%)',
|
||||
maxWidth: remFromPx(ATTACHMENT_CARD_WIDTH),
|
||||
minWidth: `min(${remFromPx(ATTACHMENT_CARD_WIDTH)}, 100%)`,
|
||||
};
|
||||
}
|
||||
const handleDownload = async (e: React.MouseEvent) => {
|
||||
|
||||
@@ -23,6 +23,10 @@ import {
|
||||
EmbedProvider,
|
||||
EmbedTitle,
|
||||
} from '@app/features/channel/components/embeds/channel_embed/EmbedParts';
|
||||
import {
|
||||
EMBED_TEXT_OUTER_WIDTH,
|
||||
formatResponsiveEmbedWidth,
|
||||
} from '@app/features/channel/components/embeds/EmbedRenderUtils';
|
||||
import markupStyles from '@app/features/theme/styles/Markup.module.css';
|
||||
import {MessageEmbedTypes} from '@fluxer/constants/src/ChannelConstants';
|
||||
import type {EmbedMedia} from '@fluxer/schema/src/domains/message/EmbedSchemas';
|
||||
@@ -58,7 +62,7 @@ export const RichEmbed: FC<EmbedProps> = observer(
|
||||
data-search-highlight-scope="message"
|
||||
style={{
|
||||
borderLeft: `4px solid ${getBorderColor(embed.color)}`,
|
||||
maxWidth: useNarrowWidth ? `min(100%, ${EMBED_MEDIA_MAX_WIDTH}px)` : 'min(100%, 516px)',
|
||||
maxWidth: formatResponsiveEmbedWidth(useNarrowWidth ? EMBED_MEDIA_MAX_WIDTH : EMBED_TEXT_OUTER_WIDTH),
|
||||
}}
|
||||
data-flx="channel.embeds.embed.rich-embed.embed"
|
||||
>
|
||||
|
||||
@@ -4,6 +4,7 @@ import {deriveDefaultNameFromMessage} from '@app/features/channel/components/emb
|
||||
import styles from '@app/features/channel/components/embeds/media/EmbedAudio.module.css';
|
||||
import {getMediaButtonVisibility} from '@app/features/channel/components/embeds/media/MediaButtonUtils';
|
||||
import type {BaseMediaProps} from '@app/features/channel/components/embeds/media/MediaTypes';
|
||||
import {ATTACHMENT_CARD_WIDTH} from '@app/features/channel/components/MessageAttachmentUtils';
|
||||
import {useMaybeMessageViewContext} from '@app/features/channel/components/MessageViewContext';
|
||||
import {DELETE_ATTACHMENT_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
|
||||
import {useDeleteAttachment} from '@app/features/messaging/hooks/useDeleteAttachment';
|
||||
@@ -130,8 +131,8 @@ const EmbedAudio: FC<EmbedAudioProps> = observer(
|
||||
: {
|
||||
display: 'grid',
|
||||
width: '100%',
|
||||
maxWidth: 'min(100%, 400px)',
|
||||
minWidth: 'min(400px, 100%)',
|
||||
maxWidth: `min(100%, ${remFromPx(ATTACHMENT_CARD_WIDTH)})`,
|
||||
minWidth: `min(${remFromPx(ATTACHMENT_CARD_WIDTH)}, 100%)`,
|
||||
};
|
||||
const {showDeleteButton, showDownloadButton} = getMediaButtonVisibility(
|
||||
canFavorite,
|
||||
|
||||
@@ -233,7 +233,7 @@ export const EmbedYouTube: FC<EmbedYouTubeProps> = observer(({embed, width = YOU
|
||||
className={styles.videoContainer}
|
||||
style={{
|
||||
...containerStyle,
|
||||
width: `${dimensions.width}px`,
|
||||
width: remFromPx(dimensions.width),
|
||||
aspectRatio,
|
||||
maxWidth: '100%',
|
||||
}}
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import Accessibility from '@app/features/accessibility/state/Accessibility';
|
||||
import {LimitResolver} from '@app/features/app/utils/LimitResolverAdapter';
|
||||
import {TextareaButton} from '@app/features/channel/components/textarea/TextareaButton';
|
||||
import styles from '@app/features/channel/components/textarea/TextareaButtons.module.css';
|
||||
import {Logger} from '@app/features/platform/utils/AppLogger';
|
||||
import {prepareVoiceMessageWav} from '@app/features/voice/utils/VoiceMessageRecordingUtils';
|
||||
import {sendVoiceMessage} from '@app/features/voice/utils/VoiceMessageSendUtils';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
import {useLingui} from '@lingui/react/macro';
|
||||
import {MicrophoneIcon} from '@phosphor-icons/react';
|
||||
import {clsx} from 'clsx';
|
||||
import {motion, type Variants} from 'framer-motion';
|
||||
import type React from 'react';
|
||||
import {useCallback, useMemo, useRef, useState} from 'react';
|
||||
|
||||
const RECORDING_FAILED_PLEASE_TRY_AGAIN_DESCRIPTOR = msg({
|
||||
message: 'Recording failed. Try again.',
|
||||
comment: 'Error toast shown when the hold-to-record voice message recording fails.',
|
||||
});
|
||||
const VOICE_MESSAGE_DESCRIPTOR = msg({
|
||||
message: 'Voice message',
|
||||
comment: 'Filename label used for voice messages sent via the hold-to-record button.',
|
||||
});
|
||||
const UNABLE_TO_SEND_VOICE_MESSAGE_PLEASE_TRY_AGAIN_DESCRIPTOR = msg({
|
||||
message: 'Unable to send voice message. Try again.',
|
||||
comment: 'Error toast shown when sending a hold-to-record voice message fails.',
|
||||
});
|
||||
const VOICE_RECORDING_IS_NOT_SUPPORTED_IN_THIS_BROWSER_DESCRIPTOR = msg({
|
||||
message: 'Voice recording is not supported in this browser.',
|
||||
comment: 'Inline error when MediaRecorder is unsupported for the hold-to-record voice message flow.',
|
||||
});
|
||||
const UNABLE_TO_START_RECORDING_PLEASE_ALLOW_MICROPHONE_ACCESS_DESCRIPTOR = msg({
|
||||
message: 'Unable to start recording. Allow microphone access.',
|
||||
comment: 'Inline error when microphone permission is denied for the hold-to-record voice message flow.',
|
||||
});
|
||||
const RELEASE_TO_SEND_DESCRIPTOR = msg({
|
||||
message: 'Release to send',
|
||||
comment: 'Tooltip on the hold-to-record button while the user is actively holding to record.',
|
||||
});
|
||||
const HOLD_TO_SEND_VOICE_MESSAGE_DESCRIPTOR = msg({
|
||||
message: 'Hold to send voice message',
|
||||
comment: 'Tooltip on the hold-to-record button in its idle state.',
|
||||
});
|
||||
const logger = new Logger('HoldToRecordButton');
|
||||
const recordingVariants: Variants = {
|
||||
idle: {
|
||||
scale: 1,
|
||||
boxShadow: '0 0 0 0px rgba(252, 95, 105, 0)',
|
||||
},
|
||||
recording: {
|
||||
scale: [1, 1.05, 1],
|
||||
boxShadow: [
|
||||
'0 0 0 0px rgba(252, 95, 105, 0.35)',
|
||||
'0 0 0 8px rgba(252, 95, 105, 0.15)',
|
||||
'0 0 0 0px rgba(252, 95, 105, 0.35)',
|
||||
],
|
||||
transition: {
|
||||
duration: 1.2,
|
||||
repeat: Infinity,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
interface HoldToRecordButtonProps {
|
||||
channelId: string;
|
||||
disabled?: boolean;
|
||||
onFallback?: () => void;
|
||||
}
|
||||
|
||||
const HoldToRecordButton: React.FC<HoldToRecordButtonProps> = ({channelId, disabled, onFallback}) => {
|
||||
const {i18n} = useLingui();
|
||||
const [status, setStatus] = useState<'idle' | 'recording' | 'sending'>('idle');
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const mediaRecorderRef = useRef<MediaRecorder | null>(null);
|
||||
const streamRef = useRef<MediaStream | null>(null);
|
||||
const chunksRef = useRef<Array<Blob>>([]);
|
||||
const pointerIdRef = useRef<number | null>(null);
|
||||
const maxTimerRef = useRef<number | null>(null);
|
||||
const maxRecordingSeconds = useMemo(
|
||||
() => LimitResolver.resolve({key: 'max_voice_message_duration', fallback: 1200}),
|
||||
[],
|
||||
);
|
||||
const clearMaxTimer = useCallback(() => {
|
||||
if (maxTimerRef.current) {
|
||||
clearTimeout(maxTimerRef.current);
|
||||
maxTimerRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
const releaseStream = useCallback(() => {
|
||||
if (streamRef.current) {
|
||||
streamRef.current.getTracks().forEach((track) => track.stop());
|
||||
streamRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
const cleanupRecording = useCallback(() => {
|
||||
clearMaxTimer();
|
||||
releaseStream();
|
||||
mediaRecorderRef.current = null;
|
||||
}, [clearMaxTimer, releaseStream]);
|
||||
const handleRecordingStop = useCallback(
|
||||
async (blob: Blob) => {
|
||||
if (blob.size === 0) {
|
||||
setStatus('idle');
|
||||
setError(i18n._(RECORDING_FAILED_PLEASE_TRY_AGAIN_DESCRIPTOR));
|
||||
return;
|
||||
}
|
||||
setStatus('sending');
|
||||
try {
|
||||
const {file, duration, waveform} = await prepareVoiceMessageWav(blob, `voice-message-${Date.now()}.wav`);
|
||||
await sendVoiceMessage({
|
||||
channelId,
|
||||
file,
|
||||
waveform,
|
||||
duration,
|
||||
title: i18n._(VOICE_MESSAGE_DESCRIPTOR),
|
||||
});
|
||||
setStatus('idle');
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
logger.error('Failed to send voice message', err);
|
||||
setError(i18n._(UNABLE_TO_SEND_VOICE_MESSAGE_PLEASE_TRY_AGAIN_DESCRIPTOR));
|
||||
setStatus('idle');
|
||||
}
|
||||
},
|
||||
[channelId, i18n],
|
||||
);
|
||||
const handleStopEvent = useCallback(() => {
|
||||
const recorder = mediaRecorderRef.current;
|
||||
if (!recorder) {
|
||||
setStatus('idle');
|
||||
return;
|
||||
}
|
||||
const recordedChunks = chunksRef.current;
|
||||
const blob = new Blob(recordedChunks, {type: recorder.mimeType || 'audio/ogg'});
|
||||
chunksRef.current = [];
|
||||
cleanupRecording();
|
||||
if (recordedChunks.length === 0) {
|
||||
setError(i18n._(RECORDING_FAILED_PLEASE_TRY_AGAIN_DESCRIPTOR));
|
||||
setStatus('idle');
|
||||
return;
|
||||
}
|
||||
void handleRecordingStop(blob);
|
||||
}, [cleanupRecording, handleRecordingStop, i18n]);
|
||||
const handleDataAvailable = useCallback((event: BlobEvent) => {
|
||||
if (event.data && event.data.size > 0) {
|
||||
chunksRef.current = [...chunksRef.current, event.data];
|
||||
}
|
||||
}, []);
|
||||
const stopRecording = useCallback(() => {
|
||||
const recorder = mediaRecorderRef.current;
|
||||
if (recorder && recorder.state !== 'inactive') {
|
||||
recorder.stop();
|
||||
}
|
||||
}, []);
|
||||
const startRecording = useCallback(async () => {
|
||||
if (status === 'recording' || status === 'sending') return;
|
||||
if (typeof navigator.mediaDevices === 'undefined' || typeof MediaRecorder === 'undefined') {
|
||||
setError(i18n._(VOICE_RECORDING_IS_NOT_SUPPORTED_IN_THIS_BROWSER_DESCRIPTOR));
|
||||
onFallback?.();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
|
||||
const preferredType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')
|
||||
? 'audio/ogg;codecs=opus'
|
||||
: 'audio/webm;codecs=opus';
|
||||
const recorder = new MediaRecorder(stream, {mimeType: preferredType});
|
||||
chunksRef.current = [];
|
||||
recorder.addEventListener('dataavailable', handleDataAvailable);
|
||||
recorder.addEventListener('stop', handleStopEvent, {once: true});
|
||||
recorder.start();
|
||||
mediaRecorderRef.current = recorder;
|
||||
streamRef.current = stream;
|
||||
setStatus('recording');
|
||||
setError(null);
|
||||
clearMaxTimer();
|
||||
maxTimerRef.current = window.setTimeout(() => {
|
||||
stopRecording();
|
||||
}, maxRecordingSeconds * 1000);
|
||||
} catch (err) {
|
||||
logger.error('Failed to start voice recording', err);
|
||||
setError(i18n._(UNABLE_TO_START_RECORDING_PLEASE_ALLOW_MICROPHONE_ACCESS_DESCRIPTOR));
|
||||
}
|
||||
}, [clearMaxTimer, handleDataAvailable, handleStopEvent, maxRecordingSeconds, status, i18n]);
|
||||
const handlePointerDown = useCallback(
|
||||
(event: React.PointerEvent<HTMLButtonElement>) => {
|
||||
if (disabled || status === 'sending') return;
|
||||
event.preventDefault();
|
||||
pointerIdRef.current = event.pointerId;
|
||||
event.currentTarget.setPointerCapture(event.pointerId);
|
||||
void startRecording();
|
||||
},
|
||||
[disabled, startRecording, status],
|
||||
);
|
||||
const handlePointerUp = useCallback(
|
||||
(event: React.PointerEvent<HTMLButtonElement>) => {
|
||||
if (pointerIdRef.current !== event.pointerId) return;
|
||||
event.preventDefault();
|
||||
try {
|
||||
event.currentTarget.releasePointerCapture(event.pointerId);
|
||||
} catch {}
|
||||
pointerIdRef.current = null;
|
||||
stopRecording();
|
||||
},
|
||||
[stopRecording],
|
||||
);
|
||||
const handlePointerCancel = useCallback(
|
||||
(event: React.PointerEvent<HTMLButtonElement>) => {
|
||||
if (pointerIdRef.current !== event.pointerId) return;
|
||||
event.preventDefault();
|
||||
pointerIdRef.current = null;
|
||||
stopRecording();
|
||||
},
|
||||
[stopRecording],
|
||||
);
|
||||
const indicatorText = useMemo(() => {
|
||||
if (error) return error;
|
||||
if (status === 'recording') return i18n._(RELEASE_TO_SEND_DESCRIPTOR);
|
||||
return i18n._(HOLD_TO_SEND_VOICE_MESSAGE_DESCRIPTOR);
|
||||
}, [error, status, i18n.locale]);
|
||||
return (
|
||||
<motion.div
|
||||
className={styles.holdButtonWrapper}
|
||||
variants={recordingVariants}
|
||||
animate={status === 'recording' ? 'recording' : 'idle'}
|
||||
data-flx="channel.textarea.hold-to-record-button.hold-button-wrapper"
|
||||
>
|
||||
<TextareaButton
|
||||
icon={MicrophoneIcon}
|
||||
label={i18n._(VOICE_MESSAGE_DESCRIPTOR)}
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerUp={handlePointerUp}
|
||||
onPointerCancel={handlePointerCancel}
|
||||
onPointerLeave={handlePointerCancel}
|
||||
disabled={disabled || status === 'sending'}
|
||||
className={clsx(styles.holdButton, status === 'recording' && styles.holdButtonRecording)}
|
||||
data-flx="channel.textarea.hold-to-record-button.hold-button.pointer-down"
|
||||
/>
|
||||
<motion.span
|
||||
key={indicatorText}
|
||||
className={clsx(
|
||||
styles.holdIndicator,
|
||||
status === 'recording' && styles.holdIndicatorRecording,
|
||||
error && styles.holdIndicatorError,
|
||||
)}
|
||||
initial={Accessibility.useReducedMotion ? {opacity: 1, y: 0} : {opacity: 0, y: 5}}
|
||||
animate={{opacity: 1, y: 0}}
|
||||
transition={{duration: Accessibility.useReducedMotion ? 0 : 0.15}}
|
||||
data-flx="channel.textarea.hold-to-record-button.hold-indicator"
|
||||
>
|
||||
{indicatorText}
|
||||
</motion.span>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HoldToRecordButton;
|
||||
@@ -1,158 +0,0 @@
|
||||
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
||||
|
||||
.mobileTextareaWrapper {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 0.375rem;
|
||||
padding: 0.5rem;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mobilePlusButtonContainer {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
flex-shrink: 0;
|
||||
height: 2.25rem;
|
||||
}
|
||||
|
||||
.mobilePlusButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--background-tertiary);
|
||||
color: var(--text-primary);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.mobilePlusButtonIcon {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.mobileContentWrapper {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.mobileInputContainer {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
background-color: var(--background-tertiary);
|
||||
border-radius: 1.125rem;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
min-height: 2.25rem;
|
||||
}
|
||||
|
||||
.mobileInputContent {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.mobileEmojiButtonContainer {
|
||||
position: absolute;
|
||||
right: 0.25rem;
|
||||
bottom: 0.125rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.mobileEmojiButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
color: var(--text-primary-muted);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.mobileEmojiButtonIcon {
|
||||
width: 1.375rem;
|
||||
height: 1.375rem;
|
||||
}
|
||||
|
||||
.mobileRightButtonContainer {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
position: relative;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
}
|
||||
|
||||
.mobileRightButtonSlot {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mobileSendButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--brand-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mobileSendButton:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mobileVoiceButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--background-tertiary);
|
||||
color: var(--text-primary);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.mobileVoiceButton:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mobileRightButtonIcon {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import Accessibility from '@app/features/accessibility/state/Accessibility';
|
||||
import type {AutocompleteOption} from '@app/features/channel/components/Autocomplete';
|
||||
import wrapperStyles from '@app/features/channel/components/textarea/InputWrapper.module.css';
|
||||
import {transitionMobileTextareaButtonState} from '@app/features/channel/components/textarea/MobileTextareaButtonStateMachine';
|
||||
import styles from '@app/features/channel/components/textarea/MobileTextareaLayout.module.css';
|
||||
import textareaStyles from '@app/features/channel/components/textarea/TextareaInput.module.css';
|
||||
import {TextareaInputField} from '@app/features/channel/components/textarea/TextareaInputField';
|
||||
import VoiceMessageRecorder from '@app/features/channel/components/VoiceMessageRecorder';
|
||||
import {EMOJIS_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
|
||||
import type {TextareaTextChangeHint} from '@app/features/messaging/utils/TextareaSegmentManager';
|
||||
import {Scroller, type ScrollerHandle} from '@app/features/ui/components/Scroller';
|
||||
import {getReducedMotionProps, type MotionAnimation} from '@app/features/ui/utils/ReducedMotionAnimation';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
import {useLingui} from '@lingui/react/macro';
|
||||
import {ArrowUpIcon, PlusIcon, SmileyIcon} from '@phosphor-icons/react';
|
||||
import {clsx} from 'clsx';
|
||||
import {AnimatePresence, motion} from 'framer-motion';
|
||||
import type React from 'react';
|
||||
import {type Dispatch, type SetStateAction, useMemo, useRef} from 'react';
|
||||
|
||||
const OPEN_MENU_DESCRIPTOR = msg({
|
||||
message: 'Open menu',
|
||||
comment: 'Button or menu action label in the channel and chat mobile textarea layout. Keep it concise.',
|
||||
});
|
||||
const SEND_MESSAGE_DESCRIPTOR = msg({
|
||||
message: 'Send message',
|
||||
comment: 'Button or menu action label in the channel and chat mobile textarea layout. Keep it concise.',
|
||||
});
|
||||
const MOBILE_BUTTON_SWAP_MOTION: MotionAnimation = {
|
||||
initial: {opacity: 0, scale: 0.86, y: 3},
|
||||
animate: {opacity: 1, scale: 1, y: 0},
|
||||
exit: {opacity: 0, scale: 0.9, y: -2},
|
||||
transition: {
|
||||
opacity: {duration: 0.08},
|
||||
y: {duration: 0.08, ease: 'easeOut'},
|
||||
scale: {type: 'spring', stiffness: 560, damping: 28, mass: 0.55},
|
||||
},
|
||||
};
|
||||
|
||||
interface MobileTextareaLayoutProps {
|
||||
disabled: boolean;
|
||||
canAttachFiles: boolean;
|
||||
value: string;
|
||||
placeholderText: string;
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement | null>;
|
||||
scrollerRef: React.RefObject<ScrollerHandle | null>;
|
||||
isFocused: boolean;
|
||||
isAutocompleteAttached: boolean;
|
||||
autocompleteListId?: string;
|
||||
autocompleteOptions: Array<AutocompleteOption>;
|
||||
selectedIndex: number;
|
||||
channelId: string;
|
||||
isSlowmodeActive: boolean;
|
||||
isOverCharacterLimit: boolean;
|
||||
isEditingMessage: boolean;
|
||||
hasContent: boolean;
|
||||
hasAttachments: boolean;
|
||||
hasPendingSticker?: boolean;
|
||||
isEditingScheduledMessage: boolean;
|
||||
onFocus: () => void;
|
||||
onBlur: () => void;
|
||||
onChange: (value: string, inputType?: string, hint?: TextareaTextChangeHint) => void;
|
||||
onHeightChange: (height: number) => void;
|
||||
onCursorMove: () => void;
|
||||
onArrowUp: (event: React.KeyboardEvent) => void;
|
||||
onSubmit: () => void;
|
||||
onAutocompleteSelect: (option: AutocompleteOption) => void;
|
||||
setSelectedIndex: Dispatch<SetStateAction<number>>;
|
||||
onKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
||||
onPlusClick: () => void;
|
||||
onEmojiClick: () => void;
|
||||
}
|
||||
|
||||
export function MobileTextareaLayout({
|
||||
disabled,
|
||||
canAttachFiles,
|
||||
value,
|
||||
placeholderText,
|
||||
textareaRef,
|
||||
scrollerRef,
|
||||
isFocused,
|
||||
isAutocompleteAttached,
|
||||
autocompleteListId,
|
||||
autocompleteOptions,
|
||||
selectedIndex,
|
||||
channelId,
|
||||
isSlowmodeActive,
|
||||
isOverCharacterLimit,
|
||||
isEditingMessage,
|
||||
hasContent,
|
||||
hasAttachments,
|
||||
hasPendingSticker,
|
||||
isEditingScheduledMessage,
|
||||
onFocus,
|
||||
onBlur,
|
||||
onChange,
|
||||
onHeightChange,
|
||||
onCursorMove,
|
||||
onArrowUp,
|
||||
onSubmit,
|
||||
onAutocompleteSelect,
|
||||
setSelectedIndex,
|
||||
onKeyDown,
|
||||
onPlusClick,
|
||||
onEmojiClick,
|
||||
}: MobileTextareaLayoutProps) {
|
||||
const {i18n} = useLingui();
|
||||
const buttonSwapMotion = getReducedMotionProps(MOBILE_BUTTON_SWAP_MOTION, Accessibility.useReducedMotion);
|
||||
const rightButtonState = useMemo(
|
||||
() =>
|
||||
transitionMobileTextareaButtonState({
|
||||
disabled,
|
||||
canRecordVoice: canAttachFiles,
|
||||
value,
|
||||
isSlowmodeActive,
|
||||
isOverCharacterLimit,
|
||||
isEditingMessage,
|
||||
hasContent,
|
||||
hasAttachments,
|
||||
hasPendingSticker: Boolean(hasPendingSticker),
|
||||
isEditingScheduledMessage,
|
||||
}),
|
||||
[
|
||||
canAttachFiles,
|
||||
disabled,
|
||||
hasAttachments,
|
||||
hasContent,
|
||||
hasPendingSticker,
|
||||
isEditingMessage,
|
||||
isEditingScheduledMessage,
|
||||
isOverCharacterLimit,
|
||||
isSlowmodeActive,
|
||||
value,
|
||||
],
|
||||
);
|
||||
const sendButtonDisabled = rightButtonState.sendButton.disabled;
|
||||
const sendLockRef = useRef(false);
|
||||
const handleSendClick = () => {
|
||||
if (sendLockRef.current) {
|
||||
return;
|
||||
}
|
||||
sendLockRef.current = true;
|
||||
setTimeout(() => {
|
||||
sendLockRef.current = false;
|
||||
}, 500);
|
||||
onSubmit();
|
||||
};
|
||||
const shouldShowVoiceButton = rightButtonState.visibleButton === 'voice';
|
||||
const voiceButtonDisabled = rightButtonState.voiceButton.disabled;
|
||||
const voiceTooltipAnchorRef = useRef<HTMLDivElement | null>(null);
|
||||
return (
|
||||
<div
|
||||
className={clsx(styles.mobileTextareaWrapper, disabled && wrapperStyles.disabled)}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-textarea-wrapper"
|
||||
>
|
||||
{!disabled && canAttachFiles && (
|
||||
<div
|
||||
className={styles.mobilePlusButtonContainer}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-plus-button-container"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.mobilePlusButton}
|
||||
onClick={onPlusClick}
|
||||
aria-label={i18n._(OPEN_MENU_DESCRIPTOR)}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-plus-button.plus-click"
|
||||
>
|
||||
<PlusIcon
|
||||
className={styles.mobilePlusButtonIcon}
|
||||
weight="bold"
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-plus-button-icon"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={styles.mobileContentWrapper}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-content-wrapper"
|
||||
>
|
||||
<div
|
||||
className={styles.mobileInputContainer}
|
||||
ref={voiceTooltipAnchorRef}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-input-container"
|
||||
>
|
||||
<div
|
||||
className={styles.mobileInputContent}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-input-content"
|
||||
>
|
||||
<Scroller
|
||||
ref={scrollerRef}
|
||||
fade={true}
|
||||
className={textareaStyles.scroller}
|
||||
key="mobile-textarea-scroller"
|
||||
data-flx="channel.textarea.mobile-textarea-layout.scroller"
|
||||
>
|
||||
<div className={textareaStyles.flexColumn} data-flx="channel.textarea.mobile-textarea-layout.div">
|
||||
<TextareaInputField
|
||||
channelId={channelId}
|
||||
disabled={disabled}
|
||||
isMobile={true}
|
||||
value={value}
|
||||
placeholder={placeholderText}
|
||||
textareaRef={textareaRef}
|
||||
isFocused={isFocused}
|
||||
isAutocompleteAttached={isAutocompleteAttached}
|
||||
autocompleteListId={autocompleteListId}
|
||||
autocompleteOptions={autocompleteOptions}
|
||||
selectedIndex={selectedIndex}
|
||||
className={textareaStyles.textareaMobile}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
onHeightChange={onHeightChange}
|
||||
onCursorMove={onCursorMove}
|
||||
onArrowUp={onArrowUp}
|
||||
onEnter={onSubmit}
|
||||
onAutocompleteSelect={onAutocompleteSelect}
|
||||
setSelectedIndex={setSelectedIndex}
|
||||
onKeyDown={onKeyDown}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.textarea-input-field.change"
|
||||
/>
|
||||
</div>
|
||||
</Scroller>
|
||||
</div>
|
||||
{!disabled && (
|
||||
<div
|
||||
className={styles.mobileEmojiButtonContainer}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-emoji-button-container"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.mobileEmojiButton}
|
||||
onClick={onEmojiClick}
|
||||
aria-label={i18n._(EMOJIS_DESCRIPTOR)}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-emoji-button.emoji-click"
|
||||
>
|
||||
<SmileyIcon
|
||||
className={styles.mobileEmojiButtonIcon}
|
||||
weight="fill"
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-emoji-button-icon"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={styles.mobileRightButtonContainer}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-right-button-container"
|
||||
>
|
||||
<AnimatePresence initial={false} data-flx="channel.textarea.mobile-textarea-layout.animate-presence">
|
||||
{shouldShowVoiceButton ? (
|
||||
<motion.div
|
||||
key="voice-button"
|
||||
className={styles.mobileRightButtonSlot}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.div--2"
|
||||
{...buttonSwapMotion}
|
||||
>
|
||||
<VoiceMessageRecorder
|
||||
channelId={channelId}
|
||||
disabled={voiceButtonDisabled}
|
||||
tooltipAnchorRef={voiceTooltipAnchorRef}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.voice-message-recorder"
|
||||
/>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key="send-button"
|
||||
className={styles.mobileRightButtonSlot}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-send-button.send-click"
|
||||
{...buttonSwapMotion}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.mobileSendButton}
|
||||
onClick={handleSendClick}
|
||||
aria-label={i18n._(SEND_MESSAGE_DESCRIPTOR)}
|
||||
disabled={sendButtonDisabled}
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-send-button.button"
|
||||
>
|
||||
<ArrowUpIcon
|
||||
className={styles.mobileRightButtonIcon}
|
||||
weight="bold"
|
||||
data-flx="channel.textarea.mobile-textarea-layout.mobile-right-button-icon"
|
||||
/>
|
||||
</button>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
MobileTextareaLayout.displayName = 'MobileTextareaLayout';
|
||||
@@ -14,33 +14,17 @@
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.holdButtonWrapper {
|
||||
.mobileRightButtonContainer {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: var(--textarea-button-height);
|
||||
height: var(--textarea-button-height);
|
||||
}
|
||||
|
||||
.mobileRightButtonSlot {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.holdButton {
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.holdButtonRecording {
|
||||
box-shadow: 0 0 0 0.125rem rgba(252, 95, 105, 0.35);
|
||||
}
|
||||
|
||||
.holdIndicator {
|
||||
font-size: 0.625rem;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-primary-muted);
|
||||
}
|
||||
|
||||
.holdIndicatorRecording {
|
||||
color: var(--accent-danger);
|
||||
}
|
||||
|
||||
.holdIndicatorError {
|
||||
color: var(--accent-danger);
|
||||
font-weight: 600;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import Accessibility from '@app/features/accessibility/state/Accessibility';
|
||||
import HoldToRecordButton from '@app/features/channel/components/textarea/HoldToRecordButton';
|
||||
import {transitionMobileTextareaButtonState} from '@app/features/channel/components/textarea/MobileTextareaButtonStateMachine';
|
||||
import {TextareaButton} from '@app/features/channel/components/textarea/TextareaButton';
|
||||
import textareaButtonsStyles from '@app/features/channel/components/textarea/TextareaButtons.module.css';
|
||||
import styles from '@app/features/channel/components/textarea/TextareaInput.module.css';
|
||||
import VoiceMessageRecorder from '@app/features/channel/components/VoiceMessageRecorder';
|
||||
import type {ExpressionPickerTabType} from '@app/features/expressions/components/popouts/ExpressionPickerPopout';
|
||||
import {
|
||||
EMOJIS_DESCRIPTOR,
|
||||
@@ -12,29 +13,28 @@ import {
|
||||
MEDIA_DESCRIPTOR,
|
||||
STICKERS_DESCRIPTOR,
|
||||
} from '@app/features/i18n/utils/CommonMessageDescriptors';
|
||||
import {getReducedMotionProps} from '@app/features/ui/utils/ReducedMotionAnimation';
|
||||
import {getReducedMotionProps, type MotionAnimation} from '@app/features/ui/utils/ReducedMotionAnimation';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
import {useLingui} from '@lingui/react/macro';
|
||||
import {
|
||||
GifIcon,
|
||||
ImageSquareIcon,
|
||||
MicrophoneIcon,
|
||||
PaperPlaneRightIcon,
|
||||
SmileyIcon,
|
||||
StickerIcon,
|
||||
} from '@phosphor-icons/react';
|
||||
import {GifIcon, ImageSquareIcon, PaperPlaneRightIcon, SmileyIcon, StickerIcon} from '@phosphor-icons/react';
|
||||
import {clsx} from 'clsx';
|
||||
import {AnimatePresence, motion} from 'framer-motion';
|
||||
import React from 'react';
|
||||
import React, {useMemo} from 'react';
|
||||
|
||||
const VOICE_MESSAGE_DESCRIPTOR = msg({
|
||||
message: 'Voice message',
|
||||
comment: 'Short label in the channel and chat textarea buttons. Keep it concise.',
|
||||
});
|
||||
const SEND_MESSAGE_DESCRIPTOR = msg({
|
||||
message: 'Send message',
|
||||
comment: 'Button or menu action label in the channel and chat textarea buttons. Keep it concise.',
|
||||
});
|
||||
const MOBILE_BUTTON_SWAP_MOTION: MotionAnimation = {
|
||||
initial: {opacity: 0, scale: 0.86, y: 3},
|
||||
animate: {opacity: 1, scale: 1, y: 0},
|
||||
exit: {opacity: 0, scale: 0.9, y: -2},
|
||||
transition: {
|
||||
opacity: {duration: 0.08},
|
||||
y: {duration: 0.08, ease: 'easeOut'},
|
||||
scale: {type: 'spring', stiffness: 560, damping: 28, mass: 0.55},
|
||||
},
|
||||
};
|
||||
|
||||
interface TextareaButtonsProps {
|
||||
disabled: boolean;
|
||||
@@ -44,8 +44,10 @@ interface TextareaButtonsProps {
|
||||
showStickersButton: boolean;
|
||||
showEmojiButton: boolean;
|
||||
showMessageSendButton: boolean;
|
||||
showVoiceMessageButton?: boolean;
|
||||
onVoiceMessageClick?: () => void;
|
||||
canRecordVoice: boolean;
|
||||
isEditingMessage: boolean;
|
||||
hasPendingSticker: boolean;
|
||||
voiceTooltipAnchorRef?: React.RefObject<HTMLElement | null>;
|
||||
channelId: string;
|
||||
expressionPickerOpen: boolean;
|
||||
selectedTab: ExpressionPickerTabType;
|
||||
@@ -71,8 +73,10 @@ export const TextareaButtons = React.forwardRef<HTMLDivElement, TextareaButtonsP
|
||||
showStickersButton,
|
||||
showEmojiButton,
|
||||
showMessageSendButton,
|
||||
showVoiceMessageButton,
|
||||
onVoiceMessageClick,
|
||||
canRecordVoice,
|
||||
isEditingMessage,
|
||||
hasPendingSticker,
|
||||
voiceTooltipAnchorRef,
|
||||
channelId,
|
||||
expressionPickerOpen,
|
||||
selectedTab,
|
||||
@@ -90,22 +94,40 @@ export const TextareaButtons = React.forwardRef<HTMLDivElement, TextareaButtonsP
|
||||
ref,
|
||||
) => {
|
||||
const {i18n} = useLingui();
|
||||
const buttonModel = useMemo(
|
||||
() =>
|
||||
transitionMobileTextareaButtonState({
|
||||
disabled,
|
||||
canRecordVoice,
|
||||
value: '',
|
||||
isSlowmodeActive,
|
||||
isOverCharacterLimit: isOverLimit,
|
||||
isEditingMessage,
|
||||
hasContent,
|
||||
hasAttachments,
|
||||
hasPendingSticker,
|
||||
isEditingScheduledMessage: false,
|
||||
}),
|
||||
[
|
||||
canRecordVoice,
|
||||
disabled,
|
||||
hasAttachments,
|
||||
hasContent,
|
||||
hasPendingSticker,
|
||||
isEditingMessage,
|
||||
isOverLimit,
|
||||
isSlowmodeActive,
|
||||
],
|
||||
);
|
||||
if (disabled) {
|
||||
return null;
|
||||
}
|
||||
const buttonSwapMotion = getReducedMotionProps(
|
||||
{
|
||||
initial: {scale: 0.8, opacity: 0},
|
||||
animate: {scale: 1, opacity: 1},
|
||||
exit: {scale: 0.8, opacity: 0},
|
||||
transition: {duration: 0.15, ease: 'easeOut'},
|
||||
},
|
||||
Accessibility.useReducedMotion,
|
||||
);
|
||||
const buttonSwapMotion = getReducedMotionProps(MOBILE_BUTTON_SWAP_MOTION, Accessibility.useReducedMotion);
|
||||
const shouldShowDesktopSendButton = showMessageSendButton;
|
||||
const baseSendDisabled = isSlowmodeActive || isOverLimit || disableSendButton;
|
||||
const sendButtonDisabled = baseSendDisabled || (!hasContent && !hasAttachments);
|
||||
const shouldShowHoldToRecord = isMobile && showVoiceMessageButton && !hasContent && !hasAttachments;
|
||||
const desktopSendDisabled =
|
||||
isSlowmodeActive || isOverLimit || (!hasContent && !hasAttachments) || disableSendButton;
|
||||
const shouldShowVoiceButton = buttonModel.visibleButton === 'voice';
|
||||
const mobileSendDisabled = buttonModel.sendButton.disabled || Boolean(disableSendButton);
|
||||
return (
|
||||
<div
|
||||
className={clsx(styles.buttonContainerDense, styles.sideButtonPadding)}
|
||||
@@ -167,48 +189,51 @@ export const TextareaButtons = React.forwardRef<HTMLDivElement, TextareaButtonsP
|
||||
className={textareaButtonsStyles.invisibleTrigger}
|
||||
data-flx="channel.textarea.textarea-buttons.div"
|
||||
/>
|
||||
{isMobile && showVoiceMessageButton && onVoiceMessageClick && !shouldShowHoldToRecord && (
|
||||
<TextareaButton
|
||||
icon={MicrophoneIcon}
|
||||
label={i18n._(VOICE_MESSAGE_DESCRIPTOR)}
|
||||
onClick={onVoiceMessageClick}
|
||||
data-flx="channel.textarea.textarea-buttons.textarea-button.voice-message-click"
|
||||
/>
|
||||
)}
|
||||
{isMobile && (
|
||||
<AnimatePresence mode="wait" initial={false} data-flx="channel.textarea.textarea-buttons.animate-presence">
|
||||
{shouldShowHoldToRecord ? (
|
||||
<motion.div
|
||||
key="hold-to-record"
|
||||
data-flx="channel.textarea.textarea-buttons.div--2"
|
||||
{...buttonSwapMotion}
|
||||
>
|
||||
<HoldToRecordButton
|
||||
channelId={channelId}
|
||||
disabled={baseSendDisabled}
|
||||
onFallback={onVoiceMessageClick}
|
||||
data-flx="channel.textarea.textarea-buttons.hold-to-record-button"
|
||||
/>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div key="send-button" data-flx="channel.textarea.textarea-buttons.div--3" {...buttonSwapMotion}>
|
||||
<TextareaButton
|
||||
disabled={sendButtonDisabled}
|
||||
icon={PaperPlaneRightIcon}
|
||||
label={i18n._(SEND_MESSAGE_DESCRIPTOR)}
|
||||
onClick={onSubmit}
|
||||
keybindCombo={{key: 'Enter'}}
|
||||
data-flx="channel.textarea.textarea-buttons.textarea-button.submit"
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<div
|
||||
className={textareaButtonsStyles.mobileRightButtonContainer}
|
||||
data-flx="channel.textarea.textarea-buttons.mobile-right-button-container"
|
||||
>
|
||||
<AnimatePresence initial={false} data-flx="channel.textarea.textarea-buttons.animate-presence">
|
||||
{shouldShowVoiceButton ? (
|
||||
<motion.div
|
||||
key="voice-button"
|
||||
className={textareaButtonsStyles.mobileRightButtonSlot}
|
||||
data-flx="channel.textarea.textarea-buttons.div--2"
|
||||
{...buttonSwapMotion}
|
||||
>
|
||||
<VoiceMessageRecorder
|
||||
channelId={channelId}
|
||||
disabled={buttonModel.voiceButton.disabled}
|
||||
tooltipAnchorRef={voiceTooltipAnchorRef}
|
||||
data-flx="channel.textarea.textarea-buttons.voice-message-recorder"
|
||||
/>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key="send-button"
|
||||
className={textareaButtonsStyles.mobileRightButtonSlot}
|
||||
data-flx="channel.textarea.textarea-buttons.div--3"
|
||||
{...buttonSwapMotion}
|
||||
>
|
||||
<TextareaButton
|
||||
disabled={mobileSendDisabled}
|
||||
icon={PaperPlaneRightIcon}
|
||||
label={i18n._(SEND_MESSAGE_DESCRIPTOR)}
|
||||
onClick={onSubmit}
|
||||
keybindCombo={{key: 'Enter'}}
|
||||
data-flx="channel.textarea.textarea-buttons.textarea-button.submit"
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)}
|
||||
{!isMobile && shouldShowDesktopSendButton && (
|
||||
<>
|
||||
<div className={styles.divider} data-flx="channel.textarea.textarea-buttons.divider" />
|
||||
<TextareaButton
|
||||
disabled={isSlowmodeActive || isOverLimit || (!hasContent && !hasAttachments) || disableSendButton}
|
||||
disabled={desktopSendDisabled}
|
||||
icon={PaperPlaneRightIcon}
|
||||
label={i18n._(SEND_MESSAGE_DESCRIPTOR)}
|
||||
onClick={onSubmit}
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "مساعدة"
|
||||
msgid "Help center"
|
||||
msgstr "مركز المساعدة"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "ساعد في ترجمة {PRODUCT_NAME} إلى لغتك على <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "الترجمة"
|
||||
msgid "Translation provider"
|
||||
msgstr "مزوّد الترجمة"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "الترجمات آلية. ساعد في تحسين الترجمة عن طريق إرسال بريد إلكتروني إلى <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -1410,6 +1410,9 @@
|
||||
{
|
||||
"msgid": "Granted"
|
||||
},
|
||||
{
|
||||
"msgid": "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
},
|
||||
{
|
||||
"msgid": "How much of the member's recent message history to delete."
|
||||
},
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Помощ"
|
||||
msgid "Help center"
|
||||
msgstr "Помощен център"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Помогнете да преведем {PRODUCT_NAME} на вашия език на <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Превод"
|
||||
msgid "Translation provider"
|
||||
msgstr "Доставчик на преводи"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Преводите са автоматични. Помогнете за подобряване на локализацията, като изпратите имейл на <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Nápověda"
|
||||
msgid "Help center"
|
||||
msgstr "Centrum nápovědy"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Pomozte přeložit {PRODUCT_NAME} do vašeho jazyka na <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Překlad"
|
||||
msgid "Translation provider"
|
||||
msgstr "Poskytovatel překladu"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Překlady jsou automatické. Pomozte nám zlepšit lokalizaci e-mailem na <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Hjælp"
|
||||
msgid "Help center"
|
||||
msgstr "Hjælp"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Hjælp med at oversætte {PRODUCT_NAME} til dit sprog på <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Oversættelse"
|
||||
msgid "Translation provider"
|
||||
msgstr "Oversættelsesudbyder"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Oversættelser er automatiske. Hjælp os med at forbedre lokaliseringen ved at sende en e-mail til <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Hilfe"
|
||||
msgid "Help center"
|
||||
msgstr "Hilfebereich"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Hilf mit, {PRODUCT_NAME} in deiner Sprache auf <0>{I18N_WEBLATE_DOMAIN}</0> zu übersetzen."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Übersetzung"
|
||||
msgid "Translation provider"
|
||||
msgstr "Übersetzungsanbieter"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Übersetzungen erfolgen automatisch. Helfen Sie uns, die Lokalisierung zu verbessern, indem Sie eine E-Mail an <0>{I18N_EMAIL}</0> senden."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Βοήθεια"
|
||||
msgid "Help center"
|
||||
msgstr "Κέντρο βοήθειας"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Βοηθήστε στη μετάφραση του {PRODUCT_NAME} στη γλώσσα σας στο <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Μετάφραση"
|
||||
msgid "Translation provider"
|
||||
msgstr "Πάροχος μετάφρασης"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Οι μεταφράσεις είναι αυτοματοποιημένες. Βοηθήστε να βελτιώσουμε την τοπική προσαρμογή στέλνοντας email στο <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -14635,7 +14635,6 @@ msgstr "Filter mentions"
|
||||
msgid "Filtering"
|
||||
msgstr "Filtering"
|
||||
|
||||
# auto-i18n: reviewed unchanged
|
||||
#. Label for the discovery language filter button once a language is chosen. Preserve {language}; it is inserted by code.
|
||||
#: src/features/discovery/discovery/DiscoveryLanguageButton.tsx:20
|
||||
msgid "Filtering by {language}"
|
||||
@@ -16226,6 +16225,11 @@ msgstr "Help"
|
||||
msgid "Help center"
|
||||
msgstr "Help centre"
|
||||
|
||||
# auto-i18n: reviewed unchanged
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33557,10 +33561,6 @@ msgstr "Translation"
|
||||
msgid "Translation provider"
|
||||
msgstr "Translation provider"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Translations are automated. Help improve localisation by emailing <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16226,6 +16226,10 @@ msgstr "Help"
|
||||
msgid "Help center"
|
||||
msgstr "Help center"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33557,10 +33561,6 @@ msgstr "Translation"
|
||||
msgid "Translation provider"
|
||||
msgstr "Translation provider"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Ayuda"
|
||||
msgid "Help center"
|
||||
msgstr "Centro de ayuda"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Ayuda a traducir {PRODUCT_NAME} a tu idioma en <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Traducción"
|
||||
msgid "Translation provider"
|
||||
msgstr "Proveedor de traducción"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Las traducciones son automáticas. Ayuda a mejorar la localización enviando un correo electrónico a <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Ayuda"
|
||||
msgid "Help center"
|
||||
msgstr "Centro de ayuda"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Ayuda a traducir {PRODUCT_NAME} a tu idioma en <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Traducción"
|
||||
msgid "Translation provider"
|
||||
msgstr "Proveedor de traducción"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Las traducciones son automáticas. Ayuda a mejorar la localización enviando un correo electrónico a <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Ohje"
|
||||
msgid "Help center"
|
||||
msgstr "Ohjekeskus"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Auta kääntämään {PRODUCT_NAME} kielellesi osoitteessa <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Käännös"
|
||||
msgid "Translation provider"
|
||||
msgstr "Käännöspalvelu"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Käännökset ovat automaattisia. Auta parantamaan lokalisointia lähettämällä sähköpostia osoitteeseen <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Aide"
|
||||
msgid "Help center"
|
||||
msgstr "Centre d'aide"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Aidez à traduire {PRODUCT_NAME} dans votre langue sur <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Traduction"
|
||||
msgid "Translation provider"
|
||||
msgstr "Fournisseur de traduction"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Les traductions sont automatiques. Aidez-nous à améliorer la localisation en envoyant un e-mail à <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "עזרה"
|
||||
msgid "Help center"
|
||||
msgstr "מרכז עזרה"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "עזרו לתרגם את {PRODUCT_NAME} לשפה שלכם ב-<0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "תרגום"
|
||||
msgid "Translation provider"
|
||||
msgstr "ספק תרגום"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "התרגומים הם אוטומטיים. עזרו לשפר את הלוקליזציה על ידי שליחת אימייל לכתובת <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "सहायता"
|
||||
msgid "Help center"
|
||||
msgstr "सहायता केंद्र"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "{PRODUCT_NAME} को अपनी भाषा में <0>{I18N_WEBLATE_DOMAIN}</0> पर अनुवाद करने में मदद करें।"
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "अनुवाद"
|
||||
msgid "Translation provider"
|
||||
msgstr "अनुवाद प्रदाता"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "अनुवाद स्वचालित हैं। <0>{I18N_EMAIL}</0> पर ईमेल करके स्थानीयकरण को बेहतर बनाने में मदद करें।"
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Pomoć"
|
||||
msgid "Help center"
|
||||
msgstr "Centar za pomoć"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Pomozite prevesti {PRODUCT_NAME} na vaš jezik na <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Prijevod"
|
||||
msgid "Translation provider"
|
||||
msgstr "Davatelj prijevoda"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Prijevodi su automatizirani. Pomozite poboljšati lokalizaciju slanjem e-pošte na <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Súgó"
|
||||
msgid "Help center"
|
||||
msgstr "Súgóközpont"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Segíts a(z) {PRODUCT_NAME} fordításában a saját nyelvedre itt: <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Fordítás"
|
||||
msgid "Translation provider"
|
||||
msgstr "Fordítószolgáltató"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "A fordítások automatizáltak. Segíts a lokalizáció javításában, írj egy e-mailt ide: <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Bantuan"
|
||||
msgid "Help center"
|
||||
msgstr "Pusat bantuan"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Bantu terjemahkan {PRODUCT_NAME} ke bahasa Anda di <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Terjemahan"
|
||||
msgid "Translation provider"
|
||||
msgstr "Penyedia terjemahan"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Terjemahan dilakukan secara otomatis. Bantu tingkatkan lokalisasi dengan mengirim email ke <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Aiuto"
|
||||
msgid "Help center"
|
||||
msgstr "Centro assistenza"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Aiuta a tradurre {PRODUCT_NAME} nella tua lingua su <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Traduzione"
|
||||
msgid "Translation provider"
|
||||
msgstr "Fornitore di traduzione"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Le traduzioni sono automatiche. Contribuisci a migliorare la localizzazione inviando un'email a <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "ヘルプ"
|
||||
msgid "Help center"
|
||||
msgstr "ヘルプセンター"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "{PRODUCT_NAME} をあなたの言語に翻訳するお手伝いをしてください。 <0>{I18N_WEBLATE_DOMAIN}</0>"
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "翻訳"
|
||||
msgid "Translation provider"
|
||||
msgstr "翻訳プロバイダ"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "翻訳は自動で行われています。<0>{I18N_EMAIL}</0>までメールでご連絡いただければ、ローカライズの改善にご協力いただけます。"
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "도움말"
|
||||
msgid "Help center"
|
||||
msgstr "고객센터"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "{PRODUCT_NAME}을(를) 귀하의 언어로 번역하는 데 참여해 주세요. <0>{I18N_WEBLATE_DOMAIN}</0>에서 가능합니다."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "번역"
|
||||
msgid "Translation provider"
|
||||
msgstr "번역 서비스 제공자"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "번역은 자동입니다. <0>{I18N_EMAIL}</0>로 이메일을 보내 현지화 개선에 도움을 주세요."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Pagalba"
|
||||
msgid "Help center"
|
||||
msgstr "Pagalbos centras"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Padėkite versti {PRODUCT_NAME} į jūsų kalbą per <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Vertimas"
|
||||
msgid "Translation provider"
|
||||
msgstr "Vertimo paslaugų teikėjas"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Vertimai yra automatiniai. Padėkite pagerinti lokalizavimą el. paštu <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Help"
|
||||
msgid "Help center"
|
||||
msgstr "Helpcentrum"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Help {PRODUCT_NAME} vertalen naar jouw taal op <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Vertaling"
|
||||
msgid "Translation provider"
|
||||
msgstr "Vertaalservice"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Vertalingen zijn geautomatiseerd. Help de lokalisatie te verbeteren door een e-mail te sturen naar <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Hjelp"
|
||||
msgid "Help center"
|
||||
msgstr "Hjelpesenter"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Hjelp til med å oversette {PRODUCT_NAME} til ditt språk på <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Oversettelse"
|
||||
msgid "Translation provider"
|
||||
msgstr "Oversettelsesleverandør"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Oversettelser er automatiserte. Hjelp oss med å forbedre lokaliseringen ved å sende en e-post til <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Pomoc"
|
||||
msgid "Help center"
|
||||
msgstr "Centrum pomocy"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Pomóż przetłumaczyć {PRODUCT_NAME} na swój język na stronie <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Tłumaczenie"
|
||||
msgid "Translation provider"
|
||||
msgstr "Dostawca tłumaczeń"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Tłumaczenia są automatyczne. Pomóż nam je ulepszyć, wysyłając e-mail na adres <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Ajuda"
|
||||
msgid "Help center"
|
||||
msgstr "Central de ajuda"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Ajude a traduzir o {PRODUCT_NAME} para o seu idioma em <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Tradução"
|
||||
msgid "Translation provider"
|
||||
msgstr "Provedor de tradução"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "As traduções são automáticas. Ajude a melhorar a localização enviando um e-mail para <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Ajutor"
|
||||
msgid "Help center"
|
||||
msgstr "Centrul de ajutor"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Ajută la traducerea {PRODUCT_NAME} în limba ta pe <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Traducere"
|
||||
msgid "Translation provider"
|
||||
msgstr "Furnizor de traduceri"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Traducerile sunt automate. Ajută la îmbunătățirea localizării trimițând un e-mail la <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Помощь"
|
||||
msgid "Help center"
|
||||
msgstr "Справочный центр"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Помогите перевести {PRODUCT_NAME} на ваш язык на <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Перевод"
|
||||
msgid "Translation provider"
|
||||
msgstr "Провайдер перевода"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Перевод автоматический. Помогите улучшить локализацию, написав на <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Hjälp"
|
||||
msgid "Help center"
|
||||
msgstr "Hjälpcenter"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Hjälp till att översätta {PRODUCT_NAME} till ditt språk på <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Översättning"
|
||||
msgid "Translation provider"
|
||||
msgstr "Översättningstjänst"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Översättningar är automatiserade. Hjälp oss att förbättra lokaliseringen genom att mejla <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "ช่วยเหลือ"
|
||||
msgid "Help center"
|
||||
msgstr "ศูนย์ช่วยเหลือ"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "ช่วยแปล {PRODUCT_NAME} เป็นภาษาของคุณบน <0>{I18N_WEBLATE_DOMAIN}</0>"
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "การแปลภาษา"
|
||||
msgid "Translation provider"
|
||||
msgstr "ผู้ให้บริการแปลภาษา"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "การแปลเป็นแบบอัตโนมัติ ช่วยปรับปรุงการแปลให้ดียิ่งขึ้นได้โดยส่งอีเมลไปที่ <0>{I18N_EMAIL}</0>"
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Yardım"
|
||||
msgid "Help center"
|
||||
msgstr "Yardım merkezi"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "{PRODUCT_NAME}'i <0>{I18N_WEBLATE_DOMAIN}</0> üzerinde dilinize çevirmemize yardım edin."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Çeviri"
|
||||
msgid "Translation provider"
|
||||
msgstr "Çeviri sağlayıcısı"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Çeviriler otomatiktir. Yerelleştirmeyi iyileştirmeye yardımcı olmak için <0>{I18N_EMAIL}</0> adresine e-posta gönderin."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Довідка"
|
||||
msgid "Help center"
|
||||
msgstr "Довідковий центр"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Допоможіть перекласти {PRODUCT_NAME} вашою мовою на <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Переклад"
|
||||
msgid "Translation provider"
|
||||
msgstr "Провайдер перекладу"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Переклад автоматичний. Допоможіть покращити локалізацію, написавши на <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "Trợ giúp"
|
||||
msgid "Help center"
|
||||
msgstr "Trung tâm trợ giúp"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "Giúp dịch {PRODUCT_NAME} sang ngôn ngữ của bạn trên <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "Bản dịch"
|
||||
msgid "Translation provider"
|
||||
msgstr "Nhà cung cấp dịch"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "Bản dịch được tự động hóa. Hãy giúp cải thiện bản địa hóa bằng cách gửi email tới <0>{I18N_EMAIL}</0>."
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "帮助"
|
||||
msgid "Help center"
|
||||
msgstr "帮助中心"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "帮助我们将 {PRODUCT_NAME} 翻译成您的语言,请访问 <0>{I18N_WEBLATE_DOMAIN}</0>。"
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "翻译"
|
||||
msgid "Translation provider"
|
||||
msgstr "翻译提供方"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "翻译由机器自动完成。发送邮件至<0>{I18N_EMAIL}</0>,帮助我们改进本地化。"
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -16225,6 +16225,10 @@ msgstr "說明"
|
||||
msgid "Help center"
|
||||
msgstr "說明中心"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Help translate {PRODUCT_NAME} into your language on <0>{I18N_WEBLATE_DOMAIN}</0>."
|
||||
msgstr "協助將 {PRODUCT_NAME} 翻譯成您的語言,請前往 <0>{I18N_WEBLATE_DOMAIN}</0>。"
|
||||
|
||||
#. Description for a WebRTC content hint select. Keep Motion, Detail, and text as plain option concepts.
|
||||
#: src/features/user/components/modals/tabs/advanced_settings_tab/AdvancedVideoControls.tsx:135
|
||||
msgid "Helps the browser choose motion, detail, or text handling."
|
||||
@@ -33556,10 +33560,6 @@ msgstr "翻譯"
|
||||
msgid "Translation provider"
|
||||
msgstr "翻譯服務供應商"
|
||||
|
||||
#: src/features/user/components/modals/tabs/LanguageTab.tsx:360
|
||||
msgid "Translations are automated. Help improve localization by emailing <0>{I18N_EMAIL}</0>."
|
||||
msgstr "翻譯為自動產生。請來信至 <0>{I18N_EMAIL}</0> 協助改進本地化。"
|
||||
|
||||
#. Settings search synonym. Used to match this term when the user types it in the settings search bar.
|
||||
#: src/features/user/components/settings_utils/search_index/ChatSettingsIndex.ts:247
|
||||
msgid "Translator"
|
||||
|
||||
@@ -152,8 +152,8 @@ export class ComposerCustomEmojiNode extends DecoratorNode<JSX.Element> {
|
||||
return true;
|
||||
}
|
||||
|
||||
override isKeyboardSelectable(): boolean {
|
||||
return true;
|
||||
override isKeyboardSelectable(): false {
|
||||
return false;
|
||||
}
|
||||
|
||||
override decorate(): JSX.Element {
|
||||
|
||||
@@ -184,8 +184,8 @@ export class ComposerMentionNode extends DecoratorNode<JSX.Element> {
|
||||
return true;
|
||||
}
|
||||
|
||||
override isKeyboardSelectable(): boolean {
|
||||
return true;
|
||||
override isKeyboardSelectable(): false {
|
||||
return false;
|
||||
}
|
||||
|
||||
override decorate(): JSX.Element {
|
||||
|
||||
@@ -143,8 +143,8 @@ export class ComposerStandardEmojiNode extends DecoratorNode<JSX.Element> {
|
||||
return true;
|
||||
}
|
||||
|
||||
override isKeyboardSelectable(): boolean {
|
||||
return true;
|
||||
override isKeyboardSelectable(): false {
|
||||
return false;
|
||||
}
|
||||
|
||||
override decorate(): JSX.Element {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Accessibility from '@app/features/accessibility/state/Accessibility';
|
||||
import {useMergeRefs} from '@app/features/app/hooks/useMergeRefs';
|
||||
import {Logger} from '@app/features/platform/utils/AppLogger';
|
||||
import {remFromPx} from '@app/features/theme/layout/RemFromPx';
|
||||
import * as ToastCommands from '@app/features/ui/commands/ToastCommands';
|
||||
import {usePortalHost} from '@app/features/ui/overlay/PortalHostContext';
|
||||
import ContextMenuState from '@app/features/ui/state/ContextMenu';
|
||||
@@ -77,8 +78,8 @@ export const useTooltipPortalRoot = (enabled = true, targetDocument?: Document):
|
||||
return scopedPortalHost ?? root;
|
||||
};
|
||||
const MAX_WIDTH_MAP = {
|
||||
default: 190,
|
||||
xl: 350,
|
||||
default: remFromPx(190),
|
||||
xl: remFromPx(350),
|
||||
none: 'none' as const,
|
||||
};
|
||||
const TooltipPositionToStyle: Record<TooltipPosition, string> = {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
SettingsTabSection,
|
||||
} from '@app/features/app/components/dialogs/shared/SettingsTabLayout';
|
||||
import {ExternalLink} from '@app/features/app/components/shared/ExternalLink';
|
||||
import {I18N_EMAIL, I18N_EMAIL_MAILTO, PRODUCT_NAME} from '@app/features/app/config/I18nDisplayConstants';
|
||||
import {I18N_WEBLATE_DOMAIN, I18N_WEBLATE_URL, PRODUCT_NAME} from '@app/features/app/config/I18nDisplayConstants';
|
||||
import * as EmojiUtils from '@app/features/expressions/utils/EmojiUtils';
|
||||
import Spellcheck from '@app/features/messaging/state/Spellcheck';
|
||||
import type {SpellcheckEngine} from '@app/features/platform/types/Electron';
|
||||
@@ -358,9 +358,9 @@ const LanguageTab = observer(() => {
|
||||
<div className={styles.notice} data-flx="user.language-tab.notice">
|
||||
<p className={styles.noticeText} data-flx="user.language-tab.notice-text">
|
||||
<Trans>
|
||||
Translations are automated. Help improve localization by emailing{' '}
|
||||
<ExternalLink href={I18N_EMAIL_MAILTO} className={styles.link} data-flx="user.language-tab.link">
|
||||
{I18N_EMAIL}
|
||||
Help translate {PRODUCT_NAME} into your language on{' '}
|
||||
<ExternalLink href={I18N_WEBLATE_URL} className={styles.link} data-flx="user.language-tab.link">
|
||||
{I18N_WEBLATE_DOMAIN}
|
||||
</ExternalLink>
|
||||
.
|
||||
</Trans>
|
||||
|
||||
+27
-19
@@ -1,25 +1,22 @@
|
||||
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(14rem, min(max-content, 55%));
|
||||
align-items: center;
|
||||
column-gap: 1rem;
|
||||
row-gap: 1.5rem;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
min-height: 2.75rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 0.25rem 0;
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
min-height: 2.25rem;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
flex: 1 1 0%;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-weight: 500;
|
||||
@@ -34,13 +31,14 @@
|
||||
}
|
||||
|
||||
.choiceWrap {
|
||||
flex: 0 0 14rem;
|
||||
width: 14rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.choiceGroup {
|
||||
position: relative;
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: minmax(0, 1fr);
|
||||
width: 100%;
|
||||
height: 2.125rem;
|
||||
border-radius: 0.5rem;
|
||||
@@ -57,7 +55,6 @@
|
||||
.choiceButton {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
border: 0;
|
||||
border-radius: 0.375rem;
|
||||
@@ -69,6 +66,8 @@
|
||||
line-height: 1rem;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
transition: color 150ms ease;
|
||||
}
|
||||
@@ -101,13 +100,22 @@
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.row {
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.choiceWrap {
|
||||
flex: none;
|
||||
width: min(100%, 14rem);
|
||||
.row {
|
||||
display: flex;
|
||||
min-height: 2.75rem;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +277,14 @@ The `fluxer-static` image is part of the default stack, so static asset updates
|
||||
|
||||
To pin a specific release, set `FLUXER_IMAGE_TAG` in `.env` to the release tag you want, then pull and restart.
|
||||
|
||||
Each upgrade leaves the previous images behind, which adds up to a few gigabytes over time. To reclaim that space:
|
||||
|
||||
```bash
|
||||
docker image prune -f
|
||||
```
|
||||
|
||||
This removes only the untagged images the upgrade replaced. `docker system prune -a` reclaims more, but it also deletes every image not currently used by a running container, including ones unrelated to Fluxer.
|
||||
|
||||
## Getting help
|
||||
|
||||
- File issues and follow development on [GitHub](https://github.com/fluxerapp/fluxer).
|
||||
|
||||
+1
-1
Submodule fluxer_marketing updated: a31164caf7...f39eced3b8
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: ar\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -571,10 +571,8 @@ msgstr "الكود المصدري"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"يجب أن تبدو الترجمة طبيعية لمن يتحدث اللغة. صحّح عبارة أو ابدأ لغة جديدة على {weblate}، وسنضمّها إلى {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "ساعد في ترجمة {product_name} إلى لغتك على {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2373,8 +2371,8 @@ msgstr "شارة مشترك على ملفك الشخصي"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "عناصر الوسائط التي يمكنك حفظها"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "عناصر الوسائط التي يمكنك حفظها لوقت لاحق"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: bg\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -573,10 +573,8 @@ msgstr "Изходен код"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Помогнете да преведем {product_name} на вашия език на {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2387,8 +2385,8 @@ msgstr "Subscriber badge on your profile"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Media items you can save"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Медийни файлове, които можеш да запазиш за по-късно"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: cs\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -572,10 +572,8 @@ msgstr "Zdrojový kód"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Překlad by měl znít přirozeně tomu, kdo daným jazykem mluví. Oprav formulaci nebo založ nový jazyk na {weblate} a my ho do {product_name} zahrneme."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Pomozte přeložit {product_name} do vašeho jazyka na {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2381,8 +2379,8 @@ msgstr "Odznak předplatitele na profilu"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Média, která si můžeš uložit"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Média, která si můžeš uložit na později"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: da\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -573,10 +573,8 @@ msgstr "Kildekode"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"En oversættelse skal lyde naturlig for en, der taler sproget. Ret en formulering eller start et nyt sprog på {weblate}, så tager vi det med i {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Hjælp med at oversætte {product_name} til dit sprog på {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2388,8 +2386,8 @@ msgstr "Abonnentbadge på din profil"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Medieelementer, du kan gemme"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Medieelementer, du kan gemme til senere"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -575,10 +575,8 @@ msgstr "Quellcode"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Eine Übersetzung sollte für Muttersprachler natürlich klingen. Korrigiere eine Formulierung oder starte eine neue Sprache auf {weblate}, und wir nehmen sie in {product_name} auf."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Hilf mit, {product_name} in deiner Sprache auf {weblate} zu übersetzen."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2396,8 +2394,8 @@ msgstr "Abonnenten-Abzeichen auf deinem Profil"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Medien, die du speichern kannst"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Medien, die du für später speichern kannst"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: el\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -576,10 +576,8 @@ msgstr "Πηγαίος κώδικας"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Μια μετάφραση πρέπει να ακούγεται φυσική σε κάποιον που μιλάει τη γλώσσα. Διόρθωσε μια φράση ή ξεκίνα μια νέα γλώσσα στο {weblate} και θα την προσθέσουμε στο {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Βοηθήστε στη μετάφραση του {product_name} στη γλώσσα σας στο {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2404,8 +2402,8 @@ msgstr "Σήμα συνδρομητή στο προφίλ σου"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Πολυμέσα που μπορείς να αποθηκεύσεις"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Πολυμέσα που μπορείς να αποθηκεύσεις για αργότερα"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: en-GB\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -571,10 +571,8 @@ msgstr "Source code"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Help translate {product_name} into your language on {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2379,8 +2377,8 @@ msgstr "Subscriber badge on your profile"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Media items you can save"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Media items you can save for later"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: en-US\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -571,10 +571,8 @@ msgstr "Source code"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Help translate {product_name} into your language on {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2379,8 +2377,8 @@ msgstr "Subscriber badge on your profile"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Media items you can save"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Media items you can save for later"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: es-419\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -576,10 +576,8 @@ msgstr "Código fuente"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Una traducción debe sonar natural para quien habla el idioma. Corrige una frase o empieza un idioma nuevo en {weblate} y lo incluiremos en {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Ayuda a traducir {product_name} a tu idioma en {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2393,8 +2391,8 @@ msgstr "Insignia de suscriptor en tu perfil"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Elementos multimedia que puedes guardar"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Elementos multimedia que puedes guardar para después"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: es-ES\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -576,10 +576,8 @@ msgstr "Código fuente"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Una traducción debe sonar natural para quien habla el idioma. Corrige una frase o empieza un idioma nuevo en {weblate} y lo incluiremos en {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Ayuda a traducir {product_name} a tu idioma en {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2395,8 +2393,8 @@ msgstr "Insignia de suscriptor en tu perfil"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Elementos multimedia que puedes guardar"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Elementos multimedia que puedes guardar para más tarde"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: fi\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -575,10 +575,8 @@ msgstr "Lähdekoodi"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Käännöksen pitäisi kuulostaa luontevalta kieltä puhuvan korvaan. Korjaa jokin ilmaus tai aloita uusi kieliversio {weblate}-palvelussa, niin otamme sen mukaan {product_name}-palveluun."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Auta kääntämään {product_name} kielellesi osoitteessa {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2394,8 +2392,8 @@ msgstr "Tilaajamerkki profiilissasi"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Tallennettavat mediatiedostot"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Mediatiedostot, jotka voit tallentaa myöhempää varten"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -576,10 +576,8 @@ msgstr "Code source"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Une traduction doit sonner juste pour quelqu'un qui parle la langue. Corrige une formulation ou lance une nouvelle langue sur {weblate}, et nous l'intégrerons à {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Aidez à traduire {product_name} dans votre langue sur {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2400,8 +2398,8 @@ msgstr "Badge d'abonné sur ton profil"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Médias que tu peux enregistrer"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Médias que tu peux enregistrer pour plus tard"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: he\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -570,10 +570,8 @@ msgstr "קוד מקור"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"תרגום צריך להישמע טבעי למי שדובר את השפה. אפשר לתקן ניסוח או להתחיל שפה חדשה ב-{weblate}, ואנחנו נכלול אותו ב-{product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "עזרו לתרגם את {product_name} לשפה שלכם ב-{weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2366,8 +2364,8 @@ msgstr "תג מנוי בפרופיל שלך"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "פריטי מדיה שאפשר לשמור"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "פריטי מדיה שאפשר לשמור למועד מאוחר יותר"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: hi\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -572,10 +572,8 @@ msgstr "सोर्स कोड"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"अनुवाद ऐसा होना चाहिए जो उस भाषा को बोलने वाले किसी व्यक्ति को स्वाभाविक लगे। {weblate} पर किसी वाक्य को सुधारें या नई लोकेल शुरू करें, और हम उसे {product_name} में शामिल करेंगे।"
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "{product_name} को अपनी भाषा में {weblate} पर अनुवाद करने में मदद करें।"
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2383,8 +2381,8 @@ msgstr "आपकी प्रोफ़ाइल पर सब्सक्रा
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "सेव किए जा सकने वाले मीडिया आइटम"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "बाद के लिए सेव किए जा सकने वाले मीडिया आइटम"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: hr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -574,10 +574,8 @@ msgstr "Izvorni kod"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Prijevod bi trebao zvučati prirodno onome tko govori taj jezik. Ispravi izraz ili pokreni novi jezik na {weblate}, a mi ćemo ga uključiti u {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Pomozite prevesti {product_name} na vaš jezik na {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2385,8 +2383,8 @@ msgstr "Značka pretplatnika na tvom profilu"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Medijski sadržaji koje možeš spremiti"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Medijski sadržaji koje možeš spremiti za poslije"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: hu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -575,10 +575,8 @@ msgstr "Forráskód"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Egy fordításnak természetesen kell hangoznia annak, aki beszéli az adott nyelvet. Javíts egy kifejezést vagy indíts új nyelvet a {weblate} oldalon, mi pedig beépítjük a {product_name}-be."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Segíts a(z) {product_name} fordításában a saját nyelvedre itt: {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2395,8 +2393,8 @@ msgstr "Előfizetői jelvény a profilodon"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Elmenthető médiaelemek"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Későbbre elmenthető médiaelemek"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: id\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -574,10 +574,8 @@ msgstr "Kode sumber"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Terjemahan seharusnya terdengar wajar bagi penutur bahasanya. Perbaiki satu frasa atau mulai bahasa baru di {weblate}, dan kami akan menyertakannya di {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Bantu terjemahkan {product_name} ke bahasa Anda di {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2384,8 +2382,8 @@ msgstr "Lencana pelanggan di profilmu"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Media yang bisa kamu simpan"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Media yang bisa kamu simpan untuk nanti"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -575,10 +575,8 @@ msgstr "Codice sorgente"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Una traduzione deve suonare naturale a chi parla quella lingua. Correggi una frase o avvia una nuova lingua su {weblate} e la includeremo in {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Aiuta a tradurre {product_name} nella tua lingua su {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2392,8 +2390,8 @@ msgstr "Badge da abbonato sul tuo profilo"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Elementi multimediali che puoi salvare"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Elementi multimediali che puoi salvare per dopo"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: ja\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -567,10 +567,8 @@ msgstr "ソースコード"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"翻訳は、その言語を話す人にとって自然に聞こえるものであるべきです。{weblate}で表現を修正したり、新しい言語を追加したりしていただければ、{product_name}に取り込みます。"
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "{product_name} をあなたの言語に翻訳するお手伝いをしてください。 {weblate}"
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2350,8 +2348,8 @@ msgstr "プロフィールに表示される加入者バッジ"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "保存できるメディア数"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "後で見るために保存できるメディア数"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: ko\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -566,10 +566,8 @@ msgstr "소스 코드"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"번역은 그 언어를 쓰는 사람에게 자연스럽게 들려야 합니다. {weblate}에서 문구를 고치거나 새로운 언어를 시작해 주시면 {product_name}에 반영하겠습니다."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "{product_name}을(를) 귀하의 언어로 번역하는 데 참여해 주세요. {weblate}에서 가능합니다."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2349,8 +2347,8 @@ msgstr "프로필에 표시되는 구독자 배지"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "저장할 수 있는 미디어 수"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "나중에 보려고 저장할 수 있는 미디어 수"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: lt\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -575,10 +575,8 @@ msgstr "Šaltinio kodas"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Vertimas turi skambėti natūraliai tam, kas kalba ta kalba. Pataisyk frazę arba pradėk naują kalbą {weblate} platformoje, o mes ją įtrauksime į {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Padėkite versti {product_name} į jūsų kalbą per {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2389,8 +2387,8 @@ msgstr "Prenumeratoriaus ženklelis profilyje"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Medijos elementai, kuriuos gali išsaugoti"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Medijos elementai, kuriuos gali išsaugoti vėlesniam laikui"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: nl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -574,10 +574,8 @@ msgstr "Broncode"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Een vertaling moet natuurlijk klinken voor iemand die de taal spreekt. Corrigeer een zin of begin een nieuwe taal op {weblate}, dan nemen we die op in {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Help {product_name} vertalen naar jouw taal op {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2389,8 +2387,8 @@ msgstr "Abonneebadge op je profiel"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Media die je kunt opslaan"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Media die je kunt opslaan voor later"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: no\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -573,10 +573,8 @@ msgstr "Kildekode"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"En oversettelse skal høres naturlig ut for noen som snakker språket. Rett opp en formulering eller start et nytt språk på {weblate}, så tar vi det med i {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Hjelp til med å oversette {product_name} til ditt språk på {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2386,8 +2384,8 @@ msgstr "Abonnentmerke på profilen din"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Medieelementer du kan lagre"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Medieelementer du kan lagre til senere"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: pl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -575,10 +575,8 @@ msgstr "Kod źródłowy"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Tłumaczenie powinno brzmieć naturalnie dla osoby mówiącej danym językiem. Popraw sformułowanie lub rozpocznij nowy język na {weblate}, a dodamy je do {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Pomóż przetłumaczyć {product_name} na swój język na stronie {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2391,8 +2389,8 @@ msgstr "Odznaka subskrybenta na profilu"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Elementy multimedialne, które możesz zapisać"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Elementy multimedialne, które możesz zapisać na później"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: pt-BR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -575,10 +575,8 @@ msgstr "Código-fonte"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Uma tradução deve soar natural para quem fala o idioma. Corrija uma frase ou comece um novo idioma no {weblate}, e nós o incluiremos no {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Ajude a traduzir o {product_name} para o seu idioma em {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2391,8 +2389,8 @@ msgstr "Selo de assinante no seu perfil"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Itens de mídia que você pode salvar"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Itens de mídia que você pode salvar para depois"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: ro\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -575,10 +575,8 @@ msgstr "Cod sursă"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"O traducere ar trebui să sune natural pentru cineva care vorbește limba respectivă. Corectează o expresie sau începe o limbă nouă pe {weblate}, iar noi o vom include în {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Ajută la traducerea {product_name} în limba ta pe {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2395,8 +2393,8 @@ msgstr "Insignă de abonat pe profilul tău"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Elemente media pe care le poți salva"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Elemente media pe care le poți salva pentru mai târziu"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -574,10 +574,8 @@ msgstr "Исходный код"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Перевод должен звучать естественно для того, кто говорит на этом языке. Исправь формулировку или начни новую локаль на {weblate}, и мы добавим её в {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Помогите перевести {product_name} на ваш язык на {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2391,8 +2389,8 @@ msgstr "Значок подписчика в профиле"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Медиафайлы, которые можно сохранить"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Медиафайлы, которые можно сохранить на потом"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: sv-SE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -574,10 +574,8 @@ msgstr "Källkod"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"En översättning ska låta naturlig för den som talar språket. Rätta en formulering eller starta ett nytt språk på {weblate}, så tar vi med den i {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Hjälp till att översätta {product_name} till ditt språk på {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2389,8 +2387,8 @@ msgstr "Prenumerantmärke på din profil"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Medieobjekt du kan spara"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Medieobjekt du kan spara till senare"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: th\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -573,10 +573,8 @@ msgstr "ซอร์สโค้ด"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"คำแปลที่ดีควรอ่านแล้วเป็นธรรมชาติสำหรับเจ้าของภาษา แก้ไขคำแปลหรือเริ่มภาษาใหม่ได้ที่ {weblate} แล้วเราจะนำไปรวมไว้ใน {product_name}"
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "ช่วยแปล {product_name} เป็นภาษาของคุณบน {weblate}"
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2378,8 +2376,8 @@ msgstr "ป้ายสมาชิกบนโปรไฟล์ของคุ
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "จำนวนสื่อที่บันทึกได้"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "จำนวนสื่อที่บันทึกไว้ดูภายหลังได้"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: tr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -573,10 +573,8 @@ msgstr "Kaynak kodu"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Bir çeviri, o dili konuşan birine doğal gelmelidir. {weblate} üzerinde bir ifadeyi düzelt veya yeni bir dil başlat; biz de onu {product_name}'a ekleyelim."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "{product_name}'i {weblate} üzerinde dilinize çevirmemize yardım edin."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2387,8 +2385,8 @@ msgstr "Profilinde abone rozeti"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Kaydedebileceğin medya öğesi sayısı"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Daha sonrası için kaydedebileceğin medya öğesi sayısı"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: uk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -575,10 +575,8 @@ msgstr "Вихідний код"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Переклад має звучати природно для того, хто говорить цією мовою. Виправ формулювання або почни нову локаль на {weblate}, і ми додамо її до {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Допоможіть перекласти {product_name} вашою мовою на {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2389,8 +2387,8 @@ msgstr "Значок передплатника в профілі"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Медіа, які можна зберегти"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Медіа, які можна зберегти на потім"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fluxer-marketing\n"
|
||||
"POT-Creation-Date: 2026-08-15 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-15 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-08-19 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-08-19 00:00+0000\n"
|
||||
"Language: vi\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -575,10 +575,8 @@ msgstr "Mã nguồn"
|
||||
#. Body copy in the locale picker inviting translation contributions through Weblate. Preserve the {weblate} and {product_name} placeholders exactly.
|
||||
#: fluxer_marketing/generated:company_and_resources.source_and_contribution.translation.weblate_note
|
||||
msgctxt "company_and_resources.source_and_contribution.translation.weblate_note"
|
||||
msgid ""
|
||||
"A translation should sound natural to someone who speaks the language. Correct a phrase or start a new locale on {weblate}, and we will include it in {product_name}."
|
||||
msgstr ""
|
||||
"Một bản dịch cần nghe thật tự nhiên với người nói ngôn ngữ đó. Hãy sửa một câu chữ hoặc bắt đầu một ngôn ngữ mới trên {weblate}, và chúng tôi sẽ đưa nó vào {product_name}."
|
||||
msgid "Help translate {product_name} into your language on {weblate}."
|
||||
msgstr "Giúp dịch {product_name} sang ngôn ngữ của bạn trên {weblate}."
|
||||
|
||||
#. Department or policy category label on content pages. Translate as a compact noun phrase, not as a sentence.
|
||||
#: fluxer_marketing/generated:content.labels.community
|
||||
@@ -2389,8 +2387,8 @@ msgstr "Huy hiệu người đăng ký trên hồ sơ của bạn"
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.saved_media
|
||||
msgctxt "pricing_and_tiers.plutonium.perks.saved_media"
|
||||
msgid "Media items you can save"
|
||||
msgstr "Mục phương tiện bạn có thể lưu"
|
||||
msgid "Media items you can save for later"
|
||||
msgstr "Mục phương tiện bạn có thể lưu để xem sau"
|
||||
|
||||
#. Feature label in the Plutonium comparison table. Keep it concise and consistent with product terminology.
|
||||
#: fluxer_marketing/generated:pricing_and_tiers.plutonium.perks.use_animated_emojis
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user