/** * Source dictionary. Every key the app can translate is declared here, and its * type is derived from this object — a typo or a missing key fails typecheck * rather than silently rendering the raw key at runtime. * * Keys are flat and dot-namespaced by system (`settings.voice.*`), so a * translation pass can take one system at a time. */ export const en = { // Settings — navigation 'settings.tab.account': 'Account', 'settings.tab.voice': 'Voice & Video', 'settings.tab.privacy': 'Privacy', 'settings.tab.connections': 'Connections', 'settings.tab.keybinds': 'Keybinds', 'settings.tab.desktop': 'Desktop', 'settings.tab.instance': 'Instance', 'settings.tab.language': 'Language', // Settings — language 'settings.language.title': 'Language', 'settings.language.description': 'Choose the language for the interface. Anything not yet translated stays in English.', 'settings.language.en': 'English', 'settings.language.ptBR': 'Portuguese (Brazil)', // Settings — voice: input 'settings.voice.input.title': 'Input Device', 'settings.voice.input.volume': 'Input Volume', 'settings.voice.micTest.start': "Let's Check", 'settings.voice.micTest.stop': 'Stop Testing', 'settings.voice.micTest.playing': 'Playing your mic back to you — say something.', 'settings.voice.micTest.inCall': 'The level meter is live while you are in a call.', 'settings.voice.micTest.idle': 'Test your mic without joining a call.', 'settings.voice.micTest.failed': 'Could not open the microphone. Check the device and its permission.', // Soundboard 'soundboard.title': 'Soundboard', 'soundboard.empty': 'No sounds yet.', 'soundboard.add': 'Add sound', 'soundboard.adding': 'Uploading...', 'soundboard.remove': 'Remove', 'soundboard.namePrompt': 'Name for this sound', 'soundboard.confirm': 'Add', 'soundboard.cancel': 'Cancel', 'soundboard.joinFirst': 'Join a voice channel to use the soundboard.', 'soundboard.tooLarge': 'Sound must be under 2 MB and a few seconds long.', 'soundboard.uploadFailed': 'Could not upload that file. Try a different one.', // Account menu (own name in the user bar) 'accountMenu.editProfile': 'Edit Profile', 'accountMenu.status': 'Status', 'accountMenu.status.online': 'Online', 'accountMenu.status.idle': 'Idle', 'accountMenu.status.dnd': 'Do Not Disturb', 'accountMenu.status.offline': 'Invisible', 'accountMenu.copyId': 'Copy User ID', 'accountMenu.copied': 'Copied', // In-app notifications 'notify.attachment': 'Sent an attachment', 'notify.jump': 'Open', // Desktop update 'update.ready': 'Update ready', 'update.readyVersion': 'Version {version} is ready to install.', 'update.restart': 'Restart to update', 'update.later': 'Later', // Custom emojis and stickers 'expressions.title': 'Emojis & Stickers', 'expressions.emojis': 'Emojis', 'expressions.stickers': 'Stickers', 'expressions.addEmoji': 'Add emoji', 'expressions.addSticker': 'Add sticker', 'expressions.emptyEmojis': 'No custom emojis yet.', 'expressions.emptyStickers': 'No stickers yet.', 'expressions.namePrompt': 'Name', 'expressions.nameHintEmoji': 'Letters, numbers and underscore. Used as :name: in messages.', 'expressions.confirm': 'Add', 'expressions.cancel': 'Cancel', 'expressions.remove': 'Remove', 'expressions.tooLarge': 'Image must be under 512 KB.', 'expressions.uploadFailed': 'Could not add that image. Try another one.', 'expressions.nameTaken': 'That name is already in use in this server.', 'expressions.uploading': 'Uploading…', 'expressions.pickerTitle': 'Stickers', 'expressions.pickerEmpty': 'This server has no stickers yet.', // Search 'search.placeholder': 'Search messages…', 'search.filters': 'Filters', 'search.from': 'From', 'search.fromPlaceholder': 'username', 'search.has': 'Has', 'search.hasAny': 'Any', 'search.hasFile': 'File', 'search.hasImage': 'Image', 'search.hasLink': 'Link', 'search.before': 'Before', 'search.after': 'After', 'search.hint': 'Tip: type from:name, has:image, before:2026-01-31 straight into the search box.', // Pinned messages 'pins.pin': 'Pin Message', 'pins.unpin': 'Unpin Message', 'pins.title': 'Pinned Messages', 'pins.empty': 'No pinned messages in this channel yet.', 'pins.limit': 'This channel has reached the pin limit.', // Sidebar — spaces, channels and members 'sidebar.friends': 'Friends', 'sidebar.directMessages': 'Direct Messages', 'sidebar.noChannels': 'No channels', 'sidebar.comingSoon': 'Coming Soon', 'sidebar.loadingChannels': 'Loading channels', 'sidebar.loadingMembers': 'Loading members', 'sidebar.groupOnline': 'ONLINE', 'sidebar.groupOwner': 'OWNER', 'sidebar.createChannel': 'Create Channel', 'sidebar.createCategory': 'Create Category', 'sidebar.categorySettings': 'Category Settings', 'sidebar.deleteCategory': 'Delete Category', 'sidebar.invitePeople': 'Invite People', 'sidebar.spaceSettings': 'Space Settings', 'sidebar.leaveGroup': 'Leave Group', 'sidebar.voiceSettings': 'Voice Settings', 'sidebar.inputDevice': 'Input Device', 'sidebar.inputVolume': 'Input Volume', 'sidebar.outputDevice': 'Output Device', 'sidebar.outputVolume': 'Output Volume', // Chat — composer and messages 'chat.composer.placeholder': 'Message {channel}', 'chat.composer.attach': 'Attach file', 'chat.composer.emoji': 'Emoji', 'chat.composer.gif': 'GIF', 'chat.composer.gifPicker': 'GIF picker', 'chat.composer.emojiPicker': 'Emoji picker', 'chat.composer.send': 'Send', 'chat.composer.sendMessage': 'Send message', 'chat.composer.uploading': 'Uploading', 'chat.composer.uploadingEllipsis': 'Uploading…', 'chat.composer.removeAttachment': 'Remove attachment', 'chat.composer.failedAttachment': 'Remove or retry the failed attachment to send', 'chat.composer.replyingTo': 'Replying to', 'chat.composer.cancelReply': 'Cancel reply', 'chat.message.addReaction': 'Add reaction', 'chat.message.edit': 'Edit', 'chat.message.reply': 'Reply', // Statistics 'stats.title': 'Statistics', 'stats.range.7': 'Last 7 days', 'stats.range.30': 'Last 30 days', 'stats.range.365': 'Last year', 'stats.voice.title': 'Time in voice', 'stats.messages.title': 'Messages sent', 'stats.empty': 'Nothing recorded in this period yet.', 'stats.total.voice': '{value} total', 'stats.total.messages': '{value} messages in total', 'stats.hours': '{hours}h {minutes}m', 'stats.minutes': '{minutes}m', 'stats.note': 'Counting started when this feature was installed — earlier activity is not included.', // Audit log 'audit.title': 'Audit Log', 'audit.empty': 'Nothing recorded yet. Changes to the server show up here.', 'audit.loadMore': 'Load more', 'audit.unknownActor': 'Deleted account', 'audit.action.space.update': '{actor} updated the server settings', 'audit.action.space.transfer_ownership': '{actor} transferred ownership of the server', 'audit.action.channel.create': '{actor} created the channel {name}', 'audit.action.channel.update': '{actor} updated the channel {name}', 'audit.action.channel.delete': '{actor} deleted the channel {name}', 'audit.action.member.kick': '{actor} removed a member', 'audit.action.member.leave': '{actor} left the server', 'audit.action.member.ban': '{actor} banned a member', 'audit.action.member.unban': '{actor} unbanned a member', 'audit.action.role.create': '{actor} created the role {name}', 'audit.action.role.update': '{actor} updated the role {name}', 'audit.action.role.delete': '{actor} deleted a role', 'audit.action.invite.create': '{actor} created an invite', 'audit.action.message.delete': '{actor} deleted a message', 'audit.action.unknown': '{actor} performed an action', // GIF picker 'gif.search': 'Search GIFs', 'gif.tab.favorites': 'Favorites', 'gif.tab.trending': 'Trending', 'gif.empty.search': 'No GIFs found', 'gif.empty.trending': 'No trending GIFs', 'gif.empty.favorites': 'No favorites yet — tap the star on any GIF.', 'gif.favorite.add': 'Add to favorites', 'gif.favorite.remove': 'Remove from favorites', 'gif.category.hello': 'hello', 'gif.category.lol': 'lol', 'gif.category.love': 'love', 'gif.category.birthday': 'happy birthday', 'gif.category.dance': 'dance', 'gif.category.facepalm': 'facepalm', // Settings — privacy 'privacy.title': 'Privacy', 'privacy.discoverable.label': 'Allow others to find my profile', 'privacy.discoverable.description': 'When enabled, your profile appears in Discover People. Others can always add you by exact username.', 'privacy.activity.label': 'Share Activity Status', 'privacy.activity.description': "Allow others to see what you're up to, like games you're playing or music you're listening to.", // Settings — connections 'connections.title': 'Connections', 'connections.spotify.description': 'Show what you are listening to on your profile.', 'connections.spotify.connect': 'Connect Spotify', 'connections.spotify.disconnect': 'Disconnect', 'connections.spotify.connected': 'Connected', 'connections.spotify.notConfigured': 'This instance has no Spotify credentials configured.', 'connections.spotify.hint': 'Only what is playing is read — playback cannot be controlled.', 'connections.spotify.error.denied': 'Authorisation was cancelled on Spotify.', 'connections.spotify.error.invalid_state': 'The authorisation link expired. Try again.', 'connections.spotify.error.exchange_failed': 'Spotify refused the authorisation. Try again.', // Profile card 'profile.aboutMe': 'About Me', 'profile.memberSince': 'Member Since', 'profile.sendMessage': 'Send Message', 'profile.activity.playing': 'Playing', 'profile.activity.listening': 'Listening to', 'profile.activity.watching': 'Watching', 'profile.activity.streaming': 'Streaming', 'profile.activity.elapsed': '{time} elapsed', } as const; export type TranslationKey = keyof typeof en; export type Dictionary = Record;