Merge branch 'main' of github.com:elk-zone/elk into feature/remove-link-if-matches-preview-URL

This commit is contained in:
Ayo 2023-03-14 19:28:33 +01:00
commit 8fc435c60e
149 changed files with 6547 additions and 2344 deletions

View file

@ -1,6 +1,6 @@
NUXT_PUBLIC_TRANSLATE_API= NUXT_PUBLIC_TRANSLATE_API=
NUXT_PUBLIC_DEFAULT_SERVER= NUXT_PUBLIC_DEFAULT_SERVER=
SINGLE_INSTANCE_SERVER= NUXT_PUBLIC_SINGLE_INSTANCE=
NUXT_PUBLIC_PRIVACY_POLICY_URL= NUXT_PUBLIC_PRIVACY_POLICY_URL=
# Production only # Production only

View file

@ -9,3 +9,5 @@ public/
https-dev-config/localhost.crt https-dev-config/localhost.crt
https-dev-config/localhost.key https-dev-config/localhost.key
Dockerfile Dockerfile
elk-translation-status.json
docs/translation-status.json

1
.gitignore vendored
View file

@ -9,6 +9,7 @@ dist
.vite-inspect .vite-inspect
.netlify/ .netlify/
.eslintcache .eslintcache
elk-translation-status.json
public/shiki public/shiki
public/emojis public/emojis

1
.nvmrc Normal file
View file

@ -0,0 +1 @@
v18

View file

@ -22,7 +22,7 @@
], ],
"i18n-ally.preferredDelimiter": "_", "i18n-ally.preferredDelimiter": "_",
"i18n-ally.sortKeys": true, "i18n-ally.sortKeys": true,
"i18n-ally.sourceLanguage": "en-US", "i18n-ally.sourceLanguage": "en",
"prettier.enable": false, "prettier.enable": false,
"volar.completion.preferredTagNameCase": "pascal", "volar.completion.preferredTagNameCase": "pascal",
"volar.completion.preferredAttrNameCase": "kebab" "volar.completion.preferredAttrNameCase": "kebab"

45
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,45 @@
# Code Of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, political party, or sexual identity and orientation. Note, however, that religion, political party, or other ideological affiliation provide no exemptions for the behavior we outline as unacceptable in this Code of Conduct.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team by DM at [the Elk Discord](https://chat.elk.zone). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org

View file

@ -1,9 +1,11 @@
# Contributing Guide # Contributing Guide
Hi! We are really excited that you are interested in contributing to Elk. Before submitting your contribution, please make sure to take a moment and read through the following guide. Hi! We are excited that you are interested in contributing to Elk. Before submitting your contribution, please make sure to take a moment and read through the following guide.
Refer also to https://github.com/antfu/contribute. Refer also to https://github.com/antfu/contribute.
For guidelines on contributing to the documentation, refer to the [docs README](./docs/README.md).
### Online ### Online
You can use [StackBlitz Codeflow](https://stackblitz.com/codeflow) to fix bugs or implement features. You'll also see a Codeflow button on PRs to review them without a local setup. Once the elk repo has been cloned in Codeflow, the dev server will start automatically and print the URL to open the App. You should receive a prompt in the bottom-right suggesting to open it in the Editor or in another Tab. To learn more, check out the [Codeflow docs](https://developer.stackblitz.com/codeflow/what-is-codeflow). You can use [StackBlitz Codeflow](https://stackblitz.com/codeflow) to fix bugs or implement features. You'll also see a Codeflow button on PRs to review them without a local setup. Once the elk repo has been cloned in Codeflow, the dev server will start automatically and print the URL to open the App. You should receive a prompt in the bottom-right suggesting to open it in the Editor or in another Tab. To learn more, check out the [Codeflow docs](https://developer.stackblitz.com/codeflow/what-is-codeflow).
@ -16,7 +18,9 @@ To develop and test the Elk package:
1. Fork the Elk repository to your own GitHub account and then clone it to your local device. 1. Fork the Elk repository to your own GitHub account and then clone it to your local device.
2. Ensure using the latest Node.js (16.x) 2. Ensure using the latest Node.js (16.x).
If you have [nvm](https://github.com/nvm-sh/nvm), you can run `nvm i` to install the required version.
3. The package manager used to install and link dependencies must be [pnpm](https://pnpm.io/) v7. To use it you must first enable [Corepack](https://github.com/nodejs/corepack) by running `corepack enable`. (Note: on Linux in a standard Node 16+ environment, you should follow the instructions to install via Node's `corepack` rather than using the `curl` command) 3. The package manager used to install and link dependencies must be [pnpm](https://pnpm.io/) v7. To use it you must first enable [Corepack](https://github.com/nodejs/corepack) by running `corepack enable`. (Note: on Linux in a standard Node 16+ environment, you should follow the instructions to install via Node's `corepack` rather than using the `curl` command)
@ -50,16 +54,16 @@ nr test
In order to run Elk with PWA enabled, run `pnpm dev:pwa` in Elk's root folder to start dev server or `pnpm dev:mocked:pwa` to start dev server with `@elkdev@universeodon.com` user. In order to run Elk with PWA enabled, run `pnpm dev:pwa` in Elk's root folder to start dev server or `pnpm dev:mocked:pwa` to start dev server with `@elkdev@universeodon.com` user.
You should test the Elk PWA application on private browsing mode on any Chromium based browser: will not work on Firefox and Safari. You should test the Elk PWA application on private browsing mode on any Chromium-based browser: will not work on Firefox and Safari.
If not using private browsing mode, you will need to uninstall the PWA application from your browser once you finish testing: If not using private browsing mode, you will need to uninstall the PWA application from your browser once you finish testing:
- Open `Dev Tools` (`Option + ⌘ + J` on macOS, `Shift + CTRL + J` on Windows/Linux) - Open `Dev Tools` (`Option + ⌘ + J` on macOS, `Shift + CTRL + J` on Windows/Linux)
- Go to `Application > Storage`, you should check following checkboxes: - Go to `Application > Storage`, you should check the following checkboxes:
- Application: [x] Unregister service worker - Application: [x] Unregister service worker
- Storage: [x] IndexedDB and [x] Local and session storage - Storage: [x] IndexedDB and [x] Local and session storage
- Cache: [x] Cache storage and [x] Application cache - Cache: [x] Cache storage and [x] Application cache
- Click on `Clear site data` button - Click on `Clear site data` button
- Go to `Application > Service Workers` and check the current `service worker` is missing or has the state `deleted` or `redundant` - Go to `Application > Service Workers` and check if the current `service worker` is missing or has the state `deleted` or `redundant`
## CI errors ## CI errors
@ -78,23 +82,30 @@ Elk supports `right-to-left` languages, we need to make sure that the UI is work
Simple approach used by most websites of relying on direction set in HTML element does not work because direction for various items, such as timeline, does not always match direction set in HTML. Simple approach used by most websites of relying on direction set in HTML element does not work because direction for various items, such as timeline, does not always match direction set in HTML.
We've added some `UnoCSS` utilities styles to help you with that: We've added some `UnoCSS` utilities styles to help you with that:
- Do not use `left/right` padding and margin: for example `pl-1`. Use `padding-inline-start/end` instead. So `pl-1` should be `ps-1`, `pr-1` should be `pe-1`. Same rules applies for margin. - Do not use `left/right` padding and margin: for example `pl-1`. Use `padding-inline-start/end` instead. So `pl-1` should be `ps-1`, `pr-1` should be `pe-1`. The same rules apply to margin.
- Do not use `rtl-` classes, such as `rtl-left-0`. - Do not use `rtl-` classes, such as `rtl-left-0`.
- For icons that should be rotated for RTL, add `class="rtl-flip"`. This can only be used for icons outside of elements with `dir="auto"`, such as timeline, and is the only exception from rule above. For icons inside timeline it might not work as expected. - For icons that should be rotated for RTL, add `class="rtl-flip"`. This can only be used for icons outside of elements with `dir="auto"`, such as timeline, and is the only exception from the rule above. For icons inside the timeline, it might not work as expected.
- For absolute positioned elements, don't use `left/right`: for example `left-0`. Use `inset-inline-start/end` instead. `UnoCSS` shortcuts are `inset-is` for `inset-inline-start` and `inset-ie` for `inset-inline-end`. Example: `left-0` should be replaced with `inset-is-0`. - For absolute positioned elements, don't use `left/right`: for example `left-0`. Use `inset-inline-start/end` instead. `UnoCSS` shortcuts are `inset-is` for `inset-inline-start` and `inset-ie` for `inset-inline-end`. Example: `left-0` should be replaced with `inset-is-0`.
- If you need to change border radius for an entire left or right side, use `border-inline-start/end`. `UnoCSS` shortcuts are `rounded-is` for left side, `rounded-ie` for right side. Example: `rounded-l-5` should be replaced with `rounded-ie-5`. - If you need to change the border radius for an entire left or right side, use `border-inline-start/end`. `UnoCSS` shortcuts are `rounded-is` for left side, `rounded-ie` for right side. Example: `rounded-l-5` should be replaced with `rounded-ie-5`.
- If you need to change border radius for one corner, use `border-start-end-radius` and similar rules. `UnoCSS` shortcuts are `rounded` + top/bottom as either `-bs` (top) or `-be` (bottom) + left/right as either `-is` (left) or `-ie` (right). Example: `rounded-tl-0` should be replaced with `rounded-bs-is-0`. - If you need to change the border radius for one corner, use `border-start-end-radius` and similar rules. `UnoCSS` shortcuts are `rounded` + top/bottom as either `-bs` (top) or `-be` (bottom) + left/right as either `-is` (left) or `-ie` (right). Example: `rounded-tl-0` should be replaced with `rounded-bs-is-0`.
## Internationalization ## Internationalization
We are using [vue-i18n](https://vue-i18n.intlify.dev/) via [nuxt-i18n](https://i18n.nuxtjs.org/) to handle internationalization. We are using [vue-i18n](https://vue-i18n.intlify.dev/) via [nuxt-i18n](https://v8.i18n.nuxtjs.org/) to handle internationalization.
You can check the current [translation status](https://docs.elk.zone/docs/guide/contributing#translation-status): more instructions on the table caption.
If you are updating a translation in your local environment, you can run the following commands to check the status:
- from root folder: `nr prepare-translation-status`
- change to `docs` folder and run docs dev server `nr dev`
- open `http://localhost:3000/docs/guide/contributing#translation-status` in your browser
### Adding a new language ### Adding a new language
1. Add a new file in [locales](./locales) folder with the language code as the filename. 1. Add a new file in [locales](./locales) folder with the language code as the filename.
2. Copy [en-US](./locales/en-US.json) and translate the strings. 2. Copy [en-US](./locales/en-US.json) and translate the strings.
3. Add the language to the `locales` array in [config/i18n.ts](./config/i18n.ts#L61), below `en` and `ar`: 3. Add the language to the `locales` array in [config/i18n.ts](./config/i18n.ts#L61), below `en` and `ar`:
- If your language have multiple country variants, add the generic one for language only (only if there are a lot of common entries, you can always add it as a new one) - If your language has multiple country variants, add the generic one for language only (only if there are a lot of common entries, you can always add it as a new one)
- Add all country variants in [country variants object](./config/i18n.ts#L12) - Add all country variants in [country variants object](./config/i18n.ts#L12)
- Add all country variants files with empty `messages` object: `{}` - Add all country variants files with empty `messages` object: `{}`
- Translate the strings in the generic language file - Translate the strings in the generic language file
@ -109,7 +120,7 @@ Check [Pluralization rule callback](https://vue-i18n.intlify.dev/guide/essential
### Messages interpolation ### Messages interpolation
Most of the messages used in Elk do not require any interpolation, however, there are some messages that require interpolation: check [Message Format Syntax](https://vue-i18n.intlify.dev/guide/essentials/syntax.html) for more info. Most of the messages used in Elk do not require any interpolation, however, some messages require interpolation: check [Message Format Syntax](https://vue-i18n.intlify.dev/guide/essentials/syntax.html) for more info.
We're using these types of interpolation: We're using these types of interpolation:
- [List interpolation](https://vue-i18n.intlify.dev/guide/essentials/syntax.html#list-interpolation) - [List interpolation](https://vue-i18n.intlify.dev/guide/essentials/syntax.html#list-interpolation)
@ -131,7 +142,7 @@ Check [Custom Plural Number Formatting Entries](#custom-plural-number-formatting
When using plural number formatting, we'll have always `{n}` available in the message, for example, `You have {n} new notifications|You have {n} new notification|You have {n} new notifications` or `You have no new notifications|You have 1 new notification|You have {n} new notifications`. When using plural number formatting, we'll have always `{n}` available in the message, for example, `You have {n} new notifications|You have {n} new notification|You have {n} new notifications` or `You have no new notifications|You have 1 new notification|You have {n} new notifications`.
We've included `v` named parameter, it will be used to pass the formatted number using [Intl.NumberFormat::format](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/format): will be the number with separators symbols. The exception to previous rule is when we're using `plural` **with** `i18n-t` component, in this case, we'll need to use `{0}` instead `{v}` to access the number. We've included `v` named parameter, it will be used to pass the formatted number using [Intl.NumberFormat::format](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/format): will be the number with separators symbols. The exception to the previous rule is when we're using `plural` **with** `i18n-t` component, in this case, we'll need to use `{0}` instead `{v}` to access the number.
Additionally, Elk will use [compact notation for numbers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#parameters) for some entries, check `notation` and `compactDisplay` options: for example, `1K` for `1000`, `1M` for `1000000`, `1B` for `1000000000` and so on. That entry will be available in the message using `{v}` named parameter (or `{0}` if using the message **with** `i18n-t` component). Additionally, Elk will use [compact notation for numbers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#parameters) for some entries, check `notation` and `compactDisplay` options: for example, `1K` for `1000`, `1M` for `1000000`, `1B` for `1000000000` and so on. That entry will be available in the message using `{v}` named parameter (or `{0}` if using the message **with** `i18n-t` component).

View file

@ -34,8 +34,8 @@ ARG GID=911
# Create a dedicated user and group # Create a dedicated user and group
RUN set -eux; \ RUN set -eux; \
addgroup -g $UID elk; \ addgroup -g $GID elk; \
adduser -u $GID -D -G elk elk; adduser -u $UID -D -G elk elk;
USER elk USER elk

View file

@ -37,10 +37,26 @@ The Elk team maintains a deployment at:
- 🦌 Production: [elk.zone](https://elk.zone) - 🦌 Production: [elk.zone](https://elk.zone)
- 🐙 Canary: [main.elk.zone](https://main.elk.zone) (deploys on every commit to `main` branch) - 🐙 Canary: [main.elk.zone](https://main.elk.zone) (deploys on every commit to `main` branch)
### Self-Host Docker Deployment
In order to host Elk yourself you can use the provided Dockerfile to build a container with elk. Be aware, that Elk only loads properly of the connection is done via SSL/TLS. The Docker container itself does not provide any SSL/TLS handling. You'll have to add this bit yourself.
One could put Elk behind popular reverse proxies with SSL Handling like Traefik, NGINX etc.
1. checkout source ```git clone https://github.com/elk-zone/elk.git```
1. got into new source dir: ```cd elk```
1. build Docker image: ```docker build .```
1. create local storage directory for settings: ```mkdir elk-storage```
1. adjust permissions of storage dir: ```sudo chown 911:911 ./elk-storage```
1. start container: ```docker-compose up -d```
Note: The provided Dockerfile creates a container which will eventually run Elk as non-root user and create a persistent named Docker volume upon first start (if that volume does not yet exist). This volume is always created with root permission. Failing to change the permissions of ```/elk/data``` inside this volume to UID:GID 911 (as specified for Elk in the Dockerfile) will prevent Elk from storing it's config for user accounts. You either have to fix the permission in the created named volume, or mount a directory with the correct permission to ```/elk/data``` into the container.
### Ecosystem ### Ecosystem
These are known deployments using Elk as an alternative Web client for Mastodon servers or as a base for other projects in the fediverse: These are known deployments using Elk as an alternative Web client for Mastodon servers or as a base for other projects in the fediverse:
- [elk.fedified.com](https://elk.fedified.com) - Use Elk to log into any compatible instance
- [elk.h4.io](https://elk.h4.io) - Use Elk for the `h4.io` Server - [elk.h4.io](https://elk.h4.io) - Use Elk for the `h4.io` Server
- [elk.universeodon.com](https://elk.universeodon.com) - Use Elk for the Universeodon Server - [elk.universeodon.com](https://elk.universeodon.com) - Use Elk for the Universeodon Server
- [elk.vmst.io](https://elk.vmst.io) - Use Elk for the `vmst.io` Server - [elk.vmst.io](https://elk.vmst.io) - Use Elk for the `vmst.io` Server
@ -117,6 +133,10 @@ Elk uses [Vitest](https://vitest.dev). You can run the test suite with:
nr test nr test
``` ```
## 📲 PWA
You can consult the [PWA documentation](https://docs.elk.zone/docs/pwa) to learn more about the PWA capabilities on Elk, how to install Elk PWA in your desktop or mobile device and some hints about PWA stuff on Elk.
## 🦄 Stack ## 🦄 Stack
- [Vite](https://vitejs.dev/) - Next Generation Frontend Tooling - [Vite](https://vitejs.dev/) - Next Generation Frontend Tooling
@ -129,7 +149,7 @@ nr test
- [Iconify](https://github.com/iconify/icon-sets#iconify-icon-sets-in-json-format) - Iconify icon sets in JSON format - [Iconify](https://github.com/iconify/icon-sets#iconify-icon-sets-in-json-format) - Iconify icon sets in JSON format
- [Masto.js](https://neet.github.io/masto.js) - Mastodon API client in TypeScript - [Masto.js](https://neet.github.io/masto.js) - Mastodon API client in TypeScript
- [shiki](https://shiki.matsu.io/) - A beautiful Syntax Highlighter - [shiki](https://shiki.matsu.io/) - A beautiful Syntax Highlighter
- [vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa) - Prompt for update and push notifications - [vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa) - Prompt for update, Web Push Notifications and Web Share Target API
## 👨‍💻 Contributors ## 👨‍💻 Contributors

View file

@ -11,7 +11,7 @@ defineProps<{
text-secondary-light text-secondary-light
> >
<slot name="prepend" /> <slot name="prepend" />
<CommonTooltip :content="$t('account.bot')" :disabled="showLabel"> <CommonTooltip no-auto-focus :content="$t('account.bot')" :disabled="showLabel">
<div i-mdi:robot-outline /> <div i-mdi:robot-outline />
</CommonTooltip> </CommonTooltip>
<div v-if="showLabel"> <div v-if="showLabel">

View file

@ -1,10 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { decode } from 'blurhash' import { decode } from 'blurhash'
const { blurhash, src, srcset } = defineProps<{ const { blurhash, src, srcset, shouldLoadImage = true } = defineProps<{
blurhash?: string | null | undefined blurhash?: string | null | undefined
src: string src: string
srcset?: string srcset?: string
shouldLoadImage?: boolean
}>() }>()
defineOptions({ defineOptions({
@ -19,7 +20,7 @@ const placeholderSrc = $computed(() => {
return getDataUrlFromArr(pixels, 32, 32) return getDataUrlFromArr(pixels, 32, 32)
}) })
onMounted(() => { function loadImage() {
const img = document.createElement('img') const img = document.createElement('img')
img.onload = () => { img.onload = () => {
@ -34,6 +35,16 @@ onMounted(() => {
setTimeout(() => { setTimeout(() => {
isLoaded.value = true isLoaded.value = true
}, 3_000) }, 3_000)
}
onMounted(() => {
if (shouldLoadImage)
loadImage()
})
watch(() => shouldLoadImage, () => {
if (shouldLoadImage)
loadImage()
}) })
</script> </script>

View file

@ -35,6 +35,8 @@ const previewImage = ref('')
const imageSrc = computed<string>(() => previewImage.value || defaultImage.value) const imageSrc = computed<string>(() => previewImage.value || defaultImage.value)
const pickImage = async () => { const pickImage = async () => {
if (process.server)
return
const image = await fileOpen({ const image = await fileOpen({
description: 'Image', description: 'Image',
mimeTypes: props.allowedFileTypes, mimeTypes: props.allowedFileTypes,

View file

@ -14,7 +14,7 @@ const build = useBuildInfo()
<p> <p>
<i18n-t keypath="help.build_preview.desc1"> <i18n-t keypath="help.build_preview.desc1">
<NuxtLink :href="`https://github.com/elk-zone/elk/commit/${build.commit}`" target="_blank" text-rose hover:underline> <NuxtLink :href="`https://github.com/elk-zone/elk/commit/${build.commit}`" target="_blank" text-rose hover:underline>
<code>{{ build.commit.slice(0, 7) }}</code> <code>{{ build.shortCommit }}</code>
</NuxtLink> </NuxtLink>
</i18n-t> </i18n-t>
</p> </p>

View file

@ -29,7 +29,7 @@ useCommands(() => command
</script> </script>
<template> <template>
<div flex w-full items-center lg:text-lg of-x-auto scrollbar-hide> <div flex w-full items-center lg:text-lg of-x-auto scrollbar-hide border="b base">
<template <template
v-for="(option, index) in options" v-for="(option, index) in options"
:key="option?.name || index" :key="option?.name || index"

View file

@ -1,9 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Popper as VTooltipType } from 'floating-vue/dist' import type { Popper as VTooltipType } from 'floating-vue/dist'
defineProps<{ export interface Props extends Partial<typeof VTooltipType> {
content?: string content?: string
} & Partial<typeof VTooltipType>>() }
defineProps<Props>()
</script> </script>
<template> <template>

View file

@ -10,7 +10,7 @@ const emit = defineEmits<{
<div i-ri:close-line /> <div i-ri:close-line />
</button> </button>
<img :alt="$t('app_logo')" src="/logo.svg" w-20 h-20 height="80" width="80" mxa class="rtl-flip"> <img :alt="$t('app_logo')" :src="`/${''}logo.svg`" w-20 h-20 height="80" width="80" mxa class="rtl-flip">
<h1 mxa text-4xl mb4> <h1 mxa text-4xl mb4>
{{ $t('help.title') }} {{ $t('help.title') }}
</h1> </h1>

View file

@ -0,0 +1,119 @@
<script setup lang="ts">
const emit = defineEmits(['close'])
const { t } = useI18n()
/* TODOs:
* - I18n
*/
interface ShortcutDef {
keys: string[]
isSequence: boolean
}
interface ShortcutItem {
description: string
shortcut: ShortcutDef
}
interface ShortcutItemGroup {
name: string
items: ShortcutItem[]
}
const shortcutItemGroups: ShortcutItemGroup[] = [
{
name: t('magic_keys.groups.navigation.title'),
items: [
{
description: t('magic_keys.groups.navigation.shortcut_help'),
shortcut: { keys: ['?'], isSequence: false },
},
// {
// description: t('magic_keys.groups.navigation.next_status'),
// shortcut: { keys: ['j'], isSequence: false },
// },
// {
// description: t('magic_keys.groups.navigation.previous_status'),
// shortcut: { keys: ['k'], isSequence: false },
// },
{
description: t('magic_keys.groups.navigation.go_to_home'),
shortcut: { keys: ['g', 'h'], isSequence: true },
},
{
description: t('magic_keys.groups.navigation.go_to_notifications'),
shortcut: { keys: ['g', 'n'], isSequence: true },
},
],
},
{
name: t('magic_keys.groups.actions.title'),
items: [
{
description: t('magic_keys.groups.actions.command_mode'),
shortcut: { keys: ['cmd', '/'], isSequence: false },
},
{
description: t('magic_keys.groups.actions.compose'),
shortcut: { keys: ['c'], isSequence: false },
},
{
description: t('magic_keys.groups.actions.favourite'),
shortcut: { keys: ['f'], isSequence: false },
},
{
description: t('magic_keys.groups.actions.boost'),
shortcut: { keys: ['b'], isSequence: false },
},
{
description: t('magic_keys.groups.actions.zen_mode'),
shortcut: { keys: ['z'], isSequence: false },
},
],
},
{
name: t('magic_keys.groups.media.title'),
items: [],
},
]
</script>
<template>
<div px-3 sm:px-5 py-2 sm:py-4 max-w-220 relative max-h-screen>
<button btn-action-icon absolute top-1 sm:top-2 right-1 sm:right-2 m1 :aria-label="$t('modals.aria_label_close')" @click="emit('close')">
<div i-ri:close-fill />
</button>
<h2 text-xl font-700 mb3>
{{ $t('magic_keys.dialog_header') }}
</h2>
<div mb2 grid grid-cols-1 md:grid-cols-3 gap-y- md:gap-x-6 lg:gap-x-8>
<div
v-for="group in shortcutItemGroups"
:key="group.name"
>
<h3 font-700 my-2 text-lg>
{{ group.name }}
</h3>
<div
v-for="item in group.items"
:key="item.description"
flex my-1 lg:my-2 justify-between place-items-center max-w-full text-base
>
<div mr-2 break-words overflow-hidden leading-4 h-full inline-block align-middle>
{{ item.description }}
</div>
<div>
<template
v-for="(key, idx) in item.shortcut.keys"
:key="idx"
>
<span v-if="idx !== 0" mx1 text-sm op80>{{ item.shortcut.isSequence ? $t('magic_keys.sequence_then') : '+' }}</span>
<code class="px2 md:px1.5 lg:px2 lg:px2 py0 lg:py-0.5" rounded bg-code border="px $c-border-code" shadow-sm my1 font-mono font-600>{{ key }}</code>
</template>
</div>
</div>
</div>
</div>
</div>
</template>

View file

@ -17,10 +17,10 @@ const wideLayout = computed(() => route.meta.wideLayout ?? false)
<div <div
sticky top-0 z10 backdrop-blur sticky top-0 z10 backdrop-blur
pt="[env(safe-area-inset-top,0)]" pt="[env(safe-area-inset-top,0)]"
border="b base" bg="[rgba(var(--rgb-bg-base),0.7)]" bg="[rgba(var(--rgb-bg-base),0.7)]"
class="native:lg:w-[calc(100vw-5rem)] native:xl:w-[calc(135%+(100vw-1200px)/2)]" class="native:lg:w-[calc(100vw-5rem)] native:xl:w-[calc(135%+(100vw-1200px)/2)]"
> >
<div flex justify-between px5 py2 :class="{ 'xl:hidden': $route.name !== 'tag' }" class="native:xl:flex"> <div flex justify-between px5 py2 :class="{ 'xl:hidden': $route.name !== 'tag' }" class="native:xl:flex" border="b base">
<div flex gap-3 items-center :overflow-hidden="!noOverflowHidden ? '' : false" py2 w-full> <div flex gap-3 items-center :overflow-hidden="!noOverflowHidden ? '' : false" py2 w-full>
<NuxtLink <NuxtLink
v-if="backOnSmallScreen || back" flex="~ gap1" items-center btn-text p-0 xl:hidden v-if="backOnSmallScreen || back" flex="~ gap1" items-center btn-text p-0 xl:hidden
@ -41,9 +41,10 @@ const wideLayout = computed(() => route.meta.wideLayout ?? false)
<NavUserSkeleton v-else /> <NavUserSkeleton v-else />
</div> </div>
</div> </div>
<slot name="header" /> <slot name="header">
<div hidden :class="{ 'xl:block': $route.name !== 'tag' }" h-6 />
</slot>
</div> </div>
<div :class="{ 'xl:block': $route.name !== 'tag' }" hidden h-6 />
<PwaInstallPrompt lg:hidden /> <PwaInstallPrompt lg:hidden />
<div :class="isHydrated && wideLayout ? 'xl:w-full sm:max-w-600px' : 'sm:max-w-600px md:shrink-0'" m-auto> <div :class="isHydrated && wideLayout ? 'xl:w-full sm:max-w-600px' : 'sm:max-w-600px md:shrink-0'" m-auto>
<slot /> <slot />

View file

@ -7,6 +7,7 @@ import {
isEditHistoryDialogOpen, isEditHistoryDialogOpen,
isErrorDialogOpen, isErrorDialogOpen,
isFavouritedBoostedByDialogOpen, isFavouritedBoostedByDialogOpen,
isKeyboardShortcutsDialogOpen,
isMediaPreviewOpen, isMediaPreviewOpen,
isPreviewHelpOpen, isPreviewHelpOpen,
isPublishDialogOpen, isPublishDialogOpen,
@ -98,5 +99,8 @@ const handleFavouritedBoostedByClose = () => {
> >
<StatusFavouritedBoostedBy /> <StatusFavouritedBoostedBy />
</ModalDialog> </ModalDialog>
<ModalDialog v-model="isKeyboardShortcutsDialogOpen" max-w-full sm:max-w-140 md:max-w-170 lg:max-w-220 md:min-w-160>
<MagickeysKeyboardShortcuts @close="closeKeyboardShortcuts()" />
</ModalDialog>
</template> </template>
</template> </template>

View file

@ -21,7 +21,7 @@ const { modelValue } = defineModel<{
const target = ref() const target = ref()
const animateTimeout = useTimeout(10) const animateTimeout = useTimeout(10)
const reduceMotion = useReducedMotion() const reduceMotion = process.server ? ref(false) : useReducedMotion()
const canAnimate = computed(() => !reduceMotion.value && animateTimeout.value) const canAnimate = computed(() => !reduceMotion.value && animateTimeout.value)
@ -64,7 +64,7 @@ const { isSwiping, lengthX, lengthY, direction } = useSwipe(target, {
useGesture({ useGesture({
onPinch({ offset: [distance, angle] }) { onPinch({ offset: [distance, angle] }) {
set({ scale: 1 + distance / 200 }) set({ scale: Math.max(0.5, 1 + distance / 200) })
}, },
onMove({ movement: [x, y], dragging, pinching }) { onMove({ movement: [x, y], dragging, pinching }) {
if (dragging && !pinching) if (dragging && !pinching)

View file

@ -1,6 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
// only one icon can be lit up at the same time // only one icon can be lit up at the same time
const moreMenuVisible = ref(false) const moreMenuVisible = ref(false)
const { notifications } = useNotifications()
</script> </script>
<template> <template>
@ -11,27 +13,32 @@ const moreMenuVisible = ref(false)
> >
<!-- These weird styles above are used for scroll locking, don't change it unless you know exactly what you're doing. --> <!-- These weird styles above are used for scroll locking, don't change it unless you know exactly what you're doing. -->
<template v-if="currentUser"> <template v-if="currentUser">
<NuxtLink to="/home" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 @click="$scrollToTop"> <NuxtLink to="/home" :aria-label="$t('nav.home')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
<div i-ri:home-5-line /> <div i-ri:home-5-line />
</NuxtLink> </NuxtLink>
<NuxtLink :to="isHydrated ? `/${currentServer}/explore` : '/explore'" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 @click="$scrollToTop"> <NuxtLink :to="isHydrated ? `/${currentServer}/explore` : '/explore'" :aria-label="$t('nav.explore')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
<div i-ri:search-line /> <div i-ri:search-line />
</NuxtLink> </NuxtLink>
<NuxtLink to="/notifications" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 @click="$scrollToTop"> <NuxtLink to="/notifications" :aria-label="$t('nav.notifications')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
<div i-ri:notification-4-line /> <div flex relative>
<div class="i-ri:notification-4-line" text-xl />
<div v-if="notifications" class="top-[-0.3rem] right-[-0.3rem]" absolute font-bold rounded-full h-4 w-4 text-xs bg-primary text-inverted flex items-center justify-center>
{{ notifications < 10 ? notifications : '•' }}
</div>
</div>
</NuxtLink> </NuxtLink>
<NuxtLink to="/conversations" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 @click="$scrollToTop"> <NuxtLink to="/conversations" :aria-label="$t('nav.conversations')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
<div i-ri:at-line /> <div i-ri:at-line />
</NuxtLink> </NuxtLink>
</template> </template>
<template v-else> <template v-else>
<NuxtLink :to="`/${currentServer}/explore`" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 @click="$scrollToTop"> <NuxtLink :to="`/${currentServer}/explore`" :aria-label="$t('nav.explore')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
<div i-ri:hashtag /> <div i-ri:hashtag />
</NuxtLink> </NuxtLink>
<NuxtLink group :to="`/${currentServer}/public/local`" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 @click="$scrollToTop"> <NuxtLink group :to="`/${currentServer}/public/local`" :aria-label="$t('nav.local')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
<div i-ri:group-2-line /> <div i-ri:group-2-line />
</NuxtLink> </NuxtLink>
<NuxtLink :to="`/${currentServer}/public`" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 @click="$scrollToTop"> <NuxtLink :to="`/${currentServer}/public`" :aria-label="$t('nav.federated')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
<div i-ri:earth-line /> <div i-ri:earth-line />
</NuxtLink> </NuxtLink>
</template> </template>

View file

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
const buildInfo = useAppConfig().buildInfo const buildInfo = useBuildInfo()
const timeAgoOptions = useTimeAgoOptions() const timeAgoOptions = useTimeAgoOptions()
const userSettings = useUserSettings() const userSettings = useUserSettings()
@ -65,7 +65,7 @@ function toggleDark() {
target="_blank" target="_blank"
font-mono font-mono
> >
{{ buildInfo.commit.slice(0, 7) }} {{ buildInfo.shortCommit }}
</NuxtLink> </NuxtLink>
</template> </template>
</div> </div>

View file

@ -6,7 +6,7 @@ const { notifications } = useNotifications()
</script> </script>
<template> <template>
<nav sm:px3 flex="~ col gap2" shrink text-size-base leading-normal md:text-lg h-full> <nav sm:px3 flex="~ col gap2" shrink text-size-base leading-normal md:text-lg h-full mt-1>
<SearchWidget lg:ms-1 lg:me-5 hidden xl:block /> <SearchWidget lg:ms-1 lg:me-5 hidden xl:block />
<NavSideItem :text="$t('nav.search')" :to="isHydrated ? `/${currentServer}/explore` : '/explore'" icon="i-ri:search-line" hidden sm:block xl:hidden :command="command" /> <NavSideItem :text="$t('nav.search')" :to="isHydrated ? `/${currentServer}/explore` : '/explore'" icon="i-ri:search-line" hidden sm:block xl:hidden :command="command" />

View file

@ -36,6 +36,14 @@ const { modelValue } = defineModel<{
</CommonTooltip> </CommonTooltip>
</head> </head>
<p>{{ message }}</p> <p>{{ message }}</p>
<p py-2>
<i18n-t keypath="settings.notifications.push_notifications.subscription_error.error_hint">
<NuxtLink font-bold href="https://docs.elk.zone/pwa#faq" target="_blank" inline-flex="~ row" items-center gap-x-2>
https://docs.elk.zone/pwa#faq
<span inline-block aria-hidden="true" i-ri:external-link-line class="rtl-flip" />
</NuxtLink>
</i18n-t>
</p>
<p py-2> <p py-2>
<NuxtLink font-bold text-primary href="https://github.com/elk-zone/elk" target="_blank" flex="~ row" items-center gap-x-2> <NuxtLink font-bold text-primary href="https://github.com/elk-zone/elk" target="_blank" flex="~ row" items-center gap-x-2>
{{ $t('settings.notifications.push_notifications.subscription_error.repo_link') }} {{ $t('settings.notifications.push_notifications.subscription_error.repo_link') }}

View file

@ -15,6 +15,9 @@ const emit = defineEmits<{
(evt: 'setDescription', description: string): void (evt: 'setDescription', description: string): void
}>() }>()
// from https://github.com/mastodon/mastodon/blob/dfa984/app/models/media_attachment.rb#L40
const maxDescriptionLength = 1500
const isEditDialogOpen = ref(false) const isEditDialogOpen = ref(false)
const description = ref(props.attachment.description ?? '') const description = ref(props.attachment.description ?? '')
const toggleApply = () => { const toggleApply = () => {
@ -25,7 +28,7 @@ const toggleApply = () => {
<template> <template>
<div relative group> <div relative group>
<StatusAttachment :attachment="attachment" w-full /> <StatusAttachment :attachment="attachment" w-full is-preview />
<div absolute right-2 top-2> <div absolute right-2 top-2>
<div <div
v-if="removable" v-if="removable"
@ -55,7 +58,10 @@ const toggleApply = () => {
</h1> </h1>
<div flex flex-col gap-2> <div flex flex-col gap-2>
<textarea v-model="description" p-3 h-50 bg-base rounded-2 border-strong border-1 md:w-100 /> <textarea v-model="description" p-3 h-50 bg-base rounded-2 border-strong border-1 md:w-100 />
<button btn-outline @click="toggleApply"> <div flex flex-row-reverse>
<PublishCharacterCounter :length="description.length" :max="maxDescriptionLength" />
</div>
<button btn-outline :disabled="description.length > maxDescriptionLength" @click="toggleApply">
{{ $t('action.apply') }} {{ $t('action.apply') }}
</button> </button>
</div> </div>
@ -63,7 +69,7 @@ const toggleApply = () => {
{{ $t('action.close') }} {{ $t('action.close') }}
</button> </button>
</div> </div>
<StatusAttachment :attachment="attachment" w-full /> <StatusAttachment :attachment="attachment" w-full is-preview />
</div> </div>
</ModalDialog> </ModalDialog>
</div> </div>

View file

@ -0,0 +1,12 @@
<script setup lang="ts">
const props = defineProps<{
max: number
length: number
}>()
</script>
<template>
<div dir="ltr" pointer-events-none pe-1 pt-2 text-sm tabular-nums text-secondary flex gap="0.5" :class="{ 'text-rose-500': length > max }">
{{ length ?? 0 }}<span text-secondary-light>/</span><span text-secondary-light>{{ max }}</span>
</div>
</template>

View file

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import importEmojiLang from 'virtual:emoji-mart-lang-importer'
import type { Picker } from 'emoji-mart' import type { Picker } from 'emoji-mart'
const emit = defineEmits<{ const emit = defineEmits<{
@ -6,12 +7,15 @@ const emit = defineEmits<{
(e: 'selectCustom', image: any): void (e: 'selectCustom', image: any): void
}>() }>()
const { locale } = useI18n()
const el = $ref<HTMLElement>() const el = $ref<HTMLElement>()
let picker = $ref<Picker>() let picker = $ref<Picker>()
const colorMode = useColorMode() const colorMode = useColorMode()
async function openEmojiPicker() { async function openEmojiPicker() {
await updateCustomEmojis() await updateCustomEmojis()
if (picker) { if (picker) {
picker.update({ picker.update({
theme: colorMode.value, theme: colorMode.value,
@ -19,10 +23,14 @@ async function openEmojiPicker() {
}) })
} }
else { else {
const promise = import('@emoji-mart/data/sets/14/twitter.json').then(r => r.default) const [Picker, dataPromise, i18n] = await Promise.all([
const { Picker } = await import('emoji-mart') import('emoji-mart').then(({ Picker }) => Picker),
import('@emoji-mart/data/sets/14/twitter.json').then((r: any) => r.default).catch(() => {}),
importEmojiLang(locale.value.split('-')[0]),
])
picker = new Picker({ picker = new Picker({
data: () => promise, data: () => dataPromise,
onEmojiSelect({ native, src, alt, name }: any) { onEmojiSelect({ native, src, alt, name }: any) {
native native
? emit('select', native) ? emit('select', native)
@ -31,6 +39,7 @@ async function openEmojiPicker() {
set: 'twitter', set: 'twitter',
theme: colorMode.value, theme: colorMode.value,
custom: customEmojisData.value, custom: customEmojisData.value,
i18n,
}) })
} }
await nextTick() await nextTick()

View file

@ -64,7 +64,25 @@ const { editor } = useTiptap({
}) })
const characterCount = $computed(() => { const characterCount = $computed(() => {
let length = stringLength(htmlToText(editor.value?.getHTML() || '')) const text = htmlToText(editor.value?.getHTML() || '')
let length = stringLength(text)
// taken from https://github.com/mastodon/mastodon/blob/07f8b4d1b19f734d04e69daeb4c3421ef9767aac/app/lib/text_formatter.rb
const linkRegex = /(https?:\/\/(www\.)?|xmpp:)\S+/g
// taken from https://github.com/mastodon/mastodon/blob/af578e/app/javascript/mastodon/features/compose/util/counter.js
const countableMentionRegex = /(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig
// maximum of 23 chars per link
// https://github.com/elk-zone/elk/issues/1651
const maxLength = 23
for (const [fullMatch] of text.matchAll(linkRegex))
length -= fullMatch.length - Math.min(maxLength, fullMatch.length)
for (const [fullMatch, before, handle, username] of text.matchAll(countableMentionRegex))
length -= fullMatch.length - (before + username).length - 1 // - 1 for the @
if (draft.mentions) { if (draft.mentions) {
// + 1 is needed as mentions always need a space seperator at the end // + 1 is needed as mentions always need a space seperator at the end
@ -79,6 +97,10 @@ const characterCount = $computed(() => {
return length return length
}) })
const isExceedingCharacterLimit = $computed(() => {
return characterCount > characterLimit.value
})
const postLanguageDisplay = $computed(() => languagesNameList.find(i => i.code === (draft.params.language || preferredLanguage))?.nativeName) const postLanguageDisplay = $computed(() => languagesNameList.find(i => i.code === (draft.params.language || preferredLanguage))?.nativeName)
async function handlePaste(evt: ClipboardEvent) { async function handlePaste(evt: ClipboardEvent) {
@ -134,13 +156,13 @@ defineExpose({
</script> </script>
<template> <template>
<div v-if="isHydrated && currentUser" flex="~ col gap-4" py3 px2 sm:px4> <div v-if="isHydrated && currentUser" flex="~ col gap-4" py3 px2 sm:px4 aria-roledescription="publish-widget">
<template v-if="draft.editingStatus"> <template v-if="draft.editingStatus">
<div flex="~ col gap-1"> <div flex="~ col gap-1">
<div id="state-editing" text-secondary self-center> <div id="state-editing" text-secondary self-center>
{{ $t('state.editing') }} {{ $t('state.editing') }}
</div> </div>
<StatusCard :status="draft.editingStatus" :actions="false" :hover="false" px-0 /> <StatusCard :status="draft.editingStatus" :actions="false" :hover="false" is-preview px-0 />
</div> </div>
<div border="b dashed gray/40" /> <div border="b dashed gray/40" />
</template> </template>
@ -274,9 +296,7 @@ defineExpose({
<div flex-auto /> <div flex-auto />
<div dir="ltr" pointer-events-none pe-1 pt-2 text-sm tabular-nums text-secondary flex gap="0.5" :class="{ 'text-rose-500': characterCount > characterLimit }"> <PublishCharacterCounter :max="characterLimit" :length="characterCount" />
{{ characterCount ?? 0 }}<span text-secondary-light>/</span><span text-secondary-light>{{ characterLimit }}</span>
</div>
<CommonTooltip placement="top" :content="$t('tooltip.change_language')"> <CommonTooltip placement="top" :content="$t('tooltip.change_language')">
<CommonDropdown placement="bottom" auto-boundary-max-size> <CommonDropdown placement="bottom" auto-boundary-max-size>
@ -319,12 +339,12 @@ defineExpose({
</button> </button>
</CommonTooltip> </CommonTooltip>
<CommonTooltip v-else id="publish-tooltip" placement="top" :content="$t('tooltip.add_publishable_content')" :disabled="!isPublishDisabled"> <CommonTooltip v-else id="publish-tooltip" placement="top" :content="$t('tooltip.add_publishable_content')" :disabled="!(isPublishDisabled || isExceedingCharacterLimit)">
<button <button
btn-solid rounded-3 text-sm w-full flex="~ gap1" items-center btn-solid rounded-3 text-sm w-full flex="~ gap1" items-center
md:w-fit md:w-fit
class="publish-button" class="publish-button"
:aria-disabled="isPublishDisabled" :aria-disabled="isPublishDisabled || isExceedingCharacterLimit"
aria-describedby="publish-tooltip" aria-describedby="publish-tooltip"
@click="publish" @click="publish"
> >

View file

@ -43,13 +43,13 @@ const chooseIcon = (i: number, text: string) => {
<div flex="~ col gap4"> <div flex="~ col gap4">
<div v-for="i in fieldCount" :key="i" flex="~ gap3" items-center> <div v-for="i in fieldCount" :key="i" flex="~ gap3" items-center>
<CommonDropdown ref="dropdown" placement="left"> <CommonDropdown ref="dropdown" placement="left">
<CommonTooltip content="Pick a icon"> <CommonTooltip :content="$t('tooltip.pick_an_icon')">
<button type="button" btn-action-icon> <button type="button" btn-action-icon>
<div :class="fieldIcons[i - 1] || 'i-ri:question-mark'" /> <div :class="fieldIcons[i - 1] || 'i-ri:question-mark'" />
</button> </button>
</CommonTooltip> </CommonTooltip>
<template #popper> <template #popper>
<div flex="~ wrap gap-1" max-w-50 m2> <div flex="~ wrap gap-1" max-w-60 m2 me1>
<CommonTooltip <CommonTooltip
v-for="(icon, text) in accountFieldIcons" v-for="(icon, text) in accountFieldIcons"
:key="icon" :key="icon"
@ -66,12 +66,14 @@ const chooseIcon = (i: number, text: string) => {
</CommonDropdown> </CommonDropdown>
<input <input
v-model="form.fieldsAttributes[i - 1].name" v-model="form.fieldsAttributes[i - 1].name"
type="text" placeholder="Label" type="text" placeholder-text-secondary
:placeholder="$t('settings.profile.appearance.profile_metadata_label')"
input-base input-base
> >
<input <input
v-model="form.fieldsAttributes[i - 1].value" v-model="form.fieldsAttributes[i - 1].value"
type="text" placeholder="Content" type="text" placeholder-text-secondary
:placeholder="$t('settings.profile.appearance.profile_metadata_value')"
input-base input-base
> >
</div> </div>

View file

@ -1,8 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ const { disabled = false } = defineProps<{
icon?: string icon?: string
text?: string text?: string
checked: boolean checked: boolean
disabled?: boolean
}>() }>()
</script> </script>
@ -10,10 +11,13 @@ defineProps<{
<button <button
exact-active-class="text-primary" exact-active-class="text-primary"
block w-full group focus:outline-none text-start block w-full group focus:outline-none text-start
:disabled="disabled"
:class="disabled ? 'opacity-50 cursor-not-allowed' : ''"
> >
<div <div
w-full flex w-fit px5 py3 md:gap2 gap4 items-center w-full flex w-fit px5 py3 md:gap2 gap4 items-center
transition-250 group-hover:bg-active transition-250
:class="disabled ? '' : 'group-hover:bg-active'"
group-focus-visible:ring="2 current" group-focus-visible:ring="2 current"
> >
<div flex-1 flex items-center md:gap2 gap4> <div flex-1 flex items-center md:gap2 gap4>

View file

@ -53,7 +53,7 @@ function removeDisabledTranslation(code: string) {
{{ availableOption.nativeName }} {{ availableOption.nativeName }}
</option> </option>
</select> </select>
<button class="btn-text" @click="addDisabledTranslation"> <button class="btn-text shrink-0" @click="addDisabledTranslation">
{{ $t('settings.language.translations.add') }} {{ $t('settings.language.translations.add') }}
</button> </button>
</div> </div>

View file

@ -1,14 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { clamp } from '@vueuse/core' import { clamp } from '@vueuse/core'
import type { mastodon } from 'masto' import type { mastodon } from 'masto'
import { decode } from 'blurhash'
const { const {
attachment, attachment,
fullSize = false, fullSize = false,
isPreview = false,
} = defineProps<{ } = defineProps<{
attachment: mastodon.v1.MediaAttachment attachment: mastodon.v1.MediaAttachment
attachments?: mastodon.v1.MediaAttachment[] attachments?: mastodon.v1.MediaAttachment[]
fullSize?: boolean fullSize?: boolean
isPreview?: boolean
}>() }>()
const src = $computed(() => attachment.previewUrl || attachment.url || attachment.remoteUrl!) const src = $computed(() => attachment.previewUrl || attachment.url || attachment.remoteUrl!)
@ -89,36 +92,83 @@ useIntersectionObserver(video, (entries) => {
}, { threshold: 0.75 }) }, { threshold: 0.75 })
const userSettings = useUserSettings() const userSettings = useUserSettings()
const shouldLoadAttachment = ref(isPreview || !getPreferences(userSettings.value, 'enableDataSaving'))
function loadAttachment() {
shouldLoadAttachment.value = true
}
const blurHashSrc = $computed(() => {
if (!attachment.blurhash)
return ''
const pixels = decode(attachment.blurhash, 32, 32)
return getDataUrlFromArr(pixels, 32, 32)
})
let videoThumbnail = shouldLoadAttachment.value
? attachment.previewUrl
: blurHashSrc
watch(shouldLoadAttachment, () => {
videoThumbnail = shouldLoadAttachment
? attachment.previewUrl
: blurHashSrc
})
</script> </script>
<template> <template>
<div relative ma flex :gap="isAudio ? '2' : ''"> <div relative ma flex :gap="isAudio ? '2' : ''">
<template v-if="type === 'video'"> <template v-if="type === 'video'">
<button
type="button"
relative
@click="!shouldLoadAttachment ? loadAttachment() : null"
>
<video <video
ref="video" ref="video"
preload="none" preload="none"
:poster="attachment.previewUrl" :poster="videoThumbnail"
muted muted
loop loop
playsinline playsinline
controls :controls="shouldLoadAttachment"
rounded-lg rounded-lg
object-cover object-cover
fullscreen:object-contain
:width="attachment.meta?.original?.width" :width="attachment.meta?.original?.width"
:height="attachment.meta?.original?.height" :height="attachment.meta?.original?.height"
:style="{ :style="{
aspectRatio, aspectRatio,
objectPosition, objectPosition,
}" }"
:class="!shouldLoadAttachment ? 'brightness-60 hover:brightness-70 transition-filter' : ''"
> >
<source :src="attachment.url || attachment.previewUrl" type="video/mp4"> <source :src="attachment.url || attachment.previewUrl" type="video/mp4">
</video> </video>
<span
v-if="!shouldLoadAttachment"
class="status-attachment-load"
absolute
text-sm
text-white
flex flex-col justify-center items-center
gap-3 w-6 h-6
pointer-events-none
i-ri:video-download-line
/>
</button>
</template> </template>
<template v-else-if="type === 'gifv'"> <template v-else-if="type === 'gifv'">
<button
type="button"
relative
@click="!shouldLoadAttachment ? loadAttachment() : null"
>
<video <video
ref="video" ref="video"
preload="none" preload="none"
:poster="attachment.previewUrl" :poster="videoThumbnail"
muted muted
loop loop
playsinline playsinline
@ -133,6 +183,18 @@ const userSettings = useUserSettings()
> >
<source :src="attachment.url || attachment.previewUrl" type="video/mp4"> <source :src="attachment.url || attachment.previewUrl" type="video/mp4">
</video> </video>
<span
v-if="!shouldLoadAttachment"
class="status-attachment-load"
absolute
text-sm
text-white
flex flex-col justify-center items-center
gap-3 w-6 h-6
pointer-events-none
i-ri:video-download-line
/>
</button>
</template> </template>
<template v-else-if="type === 'audio'"> <template v-else-if="type === 'audio'">
<audio controls h-15> <audio controls h-15>
@ -148,7 +210,8 @@ const userSettings = useUserSettings()
h-full h-full
w-full w-full
aria-label="Open image preview dialog" aria-label="Open image preview dialog"
@click="openMediaPreview(attachments ? attachments : [attachment], attachments?.indexOf(attachment) || 0)" relative
@click="!shouldLoadAttachment ? loadAttachment() : openMediaPreview(attachments ? attachments : [attachment], attachments?.indexOf(attachment) || 0)"
> >
<CommonBlurhash <CommonBlurhash
:blurhash="attachment.blurhash" :blurhash="attachment.blurhash"
@ -162,10 +225,24 @@ const userSettings = useUserSettings()
aspectRatio, aspectRatio,
objectPosition, objectPosition,
}" }"
:should-load-image="shouldLoadAttachment"
rounded-lg rounded-lg
h-full h-full
w-full w-full
object-cover object-cover
:draggable="shouldLoadAttachment"
:class="!shouldLoadAttachment ? 'brightness-60 hover:brightness-70 transition-filter' : ''"
/>
<span
v-if="!shouldLoadAttachment"
class="status-attachment-load"
absolute
text-sm
text-white
flex flex-col justify-center items-center
gap-3 w-6 h-6
pointer-events-none
i-ri:file-download-line
/> />
</button> </button>
</template> </template>
@ -201,3 +278,11 @@ const userSettings = useUserSettings()
</div> </div>
</div> </div>
</template> </template>
<style lang="postcss">
.status-attachment-load {
left: 50%;
top: 50%;
translate: -50% -50%;
}
</style>

View file

@ -8,6 +8,7 @@ const props = withDefaults(
context?: mastodon.v2.FilterContext context?: mastodon.v2.FilterContext
hover?: boolean hover?: boolean
faded?: boolean faded?: boolean
isPreview?: boolean
// If we know the prev and next status in the timeline, we can simplify the card // If we know the prev and next status in the timeline, we can simplify the card
older?: mastodon.v1.Status older?: mastodon.v1.Status
@ -84,6 +85,7 @@ const showReplyTo = $computed(() => !replyToMain && !directReply)
:class="{ 'hover:bg-active': hover }" :class="{ 'hover:bg-active': hover }"
tabindex="0" tabindex="0"
focus:outline-none focus-visible:ring="2 primary" focus:outline-none focus-visible:ring="2 primary"
aria-roledescription="status-card"
:lang="status.language ?? undefined" :lang="status.language ?? undefined"
@click="onclick" @click="onclick"
@keydown.enter="onclick" @keydown.enter="onclick"
@ -177,7 +179,7 @@ const showReplyTo = $computed(() => !replyToMain && !directReply)
</div> </div>
<!-- Content --> <!-- Content -->
<StatusContent :status="status" :newer="newer" :context="context" mb2 :class="{ 'mt-2 mb1': isDM }" /> <StatusContent :status="status" :newer="newer" :context="context" :is-preview="isPreview" mb2 :class="{ 'mt-2 mb1': isDM }" />
<StatusActions v-if="actions !== false" v-show="!userSettings.zenMode" :status="status" /> <StatusActions v-if="actions !== false" v-show="!userSettings.zenMode" :status="status" />
</div> </div>
</div> </div>

View file

@ -5,6 +5,7 @@ const { status, context } = defineProps<{
status: mastodon.v1.Status status: mastodon.v1.Status
newer?: mastodon.v1.Status newer?: mastodon.v1.Status
context?: mastodon.v2.FilterContext | 'details' context?: mastodon.v2.FilterContext | 'details'
isPreview?: boolean
}>() }>()
const isDM = $computed(() => status.visibility === 'direct') const isDM = $computed(() => status.visibility === 'direct')
@ -19,7 +20,7 @@ const isFiltered = $computed(() => status.account.id !== currentUser.value?.acco
// check spoiler text or media attachment // check spoiler text or media attachment
// needed to handle accounts that mark all their posts as sensitive // needed to handle accounts that mark all their posts as sensitive
const hasSensitiveSpoilerOrMedia = $computed(() => status.sensitive && (!!status.spoilerText || !!status.mediaAttachments.length)) const hasSpoilerOrSensitiveMedia = $computed(() => !!status.spoilerText || (status.sensitive && !!status.mediaAttachments.length))
const cleanSharedLink = !status.poll const cleanSharedLink = !status.poll
&& !status.mediaAttachments.length && !status.mediaAttachments.length
@ -35,7 +36,7 @@ const cleanSharedLink = !status.poll
}" }"
> >
<StatusBody v-if="!isFiltered && status.sensitive && !status.spoilerText" :status="status" :newer="newer" :with-action="!isDetails" :class="isDetails ? 'text-xl' : ''" /> <StatusBody v-if="!isFiltered && status.sensitive && !status.spoilerText" :status="status" :newer="newer" :with-action="!isDetails" :class="isDetails ? 'text-xl' : ''" />
<StatusSpoiler :enabled="hasSensitiveSpoilerOrMedia || isFiltered" :filter="isFiltered" :is-d-m="isDM"> <StatusSpoiler :enabled="hasSpoilerOrSensitiveMedia || isFiltered" :filter="isFiltered" :is-d-m="isDM">
<template v-if="status.spoilerText" #spoiler> <template v-if="status.spoilerText" #spoiler>
<p>{{ status.spoilerText }}</p> <p>{{ status.spoilerText }}</p>
</template> </template>
@ -48,6 +49,7 @@ const cleanSharedLink = !status.poll
<StatusMedia <StatusMedia
v-if="status.mediaAttachments?.length" v-if="status.mediaAttachments?.length"
:status="status" :status="status"
:is-preview="isPreview"
/> />
<StatusPreviewCard <StatusPreviewCard
v-if="status.card" v-if="status.card"

View file

@ -30,7 +30,7 @@ const isDM = $computed(() => status.visibility === 'direct')
</script> </script>
<template> <template>
<div :id="`status-${status.id}`" flex flex-col gap-2 pt2 pb1 ps-3 pe-4 relative :lang="status.language ?? undefined"> <div :id="`status-${status.id}`" flex flex-col gap-2 pt2 pb1 ps-3 pe-4 relative :lang="status.language ?? undefined" aria-roledescription="status-details">
<StatusActionsMore :status="status" absolute inset-ie-2 top-2 /> <StatusActionsMore :status="status" absolute inset-ie-2 top-2 />
<NuxtLink :to="getAccountRoute(status.account)" rounded-full hover:bg-active transition-100 pe5 me-a> <NuxtLink :to="getAccountRoute(status.account)" rounded-full hover:bg-active transition-100 pe5 me-a>
<AccountHoverWrapper :account="status.account"> <AccountHoverWrapper :account="status.account">

View file

@ -1,9 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import type { mastodon } from 'masto' import type { mastodon } from 'masto'
const { status } = defineProps<{ const { status, isPreview = false } = defineProps<{
status: mastodon.v1.Status | mastodon.v1.StatusEdit status: mastodon.v1.Status | mastodon.v1.StatusEdit
fullSize?: boolean fullSize?: boolean
isPreview?: boolean
}>() }>()
</script> </script>
@ -16,6 +17,7 @@ const { status } = defineProps<{
:full-size="fullSize" :full-size="fullSize"
w-full w-full
h-full h-full
:is-preview="isPreview"
/> />
</template> </template>
</div> </div>

View file

@ -57,7 +57,7 @@ const votersCount = $computed(() => poll.votersCount ?? poll.votesCount ?? 0)
<div <div
v-for="(option, index) of poll.options" v-for="(option, index) of poll.options"
:key="index" py-1 relative :key="index" py-1 relative
:style="{ '--bar-width': toPercentage((option.votesCount || 0) / poll.votesCount) }" :style="{ '--bar-width': toPercentage(votersCount === 0 ? 0 : (option.votesCount ?? 0) / votersCount) }"
> >
<div flex justify-between pb-2 w-full> <div flex justify-between pb-2 w-full>
<span inline-flex align-items> <span inline-flex align-items>
@ -67,7 +67,7 @@ const votersCount = $computed(() => poll.votersCount ?? poll.votesCount ?? 0)
<span text-primary-active> {{ formatPercentage(votersCount > 0 ? (option.votesCount || 0) / votersCount : 0) }}</span> <span text-primary-active> {{ formatPercentage(votersCount > 0 ? (option.votesCount || 0) / votersCount : 0) }}</span>
</div> </div>
<div class="bg-gray/40" rounded-l-sm rounded-r-lg h-5px w-full> <div class="bg-gray/40" rounded-l-sm rounded-r-lg h-5px w-full>
<div bg-primary-active h-full class="w-[var(--bar-width)]" /> <div bg-primary-active h-full min-w="1%" class="w-[var(--bar-width)]" />
</div> </div>
</div> </div>
</template> </template>

View file

@ -40,6 +40,13 @@ const cardTypeIconMap: Record<mastodon.v1.PreviewCardType, string> = {
video: 'i-ri:play-line', video: 'i-ri:play-line',
rich: 'i-ri:profile-line', rich: 'i-ri:profile-line',
} }
const userSettings = useUserSettings()
const shouldLoadAttachment = ref(!getPreferences(userSettings.value, 'enableDataSaving'))
function loadAttachment() {
shouldLoadAttachment.value = true
}
</script> </script>
<template> <template>
@ -66,6 +73,7 @@ const cardTypeIconMap: Record<mastodon.v1.PreviewCardType, string> = {
'w-full aspect-[1.91]': !isSquare, 'w-full aspect-[1.91]': !isSquare,
'rounded-lg': root, 'rounded-lg': root,
}" }"
relative
> >
<CommonBlurhash <CommonBlurhash
:blurhash="card.blurhash" :blurhash="card.blurhash"
@ -73,8 +81,30 @@ const cardTypeIconMap: Record<mastodon.v1.PreviewCardType, string> = {
:width="card.width" :width="card.width"
:height="card.height" :height="card.height"
:alt="alt" :alt="alt"
:should-load-image="shouldLoadAttachment"
w-full h-full object-cover w-full h-full object-cover
:class="!shouldLoadAttachment ? 'brightness-60' : ''"
/> />
<button
v-if="!shouldLoadAttachment"
type="button"
absolute
class="status-preview-card-load bg-black/64"
p-2
transition
rounded
hover:bg-black
cursor-pointer
@click.stop.prevent="!shouldLoadAttachment ? loadAttachment() : null"
>
<span
text-sm
text-white
flex flex-col justify-center items-center
gap-3 w-6 h-6
i-ri:file-download-line
/>
</button>
</div> </div>
<div <div
v-else v-else
@ -88,3 +118,11 @@ const cardTypeIconMap: Record<mastodon.v1.PreviewCardType, string> = {
<StatusPreviewCardInfo :p="isSquare ? 'x-4' : '4'" :root="root" :card="card" :provider="providerName" /> <StatusPreviewCardInfo :p="isSquare ? 'x-4' : '4'" :root="root" :card="card" :provider="providerName" />
</NuxtLink> </NuxtLink>
</template> </template>
<style lang="postcss">
.status-preview-card-load {
left: 50%;
top: 50%;
translate: -50% -50%;
}
</style>

View file

@ -27,14 +27,16 @@ const account = isSelf ? computed(() => status.account) : useAccountById(status.
</template> </template>
<template v-else> <template v-else>
<div i-ri-chat-1-line text-blue /> <div i-ri-chat-1-line text-blue />
<div ws-nowrap flex>
<i18n-t keypath="status.replying_to"> <i18n-t keypath="status.replying_to">
<template v-if="account"> <template v-if="account">
<AccountInlineInfo :account="account" :link="false" /> <AccountInlineInfo :account="account" :link="false" m-inline-1 />
</template> </template>
<template v-else> <template v-else>
{{ $t('status.someone') }} {{ $t('status.someone') }}
</template> </template>
</i18n-t> </i18n-t>
</div>
</template> </template>
</NuxtLink> </NuxtLink>
</template> </template>

View file

@ -12,6 +12,9 @@ const { items, command } = defineProps<{
}>() }>()
const emojis = computed(() => { const emojis = computed(() => {
if (process.server)
return []
return items.map((item: CustomEmoji | Emoji) => { return items.map((item: CustomEmoji | Emoji) => {
if (isCustomEmoji(item)) { if (isCustomEmoji(item)) {
return { return {
@ -80,7 +83,7 @@ defineExpose({
<template v-if="isPending"> <template v-if="isPending">
<div flex gap-1 items-center p2 animate-pulse> <div flex gap-1 items-center p2 animate-pulse>
<div i-ri:loader-2-line animate-spin /> <div i-ri:loader-2-line animate-spin />
<span>Fetching...</span> <span>{{ $t('common.fetching') }}</span>
</div> </div>
</template> </template>
<template v-if="items.length"> <template v-if="items.length">

View file

@ -52,7 +52,7 @@ defineExpose({
<div animate-spin preserve-3d> <div animate-spin preserve-3d>
<div i-ri:loader-2-line /> <div i-ri:loader-2-line />
</div> </div>
<span>Fetching...</span> <span>{{ $t('common.fetching') }}</span>
</div> </div>
</template> </template>
<template v-if="items.length"> <template v-if="items.length">

View file

@ -52,7 +52,7 @@ defineExpose({
<div animate-spin preserve-3d> <div animate-spin preserve-3d>
<div i-ri:loader-2-line /> <div i-ri:loader-2-line />
</div> </div>
<span>Fetching...</span> <span>{{ $t('common.fetching') }}</span>
</div> </div>
</template> </template>
<template v-if="items.length"> <template v-if="items.length">

View file

@ -96,7 +96,7 @@ onClickOutside(input, () => {
<template> <template>
<form text-center justify-center items-center max-w-150 py6 flex="~ col gap-3" @submit.prevent="oauth"> <form text-center justify-center items-center max-w-150 py6 flex="~ col gap-3" @submit.prevent="oauth">
<div flex="~ center" items-end mb2 gap-x-2> <div flex="~ center" items-end mb2 gap-x-2>
<img src="/logo.svg" w-12 h-12 mxa height="48" width="48" :alt="$t('app_logo')" class="rtl-flip"> <img :src="`/${''}logo.svg`" w-12 h-12 mxa height="48" width="48" :alt="$t('app_logo')" class="rtl-flip">
<div text-3xl> <div text-3xl>
{{ $t('action.sign_in') }} {{ $t('action.sign_in') }}
</div> </div>

View file

@ -10,7 +10,7 @@ const { busy, oauth, singleInstanceServer } = useSignIn()
</i18n-t> </i18n-t>
</p> </p>
<p text-sm text-secondary> <p text-sm text-secondary>
{{ $t('user.sign_in_desc') }} {{ $t(singleInstanceServer ? 'user.single_instance_sign_in_desc' : 'user.sign_in_desc') }}
</p> </p>
<button <button
v-if="singleInstanceServer" v-if="singleInstanceServer"

View file

@ -186,6 +186,13 @@ export function htmlToText(html: string) {
} }
} }
export function recursiveTreeToText(input: Node): string {
if (input && input.children && input.children.length > 0)
return input.children.map((n: Node) => recursiveTreeToText(n)).join('')
else
return treeToText(input)
}
export function treeToText(input: Node): string { export function treeToText(input: Node): string {
let pre = '' let pre = ''
let body = '' let body = ''
@ -235,8 +242,10 @@ export function treeToText(input: Node): string {
body = (input.children as Node[]).map(n => treeToText(n)).join('') body = (input.children as Node[]).map(n => treeToText(n)).join('')
if (input.name === 'img' || input.name === 'picture') { if (input.name === 'img' || input.name === 'picture') {
if (input.attributes.class?.includes('custom-emoji')) if (input.attributes.class?.includes('custom-emoji')) {
return `:${input.attributes['data-emoji-id']}:` const id = input.attributes['data-emoji-id'] ?? input.attributes.title ?? input.attributes.alt ?? 'unknown'
return id[0] !== ':' ? `:${id}:` : id
}
if (input.attributes.class?.includes('iconify-emoji')) if (input.attributes.class?.includes('iconify-emoji'))
return input.attributes.alt return input.attributes.alt
} }
@ -469,7 +478,7 @@ function replaceCustomEmoji(customEmojis: Record<string, mastodon.v1.CustomEmoji
} }
const _markdownReplacements: [RegExp, (c: (string | Node)[]) => Node][] = [ const _markdownReplacements: [RegExp, (c: (string | Node)[]) => Node][] = [
[/\*\*\*(.*?)\*\*\*/g, c => h('b', null, [h('em', null, c)])], [/\*\*\*(.*?)\*\*\*/g, ([c]) => h('b', null, [h('em', null, c)])],
[/\*\*(.*?)\*\*/g, c => h('b', null, c)], [/\*\*(.*?)\*\*/g, c => h('b', null, c)],
[/\*(.*?)\*/g, c => h('em', null, c)], [/\*(.*?)\*/g, c => h('em', null, c)],
[/~~(.*?)~~/g, c => h('del', null, c)], [/~~(.*?)~~/g, c => h('del', null, c)],

View file

@ -107,7 +107,7 @@ function handleCodeBlock(el: Node) {
const codeEl = el.children[0] as Node const codeEl = el.children[0] as Node
const classes = codeEl.attributes.class as string const classes = codeEl.attributes.class as string
const lang = classes?.split(/\s/g).find(i => i.startsWith('language-'))?.replace('language-', '') const lang = classes?.split(/\s/g).find(i => i.startsWith('language-'))?.replace('language-', '')
const code = codeEl.children[0] ? treeToText(codeEl.children[0]) : '' const code = codeEl.children && codeEl.children.length > 0 ? recursiveTreeToText(codeEl) : ''
return h(ContentCode, { lang, code: encodeURIComponent(code) }) return h(ContentCode, { lang, code: encodeURIComponent(code) })
} }
} }

View file

@ -18,6 +18,7 @@ export const isFirstVisit = useLocalStorage(STORAGE_KEY_FIRST_VISIT, !process.mo
export const isSigninDialogOpen = ref(false) export const isSigninDialogOpen = ref(false)
export const isPublishDialogOpen = ref(false) export const isPublishDialogOpen = ref(false)
export const isKeyboardShortcutsDialogOpen = ref(false)
export const isMediaPreviewOpen = ref(false) export const isMediaPreviewOpen = ref(false)
export const isEditHistoryDialogOpen = ref(false) export const isEditHistoryDialogOpen = ref(false)
export const isPreviewHelpOpen = ref(isFirstVisit.value) export const isPreviewHelpOpen = ref(isFirstVisit.value)
@ -139,3 +140,11 @@ export function openCommandPanel(isCommandMode = false) {
export function closeCommandPanel() { export function closeCommandPanel() {
isCommandPanelOpen.value = false isCommandPanelOpen.value = false
} }
export function toggleKeyboardShortcuts() {
isKeyboardShortcutsDialogOpen.value = !isKeyboardShortcutsDialogOpen.value
}
export function closeKeyboardShortcuts() {
isKeyboardShortcutsDialogOpen.value = false
}

View file

@ -1,7 +1,7 @@
import type { MaybeComputedRef, RemovableRef } from '@vueuse/core' import type { MaybeComputedRef, RemovableRef } from '@vueuse/core'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { del, get, set, update } from 'idb-keyval'
import type { UseIDBOptions } from '@vueuse/integrations/useIDBKeyval' import type { UseIDBOptions } from '@vueuse/integrations/useIDBKeyval'
import { del, get, set, update } from '~/utils/elk-idb'
const isIDBSupported = !process.test && typeof indexedDB !== 'undefined' const isIDBSupported = !process.test && typeof indexedDB !== 'undefined'

44
composables/magickeys.ts Normal file
View file

@ -0,0 +1,44 @@
import type { ComputedRef } from 'vue'
// TODO: consider to allow combinations similar to useMagicKeys using proxy?
// e.g. `const magicSequence = useMagicSequence()`
// `magicSequence['Shift+Ctrl+A']`
// `const { Ctrl_A_B } = useMagicSequence()`
/**
* source: inspired by https://github.com/vueuse/vueuse/issues/427#issuecomment-815619446
* @param keys ordered list of keys making up the sequence
*/
export function useMagicSequence(keys: string[]): ComputedRef<boolean> {
const magicKeys = useMagicKeys()
const success = ref(false)
const i = ref(0)
let down = false
watch(
() => magicKeys.current,
() => {
if (magicKeys[keys[i.value]].value && !down) {
down = true
i.value += 1
}
else if (i.value > 0 && !magicKeys[keys[i.value - 1]].value && down) {
down = false
}
else {
i.value = 0
down = false
success.value = false
}
if (i.value >= keys.length && !down) {
i.value = 0
down = false
success.value = true
}
}, {
deep: true,
})
return computed(() => success.value)
}

View file

@ -21,6 +21,7 @@ export const accountFieldIcons: Record<string, string> = Object.fromEntries(Obje
LinkedIn: 'i-ri:linkedin-box-line', LinkedIn: 'i-ri:linkedin-box-line',
Location: 'i-ri:map-pin-2-line', Location: 'i-ri:map-pin-2-line',
Mastodon: 'i-ri:mastodon-line', Mastodon: 'i-ri:mastodon-line',
Matrix: 'i-tabler:brand-matrix',
Medium: 'i-ri:medium-line', Medium: 'i-ri:medium-line',
OpenPGP: 'i-ri:key-2-line', OpenPGP: 'i-ri:key-2-line',
Patreon: 'i-ri:patreon-line', Patreon: 'i-ri:patreon-line',

View file

@ -106,6 +106,9 @@ export function useStreaming(
stream.value = cb(client.value) stream.value = cb(client.value)
}) })
if (process.client && !process.test)
useNuxtApp().$pageLifecycle.addFrozenListener(cleanup)
tryOnBeforeUnmount(() => isActive.value = false) tryOnBeforeUnmount(() => isActive.value = false)
if (controls) if (controls)

View file

@ -120,6 +120,62 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
let failedAttachments = $ref<MediaAttachmentUploadError[]>([]) let failedAttachments = $ref<MediaAttachmentUploadError[]>([])
const dropZoneRef = ref<HTMLDivElement>() const dropZoneRef = ref<HTMLDivElement>()
const maxPixels
= currentInstance.value!.configuration?.mediaAttachments?.imageMatrixLimit
?? 4096 ** 2
const loadImage = (inputFile: Blob) => new Promise<HTMLImageElement>((resolve, reject) => {
const url = URL.createObjectURL(inputFile)
const img = new Image()
img.onerror = err => reject(err)
img.onload = () => resolve(img)
img.src = url
})
function resizeImage(img: CanvasImageSource, type = 'image/png'): Promise<Blob | null> {
const { width, height } = img
const aspectRatio = (width as number) / (height as number)
const canvas = document.createElement('canvas')
const resizedWidth = canvas.width = Math.round(Math.sqrt(maxPixels * aspectRatio))
const resizedHeight = canvas.height = Math.round(Math.sqrt(maxPixels / aspectRatio))
const context = canvas.getContext('2d')
context?.drawImage(img, 0, 0, resizedWidth, resizedHeight)
return new Promise((resolve) => {
canvas.toBlob(resolve, type)
})
}
async function processImageFile(file: File) {
try {
const image = await loadImage(file) as HTMLImageElement
if (image.width * image.height > maxPixels)
file = await resizeImage(image, file.type) as File
return file
}
catch (e) {
// Resize failed, just use the original file
console.error(e)
return file
}
}
async function processFile(file: File) {
if (file.type.startsWith('image/'))
return await processImageFile(file)
return file
}
async function uploadAttachments(files: File[]) { async function uploadAttachments(files: File[]) {
isUploading = true isUploading = true
failedAttachments = [] failedAttachments = []
@ -131,7 +187,7 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
isExceedingAttachmentLimit = false isExceedingAttachmentLimit = false
try { try {
const attachment = await client.v1.mediaAttachments.create({ const attachment = await client.v1.mediaAttachments.create({
file, file: await processFile(file),
}) })
draft.attachments.push(attachment) draft.attachments.push(attachment)
} }
@ -150,6 +206,8 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
} }
async function pickAttachments() { async function pickAttachments() {
if (process.server)
return
const mimeTypes = currentInstance.value!.configuration?.mediaAttachments.supportedMimeTypes const mimeTypes = currentInstance.value!.configuration?.mediaAttachments.supportedMimeTypes
const files = await fileOpen({ const files = await fileOpen({
description: 'Attachments', description: 'Attachments',

View file

@ -72,6 +72,8 @@ export function getReplyDraft(status: mastodon.v1.Status) {
return getDefaultDraft({ return getDefaultDraft({
initialText: '', initialText: '',
inReplyToId: status!.id, inReplyToId: status!.id,
sensitive: status.sensitive,
spoilerText: status.spoilerText,
visibility: status.visibility, visibility: status.visibility,
mentions: accountsToMention, mentions: accountsToMention,
language: status.language, language: status.language,
@ -129,14 +131,14 @@ export function useDraft(
export function mentionUser(account: mastodon.v1.Account) { export function mentionUser(account: mastodon.v1.Account) {
openPublishDialog('dialog', getDefaultDraft({ openPublishDialog('dialog', getDefaultDraft({
status: `@${account.acct} `, status: `@${account.acct} `,
}), true) }))
} }
export function directMessageUser(account: mastodon.v1.Account) { export function directMessageUser(account: mastodon.v1.Account) {
openPublishDialog('dialog', getDefaultDraft({ openPublishDialog('dialog', getDefaultDraft({
status: `@${account.acct} `, status: `@${account.acct} `,
visibility: 'direct', visibility: 'direct',
}), true) }))
} }
export function clearEmptyDrafts() { export function clearEmptyDrafts() {

View file

@ -27,9 +27,11 @@ export function emojisArrayToObject(emojis: mastodon.v1.CustomEmoji[]) {
export function noop() {} export function noop() {}
export const useIsMac = () => computed(() => export const useIsMac = () => {
useRequestHeaders(['user-agent'])['user-agent']?.includes('Macintosh') const headers = useRequestHeaders(['user-agent'])
return computed(() => headers['user-agent']?.includes('Macintosh')
?? navigator?.platform?.includes('Mac') ?? false) ?? navigator?.platform?.includes('Mac') ?? false)
}
export const isEmptyObject = (object: Object) => Object.keys(object).length === 0 export const isEmptyObject = (object: Object) => Object.keys(object).length === 0

View file

@ -18,6 +18,7 @@ export interface PreferencesSettings {
hideAccountHoverCard: boolean hideAccountHoverCard: boolean
grayscaleMode: boolean grayscaleMode: boolean
enableAutoplay: boolean enableAutoplay: boolean
enableDataSaving: boolean
enablePinchToZoom: boolean enablePinchToZoom: boolean
experimentalVirtualScroller: boolean experimentalVirtualScroller: boolean
experimentalGitHubCards: boolean experimentalGitHubCards: boolean
@ -77,6 +78,7 @@ export const DEFAULT__PREFERENCES_SETTINGS: PreferencesSettings = {
hideAccountHoverCard: false, hideAccountHoverCard: false,
grayscaleMode: false, grayscaleMode: false,
enableAutoplay: true, enableAutoplay: true,
enableDataSaving: false,
enablePinchToZoom: false, enablePinchToZoom: false,
experimentalVirtualScroller: true, experimentalVirtualScroller: true,
experimentalGitHubCards: true, experimentalGitHubCards: true,

View file

@ -33,7 +33,12 @@ export function usePreferences<T extends keyof PreferencesSettings>(name: T): Re
} }
export function getPreferences<T extends keyof PreferencesSettings>(userSettings: UserSettings, name: T): PreferencesSettings[T] { export function getPreferences<T extends keyof PreferencesSettings>(userSettings: UserSettings, name: T): PreferencesSettings[T] {
return userSettings?.preferences?.[name] ?? DEFAULT__PREFERENCES_SETTINGS[name] const preference = userSettings?.preferences?.[name] ?? DEFAULT__PREFERENCES_SETTINGS[name]
if (name === 'enableAutoplay')
return getPreferences(userSettings, 'enableDataSaving') ? false : preference
return preference
} }
export function togglePreferences(key: keyof PreferencesSettings) { export function togglePreferences(key: keyof PreferencesSettings) {

View file

@ -8,14 +8,14 @@ export function setupPageHeader() {
const enablePinchToZoom = usePreferences('enablePinchToZoom') const enablePinchToZoom = usePreferences('enablePinchToZoom')
const localeMap = (locales.value as LocaleObject[]).reduce((acc, l) => { const localeMap = (locales.value as LocaleObject[]).reduce((acc, l) => {
acc[l.code!] = l.dir ?? 'auto' acc[l.code!] = l.dir ?? 'ltr'
return acc return acc
}, {} as Record<string, Directions>) }, {} as Record<string, Directions>)
useHeadFixed({ useHeadFixed({
htmlAttrs: { htmlAttrs: {
lang: () => locale.value, lang: () => locale.value,
dir: () => localeMap[locale.value] ?? 'auto', dir: () => localeMap[locale.value] ?? 'ltr',
class: () => enablePinchToZoom.value ? ['enable-pinch-to-zoom'] : [], class: () => enablePinchToZoom.value ? ['enable-pinch-to-zoom'] : [],
}, },
meta: [{ meta: [{
@ -43,7 +43,10 @@ export function setupPageHeader() {
titleTemplate = `${titleTemplate.slice(0, 60)}...${titleTemplate.endsWith('"') ? '"' : ''}` titleTemplate = `${titleTemplate.slice(0, 60)}...${titleTemplate.endsWith('"') ? '"' : ''}`
} }
titleTemplate += ` | ${t('app_name')}` if (titleTemplate.length)
titleTemplate += ' | '
titleTemplate += t('app_name')
if (buildInfo.env !== 'release') if (buildInfo.env !== 'release')
titleTemplate += ` (${buildInfo.env})` titleTemplate += ` (${buildInfo.env})`

View file

@ -1,7 +1,7 @@
import type { Ref } from 'vue' import type { Ref } from 'vue'
export const useSignIn = (input?: Ref<HTMLInputElement | undefined>) => { export const useSignIn = (input?: Ref<HTMLInputElement | undefined>) => {
const singleInstanceServer = useAppConfig().singleInstanceServer const singleInstanceServer = useRuntimeConfig().public.singleInstance
const userSettings = useUserSettings() const userSettings = useUserSettings()
const users = useUsers() const users = useUsers()
const { t } = useI18n() const { t } = useI18n()

View file

@ -1,3 +1,4 @@
import type { Editor } from '@tiptap/vue-3'
import { Extension, useEditor } from '@tiptap/vue-3' import { Extension, useEditor } from '@tiptap/vue-3'
import Placeholder from '@tiptap/extension-placeholder' import Placeholder from '@tiptap/extension-placeholder'
import Document from '@tiptap/extension-document' import Document from '@tiptap/extension-document'
@ -27,6 +28,9 @@ export interface UseTiptapOptions {
} }
export function useTiptap(options: UseTiptapOptions) { export function useTiptap(options: UseTiptapOptions) {
if (process.server)
return { editor: ref<Editor | undefined>() }
const { const {
autofocus, autofocus,
content, content,

View file

@ -16,7 +16,9 @@ export { Emoji }
export type CustomEmoji = (mastodon.v1.CustomEmoji & { custom: true }) export type CustomEmoji = (mastodon.v1.CustomEmoji & { custom: true })
export const isCustomEmoji = (emoji: CustomEmoji | Emoji): emoji is CustomEmoji => !!(emoji as CustomEmoji).custom export const isCustomEmoji = (emoji: CustomEmoji | Emoji): emoji is CustomEmoji => !!(emoji as CustomEmoji).custom
export const TiptapMentionSuggestion: Partial<SuggestionOptions> = { export const TiptapMentionSuggestion: Partial<SuggestionOptions> = process.server
? {}
: {
pluginKey: new PluginKey('mention'), pluginKey: new PluginKey('mention'),
char: '@', char: '@',
async items({ query }) { async items({ query }) {
@ -52,7 +54,7 @@ export const TiptapEmojiSuggestion: Partial<SuggestionOptions> = {
pluginKey: new PluginKey('emoji'), pluginKey: new PluginKey('emoji'),
char: ':', char: ':',
async items({ query }): Promise<(CustomEmoji | Emoji)[]> { async items({ query }): Promise<(CustomEmoji | Emoji)[]> {
if (query.length === 0) if (process.server || query.length === 0)
return [] return []
if (currentCustomEmojis.value.emojis.length === 0) if (currentCustomEmojis.value.emojis.length === 0)

View file

@ -19,7 +19,7 @@ import { useAsyncIDBKeyval } from '~/composables/idb'
const mock = process.mock const mock = process.mock
const initializeUsers = async (): Promise<Ref<UserLogin[]> | RemovableRef<UserLogin[]>> => { const initializeUsers = (): Promise<Ref<UserLogin[]> | RemovableRef<UserLogin[]>> | Ref<UserLogin[]> | RemovableRef<UserLogin[]> => {
let defaultUsers = mock ? [mock.user] : [] let defaultUsers = mock ? [mock.user] : []
// Backward compatibility with localStorage // Backward compatibility with localStorage
@ -34,7 +34,7 @@ const initializeUsers = async (): Promise<Ref<UserLogin[]> | RemovableRef<UserLo
const users = process.server const users = process.server
? ref<UserLogin[]>(defaultUsers) ? ref<UserLogin[]>(defaultUsers)
: await useAsyncIDBKeyval<UserLogin[]>(STORAGE_KEY_USERS, defaultUsers, { deep: true }) : useAsyncIDBKeyval<UserLogin[]>(STORAGE_KEY_USERS, defaultUsers, { deep: true })
if (removeUsersOnLocalStorage) if (removeUsersOnLocalStorage)
globalThis.localStorage.removeItem(STORAGE_KEY_USERS) globalThis.localStorage.removeItem(STORAGE_KEY_USERS)
@ -42,7 +42,7 @@ const initializeUsers = async (): Promise<Ref<UserLogin[]> | RemovableRef<UserLo
return users return users
} }
const users = await initializeUsers() const users = process.server ? initializeUsers() as Ref<UserLogin[]> | RemovableRef<UserLogin[]> : await initializeUsers()
const nodes = useLocalStorage<Record<string, any>>(STORAGE_KEY_NODES, {}, { deep: true }) const nodes = useLocalStorage<Record<string, any>>(STORAGE_KEY_NODES, {}, { deep: true })
const currentUserHandle = useLocalStorage<string>(STORAGE_KEY_CURRENT_USER_HANDLE, mock ? mock.user.account.id : '') const currentUserHandle = useLocalStorage<string>(STORAGE_KEY_CURRENT_USER_HANDLE, mock ? mock.user.account.id : '')
export const instanceStorage = useLocalStorage<Record<string, mastodon.v1.Instance>>(STORAGE_KEY_SERVERS, mock ? mock.server : {}, { deep: true }) export const instanceStorage = useLocalStorage<Record<string, mastodon.v1.Instance>>(STORAGE_KEY_SERVERS, mock ? mock.server : {}, { deep: true })

View file

@ -4,7 +4,7 @@ export function useWebShareTarget(listener?: (message: MessageEvent) => void) {
onBeforeMount(() => { onBeforeMount(() => {
// PWA must be installed to use share target // PWA must be installed to use share target
if (useNuxtApp().$pwa.isInstalled && 'serviceWorker' in navigator) { if (useNuxtApp().$pwa?.isInstalled && 'serviceWorker' in navigator) {
if (listener) if (listener)
navigator.serviceWorker.addEventListener('message', listener) navigator.serviceWorker.addEventListener('message', listener)

View file

@ -31,11 +31,12 @@ const git = Git()
export const getGitInfo = async () => { export const getGitInfo = async () => {
const branch = gitBranch || await git.revparse(['--abbrev-ref', 'HEAD']) const branch = gitBranch || await git.revparse(['--abbrev-ref', 'HEAD'])
const commit = await git.revparse(['HEAD']) const commit = await git.revparse(['HEAD'])
return { branch, commit } const shortCommit = await git.revparse(['--short=7', 'HEAD'])
return { branch, commit, shortCommit }
} }
export const getEnv = async () => { export const getEnv = async () => {
const { commit, branch } = await getGitInfo() const { commit, shortCommit, branch } = await getGitInfo()
const env = isDevelopment const env = isDevelopment
? 'dev' ? 'dev'
: isPreview : isPreview
@ -43,5 +44,5 @@ export const getEnv = async () => {
: branch === 'main' : branch === 'main'
? 'canary' ? 'canary'
: 'release' : 'release'
return { commit, branch, env } as const return { commit, shortCommit, branch, env } as const
} }

View file

@ -33,6 +33,13 @@ const countryLocaleVariants: Record<string, LocaleObjectData[]> = {
{ code: 'en-US', name: 'English (US)' }, { code: 'en-US', name: 'English (US)' },
{ code: 'en-GB', name: 'English (UK)' }, { code: 'en-GB', name: 'English (UK)' },
], ],
ca: [
// { code: 'ca-AD', name: 'Català (Andorra)' },
{ code: 'ca-ES', name: 'Català (Espanya)' },
{ code: 'ca-valencia', name: 'Català (valencià)' },
// { code: 'ca-FR', name: 'Català (França)' },
// { code: 'ca-IT', name: 'Català (Itàlia)' },
],
es: [ es: [
// { code: 'es-AR', name: 'Español (Argentina)' }, // { code: 'es-AR', name: 'Español (Argentina)' },
// { code: 'es-BO', name: 'Español (Bolivia)' }, // { code: 'es-BO', name: 'Español (Bolivia)' },
@ -74,6 +81,11 @@ const locales: LocaleObjectData[] = [
return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name] return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name]
}, },
} satisfies LocaleObjectData), } satisfies LocaleObjectData),
{
code: 'ca',
file: 'ca.json',
name: 'Català',
},
{ {
code: 'de-DE', code: 'de-DE',
file: 'de-DE.json', file: 'de-DE.json',
@ -104,6 +116,11 @@ const locales: LocaleObjectData[] = [
file: 'es.json', file: 'es.json',
name: 'Español', name: 'Español',
}, },
{
code: 'eu-ES',
file: 'eu-ES.json',
name: 'Euskara',
},
{ {
code: 'fr-FR', code: 'fr-FR',
file: 'fr-FR.json', file: 'fr-FR.json',
@ -198,7 +215,7 @@ const buildLocales = () => {
return useLocales.sort((a, b) => a.code.localeCompare(b.code)) return useLocales.sort((a, b) => a.code.localeCompare(b.code))
} }
const currentLocales = buildLocales() export const currentLocales = buildLocales()
const datetimeFormats = Object.values(currentLocales).reduce((acc, data) => { const datetimeFormats = Object.values(currentLocales).reduce((acc, data) => {
const dateTimeFormats = data.dateTimeFormats const dateTimeFormats = data.dateTimeFormats

View file

@ -3,6 +3,8 @@ export const APP_NAME = 'Elk'
export const DEFAULT_POST_CHARS_LIMIT = 500 export const DEFAULT_POST_CHARS_LIMIT = 500
export const DEFAULT_FONT_SIZE = '15px' export const DEFAULT_FONT_SIZE = '15px'
export const ELK_PAGE_LIFECYCLE_FROZEN = 'elk-frozen'
export const STORAGE_KEY_DRAFTS = 'elk-drafts' export const STORAGE_KEY_DRAFTS = 'elk-drafts'
export const STORAGE_KEY_USERS = 'elk-users' export const STORAGE_KEY_USERS = 'elk-users'
export const STORAGE_KEY_SERVERS = 'elk-servers' export const STORAGE_KEY_SERVERS = 'elk-servers'

View file

@ -3,5 +3,10 @@ services:
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
volumes:
# make sure this directory has the same ownership as the elk user from the Dockerfile
# otherwise Elk will not be able to store configs for accounts
# e.q. mkdir ./elk-storage; sudo chown 911:911 ./elk-storage
- './elk-storage:/elk/data'
ports: ports:
- 5314:5314 - 5314:5314

1
docs/.gitignore vendored
View file

@ -10,3 +10,4 @@ dist
sw.* sw.*
.env .env
.output .output
translation-status.json

101
docs/README.md Normal file
View file

@ -0,0 +1,101 @@
# Welcome to the Elk Docs
The documentation site publishes to https://docs.elk.zone.
We use [Docus](https://docus.dev) as the site generator and deploy through Netlify.
## Quickstart
### Prerequisites
- Github account
- Git installed on your machine
- Node >14.18 installed
- Use `node -v` to see which version you have installed.
- Use `nvm install node` to upgrade to the latest version.
- Refer to the [nvm docs](https://github.com/nvm-sh/nvm#installing-and-updating) for information on installing `nvm`.
- [pnpm](https://pnpm.io/installation) installed
### Install and Preview
1. [Fork the Elk Github project](https://github.com/elk-zone/elk/fork) into your own account
2. Clone your fork to your local machine
3. From your terminal, `cd` to the directory you cloned into
4. `cd docs` to enter the docs folder
5. Run `npm install`
Note: Run this from the project's `docs` folder, not the root of the repository on your machine!
6. Run `npm run dev` to launch a preview
7. Visit `localhost:3000/docs/` to see a live preview of the docs
### Contributing
When you are ready to submit work back to the main Elk repo, create a PR.
1. If it has been a bit, synchronize your fork with the upstream repo on Github.
2. Do your work in a branch on your fork
Use `git checkout -b branchNameToUse` to create a working branch separate from `main`.
3. Do your work in your preferred editor
4. Commit changes often and write meaningful commits
5. Push the changes from your local machine to your fork on Github
6. Go to your fork of the Elk project in your Github account
7. Select the **Pull Request** tab
8. Select **New Pull Request**
9. Confirm the repo/branches to compare
- Base repo should be **elk-zone/elk**
- base branch should be **main**
- Head repository should be your fork
- Compare branch should be your working branch you want to submit
If you don't see four drop downs, be sure you are comparing across forks.
10. Add a description of the changes your request makes
11. Select **Add Pull Request**
Other team members will review your PR and make comments or suggestions through the PR.
You can continue making additional changes and/or apply feedback by making additional commits to the branch on your fork.
ALWAYS WORK IN YOUR FORK/BRANCH.
## Writing
### Tips
- Docs are in the `docs/content` folder
- Write in standard markdown
- Refer to the Docus [writing pages](https://docus.dev/introduction/writing-pages) guide
- Docus provides additional [components](https://docus.dev/api/components) to extend basic markdown
Avoid screenshots until Elk reaches a stable release.
### Standards
Write in **American English** using spelling as found in [Merriam Webster](https://www.merriam-webster.com).
Translation and localization will be handled separately as/when availability or necessity allow.
Use [**semantic linefeeds**](https://rhodesmill.org/brandon/2012/one-sentence-per-line/) with no more than one sentence per line.
To create paragraphs, use a blank line.
There are no house style rules currently.
When we add any, they will be found in this document.
#### Style Guides
Use the first guide that mentions a usable standard from the order below:
1. Refer to the U.S. Government's [Federal Plain Language Guidelines](https://www.plainlanguage.gov/guidelines/) as a base standard.
2. For user interface, device, and other technical guidance, refer to [**Google's Developer Style Guide**](https://developers.google.com/style).
3. As a secondary reference to the Google guide, refer to [Microsoft's Style Guide](https://docs.microsoft.com/style-guide/welcome/), then the [Chicago Manual of Style, 17th Edition](https://www.chicagomanualofstyle.org/home.html).
We use [Merriam-Webster](https://www.merriam-webster.com/) as the standard dictionary for spelling.
### Images
Place image files in the /docs/public/images folder.
You can create subfolders to organize the images.
To add an image to a doc, use standard markdown:
```md
[![Label](/docs/images/image.svg)](URL.for.hyperlink)
[![StackBlitz](/docs/images/stackblitz.svg)](https://stackblitz.com/)
```
## In-house Styles
None yet defined

View file

@ -6,6 +6,11 @@ export default defineAppConfig({
socials: { socials: {
twitter: 'elk_zone', twitter: 'elk_zone',
github: 'elk-zone/elk', github: 'elk-zone/elk',
mastodon: {
label: 'Mastodon',
icon: 'IconMastodon',
href: 'https://elk.zone/@elk@webtoo.ls',
},
}, },
aside: { aside: {
level: 0, level: 0,

View file

@ -0,0 +1,15 @@
<script>
export default {
name: 'ClipboardIcon',
props: { copy: Boolean },
}
</script>
<template>
<svg v-if="copy" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="currentColor" d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-7 0a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1M7 7h10V5h2v14H5V5h2v2Z" />
</svg>
<svg v-else xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="currentColor" d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-7 0a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1M7 7h10V5h2v14H5V5h2v2m.5 6.5L9 12l2 2l4.5-4.5L17 11l-6 6l-3.5-3.5Z" />
</svg>
</template>

View file

@ -0,0 +1,15 @@
<script>
export default {
name: 'ToogleIcon',
props: { up: Boolean },
}
</script>
<template>
<svg v-if="up" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="m12 10.828l-4.95 4.95l-1.414-1.414L12 8l6.364 6.364l-1.414 1.414z" />
</svg>
<svg v-else xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="m12 13.172l4.95-4.95l1.414 1.414L12 16L5.636 9.636L7.05 8.222z" />
</svg>
</template>

View file

@ -0,0 +1,343 @@
<script setup lang="ts">
import type { TranslationStatus } from '../../types'
const localesStatuses: TranslationStatus = await import('../../translation-status.json').then(m => m.default)
const totalReference = localesStatuses.en.total
type Tab = 'missing' | 'outdated'
const hidden = ref(true)
const locale = ref()
const localeTab = ref<Tab>('missing')
const copied = ref(false)
const currentLocale = computed(() => {
if (hidden.value || !locale.value)
return undefined
return localesStatuses as Record<string, any>
})
const localeTitle = computed(() => {
if (hidden.value || !locale.value)
return undefined
return localeTab.value === 'missing'
? `Missing keys in ${locale.value.file}`
: `Outdated keys in ${locale.value.file}`
})
const missingEntries = computed<string[]>(() => {
if (hidden.value || !currentLocale.value || localeTab.value !== 'missing')
return []
return localesStatuses[locale.value].missing
})
const outdatedEntries = computed<string[]>(() => {
if (hidden.value || !currentLocale.value || localeTab.value !== 'outdated')
return []
return localesStatuses[locale.value]!.outdated
})
const showDetail = (key: string, tab: Tab = 'missing', fromTab = false) => {
if (key === locale.value && tab === localeTab.value) {
if (fromTab)
return
nextTick().then(() => hidden.value = !hidden.value)
return
}
locale.value = key
localeTab.value = tab
nextTick().then(() => hidden.value = false)
}
const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText([
`# ${localeTitle.value}`,
(localeTab.value === 'missing' ? missingEntries.value : outdatedEntries.value).join('\n')].join('\n'),
)
copied.value = true
setTimeout(() => copied.value = false, 750)
}
catch {}
}
</script>
<template>
<div>
<table class="w-full">
<caption>
<div>You can see the detail (missing and outdated keys) by clicking on the corresponding row.</div>
<div>
If you want to send a PR, click on <strong>Edit</strong> link on the corresponding translation file, it will open <strong>Codeflow</strong>:
<NuxtLink
class="inline"
target="_blank"
href="https://developer.stackblitz.com/codeflow/working-in-codeflow-ide#making-a-pr-with-codeflow-ide"
title="How to make a PR with Codeflow IDE (opens in new window)"
>
read the following guide
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h7v2H5v14h14v-7h2v7q0 .825-.587 1.413Q19.825 21 19 21Zm4.7-5.3l-1.4-1.4L17.6 5H14V3h7v7h-2V6.4Z" />
</svg>
</NuxtLink>
</div>
</caption>
<thead>
<tr>
<th>Language</th>
<th title="Keys correctly translated">
Translated
</th>
<th title="Keys missing from source which need translation for the language">
Missing
</th>
<th title="Keys which could be safely removed">
Outdated
</th>
<th>Total</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<template v-for="({ title, file, translated, missing, outdated, total, isSource }, key) in localesStatuses" :key="key">
<tr
v-if="totalReference > 0"
:class="[{ expandable: !isSource }]"
:title="!isSource ? 'Click to show detail' : undefined"
@click="!isSource && showDetail(key, 'missing')"
>
<td :class="[{ expandable: !isSource }]">
<div>
<ToogleIcon v-if="!isSource" :up="hidden || key !== locale" />
{{ title }}
</div>
</td>
<template v-if="isSource">
<td colspan="5" class="source-text">
<div>
{{ total }} keys as source
</div>
</td>
</template>
<template v-else>
<td>
<strong>{{ `${translated?.length ?? 0}` }}</strong> {{ `(${(100 * (translated?.length ?? 0) / totalReference).toFixed(1)}%)` }}
</td>
<td>
<strong>{{ `${missing?.length ?? 0}` }}</strong> {{ `(${(100 * (missing?.length ?? 0) / totalReference).toFixed(1)}%)` }}
</td>
<td>
<strong>{{ `${outdated?.length ?? 0}` }}</strong> {{ `(${(100 * (outdated?.length ?? 0) / totalReference).toFixed(1)}%)` }}
</td>
<td><strong>{{ `${total}` }}</strong></td>
<td>
<NuxtLink
v-if="outdated.length > 0 || missing.length > 0"
:href="`https://pr.new/github.com/elk-zone/elk/tree/main/locales/${file}`"
target="_blank"
class="codeflow"
title="Raise a PR with Codeflow (opens in new window)"
@click.stop
>
Edit
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h7v2H5v14h14v-7h2v7q0 .825-.587 1.413Q19.825 21 19 21Zm4.7-5.3l-1.4-1.4L17.6 5H14V3h7v7h-2V6.4Z" />
</svg>
</NuxtLink>
</td>
</template>
</tr>
<template v-if="key === locale && !hidden">
<tr>
<td colspan="6">
<div class="detail">
<header>
<h2 class="tabs">
<button
:class="localeTab === 'missing' ? 'current' : null"
@click="showDetail(key, 'missing', true)"
>
Missing keys
</button>
<button
:class="localeTab === 'outdated' ? 'current' : null"
@click="showDetail(key, 'outdated', true)"
>
Outdated keys
</button>
</h2>
</header>
<ul v-if="localeTab === 'missing'">
<li v-for="entry in missingEntries" :key="entry">
<pre>{{ entry }}</pre>
</li>
</ul>
<ul v-else>
<li v-for="entry in outdatedEntries" :key="entry">
<pre>{{ entry }}</pre>
</li>
</ul>
<button @click="copyToClipboard()">
<ClipboardIcon :copy="!copied" />
Copy to clipboard
</button>
</div>
</td>
</tr>
</template>
</template>
</tbody>
</table>
</div>
</template>
<style scoped>
table {
font-size: 0.9rem;
width: 100%;
border-collapse: collapse;
border: 1px solid #ccc;
}
pre {
font-size: 0.75rem;
}
caption {
padding: 0.3rem;
background: #eee;
border: 1px solid #ccc;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border-bottom: none;
}
caption a {
text-decoration: underline;
}
th {
text-align: left;
border-bottom: 1px solid #ccc;
padding: 0.5rem;
}
th:not(:first-of-type),
td:not(:first-of-type) {
border-left: 1px solid #eee;
}
td {
padding: 0.5rem;
}
tr.expandable td:first-of-type {
padding-left: 4px;
}
tr.expandable, tr.expandable td {
cursor: pointer;
}
a.codeflow,
a.inline,
td.expandable div {
display: flex;
align-items: center;
flex-direction: row;
column-gap: 4px;
}
td.expandable > svg {
color: currentColor;
}
tbody tr td {
border-bottom: 1px solid #eee;
}
th[title] {
text-decoration: underline dotted white;
}
.source-text {
text-align: center;
font-weight: bold;
padding: 10px 0;
text-transform: uppercase;
}
.detail {
border: 1px solid #ccc;
border-radius: 3px;
}
.detail header {
padding: 0 0.3rem;
display: flex;
background: #eee;
justify-content: space-between;
align-items: center;
}
.detail header h2 button {
font-weight: bold;
padding: 0.5rem;
background-color: #eee;
}
.detail header .tabs button.current {
background-color: white;
}
.detail header .tabs + .heading-buttons {
display: flex;
flex-direction: row;
column-gap: 0.4rem;
align-items: center;
}
.detail ul {
padding: 0.3rem 0.5rem;
max-height: 250px;
min-height: 250px;
overflow-y: auto;
border-bottom: 1px solid #eee;
}
.detail > button {
display: flex;
/*justify-content: space-between;*/
align-items: center;
column-gap: 0.3rem;
padding: 0.3rem 0.5rem;
background: transparent;
cursor: pointer;
border: 1px solid #ccc;
border-radius: 3px;
margin: 0.5rem;
}
@media (prefers-color-scheme: dark) {
.detail header {
background: #333;
color: #fff;
}
.detail header h2 button {
background-color: #333;
color: #fff;
}
.detail header .tabs button.current {
background-color: white;
color: #333;
}
table caption {
background: #333;
color: #fff;
}
}
</style>

View file

@ -2,25 +2,98 @@
## What is Elk? ## What is Elk?
::alert{type=warning} Elk is an alternative way to access your Mastodon account from your browser.
🚧 This section is a work in progress. 🚧
:: Through the Mastodon API, Elk provides similar access to posts and actions on posts you expect to be able to do to Mastodon.
You can compose a post (Toot, if you like), boost others' toots, like, bookmark, and scroll just as you would through your regular server site.
Why Elk, then?
Elk provides some features not available through the standard Mastodon web app interface.
- Notifications for the same post combine when they are sequential. No more seeing your same viral joke multiple times in the Notification feed for each like and boost. Now you can see it just one, with a list of who liked or boosted the post just above the post itself.
- You control whether you see boost, like, and follower accounts - all separately!
- See a preview of the profile when someone follows you.
You can use Elk right in your browser.
On a mobile device, you can install the app to your home screen right from your browser for easy access.
(This is called a Progressive Web App, or [PWA](../80.pwa.md).)
Want to try it out?
Visit https://elk.zone, type in your Mastodon server address, then log in.
Or, visit one of the other Elk installs in the [Elk ecosystem](https://github.com/elk-zone/elk#ecosystem).
## What is Mastodon? ## What is Mastodon?
<!-- -> external links -->
::alert{type=warning} Mastodon is a decentralized microblogging platform.
🚧 This section is a work in progress. 🚧 Mastodon uses the [ActivityPub protocol](https://www.w3.org/TR/activitypub/) to share posts and actions between users within or across different instance servers.
::
You can think of the service as something similar to Twitter, but with some key differences.
- Where Twitter is a single site that everyone who uses it belongs to, Mastodon is a platform used by many different sites.
- Where Twitter is a corporation with profit goals and advertisements, Mastodon is a free and open source software tool maintained by volunteers and paid for by patrons and voluntary contributors to their own instance servers and back to the main software project.
- Where Twitter users can only interact with other Twitter users, Mastodon users can talk to users on many other ActivityPub platforms, including services that provide video (PeerTube), photos ([PixelFed](https://pixelfed.org/)), blogs ([WriteFreely](https://writefreely.org/)), and alternative microblogging platforms ([Pleroma](https://pleroma.social/), [Hometown](https://github.com/hometown-fork/hometown), [GoToSocial](https://gotosocial.org/), and their derivatives).
For more details about Mastodon, see their [website](https://joinmastodon.org/), [blog](https://blog.joinmastodon.org), [docs](https://docs.joinmastodon.org), and [GitHub Repository](https://github.com/mastodon/mastodon).
## What is a Mastodon Client? ## What is a Mastodon Client?
<!-- -> other examples --> A Mastodon Client is software that serves up the posts and activities from a Mastodon server using the [Mastodon API](https://docs.joinmastodon.org/api/).
When you visit or sign in to a Mastodon server, you use the standard Mastodon client or the alternative Advanced Web Interface (that provides a multi-column format).
::alert{type=warning} A Mastodon client performs similar functions as the standard web interfaces.
🚧 This section is a work in progress. 🚧 Using a client, you can
- View posts from accounts you follow
- Boost, like, or bookmark posts
- Create new posts from your own account
- Explore the home, local, and federated timelines
- See what is trending on your server
- View, add, or participate in polls
- Follow, unfollow, mute, and block accounts
::alert{type="info"}
**Note:** Not all clients provide all features.
:: ::
### Installed Mastodon Clients
You may be most familiar with Mastodon Clients through your phone or tablet.
The app you download from an app store and install on your device to access your Mastodon account is a Mastodon Client.
::card{icon="logos:android-icon"}
#title
Android
#description
[Fedilab](https://fedilab.app/), [Tusky](https://tusky.app/), or [Tooot](https://tooot.app/), among others.
::
::card{icon="logos:apple"}
#title
Apple
#description
[Ivory](https://tapbots.com/ivory/), [Ice Cubes](https://apps.apple.com/us/app/ice-cubes-for-mastodon/id6444915884), [Metatext](https://github.com/metabolist/metatext), or [Toot](https://apps.apple.com/app/toot/id1229021451?ls=1)
::
::card{icon="mdi:desktop-classic"}
#title
Desktop
#description
[TheDesk](https://thedesk.top/en/), [Whalebird](https://whalebird.social/en), or [Sengi](https://nicolasconstant.github.io/sengi/)
::
All of these apps provide some combination of the features a typical Mastodon user expects for their account.
### Browser-based Mastodon Clients
Elk is a Mastodon Client, but instead of being an app to install on your phone, tablet, or desktop, it is an alternative web site you visit in a browser.
In addition to Elk, there are other browser-based alternative Mastodon clients.
Some include:
- [Semaphore](https://semaphore.social/), a continuation of the now-frozen [Pinafore](https://pinafore.social/)
- [Sengi](https://nicolasconstant.github.io/sengi/) (Also available as a desktop client)
- [Cuckoo](https://cuckoo.social)
## Sponsors ## Sponsors
We want to thank the generous sponsoring and help of: We want to thank the generous sponsoring and help of:

View file

@ -6,9 +6,77 @@
<!-- once the UI is more stable, we can add screenshots --> <!-- once the UI is more stable, we can add screenshots -->
## Multiple accounts
Elk supports logging into multiple Mastodon accounts.
Select the three vertical dot menu button next to your profile picture on a desktop or your profile picture directly on mobile devices, then select **Add an existing account**.
Switch between your accounts quickly:
- On the desktop, select the profile picture for the account to use.
- On mobile devices or smaller screens, tap your profile picture to see a list of signed-in accounts, then select the account to use.
## Rich text
Elk renders basic Markdown-like text markup in post texts as the expected HTML.
- Use one asterisk (`*`) before and after a word or phrase to *italicize** the text.
- Surround the text with two asterisks (`**word**`) to **bold** it.
::alert{type="warning"}
Many apps do not support Markdown in posts.
Mastodon itself does not support Markdown in posts.
Users who see your posts from other apps or the Mastodon web app see standard, unrendered Markdown text.
::
## Code blocks
Elk supports adding code blocks to your posts.
When writing the post, select the editor tools and then Code Block to add a block to your post.
Paste or type in the contents.
Once you've added the block, you can select the language to use for code highlighting.
Hover over the right side of the code block to see the drop down box, then select the dropdown to see the full list of supported languages.
Options include:
* plain
* c
* cpp
* csharp
* css
* dart
* go
* html
* java
* javascript
* jsx
* kotlin
* python
* rust
* svelte
* swift
* tsx
* typescript
* vue
## Connecting linked posts in the timeline
Elk reorders timeline posts that are connected to each other to display them in a connected thread in the timelines.
This helps keep the conversation context for a post and its replies.
## Improved notifications
Elk groups boosts and likes to the same post when they occur together in the notification history.
Rather than displaying each like and boost with the post contents as in other apps, Elk displays the post one time with a list of the users who liked or boosted the post.
This greatly simplifies your notification history.
<!-- ## GitHub HTML cards -->
<!-- - markdown support <!-- - markdown support
- code blocks
- reordering and connecting posts in timelines
- multi account
- GitHub HTML cards - GitHub HTML cards
- and so on... --> - and so on... -->

View file

@ -34,6 +34,10 @@ Elk uses [Vitest](https://vitest.dev). You can run the test suite with:
nr test nr test
``` ```
## Translation status
<TranslationState />
# Stack # Stack
- [Vite](https://vitejs.dev/) - Next Generation Frontend Tooling - [Vite](https://vitejs.dev/) - Next Generation Frontend Tooling
@ -46,4 +50,4 @@ nr test
- [Iconify](https://github.com/iconify/icon-sets#iconify-icon-sets-in-json-format) - Iconify icon sets in JSON format - [Iconify](https://github.com/iconify/icon-sets#iconify-icon-sets-in-json-format) - Iconify icon sets in JSON format
- [Masto.js](https://neet.github.io/masto.js) - Mastodon API client in TypeScript - [Masto.js](https://neet.github.io/masto.js) - Mastodon API client in TypeScript
- [shiki](https://shiki.matsu.io/) - A beautiful Syntax Highlighter - [shiki](https://shiki.matsu.io/) - A beautiful Syntax Highlighter
- [vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa) - Prompt for update and push notifications - [vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa) - Prompt for update, Web Push Notifications and Web Share Target API

View file

@ -45,7 +45,7 @@ There are 5 environment variables to add.
| NUXT_CLOUDFLARE_NAMESPACE_ID | This is your CloudFlare KV Namespace ID. You can find it in "Workers > KV". | | NUXT_CLOUDFLARE_NAMESPACE_ID | This is your CloudFlare KV Namespace ID. You can find it in "Workers > KV". |
| NUXT_STORAGE_DRIVER | Because we're using CloudFlare, we'll need to set this to `cloudflare`. | | NUXT_STORAGE_DRIVER | Because we're using CloudFlare, we'll need to set this to `cloudflare`. |
| NUXT_PUBLIC_DEFAULT_SERVER | This is the address of the Mastodon instance that will show up when a user visits your Elk deployment and is not logged in. If you don't make that variable, it will point to `m.webtoo.ls` by default. | | NUXT_PUBLIC_DEFAULT_SERVER | This is the address of the Mastodon instance that will show up when a user visits your Elk deployment and is not logged in. If you don't make that variable, it will point to `m.webtoo.ls` by default. |
| SINGLE_INSTANCE_SERVER | This can't be set at runtime, but if enabled at build-time it will disable signing in to servers other than the server specified in `NUXT_PUBLIC_DEFAULT_SERVER` | | NUXT_PUBLIC_SINGLE_INSTANCE | If enabled it will disable signing in to servers other than the server specified in `NUXT_PUBLIC_DEFAULT_SERVER` |
| NUXT_PUBLIC_PRIVACY_POLICY_URL | This is the URL to a web page with information on your privacy policy. | | NUXT_PUBLIC_PRIVACY_POLICY_URL | This is the URL to a web page with information on your privacy policy. |
That's it! All that's left to do is... That's it! All that's left to do is...

177
docs/content/80.pwa.md Normal file
View file

@ -0,0 +1,177 @@
# PWA
Elk provides a PWA (Progressive Web App) that can be installed on your desktop/device. This allows you to use Elk as a native app on your device, and it will work offline.
The main goal of the PWA is to provide a native app experience on mobile devices with Web Push Notifications and Web Share Target API capabilities.
Web Share Target will allow you to share content from other apps to Elk in mobile browsers.
## Mobile Support
Some mobile browsers will allow you to install the PWA as a native app, and some others will only allow you to add the PWA to your home screen.
If you're using a mobile browser that supports the PWA installation, you will see a banner at the bottom of the screen with the option to install the PWA.
If the browser also supports [beforeinstallprompt](https://web.dev/customize-install/) event, ElK will show a prompt (on the right aside on wide screens, or at top on small screens) to allow you to install the PWA directly.
If the browser doesn't support the PWA installation, check following entries:
### Safari iOS
Right now, you cannot use Web Push Notifications on Safari mobile browsers, since it doesn't support the Web Push API yet at operating system level.
You can check the status of the Push API on Safari mobile browsers on [this page](https://caniuse.com/notifications).
Visit also [this site](https://firt.dev/notes/pwa-ios/) to check the status of PWA Capabilities on Safari mobile browsers.
To install a Progressive Web App (PWA) on Safari, follow these steps:
- Tap the "Share" icon at the bottom of the screen.
- Scroll down and tap "Add to Home Screen".
- Customize the name of the app (if desired) and tap "Add".
- The PWA should now appear on your home screen.
### Other browsers on iOS
Browsers on iOS other than Safari (like Chrome) are very limited on PWA functionalities. Besides Web Push Notifications (like in Safari) not even PWA installation is supported.
If you are using one of these browsers and want to add Elk to your homescreen, you will have to use Safari and follow the instructions above.
### Firefox
To install a Progressive Web App (PWA) on Firefox, follow these steps:
- Look for the install button or icon, usually located in the address bar or in a prompt that appears on the screen.
- Click on the install button or icon and follow the prompts to complete the installation.
- If you don't see an install button or icon, you can look for the PWA in the Firefox menu. Click on the three horizontal lines in the top/bottom right corner of the browser window to open the menu, then click on "Install" option.
### Chromium based browsers
To install a Progressive Web App (PWA) on Chromium based browsers, such as Google Chrome, Microsoft Edge, or Brave, follow these steps:
- Click on the menu button (three vertical dots) at the top right corner of the screen.
- Select "Install Elk as app" or "Install Elk to Home screen".
- Customize the name of the app (if desired) and click "Install" or "Add".
- The PWA should now appear on your device's home screen or in your app drawer.
## FAQ
This FAQ is related to the PWA application on Elk, if you can't find the answer to your problem here, you can [file an issue on Elk's repository in GitHub](https://github.com/elk-zone/elk).
### Could not subscribe to push notifications
In Elk application, you can only have one push subscription per backend server, so if you have already subscribed to push notifications in another account with different backend, you will not be able to subscribe to push notifications. If that's the case, you will need to switch to the account which is subscribed to push notifications and unsubscribe it.
This is a limitation of any browser, and it's not related only to Elk application.
If you deleted any account from your backend with push notification subscription enabled in Elk, you will need to remove the push notification subscriptions from your browser:
- [Chrome - Web and Android](https://www.connecto.io/kb/knwbase/how-to-unsubscribe-from-chrome-notifications-on-web-and-android/).
- Firefox:
- Open settings
- Click on "Privacy & Security" menu entry
- Scroll down in the page and locate "Permissions > Notifications"
- Click on "Configuration" button
- Locate "Elk" website in the list and select it, then click on "Remove website" button
- Safari (WIP)
### Permission denied: enable notifications in your browser
Before enabled push notifications in your browser, you need to enable notifications, Elk will instruct the browser to show a notification permission prompt.
If you cannot see the notification permission prompt, you will need to remove push notification subscriptions, check previous entry.
### Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.
If you're receiving this error, it means that your browser doesn't support the VAPID protocol, and you will not be able to receive push notifications.
There is nothing we can do to fix this, you will need to use a browser that supports the VAPID protocol: Chrome, Edge, Safari and Firefox.
### Install Elk button not working
There are browsers that allows applications to provide a custom PWA installation prompt, but there are also browsers with that behavior broken, for example Arc browser.
There is nothing we can do to fix this problem, you will need to check browser documentation to install the PWA.
## PWA Configuration in Elk project
By default, Elk will enable the PWA integration, but can be disabled by setting `VITE_DEV_PWA` to `false` in your `.env` file.
You can find the configuration options for the PWA in the [config/pwa.ts](https://github.com/elk-zone/elk/blob/main/config/pwa.ts) module.
### PWA Web Manifest
Right now, there is no support for web manifest internationalization and theme color in any browser, we're using a custom module to generate web manifests for theme color and language variants.
Elk will generate 2 web manifests per locale, one for light theme and one for dark theme.
You can check web manifest generation on [modules/pwa/i18n.ts](https://github.com/elk-zone/elk/blob/main/modules/pwa/i18n.ts) module.
### PWA UI Components
Elk will provide a set of UI components to allow you to customize the PWA installation prompt on browsers with [beforeinstallprompt](https://web.dev/customize-install/) support.
You can find the PWA installation prompt in the [PwaInstallPrompt](https://github.com/elk-zone/elk/blob/main/components/pwa/PwaInstallPrompt.client.vue) component: will be shown on the right aside on wide screens, or at top on small screens.
Elk is using prompt for update strategy, so the PWA prompt for update will be shown only if there is a new version of Elk available.
On small screens, the PWA prompt for update will be shown as a button on the head, you can find it in [PwaBadge](https://github.com/elk-zone/elk/blob/main/components/pwa/PwaBadge.client.vue) component.
On wide screens, the PWA prompt for update will be shown as a prompt on the right aside, you can find it in [PwaPrompt](https://github.com/elk-zone/elk/blob/main/components/pwa/PwaPrompt.client.vue) component.
PWA prompt for update will take preference over PWA installation prompt, so if there is a new version of Elk available, the PWA prompt for update will be shown instead of the PWA installation prompt.
All previous UI components don't have any logic, all them will use the logic provided by the [PWA plugin](https://github.com/elk-zone/elk/blob/main/plugins/pwa.client.ts).
### Service Worker
You can find Elk custom service worker in [service-worker folder](https://github.com/elk-zone/elk/blob/main/service-worker), it provides:
- [Prompt for update strategy](https://github.com/elk-zone/elk/blob/main/service-worker/sw.ts#L14).
- [Web Push Notifications](https://github.com/elk-zone/elk/blob/main/service-worker/web-push-notifications.ts): [push notifications](https://github.com/elk-zone/elk/blob/main/service-worker/sw.ts#L105) and [push notification click](https://github.com/elk-zone/elk/blob/main/service-worker/sw.ts#L106).
- [Web Share Target API](https://github.com/elk-zone/elk/blob/main/service-worker/share-target.ts): [share target registration](https://github.com/elk-zone/elk/blob/main/service-worker/sw.ts#L107).
### Push Notifications Subscription Logic
Elk will allow you to send push notifications to your users, you can find the logic for Web Push Notifications subscription [composables/push-notifications folder](https://github.com/elk-zone/elk/blob/main/composables/push-notifications).
There is a limitation on browsers about registering multiple Web Push Notifications: you can only subscribe to one push service per browser per application: trying to register multiple push subscriptions will be treated as spam by the browser.
If you try to register multiple push subscriptions, the browser will throw an error, and you will need to unregister the previous push subscription before registering a new one.
### Debugging PWA in development
To debug the PWA in development, you will need to run `dev:pwa` or `dev:mocked:pwa` script.
Running one of previous scripts will start a development server with the PWA enabled: you can review the web manifests and debug the service worker in your browser, use any Chromium based browser, since we're registering the service worker using `type: 'module'`.
You can debug Web Push Notifications in desktop and mobile browser and Web Share Target in your mobile device, using the same URL as the development server.
Right now, we can only debug on Android Chrome mobile browsers:
- Web Push Notifications: you don't need to install de PWA, just enable the notifications in the browser on notifications page or web push notifications settings page.
- Web Share Target: you need to install the PWA, and then you can share content from other apps to Elk.
### Debugging PWA in mobile browsers
To debug the PWA service worker in your mobile browser, you will need to:
1) Enable development options in your Android device:
- Go to "Settings" on your device.
- Scroll down to "About phone" and tap it.
- Locate the "Build number" and tap it repeatedly (usually 7 times) until you see a message saying that you have enabled developer options.
- Go back to "Settings" and you should now see "Developer options" listed.
- Tap on "Developer options" and you can enable various settings such as USB debugging, mock locations, and more.
2) Connect your Android device to your computer using a USB cable.
3) Enable USB debugging in your Android device:
- Go to "Settings" on your device.
- Scroll down to "Developer options" and tap it.
- Enable "USB debugging".
- Confirm the prompt on your device to allow USB debugging.
- Open Chrome/Edge browser in your device.
4) Open Chrome on your computer and go to `chrome://inspect/#devices`.
- Your device should be listed in the "Remote Target" section after a few seconds (if not, open Chrome/Edge and navigate to any page).
- Click the "Port forwarding..." button and type "5314" into the "Port" input and "localhost:5314" into the "IP address and port" input, then press "Done".
- Enter `http://localhost:5314` in the "Open tab with url" input and click the "Open" button.
- Click on the "Inspect" button to open the DevTools.
5) Remember to remove the service worker from your device browser using dev tools once you finish testing the service worker:
- Go to `Application > Storage`, you should check following checkboxes:
- Application: `[x]` Unregister service worker
- Storage: `[x]` Local and session storage `[x]` IndexedDB
- Cache: `[x]` Cache storage and `[x]` Application cache
- Click on Clear site data button
- Go to `Application > Service Workers` and check the current service worker is missing or has the status deleted or reduntant.
6) Disable port forwarding: open the "Port forwarding..." modal again on the `chrome://inspect/#devices` page and either uncheck the "Enable port forwarding" option or remove the entry from the list and click "Done".

200
docs/package-lock.json generated Normal file
View file

@ -0,0 +1,200 @@
{
"name": "elk-docs",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "elk-docs",
"version": "0.1.0",
"devDependencies": {
"@nuxt-themes/docus": "^1.6.1",
"@types/flat": "^5.0.2",
"flat": "^5.0.2",
"flatten": "^1.0.3",
"iso-639-1": "^2.1.15",
"nuxt": "^3.1.1",
"vite-plugin-virtual": "^0.1.1"
}
},
"../node_modules/.pnpm/@nuxt-themes+docus@1.6.3_nuxt@3.1.1/node_modules/@nuxt-themes/docus": {
"version": "1.6.3",
"dev": true,
"dependencies": {
"@nuxt-themes/elements": "^0.5.2",
"@nuxt-themes/tokens": "^1.6.2",
"@nuxt-themes/typography": "^0.6.0",
"@nuxt/content": "^2.4.1",
"@nuxthq/studio": "^0.6.5",
"@vueuse/nuxt": "^9.11.1"
},
"devDependencies": {
"@algolia/client-search": "^4.14.3",
"@docsearch/css": "^3.3.2",
"@docsearch/js": "^3.3.2",
"@nuxtjs/algolia": "^1.5.0",
"@nuxtjs/eslint-config-typescript": "^12.0.0",
"eslint": "^8.32.0",
"nuxt": "3.1.1",
"nuxt-plausible": "^0.1.2",
"release-it": "^15.6.0",
"typescript": "^4.9.4",
"vue": "^3.2.45"
}
},
"../node_modules/.pnpm/flat@5.0.2/node_modules/flat": {
"version": "5.0.2",
"dev": true,
"license": "BSD-3-Clause",
"bin": {
"flat": "cli.js"
},
"devDependencies": {
"mocha": "~8.1.1",
"standard": "^14.3.4"
}
},
"../node_modules/.pnpm/flatten@1.0.3/node_modules/flatten": {
"version": "1.0.3",
"dev": true,
"license": "MIT",
"devDependencies": {}
},
"../node_modules/.pnpm/iso-639-1@2.1.15/node_modules/iso-639-1": {
"version": "2.1.15",
"dev": true,
"license": "MIT",
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"clean-webpack-plugin": "^0.1.17",
"mocha": "^4.0.1",
"webpack": "^3.10.0"
},
"engines": {
"node": ">=6.0"
}
},
"../node_modules/.pnpm/nuxt@3.1.1/node_modules/nuxt": {
"version": "3.1.1",
"dev": true,
"license": "MIT",
"dependencies": {
"@nuxt/devalue": "^2.0.0",
"@nuxt/kit": "3.1.1",
"@nuxt/schema": "3.1.1",
"@nuxt/telemetry": "^2.1.9",
"@nuxt/ui-templates": "^1.1.0",
"@nuxt/vite-builder": "3.1.1",
"@unhead/ssr": "^1.0.18",
"@vue/reactivity": "^3.2.45",
"@vue/shared": "^3.2.45",
"@vueuse/head": "^1.0.23",
"chokidar": "^3.5.3",
"cookie-es": "^0.5.0",
"defu": "^6.1.2",
"destr": "^1.2.2",
"escape-string-regexp": "^5.0.0",
"estree-walker": "^3.0.3",
"fs-extra": "^11.1.0",
"globby": "^13.1.3",
"h3": "^1.0.2",
"hash-sum": "^2.0.0",
"hookable": "^5.4.2",
"jiti": "^1.16.2",
"knitwork": "^1.0.0",
"magic-string": "^0.27.0",
"mlly": "^1.1.0",
"nitropack": "^2.0.0",
"nuxi": "3.1.1",
"ofetch": "^1.0.0",
"ohash": "^1.0.0",
"pathe": "^1.1.0",
"perfect-debounce": "^0.1.3",
"scule": "^1.0.0",
"strip-literal": "^1.0.0",
"ufo": "^1.0.1",
"ultrahtml": "^1.2.0",
"unctx": "^2.1.1",
"unenv": "^1.0.1",
"unhead": "^1.0.18",
"unimport": "^2.0.1",
"unplugin": "^1.0.1",
"untyped": "^1.2.2",
"vue": "^3.2.45",
"vue-bundle-renderer": "^1.0.0",
"vue-devtools-stub": "^0.1.0",
"vue-router": "^4.1.6"
},
"bin": {
"nuxi": "bin/nuxt.mjs",
"nuxt": "bin/nuxt.mjs"
},
"devDependencies": {
"@types/fs-extra": "^11.0.1",
"@types/hash-sum": "^1.0.0",
"unbuild": "latest"
},
"engines": {
"node": "^14.16.0 || ^16.10.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"../node_modules/.pnpm/vite-plugin-virtual@0.1.1/node_modules/vite-plugin-virtual": {
"version": "0.1.1",
"dev": true,
"license": "MIT",
"devDependencies": {
"@antfu/eslint-config": "^0.6.2",
"@types/jest": "^26.0.22",
"@types/node": "^14.14.37",
"@typescript-eslint/eslint-plugin": "^4.20.0",
"eslint": "^7.23.0",
"jest": "^26.6.3",
"jest-esbuild": "^0.1.5",
"rollup": "^2.44.0",
"ts-node": "^9.1.1",
"tsup": "^4.8.21",
"typescript": "^4.2.3",
"vite": "^2.1.5"
},
"peerDependencies": {
"vite": "^2.0.0"
}
},
"node_modules/@nuxt-themes/docus": {
"resolved": "../node_modules/.pnpm/@nuxt-themes+docus@1.6.3_nuxt@3.1.1/node_modules/@nuxt-themes/docus",
"link": true
},
"node_modules/@types/flat": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@types/flat/-/flat-5.0.2.tgz",
"integrity": "sha512-3zsplnP2djeps5P9OyarTxwRpMLoe5Ash8aL9iprw0JxB+FAHjY+ifn4yZUuW4/9hqtnmor6uvjSRzJhiVbrEQ==",
"dev": true
},
"node_modules/flat": {
"resolved": "../node_modules/.pnpm/flat@5.0.2/node_modules/flat",
"link": true
},
"node_modules/flatten": {
"resolved": "../node_modules/.pnpm/flatten@1.0.3/node_modules/flatten",
"link": true
},
"node_modules/iso-639-1": {
"resolved": "../node_modules/.pnpm/iso-639-1@2.1.15/node_modules/iso-639-1",
"link": true
},
"node_modules/nuxt": {
"resolved": "../node_modules/.pnpm/nuxt@3.1.1/node_modules/nuxt",
"link": true
},
"node_modules/vite-plugin-virtual": {
"resolved": "../node_modules/.pnpm/vite-plugin-virtual@0.1.1/node_modules/vite-plugin-virtual",
"link": true
}
}
}

View file

@ -9,7 +9,7 @@
"preview": "nuxi preview" "preview": "nuxi preview"
}, },
"devDependencies": { "devDependencies": {
"@nuxt-themes/docus": "^1.6.1", "@nuxt-themes/docus": "^1.8.1",
"nuxt": "^3.1.1" "nuxt": "^3.2.0"
} }
} }

11
docs/types.ts Normal file
View file

@ -0,0 +1,11 @@
export interface LocaleEntry {
title: string
file: string
translated: string[]
missing: string[]
outdated: string[]
total: number
isSource?: boolean
}
export type TranslationStatus = Record<string, LocaleEntry>

3
emoji-mart-traslation.d.ts vendored Normal file
View file

@ -0,0 +1,3 @@
declare module 'virtual:emoji-mart-lang-importer' {
export default function(lang: string): Promise<any>
}

1
locales/ca-ES.json Normal file
View file

@ -0,0 +1 @@
{}

412
locales/ca-valencia.json Normal file
View file

@ -0,0 +1,412 @@
{
"a11y": {
"loading_page": "S'està carregant",
"loading_titled_page": "S'està carregant la pàgina {0}",
"locale_changed": "S'ha canviat la llengua a {0}",
"locale_changing": "S'està canviant la llengua"
},
"account": {
"avatar_description": "Foto de perfil de: {0}",
"blocked_by": "Este usuari t'ha bloquejat",
"blocked_domains": "Domini bloquejat",
"favourites": "Preferit",
"follow_back": "Segueix també",
"follow_requested": "Sol·licitat",
"go_to_profile": "Ves al perfil",
"joined": "Data d'unió",
"moved_title": "ha indicat que el seu compte és ara:",
"notifications_on_post_disable": "No em notifiques quan {username} publique",
"notifications_on_post_enable": "Notifica'm quan {username} publique",
"profile_description": "Capçalera de: {0}",
"unmute": "Deixa de silenciar",
"view_other_followers": "És possible que no es mostren els seguidors d'altres instàncies.",
"view_other_following": "És possible que no es mostren els seguits d'altres instàncies."
},
"action": {
"apply": "Aplica",
"bookmark": "Afig als marcadors",
"bookmarked": "Als marcadors",
"boost": "Impulsa",
"boosted": "Impulsat",
"clear_upload_failed": "Esborra els errors de la pujada de fitxers",
"compose": "Redacta",
"confirm": "Confirma",
"favourite": "Marca com a preferit",
"favourited": "Preferit",
"publish": "Publica"
},
"app_desc_short": "Un client web Mastodon àgil",
"app_logo": "Logo d'Elk",
"attachment": {
"remove_label": "Elimina l'adjunt"
},
"command": {
"activate": "Activa",
"n-people-in-the-past-n-days": "{0} persones en els últims {1} dies",
"select_lang": "Selecciona una llengua",
"sign_in_desc": "Afig un compte existent",
"toggle_dark_mode": "Activa el mode fosc",
"toggle_zen_mode": "Activa el mode zen"
},
"common": {
"end_of_list": "Fi de la llista",
"in": "in",
"not_found": "404 No s'ha trobat",
"offline_desc": "Sembla que estàs fora de línia. Comprova la connexió de xarxa."
},
"compose": {},
"confirm": {
"block_account": {
"cancel": "Cancel·la",
"confirm": "Bloqueja",
"title": "Segur que vols bloquejar {0}?"
},
"block_domain": {
"cancel": "Cancel·la",
"confirm": "Bloqueja",
"title": "Segur que vols bloquejar {0}?"
},
"delete_list": {
"cancel": "Cancel·la",
"confirm": "Elimina",
"title": "Segur que vols eliminar la llista «{0}»?"
},
"delete_posts": {
"cancel": "Cancel·la",
"confirm": "Elimina",
"title": "Segur que vols eliminar la publicació?"
},
"mute_account": {
"cancel": "Cancel·la",
"confirm": "Silencia",
"title": "Segur que vols silenciar {0}?"
},
"show_reblogs": {
"cancel": "Cancel·la",
"confirm": "Mostra",
"title": "Segur que vols mostrar els impulsos de: {0}?"
},
"unfollow": {
"cancel": "Cancel·la",
"title": "Segur que vols deixar de seguir-lo?"
}
},
"conversation": {},
"custom_cards": {
"stackblitz": {
"open": "Obri",
"snippet_from": "Fragment de: {0}"
}
},
"error": {
"explore-list-empty": "No hi ha res en tendències. Comprova-ho més tard!",
"file_size_cannot_exceed_n_mb": "El fitxer no pot superar els {0} MB",
"unsupported_file_format": "Format del fitxer no compatible"
},
"help": {
"build_preview": {
"desc1": "Estàs veient una versió prèvia d'Elk de la comunitat - {0}.",
"desc2": "Pot contindre canvis no revisats o fins i tot maliciosos.",
"desc3": "No inicies sessió amb el teu compte real."
},
"desc_highlight": "Espereu trobar alguns errors i que falten funcions ací i allà.",
"desc_para1": "Gràcies pel teu interès en provar Elk, el nostre client web Mastodon en procés",
"desc_para2": "estem treballant de valent en el desenvolupament i en millorar-lo.",
"desc_para3": "Per a impulsar el desenvolupament, pots patrocinar l'equip a través dels patrocis de GitHub. Esperem que gaudisques d'Elk!",
"desc_para4": "Elk és de codi obert. Si vols ajudar amb proves, comentaris o contribucions,",
"desc_para5": "troba'ns a GitHub",
"desc_para6": "i col·labora.",
"footer_team": "L'equip d'Elk",
"title": "Elk està en proves!"
},
"language": {},
"list": {
"add_account": "Afig el compte a una llista",
"create": "Crea",
"delete": "Elimina la llista",
"delete_error": "S'ha produït un error en eliminar la llista",
"edit": "Edita la llista",
"modify_account": "Edita les llistes amb este compte",
"remove_account": "Elimina el compte d'una llista"
},
"menu": {
"copy_link_to_post": "Copia l'enllaç a la publicació",
"copy_original_link_to_post": "Copia l'enllaç original a la publicació",
"delete": "Elimina",
"delete_and_redraft": "Elimina i fes un esborrany",
"direct_message_account": "Envia un missatge directe a {0}",
"hide_reblogs": "Amaga els impulsos de: {0}",
"mention_account": "Menciona {0}",
"mute_conversation": "Silencia esta publicació",
"open_in_original_site": "Obri en el lloc original",
"share_post": "Comparteix la publicació",
"show_favourited_and_boosted_by": "Mostra qui ho ha marcat com a preferit i impulsat",
"show_reblogs": "Mostra els impulsos de: {0}",
"toggle_theme": {
"dark": "Activa el mode fosc",
"light": "Activa el mode clar"
},
"unmute_conversation": "Deixa de silenciar la publicació",
"unpin_on_profile": "Deixa de fixar-ho al perfil"
},
"nav": {
"blocked_users": "Usuarid bloquejats",
"built_at": "Compilat {0}",
"compose": "Redacta",
"federated": "Federada",
"privacy": "Privacitat",
"select_feature_flags": "Toggle Feature Flags",
"select_font_size": "Grandària de la lletra",
"select_language": "Llengua a mostrar"
},
"notification": {
"favourited_post": "ha marcat com a preferit",
"followed_you_count": "{0} persones t'han seguit|{0} persona t'ha seguit|{0} persones t'han seguit",
"missing_type": "MISSING notification.type:",
"reblogged_post": "t'ha impulsat",
"request_to_follow": "ha demanat seguir-te",
"signed_up": "s'ha registrat",
"update_status": "ha actualitzat la publicació"
},
"placeholder": {
"content_warning": "Escriu ací l'avís"
},
"pwa": {
"dismiss": "Omet",
"install": "Instal·la",
"install_title": "Instal·la Elk",
"title": "Hi ha una actualització d'Elk disponible!",
"update": "Actualitza",
"webmanifest": {
"canary": {
"description": "Un client web àgil de Mastodon (canary)",
"name": "Elk (canary)",
"short_name": "Elk (canary)"
},
"dev": {
"description": "Un client web àgil de Mastodon (desenvolupament)",
"name": "Elk (desenvolupament)"
},
"preview": {
"description": "Un client web àgil de Mastodon (vista prèvia)"
},
"release": {
"description": "Un client web àgil de Mastodon"
}
}
},
"search": {
"search_desc": "Cerca persones i etiquetes",
"search_empty": "No s'ha trobat res per al termes de la cerca"
},
"settings": {
"about": {
"built_at": "Compilació",
"label": "Quant a",
"sponsor_action_desc": "Per a ajudar l'equip a desenvolupar Elk",
"sponsors": "Patrocinis",
"sponsors_body_2": "I totes les empreses i individus que patrocinen l'equip d'Elk i els membres.",
"sponsors_body_3": "Si t'agrada l'aplicació, considera patrocinar-nos:"
},
"account_settings": {
"description": "Edita la configuració del teu compte a la interfície de Mastodon"
},
"interface": {
"default": " (per defecte)",
"font_size": "Grandària de la lletra",
"theme_color": "Color de tema"
},
"language": {
"display_language": "Llengua a mostrar",
"label": "Llengua",
"translations": {
"add": "Afig",
"choose_language": "Tria una llengua",
"remove": "Elimina"
}
},
"notifications": {
"notifications": {
"label": "Configuració de les notificacions"
},
"push_notifications": {
"alerts": {
"follow": "Seguidors nous",
"reblog": "Impulsos en publicacions teues",
"title": "Quines notificacions rebré?"
},
"description": "Rep notificacions fins i tot quan no estigues utilitzant Elk.",
"instructions": "No oblides desar els canvis utilitzant el botó de @:settings.notifications.push_notifications.save_settings!",
"label": "Configuració de les notificacions emergents",
"policy": {
"all": "De tots",
"followed": "De persones que seguisc",
"follower": "De persones que em segueixen",
"title": "De qui rebré notificacions?"
},
"save_settings": "Desa la configuració",
"subscription_error": {
"invalid_vapid_key": "Sembla que la clau pública VAPID no és vàlida.",
"permission_denied": "Permís denegat: activa les notificacions al navegador.",
"repo_link": "Repositori d'Elk a Github",
"request_error": "S'ha produït un error en sol·licitar la subscripció, torna-ho a provar i si l'error persisteix, informa del problema al repositori d'Elk.",
"title": "No s'han pogut activar les notificacions emergents",
"too_many_registrations": "A causa de les limitacions del navegador, Elk no pot utilitzar el servei de notificacions emergents per a diversos comptes en diferents servidors. Hauries de desactivar les notificacions emergents de l'altre compte i tornar-ho a provar.",
"vapid_not_supported": "El navegador admet les notificacions emergents web, però sembla que no ha implementat el protocol VAPID."
},
"title": "Configuració de les notificacions emergents",
"undo_settings": "Desfés els canvis",
"unsubscribe": "Desactiva les notificacions emergents",
"unsupported": "Les notificacions emergents no són compatibles amb el teu navegador.",
"warning": {
"enable_description": "Per a rebre notificacions quan Elk no estiga obert, activa les notificacions emergents. Pots definir de quin tipus d'interaccions vols rebre notificacions emergents des del botó \"@:settings.notifications.show_btn{'\"'} quan les hages activat.",
"enable_description_desktop": "Per a rebre notificacions quan Elk no estiga obert, activa les notificacions emergents. Pots definir de quin tipus d'interaccions vols rebre notificacions emergents des de \"Configuració > Notificacions > Configuració de les notificacions emergents\" quan les hages activat.",
"enable_description_mobile": "També pots accedir a la configuració de del menú \"Configuració > Notificacions > Configuració de les notificacions emergents\".",
"enable_description_settings": "Per a rebre notificacions quan Elk no estiga obert, activa les notificacions emergents. Pots definir de quin tipus d'interaccions vols rebre notificacions emergents d'esta pantalla quan les hages activat.",
"enable_desktop": "Activa les notificacions emergents",
"enable_title": "No et perdes res",
"re_auth": "Sembla que les notificacions emergents no són compatibles amb el teu servidor. Prova tancant la sessió i tornant a entrar. Si este missatge encara apreix, posa't en contacte amb l'administració del teu servidor."
}
},
"show_btn": "Ves a la configuració de les notificacions"
},
"preferences": {
"enable_pinch_to_zoom": "Activa el pessic per a fer zoom",
"github_cards": "GitHub Cards",
"grayscale_mode": "Mode en escala de grisos",
"hide_account_hover_card": "Amaga la targeta de perfil flotant",
"hide_alt_indi_on_posts": "Amaga l'indicador «alt» a les publicacions",
"hide_boost_count": "Amaga el nombre d'impulsos",
"hide_favorite_count": "Amaga el nombre de preferits",
"hide_follower_count": "Amaga el nombre seguidors",
"hide_reply_count": "Amaga el nombre de respostes",
"hide_username_emojis": "Amaga els emojis dels noms d'usuari",
"label": "Configuració",
"title": "Funcions experimentals",
"user_picker": "Selector d'usuari"
},
"profile": {
"appearance": {
"bio": "Biografia",
"description": "Edita la foto de perfil, nom d'usuari, perfil, etc.",
"display_name": "Nom"
},
"featured_tags": {
"description": "La gent pot trobar les publicacions visibles amb estes etiquetes.",
"label": "Etiquetes destacades"
}
},
"select_a_settings": "Selecciona una opció",
"users": {
"export": "Exporta els tokens de l'usuari",
"import": "Importa els tokens de l'usuari",
"label": "Usuaris iniciats"
}
},
"share-target": {
"description": "Elk es pot configurar perquè pugues compartir contingut d'altres aplicacions, simplement instal·la Elk al teu dispositiu o ordinador i inicia la sessió.",
"hint": "Per a compartir contingut amb Elk, Elk ha d'estar instal·lat i has d'iniciar la sessió."
},
"state": {
"attachments_exceed_server_limit": "El nombre d'adjunts supera el límit per publicació.",
"edited": "(editat)",
"editing": "S'està editant",
"loading": "S'està carregant...",
"publish_failed": "La publicació ha fallat",
"publishing": "S'està publicant",
"upload_failed": "La pujada ha fallat",
"uploading": "S'està pujant..."
},
"status": {
"favourited_by": "Marcat com a preferit per",
"filter_removed_phrase": "Eliminat pel filtre",
"filter_show_anyway": "Mostra-ho igualment",
"img_alt": {
"dismiss": "Descarta",
"read": "Llig la descripció de: {0}"
},
"poll": {
"ends": "acaba en {0}",
"finished": "finalitzada {0}"
},
"reblogged": "{0} ha impulsat",
"replying_to": "En resposta a {0}",
"spoiler_show_less": "Mostra'm menys",
"spoiler_show_more": "Mostra'm més",
"try_original_site": "Prova al lloc original"
},
"status_history": {
"created": "creat el {0}",
"edited": "editat el {0}"
},
"tab": {
"hashtags": "Etiquetes",
"media": "Multimèdia",
"notifications_all": "Totes",
"notifications_mention": "Mencions",
"posts_with_replies": "Amb respostes"
},
"tag": {
"follow": "Segueix",
"follow_label": "Segueix l'etiqueta {0}"
},
"time_ago_options": {
"day_future": "en 0 dies|demà|en {n} dia",
"just_now": "ara",
"month_future": "en 0 mesos|el mes que ve|en {n} mesos",
"second_future": "ara|en {n} segon|en {n} segons",
"second_past": "ara|fa {n} segon|fa {n} segons",
"short_day_future": "en {n} d",
"short_day_past": "{n} d",
"short_hour_future": "en {n} h",
"short_hour_past": "{n} h",
"short_minute_future": "en {n} min",
"short_minute_past": "{n} min",
"short_month_future": "en {n} mes",
"short_month_past": "{n} mes",
"short_week_past": "{n}w",
"short_year_future": "en {n} a",
"short_year_past": "{n} a",
"week_future": "en 0 setmanes|la setmana que ve|en {n} setmanes",
"year_future": "en 0 anys|l'any que ve|en {n} anys"
},
"timeline": {
"show_new_items": "Mostra {v} element nou|Mostra {v} element nou|Mostra {v} element nous",
"view_older_posts": "És possible que no es mostren missatges antics d'altres instàncies."
},
"title": {
"federated_timeline": "Línia de temps federada",
"local_timeline": "Línia de temps local"
},
"tooltip": {
"add_content_warning": "Afig un avís de contingut",
"add_emojis": "Afig emojis",
"add_media": "Afig imatges, un vídeo o un àudio",
"add_publishable_content": "Afig contingut per a publicar",
"change_language": "Canvia la llengua",
"emoji": "Emojis",
"explore_links_intro": "Notícies que gent d'este i d'altres servidors de la xarxa descentralitzada comenta ara mateix.",
"explore_posts_intro": "Publicacions d'este i d'altres servidors de la xarxa descentralitzada que estan guanyant popularitat en este servidor ara mateix.",
"explore_tags_intro": "Etiquetes que estan guanyant popularitat entre la gent d'este i altres servidors de la xarxa descentralitzada ara mateix.",
"open_editor_tools": "Ferramentes d'edició",
"publish_failed": "Tanca els missatges fallits a la part superior de l'editor per a tornar a publicar",
"toggle_bold": "Negreta",
"toggle_code_block": "Bloc de codi",
"toggle_italic": "Cursiva"
},
"user": {
"add_existing": "Afig un compte existent",
"server_address_label": "Adreça del servidor de Mastodon",
"sign_in_desc": "Inicia la sessió per a seguir perfils o etiquetes, marcar com a preferides, compartir i respondre a les publicacions, o interactuar des d'un compte en un servidor diferent.",
"sign_in_notice_title": "Veient les dades públiques de: {0}",
"sign_out_account": "Tanca {0}",
"tip_no_account": "Si encara no tens un compte de Mastodon, {0}.",
"tip_register_account": "tria un servidor i registra't"
},
"visibility": {
"direct_desc": "Visible només per als usuaris mencionats",
"private": "Només a seguidors",
"public_desc": "Visible per a tots",
"unlisted_desc": "Visible per a tots, però sense mostrar-ho en les funcions de descobriment"
}
}

594
locales/ca.json Normal file
View file

@ -0,0 +1,594 @@
{
"a11y": {
"loading_page": "S'està carregant la pàgina, espereu",
"loading_titled_page": "S'està carregant la pàgina {0}, espereu",
"locale_changed": "L'idioma ha canviat a {0}",
"locale_changing": "S'està canviant l'idioma, espereu",
"route_loaded": "S'ha carregat la pàgina {0}"
},
"account": {
"avatar_description": "Avatar de {0}",
"blocked_by": "Estàs bloquejat per aquest usuari.",
"blocked_domains": "Dominis bloquejats",
"blocked_users": "Usuaris bloquejats",
"blocking": "Bloquejat",
"bot": "BOT",
"favourites": "Preferits",
"follow": "Segueix",
"follow_back": "Segueix de tornada",
"follow_requested": "Demanat",
"followers": "Seguidors",
"followers_count": "{0} Seguidors|{0} Seguidor|{0} Seguidors",
"following": "Seguint",
"following_count": "{0} Seguint",
"follows_you": "Et segueix",
"go_to_profile": "Vés al perfil",
"joined": "S'ha unit",
"moved_title": "ha indicat que el seu nou compte és ara:",
"muted_users": "Usuaris silenciats",
"muting": "Silenciat",
"mutuals": "Mutu",
"notifications_on_post_disable": "Deixa de notificar-me quan {username} publique",
"notifications_on_post_enable": "Aviseu-me quan {username} publique",
"pinned": "Fixat",
"posts": "Publicacions",
"posts_count": "{0} Publicacions|{0} Publicació|{0} Publicacions",
"profile_description": "Capçalera del perfil de {0}",
"profile_unavailable": "Perfil no disponible",
"request_follow": "Sol·licitud de seguiment",
"unblock": "Desbloqueja",
"unfollow": "Deixa de seguir",
"unmute": "Activa el so",
"view_other_followers": "És possible que no es mostrin els seguidors d'altres instàncies.",
"view_other_following": "És possible que no es mostrin els seguits d'altres instàncies."
},
"action": {
"apply": "Aplicar",
"bookmark": "Afegir marcador",
"bookmarked": "Afegit a marcadors",
"boost": "Estímul",
"boost_count": "{0}",
"boosted": "Potenciat",
"clear_publish_failed": "Esborra els errors de publicació",
"clear_upload_failed": "Esborra els errors de càrrega de fitxers",
"close": "Tanca",
"compose": "Redactar",
"confirm": "Confirmeu",
"edit": "Edita",
"enter_app": "Entra a l'aplicació",
"favourite": "Favorit",
"favourite_count": "{0}",
"favourited": "Favorit",
"more": "Més",
"next": "Següent",
"prev": "Anterior",
"publish": "Publicar",
"reply": "Respon",
"reply_count": "{0}",
"reset": "Restableix",
"save": "Desa",
"save_changes": "Desa els canvis",
"sign_in": "Inicia sessió",
"sign_in_to": "Inicia sessió a {0}",
"switch_account": "Canvia de compte",
"vote": "Vota"
},
"app_desc_short": "Un client web àgil Mastodon",
"app_logo": "Logotip d'Elk",
"app_name": "Elk",
"attachment": {
"edit_title": "Descripció",
"remove_label": "Elimina el fitxer adjunt"
},
"command": {
"activate": "Activar",
"complete": "Completa",
"compose_desc": "Escriu una publicació nova",
"n-people-in-the-past-n-days": "{0} persones en els darrers {1} dies",
"select_lang": "Escolliu l'idioma",
"sign_in_desc": "Afegeix un compte existent",
"switch_account": "Canvia a {0}",
"switch_account_desc": "Canvia a un altre compte",
"toggle_dark_mode": "Canvia el mode fosc",
"toggle_zen_mode": "Canvia el mode zen"
},
"common": {
"end_of_list": "Final de la llista",
"error": "ERROR",
"fetching": "S'està recuperant...",
"in": "en",
"not_found": "404 No trobat",
"offline_desc": "Sembla que estàs fora de línia. Comproveu la vostra connexió de xarxa."
},
"compose": {
"draft_title": "Esborrany {0}",
"drafts": "Esborranys ({v})"
},
"confirm": {
"block_account": {
"cancel": "Cancel·lar",
"confirm": "Bloc",
"title": "Confirmes que vols bloquejar {0}?"
},
"block_domain": {
"cancel": "Cancel·lar",
"confirm": "Bloc",
"title": "Confirmes que vols bloquejar {0}?"
},
"common": {
"cancel": "No",
"confirm": "Sí"
},
"delete_list": {
"cancel": "Cancel·lar",
"confirm": "Suprimeix",
"title": "Esteu segur que voleu suprimir la llista \"{0}\"?"
},
"delete_posts": {
"cancel": "Cancel·lar",
"confirm": "Suprimeix",
"title": "Esteu segur que voleu suprimir aquesta publicació?"
},
"mute_account": {
"cancel": "Cancel·lar",
"confirm": "Silenciar",
"title": "Confirmes que vols silenciar {0}?"
},
"show_reblogs": {
"cancel": "Cancel·lar",
"confirm": "Espectacle",
"title": "Confirmes que vols mostrar els augments de {0}?"
},
"unfollow": {
"cancel": "Cancel·lar",
"confirm": "Deixa de seguir",
"title": "Estàs segur que vols deixar de seguir?"
}
},
"conversation": {
"with": "amb"
},
"custom_cards": {
"stackblitz": {
"lines": "Línies {0}",
"open": "Obert",
"snippet_from": "Fragment de {0}"
}
},
"error": {
"account_not_found": "No s'ha trobat el compte {0}",
"explore-list-empty": "No hi ha res de tendència ara mateix. Torna més tard!",
"file_size_cannot_exceed_n_mb": "La mida del fitxer no pot superar els {0}MB",
"sign_in_error": "No es pot connectar al servidor.",
"status_not_found": "No s'ha trobat la publicació",
"unsupported_file_format": "Format de fitxer no compatible"
},
"help": {
"build_preview": {
"desc1": "Actualment esteu veient una versió prèvia d'Elk de la comunitat: {0}.",
"desc2": "Pot contenir canvis no revisats o fins i tot maliciosos.",
"desc3": "No inicieu sessió amb el vostre compte real.",
"title": "Vista prèvia del desplegament"
},
"desc_highlight": "Espereu alguns errors i característiques que falten aquí i allà.",
"desc_para1": "Gràcies pel vostre interès a provar Elk, el nostre client web en desenvolupament per a Mastodon!",
"desc_para2": "Estem treballant intensament en el desenvolupament i millorant-lo amb el temps.",
"desc_para3": "Per impulsar el desenvolupament, podeu patrocinar l'equip a través dels patrocinadors de GitHub. Esperem que gaudiu d'Elk!",
"desc_para4": "Elk és de codi obert. Si voleu ajudar amb les proves, fer comentaris o contribuir-hi,",
"desc_para5": "posa't en contacte amb nosaltres a GitHub",
"desc_para6": "i participa.",
"footer_team": "L'equip Elk",
"title": "Elk esta en Vista Previa!"
},
"language": {
"search": "Cerca"
},
"list": {
"add_account": "Afegeix un compte a la llista",
"cancel_edit": "Cancel·la l'edició",
"clear_error": "Esborra l'error",
"create": "Crear",
"delete": "Suprimeix aquesta llista",
"delete_error": "S'ha produït un error en suprimir la llista",
"edit": "Editeu aquesta llista",
"edit_error": "S'ha produït un error en actualitzar la llista",
"error": "S'ha produït un error en crear la llista",
"error_prefix": "Error:",
"list_title_placeholder": "Títol de la llista",
"modify_account": "Modificar llistes amb compte",
"remove_account": "Elimina el compte de la llista",
"save": "Desa els canvis"
},
"menu": {
"block_account": "Bloqueja {0}",
"block_domain": "Bloqueja el domini {0}",
"copy_link_to_post": "Copia l'enllaç a aquesta publicació",
"copy_original_link_to_post": "Copia l'enllaç original a aquesta publicació",
"delete": "Suprimeix",
"delete_and_redraft": "Esborra i torna a redactar",
"direct_message_account": "Missatge directe {0}",
"edit": "Edita",
"hide_reblogs": "Amaga els augments de {0}",
"mention_account": "Esmenta {0}",
"mute_account": "Silencia {0}",
"mute_conversation": "Silencia aquesta publicació",
"open_in_original_site": "Obre al lloc original",
"pin_on_profile": "Fixa al perfil",
"share_post": "Comparteix aquesta publicació",
"show_favourited_and_boosted_by": "Mostra qui ha fet favorits i qui ha augmentat",
"show_reblogs": "Mostra els augments de {0}",
"show_untranslated": "Mostra sense traduir",
"toggle_theme": {
"dark": "Canvia a mode fosc",
"light": "Canvia a mode clar"
},
"translate_post": "Tradueix",
"unblock_account": "Desbloqueja {0}",
"unblock_domain": "Desbloqueja el domini {0}",
"unmute_account": "Deixa de silenciar {0}",
"unmute_conversation": "Deixa de silenciar aquesta publicació",
"unpin_on_profile": "No fixis al perfil"
},
"nav": {
"back": "Torna",
"blocked_domains": "Dominis bloquejats",
"blocked_users": "Usuaris bloquejats",
"bookmarks": "Marcadors",
"built_at": "Construït {0}",
"compose": "Redactar",
"conversations": "Converses",
"explore": "Explora",
"favourites": "Preferits",
"federated": "Federat",
"home": "Inici",
"list": "Llista",
"lists": "Llistes",
"local": "Local",
"muted_users": "Usuaris silenciats",
"notifications": "Notificacions",
"privacy": "Privadesa",
"profile": "Perfil",
"search": "Cerca",
"select_feature_flags": "Commuta les banderes de funció",
"select_font_size": "Mida de la font",
"select_language": "Idioma de visualització",
"settings": "Configuració",
"show_intro": "Mostra la introducció",
"toggle_theme": "Canvia el tema",
"zen_mode": "Mode Zen"
},
"notification": {
"favourited_post": "va posar com a preferit la teva publicació",
"followed_you": "t'ha seguit",
"followed_you_count": "{0} persones us han seguit|{0} persones us han seguit|{0} persones us han seguit",
"missing_type": "FALTA notificació.tipus:",
"reblogged_post": "ha reblogjat la teva publicació",
"request_to_follow": "demanat que us segueixi",
"signed_up": "registrat",
"update_status": "ha actualitzat la seva publicació"
},
"placeholder": {
"content_warning": "Escriu el teu avís aquí",
"default_1": "Què et passa pel cap?",
"reply_to_account": "Respon a {0}",
"replying": "Responent",
"the_thread": "el fil"
},
"pwa": {
"dismiss": "Descartar",
"install": "Instal·lar",
"install_title": "Instal·leu Elk",
"title": "Nova actualització d'Elk disponible!",
"update": "Actualització",
"update_available_short": "Actualitza Elk",
"webmanifest": {
"canary": {
"description": "Un client web àgil Mastodon (canari)",
"name": "Elk (canari)",
"short_name": "Elk (canari)"
},
"dev": {
"description": "Un client web àgil Mastodon (dev)",
"name": "Elk (dev)",
"short_name": "Elk (dev)"
},
"preview": {
"description": "Un client web àgil Mastodon (vista prèvia)",
"name": "Elk (vista prèvia)",
"short_name": "Elk (vista prèvia)"
},
"release": {
"description": "Un client web àgil Mastodon",
"name": "Elk",
"short_name": "Elk"
}
}
},
"search": {
"search_desc": "Cerca persones i hashtags",
"search_empty": "No s'ha pogut trobar res per a aquests termes de cerca"
},
"settings": {
"about": {
"built_at": "Construït",
"label": "Sobre",
"meet_the_team": "Coneix l'equip",
"sponsor_action": "Patrocina'ns",
"sponsor_action_desc": "Donar suport a l'equip que desenvolupa Elk",
"sponsors": "Patrocinadors",
"sponsors_body_1": "Elk és possible gràcies al generós patrocini i ajuda de:",
"sponsors_body_2": "I totes les empreses i particulars patrocinadors d'Elk Team i els seus membres.",
"sponsors_body_3": "Si us agrada l'aplicació, penseu a patrocinar-nos:",
"version": "Versió"
},
"account_settings": {
"description": "Editeu la configuració del vostre compte a la interfície d'usuari de Mastodon",
"label": "Configuració del compte"
},
"interface": {
"color_mode": "Mode de color",
"dark_mode": "Fosc",
"default": "(per defecte)",
"font_size": "Mida de la font",
"label": "Interfície",
"light_mode": "Clar",
"system_mode": "Sistema",
"theme_color": "Color del tema"
},
"language": {
"display_language": "Idioma de visualització",
"label": "Idioma",
"status": "Estat de la traducció: {0}/{1} ({2}%)",
"translations": {
"add": "Afegeix",
"choose_language": "Trieu l'idioma",
"heading": "Traduccions",
"hide_specific": "Amaga traduccions específiques",
"remove": "Eliminar"
}
},
"notifications": {
"label": "Notificacions",
"notifications": {
"label": "Configuració de notificacions"
},
"push_notifications": {
"alerts": {
"favourite": "Preferits",
"follow": "Nous seguidors",
"mention": "Mencions",
"poll": "Enquestes",
"reblog": "Reblogueu la vostra publicació",
"title": "Quines notificacions rebre?"
},
"description": "Rebeu notificacions fins i tot quan no feu servir Elk.",
"instructions": "No oblideu desar els vostres canvis utilitzant el botó @:settings.notifications.push_notifications.save_settings!",
"label": "Configuració de les notificacions push",
"policy": {
"all": "De qualsevol",
"followed": "De la gent que segueixo",
"follower": "De la gent que em segueix",
"none": "De ningú",
"title": "De qui puc rebre notificacions?"
},
"save_settings": "Desa els canvis",
"subscription_error": {
"clear_error": "Esborra l'error",
"error_hint": "Podeu consultar una llista de preguntes freqüents per intentar resoldre el problema: {0}.",
"invalid_vapid_key": "La clau pública VAPID sembla que no és vàlida.",
"permission_denied": "Permís denegat: activeu les notificacions al vostre navegador.",
"repo_link": "El dipòsit d'Elk a Github",
"request_error": "S'ha produït un error en sol·licitar la subscripció, torneu-ho a provar i, si l'error persisteix, informeu-lo al repositori d'Elk.",
"title": "No s'ha pogut subscriure a les notificacions push",
"too_many_registrations": "A causa de les limitacions del navegador, Elk no pot utilitzar el servei de notificacions push per a diversos comptes en diferents servidors. Hauríeu de cancel·lar la subscripció a les notificacions push d'un altre compte i tornar-ho a provar.",
"vapid_not_supported": "El vostre navegador admet les notificacions push web, però no sembla implementar el protocol VAPID."
},
"title": "Configuració de les notificacions push",
"undo_settings": "Desfer els canvis",
"unsubscribe": "Desactiva les notificacions push",
"unsupported": "El vostre navegador no admet notificacions push.",
"warning": {
"enable_close": "Tanca",
"enable_description": "Per rebre notificacions quan Elk no estigui obert, activeu les notificacions push. Podeu controlar amb precisió quins tipus d'interaccions generen notificacions push mitjançant el botó \"@:settings.notifications.show_btn{'\"'} de dalt un cop activat.",
"enable_description_desktop": "Per rebre notificacions quan Elk no estigui obert, activeu les notificacions push. Podeu controlar amb precisió quins tipus d'interaccions generen notificacions push a \"Configuració > Notificacions > Configuració de notificacions push\" un cop habilitat.",
"enable_description_mobile": "També podeu accedir a la configuració mitjançant el menú de navegació \"Configuració > Notificacions > Configuració de notificacions push\".",
"enable_description_settings": "Per rebre notificacions quan Elk no estigui obert, activeu les notificacions push. Podreu controlar amb precisió quins tipus d'interaccions generen notificacions push en aquesta mateixa pantalla un cop les habiliteu.",
"enable_desktop": "Activa les notificacions push",
"enable_title": "No et perdis mai res",
"re_auth": "Sembla que el vostre servidor no admet notificacions push. Proveu de tancar la sessió i tornar-la a iniciar, si aquest missatge encara apareix, poseu-vos en contacte amb l'administrador del vostre servidor."
}
},
"show_btn": "Vés a la configuració de notificacions",
"under_construction": "En construcció"
},
"notifications_settings": "Notificacions",
"preferences": {
"enable_autoplay": "Activa la reproducció automàtica",
"enable_data_saving": "Activa l'emmagatzematge de dades",
"enable_data_saving_description": "Deseu les dades evitant que els fitxers adjunts es carreguin automàticament.",
"enable_pinch_to_zoom": "Activa el pessic per fer zoom",
"github_cards": "Targetes GitHub",
"grayscale_mode": "Mode d'escala de grisos",
"hide_account_hover_card": "Amaga la targeta de desplaçament del compte",
"hide_alt_indi_on_posts": "Amaga l'indicador alt a les publicacions",
"hide_boost_count": "Amaga el recompte d'impulsos",
"hide_favorite_count": "Amaga el recompte de favorits",
"hide_follower_count": "Amaga el recompte de seguidors",
"hide_reply_count": "Amaga el recompte de respostes",
"hide_translation": "Amaga la traducció",
"hide_username_emojis": "Amaga els emojis del nom d'usuari",
"hide_username_emojis_description": "Amaga els emojis dels noms d'usuari a les línies de temps. Els emojis encara seran visibles als seus perfils.",
"label": "Preferències",
"title": "Característiques experimentals",
"user_picker": "Selector d'usuaris",
"virtual_scroll": "Desplaçament virtual",
"wellbeing": "Benestar"
},
"profile": {
"appearance": {
"bio": "bio",
"description": "Edita l'avatar, el nom d'usuari, el perfil, etc.",
"display_name": "Nom de visualització",
"label": "Aparença",
"profile_metadata": "Metadades del perfil",
"profile_metadata_desc": "Pots mostrar fins a {0} elements com a taula al teu perfil",
"profile_metadata_label": "Etiqueta",
"profile_metadata_value": "Contingut",
"title": "Edita el perfil"
},
"featured_tags": {
"description": "La gent pot navegar per les teves publicacions públiques amb aquests hashtags.",
"label": "Hashtags destacats"
},
"label": "Perfil"
},
"select_a_settings": "Seleccioneu una configuració",
"users": {
"export": "Exporta fitxes d'usuari",
"import": "Importa fitxes d'usuari",
"label": "Usuaris connectats"
}
},
"share-target": {
"description": "Elk es pot configurar perquè pugueu compartir contingut d'altres aplicacions, només cal que instal·leu Elk al vostre dispositiu o ordinador i inicieu sessió.",
"hint": "Per compartir contingut amb Elk, cal que l'Elk estigui instal·lat i inicieu sessió.",
"title": "Comparteix amb Elk"
},
"state": {
"attachments_exceed_server_limit": "El nombre de fitxers adjunts ha superat el límit per missatge.",
"attachments_limit_error": "S'ha superat el límit per publicació",
"edited": "(Edit)",
"editing": "Edició",
"loading": "Carregant...",
"publish_failed": "No s'ha pogut publicar",
"publishing": "Publicació",
"upload_failed": "La càrrega ha fallat",
"uploading": "S'està carregant..."
},
"status": {
"boosted_by": "Impulsat per",
"edited": "Editat {0}",
"favourited_by": "Favorit per",
"filter_hidden_phrase": "Filtrat per",
"filter_removed_phrase": "Eliminat per filtre",
"filter_show_anyway": "Mostra de totes maneres",
"img_alt": {
"ALT": "ALT",
"desc": "Descripció",
"dismiss": "Descartar",
"read": "Llegeix la descripció de {0}"
},
"poll": {
"count": "{0} vots|{0} vot|{0} vots",
"ends": "acaba {0}",
"finished": "acabat {0}"
},
"reblogged": "{0} ha reblogjat",
"replying_to": "Responent a {0}",
"show_full_thread": "Mostra el fil complet",
"someone": "algú",
"spoiler_show_less": "Mostra menys",
"spoiler_show_more": "Mostra més",
"thread": "Fil",
"try_original_site": "Proveu el lloc original"
},
"status_history": {
"created": "creat {0}",
"edited": "editat {0}"
},
"tab": {
"accounts": "Comptes",
"for_you": "Per a tu",
"hashtags": "Hashtags",
"list": "Llista",
"media": "Mitjans de comunicació",
"news": "Notícies",
"notifications_all": "Tots",
"notifications_mention": "Esmenta",
"posts": "Publicacions",
"posts_with_replies": "Publicacions"
},
"tag": {
"follow": "Seguiu",
"follow_label": "Seguiu l'etiqueta {0}",
"unfollow": "Deixa de seguir",
"unfollow_label": "Deixa de seguir l'etiqueta {0}"
},
"time_ago_options": {
"day_future": "d'aquí a 0 dies|demà|d'aquí a {n} dies",
"day_past": "fa 0 dies|ahir|fa {n} dies",
"hour_future": "en 0 hores|en 1 hora|en {n} hores",
"hour_past": "fa 0 hores|fa 1 hora|fa {n} hores",
"just_now": "ara mateix",
"minute_future": "en 0 minuts|en 1 minut|en {n} minuts",
"minute_past": "fa 0 minuts|fa 1 minut|fa {n} minuts",
"month_future": "d'aquí a 0 mesos|mes que ve|d'aquí a {n} mesos",
"month_past": "fa 0 mesos|el mes passat|fa {n} mesos",
"second_future": "ara mateix|en {n} segon|en {n} segons",
"second_past": "ara mateix|fa {n} segon|fa {n} segons",
"short_day_future": "en {n}d",
"short_day_past": "{n}d",
"short_hour_future": "en {n}h",
"short_hour_past": "{n}h",
"short_minute_future": "en {n}min",
"short_minute_past": "{n}min",
"short_month_future": "en 0 mesos|en 1 mes|en {n} mesos",
"short_month_past": "0 mesos|1 mes|{n} mesos",
"short_second_future": "en {n} s",
"short_second_past": "{n} s",
"short_week_future": "en {n} set",
"short_week_past": "{n} set",
"short_year_future": "en 0 anys|en 1 any|en {n}anys",
"short_year_past": "0 anys|1 any|{n} anys",
"week_future": "d'aquí a 0 setmanes|la setmana vinent|d'aquí a {n} setmanes",
"week_past": "fa 0 setmanes|la setmana passada|fa {n} setmanes",
"year_future": "d'aquí a 0 anys|l'any vinent|d'aquí a {n} anys",
"year_past": "fa 0 anys|l'any passat|fa {n} anys"
},
"timeline": {
"show_new_items": "Mostra {v} elements nous|Mostra {v} element nou|Mostra {v} elements nous",
"view_older_posts": "És possible que les publicacions més antigues d'altres instàncies no es mostrin."
},
"title": {
"federated_timeline": "Cronologia federada",
"local_timeline": "Cronologia local"
},
"tooltip": {
"add_content_warning": "Afegiu un avís de contingut",
"add_emojis": "Afegeix emojis",
"add_media": "Afegiu imatges, un vídeo o un fitxer d'àudio",
"add_publishable_content": "Afegeix contingut per publicar",
"change_content_visibility": "Canvia la visibilitat del contingut",
"change_language": "Canviar d'idioma",
"emoji": "Emoji",
"explore_links_intro": "Les persones d'aquest i d'altres servidors de la xarxa descentralitzada estan parlant d'aquestes notícies en aquests moments.",
"explore_posts_intro": "Aquestes publicacions d'aquest i d'altres servidors de la xarxa descentralitzada estan guanyant força en aquest servidor ara mateix.",
"explore_tags_intro": "Aquests hashtags estan guanyant força entre les persones d'aquest i d'altres servidors de la xarxa descentralitzada ara mateix.",
"open_editor_tools": "Eines de l'editor",
"pick_an_icon": "Trieu una icona",
"publish_failed": "Tanca els missatges fallits a la part superior de l'editor per tornar a publicar publicacions",
"toggle_bold": "Commuta la negreta",
"toggle_code_block": "Commuta el bloc de codi",
"toggle_italic": "Canvia la cursiva"
},
"user": {
"add_existing": "Afegeix un compte existent",
"server_address_label": "Adreça del servidor Mastodon",
"sign_in_desc": "Inicia sessió per seguir perfils o hashtags, preferir, compartir i respondre publicacions o interactuar des del vostre compte en un servidor diferent.",
"sign_in_notice_title": "S'estan visualitzant {0} dades públiques",
"sign_out_account": "Tanca la sessió {0}",
"single_instance_sign_in_desc": "Inicieu la sessió per seguir perfils o hashtags, preferir, compartir i respondre publicacions.",
"tip_no_account": "Si encara no teniu cap compte de Mastodon, {0}.",
"tip_register_account": "trieu el vostre servidor i registreu-ne un"
},
"visibility": {
"direct": "Directe",
"direct_desc": "Visible només per als usuaris esmentats",
"private": "Només seguidors",
"private_desc": "Visible només per als seguidors",
"public": "Públic",
"public_desc": "Visible per a tothom",
"unlisted": "No llistat",
"unlisted_desc": "Visible per a tothom, però s'ha desactivat les funcions de descobriment"
}
}

View file

@ -9,7 +9,7 @@
"account": { "account": {
"avatar_description": "{0}'s Avatar", "avatar_description": "{0}'s Avatar",
"blocked_by": "Du wurdest von diesem Account geblockt", "blocked_by": "Du wurdest von diesem Account geblockt",
"blocked_domains": "Gesperrte Domänen", "blocked_domains": "Geblockte Mastodon-Instanzen",
"blocked_users": "Gesperrte Accounts", "blocked_users": "Gesperrte Accounts",
"blocking": "Blockiert", "blocking": "Blockiert",
"bot": "BOT", "bot": "BOT",
@ -48,7 +48,7 @@
"boost_count": "{0}", "boost_count": "{0}",
"boosted": "Geteilt", "boosted": "Geteilt",
"clear_upload_failed": "Fehler beim Hochladen von Dateien entfernen", "clear_upload_failed": "Fehler beim Hochladen von Dateien entfernen",
"close": "Schliessen", "close": "Schließen",
"compose": "Verfassen", "compose": "Verfassen",
"confirm": "Bestätigen", "confirm": "Bestätigen",
"edit": "Bearbeiten", "edit": "Bearbeiten",
@ -92,7 +92,7 @@
"end_of_list": "Ende der Liste", "end_of_list": "Ende der Liste",
"error": "FEHLER", "error": "FEHLER",
"in": "in", "in": "in",
"not_found": "404 Nicht Gefunden", "not_found": "404 - Nicht gefunden",
"offline_desc": "Anscheinend bist du offline. Bitte überprüfe deine Netzwerkverbindung." "offline_desc": "Anscheinend bist du offline. Bitte überprüfe deine Netzwerkverbindung."
}, },
"compose": { "compose": {
@ -116,16 +116,16 @@
}, },
"error": { "error": {
"account_not_found": "Account {0} nicht gefunden", "account_not_found": "Account {0} nicht gefunden",
"explore-list-empty": "Momentan ist nichts im Trend. Schau später nochmal vorbei!", "explore-list-empty": "Momentan ist nichts in den Trends. Schau einfach später nochmal vorbei!",
"file_size_cannot_exceed_n_mb": "Die Dateigröße darf {0} MB nicht überschreiten", "file_size_cannot_exceed_n_mb": "Die Dateigröße darf {0} MB nicht überschreiten",
"sign_in_error": "Kann nicht zu Server verbinden", "sign_in_error": "Kann nicht mit Server verbinden",
"status_not_found": "Beitrag nicht gefunden", "status_not_found": "Beitrag nicht gefunden",
"unsupported_file_format": "Nicht unterstütztes Dateiformat" "unsupported_file_format": "Nicht unterstütztes Dateiformat"
}, },
"help": { "help": {
"desc_highlight": "Erwarte hier und da einige Bugs und fehlende Funktionen.", "desc_highlight": "Erwarte hier und da einige Bugs und fehlende Funktionen.",
"desc_para1": "Danke für dein Interesse in Elk, unser noch in der Bearbeitung befindliche, generische Mastodon Client!", "desc_para1": "Danke für dein Interesse an Elk, unser noch in der Bearbeitung befindlicher, generischer Mastodon-Client!",
"desc_para2": "Wir arbeiten hart an der Entwicklung und verbessern ihn mit der Zeit. Und wir laden dich schon sehr bald ein uns zu helfen, sobald wir ihn Quelloffen machen!", "desc_para2": "Wir arbeiten stetig an der Entwicklung und verbessern ihn mit der Zeit. Und wir laden dich schon sehr bald ein uns zu helfen, sobald wir ihn Quelloffen machen!",
"desc_para3": "Doch in der Zwischenzeit kannst du der Entwicklung aushelfen, indem du unsere Teammitglieder durch die unten stehenden Links unterstützt.", "desc_para3": "Doch in der Zwischenzeit kannst du der Entwicklung aushelfen, indem du unsere Teammitglieder durch die unten stehenden Links unterstützt.",
"desc_para4": "Elk ist Open Source. \nWenn du beim Testen helfen, Feedback geben oder einen Beitrag leisten möchtest,", "desc_para4": "Elk ist Open Source. \nWenn du beim Testen helfen, Feedback geben oder einen Beitrag leisten möchtest,",
"desc_para5": "Kontaktiere uns auf GitHub", "desc_para5": "Kontaktiere uns auf GitHub",
@ -149,7 +149,7 @@
"mute_conversation": "Diesem Beitrag stummschalten", "mute_conversation": "Diesem Beitrag stummschalten",
"open_in_original_site": "Auf Originalseite öffnen", "open_in_original_site": "Auf Originalseite öffnen",
"pin_on_profile": "An Profil anpinnen", "pin_on_profile": "An Profil anpinnen",
"share_post": "Teile diesen Beitrag", "share_post": "Booste diesen Beitrag",
"show_favourited_and_boosted_by": "Zeige mir, wer favorisiert und geboostet hat", "show_favourited_and_boosted_by": "Zeige mir, wer favorisiert und geboostet hat",
"show_reblogs": "Boosts von {0} anzeigen", "show_reblogs": "Boosts von {0} anzeigen",
"show_untranslated": "Übersetzung schliessen", "show_untranslated": "Übersetzung schliessen",
@ -193,7 +193,7 @@
"favourited_post": "hat deinen Beitrag favorisiert", "favourited_post": "hat deinen Beitrag favorisiert",
"followed_you": "folgt dir", "followed_you": "folgt dir",
"followed_you_count": "{n} Account folgt dir|{n} Accounts folgen dir", "followed_you_count": "{n} Account folgt dir|{n} Accounts folgen dir",
"missing_type": "MISSING notification.type:", "missing_type": "Fehlender notification.type:",
"reblogged_post": "hat deinen Beitrag geteilt", "reblogged_post": "hat deinen Beitrag geteilt",
"request_to_follow": "möchte dir folgen", "request_to_follow": "möchte dir folgen",
"signed_up": "hat sich registriert", "signed_up": "hat sich registriert",
@ -242,7 +242,7 @@
"about": { "about": {
"label": "Info", "label": "Info",
"meet_the_team": "Triff das Team", "meet_the_team": "Triff das Team",
"sponsor_action": "Sponser uns", "sponsor_action": "Sponsere uns",
"sponsor_action_desc": "Um das Team bei der Entwicklung von Elk zu unterstützen", "sponsor_action_desc": "Um das Team bei der Entwicklung von Elk zu unterstützen",
"sponsors": "Sponsoren", "sponsors": "Sponsoren",
"sponsors_body_1": "Elk wird ermöglicht durch das großzügige Sponsoring und die Hilfe von:", "sponsors_body_1": "Elk wird ermöglicht durch das großzügige Sponsoring und die Hilfe von:",
@ -294,10 +294,10 @@
"title": "Welche Benachrichtigungen erhalten?" "title": "Welche Benachrichtigungen erhalten?"
}, },
"description": "Erhalte Benachrichtigungen, auch wenn du Elk nicht verwendest.", "description": "Erhalte Benachrichtigungen, auch wenn du Elk nicht verwendest.",
"instructions": "Vergesse nicht, Änderungen mit der Schaltfläche @:settings.notifications.push_notifications.save_settings zu speichern!", "instructions": "Vergesse nicht, die Änderungen mit der Schaltfläche @:settings.notifications.push_notifications.save_settings zu speichern!",
"label": "Einstellungen für Push-Benachrichtigungen", "label": "Einstellungen für Push-Benachrichtigungen",
"policy": { "policy": {
"all": "Von irgendjemandem", "all": "Von allen",
"followed": "Von Accounts, denen ich folge", "followed": "Von Accounts, denen ich folge",
"follower": "Von Menschen, die mir folgen", "follower": "Von Menschen, die mir folgen",
"none": "Von niemandem", "none": "Von niemandem",
@ -307,7 +307,7 @@
"subscription_error": { "subscription_error": {
"clear_error": "Fehler aufräumen", "clear_error": "Fehler aufräumen",
"permission_denied": "Berechtigung verweigert: Aktiviere Benachrichtigungen im Browser.", "permission_denied": "Berechtigung verweigert: Aktiviere Benachrichtigungen im Browser.",
"request_error": "Beim Anfordern des Abonnements ist ein Fehler aufgetreten. Versuche es erneut. Wenn der Fehler weiterhin besteht, melde das Problem bitte dem Elk-Repository.", "request_error": "Beim Anfordern des Abonnements ist ein Fehler aufgetreten. Versuche es bitte erneut. Wenn der Fehler weiterhin besteht, melde das Problem bitte dem Elk-Repository.",
"title": "Push-Benachrichtigungen konnten nicht abonniert werden", "title": "Push-Benachrichtigungen konnten nicht abonniert werden",
"too_many_registrations": "Aufgrund von Browserbeschränkungen kann Elk den Push-Benachrichtigungsdienst nicht für mehrere Konten auf verschiedenen Servern verwenden. \nDu solltest Push-Benachrichtigungen für andere Konten abbestellen und es erneut versuchen." "too_many_registrations": "Aufgrund von Browserbeschränkungen kann Elk den Push-Benachrichtigungsdienst nicht für mehrere Konten auf verschiedenen Servern verwenden. \nDu solltest Push-Benachrichtigungen für andere Konten abbestellen und es erneut versuchen."
}, },
@ -344,7 +344,7 @@
"profile": { "profile": {
"appearance": { "appearance": {
"bio": "Bio", "bio": "Bio",
"description": "Avatar, Benutzername, Profil, etc. bearbeiten", "description": "Avatar, Benutzername, Profil etc. bearbeiten",
"display_name": "Anzeigename", "display_name": "Anzeigename",
"label": "Erscheinungsbild", "label": "Erscheinungsbild",
"profile_metadata": "Profil-Metadaten", "profile_metadata": "Profil-Metadaten",
@ -367,7 +367,7 @@
"share-target": { "share-target": {
"description": "Elk kann so konfiguriert werden, dass du Inhalte aus anderen Anwendungen teilen kannst, installiere einfach Elk auf deinem Gerät oder Computer und melden dich an.", "description": "Elk kann so konfiguriert werden, dass du Inhalte aus anderen Anwendungen teilen kannst, installiere einfach Elk auf deinem Gerät oder Computer und melden dich an.",
"hint": "Um Inhalte mit Elk zu teilen, muss Elk installiert sein und du musst angemeldet sein.", "hint": "Um Inhalte mit Elk zu teilen, muss Elk installiert sein und du musst angemeldet sein.",
"title": "Teile via Elk" "title": "Teile über Elk"
}, },
"state": { "state": {
"attachments_exceed_server_limit": "Die Anzahl der Anhänge hat das Limit pro Beitrag überschritten.", "attachments_exceed_server_limit": "Die Anzahl der Anhänge hat das Limit pro Beitrag überschritten.",
@ -387,7 +387,7 @@
"filter_show_anyway": "Trotzdem zeigen", "filter_show_anyway": "Trotzdem zeigen",
"img_alt": { "img_alt": {
"desc": "Beschreibung", "desc": "Beschreibung",
"dismiss": "Schliessen" "dismiss": "Schließen"
}, },
"poll": { "poll": {
"count": "{0} Stimmen|{0} Stimme|{0} Stimmen", "count": "{0} Stimmen|{0} Stimme|{0} Stimmen",
@ -401,7 +401,7 @@
"spoiler_show_less": "Zeige weniger", "spoiler_show_less": "Zeige weniger",
"spoiler_show_more": "Zeige mehr", "spoiler_show_more": "Zeige mehr",
"thread": "Thread", "thread": "Thread",
"try_original_site": "Versuche die original Seite" "try_original_site": "Versuche die ursprüngliche Seite"
}, },
"status_history": { "status_history": {
"created": "Erstellt: {0}", "created": "Erstellt: {0}",
@ -482,7 +482,7 @@
"sign_in_notice_title": "Anzeigen von {0} öffentlichen Daten", "sign_in_notice_title": "Anzeigen von {0} öffentlichen Daten",
"sign_out_account": "{0} abmelden", "sign_out_account": "{0} abmelden",
"tip_no_account": "Wenn du noch kein Mastodon-Konto hast, {0}.", "tip_no_account": "Wenn du noch kein Mastodon-Konto hast, {0}.",
"tip_register_account": "wähle einen Server aus und registriere eines" "tip_register_account": "wähle einen Server aus und registriere einen"
}, },
"visibility": { "visibility": {
"direct": "Nur erwähnte Accounts", "direct": "Nur erwähnte Accounts",

View file

@ -6,6 +6,9 @@
"favourite": "Favourite", "favourite": "Favourite",
"favourited": "Favourited" "favourited": "Favourited"
}, },
"menu": {
"show_favourited_and_boosted_by": "Show who favourited and boosted"
},
"nav": { "nav": {
"favourites": "Favourites" "favourites": "Favourites"
}, },

View file

@ -95,6 +95,7 @@
"common": { "common": {
"end_of_list": "End of the list", "end_of_list": "End of the list",
"error": "ERROR", "error": "ERROR",
"fetching": "Fetching...",
"in": "in", "in": "in",
"not_found": "404 Not Found", "not_found": "404 Not Found",
"offline_desc": "Seems like you are offline. Please check your network connection." "offline_desc": "Seems like you are offline. Please check your network connection."
@ -198,6 +199,31 @@
"remove_account": "Remove account from list", "remove_account": "Remove account from list",
"save": "Save changes" "save": "Save changes"
}, },
"magic_keys": {
"dialog_header": "Keyboard shortcuts",
"groups": {
"actions": {
"boost": "Boost",
"command_mode": "Command mode",
"compose": "Compose",
"favourite": "Favourite",
"title": "Actions",
"zen_mode": "Zen mode"
},
"media": {
"title": "Media"
},
"navigation": {
"go_to_home": "Home",
"go_to_notifications": "Notifications",
"next_status": "Next status",
"previous_status": "Previous status",
"shortcut_help": "Shortcut help",
"title": "Navigation"
}
},
"sequence_then": "then"
},
"menu": { "menu": {
"block_account": "Block {0}", "block_account": "Block {0}",
"block_domain": "Block domain {0}", "block_domain": "Block domain {0}",
@ -214,7 +240,7 @@
"open_in_original_site": "Open in original site", "open_in_original_site": "Open in original site",
"pin_on_profile": "Pin on profile", "pin_on_profile": "Pin on profile",
"share_post": "Share this post", "share_post": "Share this post",
"show_favourited_and_boosted_by": "Show who favourited and boosted", "show_favourited_and_boosted_by": "Show who favorited and boosted",
"show_reblogs": "Show boosts from {0}", "show_reblogs": "Show boosts from {0}",
"show_untranslated": "Show untranslated", "show_untranslated": "Show untranslated",
"toggle_theme": { "toggle_theme": {
@ -228,6 +254,9 @@
"unmute_conversation": "Unmute this post", "unmute_conversation": "Unmute this post",
"unpin_on_profile": "Unpin on profile" "unpin_on_profile": "Unpin on profile"
}, },
"modals": {
"aria_label_close": "Close"
},
"nav": { "nav": {
"back": "Go back", "back": "Go back",
"blocked_domains": "Blocked domains", "blocked_domains": "Blocked domains",
@ -337,6 +366,7 @@
"language": { "language": {
"display_language": "Display Language", "display_language": "Display Language",
"label": "Language", "label": "Language",
"status": "Translation status: {0}/{1} ({2}%)",
"translations": { "translations": {
"add": "Add", "add": "Add",
"choose_language": "Choose language", "choose_language": "Choose language",
@ -372,9 +402,10 @@
"save_settings": "Save settings", "save_settings": "Save settings",
"subscription_error": { "subscription_error": {
"clear_error": "Clear error", "clear_error": "Clear error",
"error_hint": "You can consult a list of frequently asked questions to try to solve the problem: {0}.",
"invalid_vapid_key": "The VAPID public key seems to be invalid.", "invalid_vapid_key": "The VAPID public key seems to be invalid.",
"permission_denied": "Permission denied: enable notifications in your browser.", "permission_denied": "Permission denied: enable notifications in your browser.",
"repo_link": "Elk's repository in Github", "repo_link": "Elk's repository in GitHub",
"request_error": "An error occurred while requesting the subscription, try again and if the error persists, please report the issue to the Elk repository.", "request_error": "An error occurred while requesting the subscription, try again and if the error persists, please report the issue to the Elk repository.",
"title": "Could not subscribe to push notifications", "title": "Could not subscribe to push notifications",
"too_many_registrations": "Due to browser limitations, Elk cannot use the push notifications service for multiple accounts on different servers. You should unsubscribe from push notifications on another account and try again.", "too_many_registrations": "Due to browser limitations, Elk cannot use the push notifications service for multiple accounts on different servers. You should unsubscribe from push notifications on another account and try again.",
@ -401,6 +432,8 @@
"notifications_settings": "Notifications", "notifications_settings": "Notifications",
"preferences": { "preferences": {
"enable_autoplay": "Enable Autoplay", "enable_autoplay": "Enable Autoplay",
"enable_data_saving": "Enable data saving",
"enable_data_saving_description": "Save data by preventing attachments from automatically loading.",
"enable_pinch_to_zoom": "Enable pinch to zoom", "enable_pinch_to_zoom": "Enable pinch to zoom",
"github_cards": "GitHub Cards", "github_cards": "GitHub Cards",
"grayscale_mode": "Grayscale mode", "grayscale_mode": "Grayscale mode",
@ -427,6 +460,8 @@
"label": "Appearance", "label": "Appearance",
"profile_metadata": "Profile metadata", "profile_metadata": "Profile metadata",
"profile_metadata_desc": "You can have up to {0} items displayed as a table on your profile", "profile_metadata_desc": "You can have up to {0} items displayed as a table on your profile",
"profile_metadata_label": "Label",
"profile_metadata_value": "Content",
"title": "Edit profile" "title": "Edit profile"
}, },
"featured_tags": { "featured_tags": {
@ -558,6 +593,7 @@
"explore_posts_intro": "These posts from this and other servers in the decentralized network are gaining traction on this server right now.", "explore_posts_intro": "These posts from this and other servers in the decentralized network are gaining traction on this server right now.",
"explore_tags_intro": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", "explore_tags_intro": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
"open_editor_tools": "Editor tools", "open_editor_tools": "Editor tools",
"pick_an_icon": "Pick an icon",
"publish_failed": "Close failed messages at the top of editor to republish posts", "publish_failed": "Close failed messages at the top of editor to republish posts",
"toggle_bold": "Toggle bold", "toggle_bold": "Toggle bold",
"toggle_code_block": "Toggle code block", "toggle_code_block": "Toggle code block",
@ -569,6 +605,7 @@
"sign_in_desc": "Sign in to follow profiles or hashtags, favorite, share and reply to posts, or interact from your account on a different server.", "sign_in_desc": "Sign in to follow profiles or hashtags, favorite, share and reply to posts, or interact from your account on a different server.",
"sign_in_notice_title": "Viewing {0} public data", "sign_in_notice_title": "Viewing {0} public data",
"sign_out_account": "Sign out {0}", "sign_out_account": "Sign out {0}",
"single_instance_sign_in_desc": "Sign in to follow profiles or hashtags, favorite, share and reply to posts.",
"tip_no_account": "If you don't have a Mastodon account yet, {0}.", "tip_no_account": "If you don't have a Mastodon account yet, {0}.",
"tip_register_account": "pick your server and register one" "tip_register_account": "pick your server and register one"
}, },

View file

@ -37,6 +37,7 @@
}, },
"common": { "common": {
"end_of_list": "Fin de la lista", "end_of_list": "Fin de la lista",
"fetching": "Listando...",
"offline_desc": "No tienes acceso a internet. Por favor, comprueba que tienes una conexión a la red." "offline_desc": "No tienes acceso a internet. Por favor, comprueba que tienes una conexión a la red."
}, },
"confirm": { "confirm": {
@ -150,7 +151,8 @@
"under_construction": "En desarrollo" "under_construction": "En desarrollo"
}, },
"preferences": { "preferences": {
"grayscale_mode": "Tema en escala de grises" "grayscale_mode": "Tema en escala de grises",
"hide_username_emojis_description": "Oculta, de la historia, los emojis en los nombres de usuario. Los emojis seguirán visibles en los perfiles."
}, },
"profile": { "profile": {
"appearance": { "appearance": {
@ -183,6 +185,7 @@
}, },
"tooltip": { "tooltip": {
"add_emojis": "Insertar emoji", "add_emojis": "Insertar emoji",
"add_media": "Insertar imágenes, un video o archivos de audio",
"change_content_visibility": "Cambiar visibilidad" "change_content_visibility": "Cambiar visibilidad"
}, },
"user": { "user": {

View file

@ -35,7 +35,7 @@
"posts_count": "{0} Publicaciones|{0} Publicación|{0} Publicaciones", "posts_count": "{0} Publicaciones|{0} Publicación|{0} Publicaciones",
"profile_description": "Encabezado del perfil de {0}", "profile_description": "Encabezado del perfil de {0}",
"profile_unavailable": "Perfil no disponible", "profile_unavailable": "Perfil no disponible",
"request_follow": "Solicitud para seguirte", "request_follow": "Solicitar seguirle",
"unblock": "Desbloquear", "unblock": "Desbloquear",
"unfollow": "Dejar de seguir", "unfollow": "Dejar de seguir",
"unmute": "Dejar de silenciar", "unmute": "Dejar de silenciar",
@ -95,6 +95,7 @@
"common": { "common": {
"end_of_list": "Fin", "end_of_list": "Fin",
"error": "ERROR", "error": "ERROR",
"fetching": "Cargando...",
"in": "en", "in": "en",
"not_found": "404 No Encontrado", "not_found": "404 No Encontrado",
"offline_desc": "Al parecer no tienes conexión a internet. Por favor, comprueba tu conexión a la red." "offline_desc": "Al parecer no tienes conexión a internet. Por favor, comprueba tu conexión a la red."
@ -337,6 +338,7 @@
"language": { "language": {
"display_language": "Idioma de pantalla", "display_language": "Idioma de pantalla",
"label": "Idioma", "label": "Idioma",
"status": "Estado traducción: {0}/{1} ({2}%)",
"translations": { "translations": {
"add": "Agregar", "add": "Agregar",
"choose_language": "Seleccionar idioma", "choose_language": "Seleccionar idioma",
@ -372,9 +374,10 @@
"save_settings": "Guardar cambios", "save_settings": "Guardar cambios",
"subscription_error": { "subscription_error": {
"clear_error": "Limpiar error", "clear_error": "Limpiar error",
"error_hint": "Puede consultar una lista de preguntas más frecuentes para tratar de solucionar el problema: {0}.",
"invalid_vapid_key": "La clave pública VAPID parece no ser válida.", "invalid_vapid_key": "La clave pública VAPID parece no ser válida.",
"permission_denied": "Permiso denegado: habilita las notificaciones en tu navegador.", "permission_denied": "Permiso denegado: habilita las notificaciones en tu navegador.",
"repo_link": "Repositorio de Elk en Github", "repo_link": "Repositorio de Elk en GitHub",
"request_error": "Se produjo un error al solicitar la suscripción, inténtalo de nuevo y si el error persiste, notifica la incidencia en el repositorio de Elk.", "request_error": "Se produjo un error al solicitar la suscripción, inténtalo de nuevo y si el error persiste, notifica la incidencia en el repositorio de Elk.",
"title": "No se pudo suscribir a las notificaciones push", "title": "No se pudo suscribir a las notificaciones push",
"too_many_registrations": "Debido a las limitaciones del navegador, Elk no puede habilitar las notificaciones push para múltiples cuentas en diferentes servidores. Deberá cancelar las subscripciones a notificaciones push en las otras cuentas e intentarlo de nuevo.", "too_many_registrations": "Debido a las limitaciones del navegador, Elk no puede habilitar las notificaciones push para múltiples cuentas en diferentes servidores. Deberá cancelar las subscripciones a notificaciones push en las otras cuentas e intentarlo de nuevo.",
@ -401,20 +404,25 @@
"notifications_settings": "Notificaciones", "notifications_settings": "Notificaciones",
"preferences": { "preferences": {
"enable_autoplay": "Habilitar reproducción automática", "enable_autoplay": "Habilitar reproducción automática",
"enable_data_saving": "Habilitar el ahorro de datos",
"enable_data_saving_description": "Ahorre datos al evitar que los archivos adjuntos se carguen automáticamente.",
"enable_pinch_to_zoom": "Habilitar pellizcar para hacer zoom", "enable_pinch_to_zoom": "Habilitar pellizcar para hacer zoom",
"github_cards": "Tarjetas GitHub", "github_cards": "Tarjetas GitHub",
"grayscale_mode": "Modo escala de grises", "grayscale_mode": "Modo escala de grises",
"hide_account_hover_card": "Ocultar tarjeta flotante de cuenta", "hide_account_hover_card": "Ocultar tarjeta flotante de cuenta",
"hide_alt_indi_on_posts": "Ocultar indicador ALT en publicaciones",
"hide_boost_count": "Ocultar contador de retoots", "hide_boost_count": "Ocultar contador de retoots",
"hide_favorite_count": "Ocultar número de publicaciones favoritas", "hide_favorite_count": "Ocultar número de publicaciones favoritas",
"hide_follower_count": "Ocultar número de seguidores", "hide_follower_count": "Ocultar número de seguidores",
"hide_reply_count": "Ocultar número de respuestas", "hide_reply_count": "Ocultar número de respuestas",
"hide_translation": "Ocultar traducción", "hide_translation": "Ocultar traducción",
"hide_username_emojis": "Ocultar emojis en el nombre de usuario", "hide_username_emojis": "Ocultar emojis en el nombre de usuario",
"hide_username_emojis_description": "Oculta los emojis de los nombres de usuarios en la línea de tiempo. Los emojis permanecerán visibles en sus perfiles.",
"label": "Preferencias", "label": "Preferencias",
"title": "Funcionalidades experimentales", "title": "Funcionalidades experimentales",
"user_picker": "Selector de usuarios", "user_picker": "Selector de usuarios",
"virtual_scroll": "Desplazamiento virtual" "virtual_scroll": "Desplazamiento virtual",
"wellbeing": "Bienestar"
}, },
"profile": { "profile": {
"appearance": { "appearance": {
@ -424,6 +432,8 @@
"label": "Apariencia", "label": "Apariencia",
"profile_metadata": "Metadatos de perfil", "profile_metadata": "Metadatos de perfil",
"profile_metadata_desc": "Puede mostrar hasta 4 elementos en forma de tabla en tu perfil", "profile_metadata_desc": "Puede mostrar hasta 4 elementos en forma de tabla en tu perfil",
"profile_metadata_label": "Texto",
"profile_metadata_value": "Contenido",
"title": "Editar perfil" "title": "Editar perfil"
}, },
"featured_tags": { "featured_tags": {
@ -463,8 +473,10 @@
"filter_removed_phrase": "Eliminado por filtrado", "filter_removed_phrase": "Eliminado por filtrado",
"filter_show_anyway": "Mostrar de todas formas", "filter_show_anyway": "Mostrar de todas formas",
"img_alt": { "img_alt": {
"ALT": "ALT",
"desc": "Descripción", "desc": "Descripción",
"dismiss": "Descartar" "dismiss": "Descartar",
"read": "Leer la descripción de la imagen {0}"
}, },
"poll": { "poll": {
"count": "{0} votos|{0} voto|{0} votos", "count": "{0} votos|{0} voto|{0} votos",
@ -553,6 +565,7 @@
"explore_posts_intro": "Estos mensajes de este y otros servidores de la red descentralizada están siendo tendencia ahora mismo en este servidor.", "explore_posts_intro": "Estos mensajes de este y otros servidores de la red descentralizada están siendo tendencia ahora mismo en este servidor.",
"explore_tags_intro": "Estas etiquetas están siendo tendencia ahora mismo entre los usuarios de este y otros servidores de la red descentralizada.", "explore_tags_intro": "Estas etiquetas están siendo tendencia ahora mismo entre los usuarios de este y otros servidores de la red descentralizada.",
"open_editor_tools": "Herramientas de edición", "open_editor_tools": "Herramientas de edición",
"pick_an_icon": "Selecciona un icono",
"publish_failed": "Cierra los mensajes fallidos en la parte superior del editor para volver a publicar", "publish_failed": "Cierra los mensajes fallidos en la parte superior del editor para volver a publicar",
"toggle_bold": "Cambiar a negrita", "toggle_bold": "Cambiar a negrita",
"toggle_code_block": "Cambiar a bloque de código", "toggle_code_block": "Cambiar a bloque de código",
@ -564,6 +577,7 @@
"sign_in_desc": "Inicia sesión para seguir perfiles o etiquetas, marcar cómo favorita, compartir y responder a publicaciones, o interactuar con un servidor diferente con tu usuario.", "sign_in_desc": "Inicia sesión para seguir perfiles o etiquetas, marcar cómo favorita, compartir y responder a publicaciones, o interactuar con un servidor diferente con tu usuario.",
"sign_in_notice_title": "Viendo información pública de {0}", "sign_in_notice_title": "Viendo información pública de {0}",
"sign_out_account": "Cerrar sesión {0}", "sign_out_account": "Cerrar sesión {0}",
"single_instance_sign_in_desc": "Inicia sesión para seguir perfiles o etiquetas, marcar cómo favorita, compartir y responder a publicaciones.",
"tip_no_account": "Si aún no tienes una cuenta Mastodon, {0}.", "tip_no_account": "Si aún no tienes una cuenta Mastodon, {0}.",
"tip_register_account": "selecciona tu servidor y registrate" "tip_register_account": "selecciona tu servidor y registrate"
}, },

593
locales/eu-ES.json Normal file
View file

@ -0,0 +1,593 @@
{
"a11y": {
"loading_page": "Orrialdea kargatzen, itxaron",
"loading_titled_page": "{0} orria kargatzen, itxaron",
"locale_changed": "Hizkuntza {0}ra aldatu da",
"locale_changing": "Hizkuntza aldatzen, itxaron",
"route_loaded": "{0} orrialdea kargatu da"
},
"account": {
"avatar_description": "{0}(r)en abatarra",
"blocked_by": "Erabiltzaile honek blokeatu zaitu.",
"blocked_domains": "Blokeatutako domeinuak",
"blocked_users": "Blokeatutako erabiltzaileak",
"blocking": "Blokeatuta",
"bot": "BOTa",
"favourites": "Gogokoak",
"follow": "Jarraitu",
"follow_back": "Jarraitu bera ere",
"follow_requested": "Eskaera bidalita",
"followers": "Jarraitzaileak",
"followers_count": "{0} jarraitzaile|Jarraitzaile {0}|{0} jarraitzaile",
"following": "Jarraitzen",
"following_count": "{0} jarraitzen",
"follows_you": "Jarraitzen dizu",
"go_to_profile": "Joan profilera",
"joined": "Batze-data:",
"moved_title": "adierazi du bere kontua aurrerantzean honakoa izango dela:",
"muted_users": "Mutututako erabiltzaileak",
"muting": "Mutututa",
"mutuals": "Komunean",
"notifications_on_post_disable": "Utzi jakinarazpenak bidaltzeari {username}(r)ek argitaratzen duenean",
"notifications_on_post_enable": "Jakinarazi {username}(r)ek argitaratzen duenean",
"pinned": "Finkatuta",
"posts": "Bidalketak",
"posts_count": "{0} bidalketa|Bidalketa {0}|{0} bidalketa",
"profile_description": "{0}(r)en profilaren goiburua",
"profile_unavailable": "Profila ez dago eskuragai",
"request_follow": "Bidali jarraipen-eskaera",
"unblock": "Utzi blokeatzeari",
"unfollow": "Utzi jarraitzeari",
"unmute": "Utzi mututzeari",
"view_other_followers": "Litekeena da beste instantziatako jarraitzaileak erakusgai ez egotea.",
"view_other_following": "Litekeena da beste instantziatan jarraitzen direnak erakusgai ez egotea."
},
"action": {
"apply": "Ezarri",
"bookmark": "Jarri laster-marka",
"bookmarked": "Laster-marka jarrita",
"boost": "Bultzatu",
"boost_count": "{0}",
"boosted": "Bultzatuta",
"clear_publish_failed": "Garbitu argitalpen erroreak",
"clear_upload_failed": "Garbitu fitxategi-igoeren erroreak",
"close": "Itxi",
"compose": "Idatzi",
"confirm": "Baieztatu",
"edit": "Editatu",
"enter_app": "Sartu aplikaziora",
"favourite": "Egin gogoko",
"favourite_count": "{0}",
"favourited": "Gogoko eginda",
"more": "Gehiago",
"next": "Hurrengoa",
"prev": "Aurrekoa",
"publish": "Argitaratu",
"reply": "Erantzun",
"reply_count": "{0}",
"reset": "Berrezarri",
"save": "Gorde",
"save_changes": "Gorde aldaketak",
"sign_in": "Hasi saioa",
"sign_in_to": "Hasi saioa {0}(e)n",
"switch_account": "Aldatu kontua",
"vote": "Eman botoa"
},
"app_desc_short": "Mastodon web-bezero arin bat",
"app_logo": "Elk Logo",
"app_name": "Elk",
"attachment": {
"edit_title": "Deskribapena",
"remove_label": "Kendu erantsitakoak"
},
"command": {
"activate": "Aktibatu",
"complete": "Osatu",
"compose_desc": "Idatzi bidalketa berria",
"n-people-in-the-past-n-days": "{0} pertsona azken {1} egunetan",
"select_lang": "Hautatu hizkuntza",
"sign_in_desc": "Gehitu dagoeneko existitzen den kontu bat",
"switch_account": "Aldatu {0}(e)ra",
"switch_account_desc": "Aldatu beste kontu batera",
"toggle_dark_mode": "Modu iluna",
"toggle_zen_mode": "ZEN modua"
},
"common": {
"end_of_list": "Zerrendaren amaiera",
"error": "ERROREA",
"fetching": "Eskuratzen…",
"in": "·",
"not_found": "404 Ez da aurkitu",
"offline_desc": "Lineaz kanpo zaudela dirudi. Egiaztatu konexioa."
},
"compose": {
"draft_title": "Zirriborroa {0}",
"drafts": "Zirriborroak ({v})"
},
"confirm": {
"block_account": {
"cancel": "Utzi",
"confirm": "Blokeatu",
"title": "Ziur {0} blokeatu nahi duzula?"
},
"block_domain": {
"cancel": "Utzi",
"confirm": "Blokeatu",
"title": "Ziur {0} domeinua blokeatu nahi duzula?"
},
"common": {
"cancel": "Ez",
"confirm": "Bai"
},
"delete_list": {
"cancel": "Utzi",
"confirm": "Ezabatu",
"title": "Ziur \"{0}\" zerrenda ezabatu nahi duzula?"
},
"delete_posts": {
"cancel": "Utzi",
"confirm": "Ezabatu",
"title": "Ziur bidalketa hau ezabatu nahi duzula?"
},
"mute_account": {
"cancel": "Utzi",
"confirm": "Mututu",
"title": "Ziur {0} mututu nahi duzula?"
},
"show_reblogs": {
"cancel": "Utzi",
"confirm": "Erakutsi",
"title": "Ziur {0}(r)en bultzadak ikusi nahi dituzula?"
},
"unfollow": {
"cancel": "Utzi",
"confirm": "Utzi jarraitzeari",
"title": "Ziur jarraitzeari utzi nahi diozula?"
}
},
"conversation": {
"with": "parte-hartzailea:"
},
"custom_cards": {
"stackblitz": {
"lines": "{0} lerro",
"open": "Ireki",
"snippet_from": "{0}(e)ko lagina"
}
},
"error": {
"account_not_found": "{0} kontua ez da aurkitu",
"explore-list-empty": "Ez dago joerarik une honetan. Zatoz geroago!",
"file_size_cannot_exceed_n_mb": "Fitxategien tamaina ezin da {0}MB baino handiagoa izan",
"sign_in_error": "Ezin izan da zerbitzarira konektatu.",
"status_not_found": "Ez da bidalketa aurkitu",
"unsupported_file_format": "Fitxategia ez da bateragarria"
},
"help": {
"build_preview": {
"desc1": "Komunitateak sortutako Elk-en aurreikuspen bat duzu aurrean - {0}.",
"desc2": "Litekeena da berrikusi ez diren aldaketak izatea, baita maltzurrak izan litezkeenak ere.",
"desc3": "Ez hasi saioa zure benetako kontuarekin.",
"title": "Preview deploy"
},
"desc_highlight": "Oso litekeena da erroreak eta ezaugarriak faltan egotea aplikazioan zehar.",
"desc_para1": "Eskerrik asko interesagatik eta Elk probatzeagatik, amaitu gabe dagoen Mastodonerako web-bezero arin bat!",
"desc_para2": "Gogor ari gara garapen- eta hobetze-lanetan.",
"desc_para3": "Garapenari bultzada emateko, GitHub Sponsors aukerari esker eman diezaiokezu babesa taldeari. Espero dugu Elk gogoko izatea!",
"desc_para4": "Elk kode irekikoa da. Laguntza eman nahi badiguzu probatzen, iritzia ematen edo kodea idazten,",
"desc_para5": "jarri harremanetan GitHub bidez",
"desc_para6": "eta hartu parte.",
"footer_team": "Elk taldea",
"title": "Elk-en aurreikuspena da hau!"
},
"language": {
"search": "Bilatu"
},
"list": {
"add_account": "Gehitu kontua zerrendara",
"cancel_edit": "Utzi editatzeari",
"clear_error": "Garbitu errorea",
"create": "Sortu",
"delete": "Ezabatu zerrenda",
"delete_error": "Errorea gertatu da zerrenda ezabatzerakoan",
"edit": "Editatu zerrenda",
"edit_error": "Errorea gertatu da zerrenda eguneratzerakoan",
"error": "Errorea gertatu da zerrenda sortzerakoan",
"error_prefix": "Errorea: ",
"list_title_placeholder": "Zerrendaren izena",
"modify_account": "Aldatu honako kontua duten zerrendak:",
"remove_account": "Kendu kontua zerrendatik",
"save": "Gorde aldaketak"
},
"menu": {
"block_account": "Blokeatu {0}",
"block_domain": "Blokeatu {0} domeinua",
"copy_link_to_post": "Kopiatu bidalketa honen esteka",
"copy_original_link_to_post": "Kopiatu bidalketa honen jatorrizko esteka",
"delete": "Ezabatu",
"delete_and_redraft": "Ezabatu eta berridatzi",
"direct_message_account": "Mezu zuzena {0}",
"edit": "Editatu",
"hide_reblogs": "Ezkutatu {0}(r)en bultzadak",
"mention_account": "Aipatu {0}",
"mute_account": "Mututu {0}",
"mute_conversation": "Mututu bidalketa",
"open_in_original_site": "Ireki jatorrizko orrian",
"pin_on_profile": "Finkatu profilean",
"share_post": "Partekatu bidalketa",
"show_favourited_and_boosted_by": "Erakutsi nork egin duen gogoko eta nork bultzatu duen",
"show_reblogs": "Erakutsi {0}(r)en bultzadak",
"show_untranslated": "Erakutsi jatorrizko hizkuntzan",
"toggle_theme": {
"dark": "Modu iluna",
"light": "Modu argia"
},
"translate_post": "Egin itzulpena",
"unblock_account": "Utzi {0} blokeatzeari",
"unblock_domain": "Utzi {0} domeinua blokeatzeari",
"unmute_account": "Utzi {0} mututzeari",
"unmute_conversation": "Utzi bidalketa mututzeari",
"unpin_on_profile": "Utzi finkatzeari"
},
"nav": {
"back": "Joan atzera",
"blocked_domains": "Blokeatutako domeinuak",
"blocked_users": "Blokeatutako erabiltzaileak",
"bookmarks": "Laster-markak",
"built_at": "Biltze-data: {0}",
"compose": "Idatzi",
"conversations": "Elkarrizketak",
"explore": "Esploratu",
"favourites": "Gogokoak",
"federated": "Federatua",
"home": "Hasiera",
"list": "Zerrenda",
"lists": "Zerrendak",
"local": "Lokala",
"muted_users": "Mutututako erabiltzaileak",
"notifications": "Jakinarazpenak",
"privacy": "Pribatutasuna",
"profile": "Profila",
"search": "Bilaketa",
"select_feature_flags": "Toggle Feature Flags",
"select_font_size": "Letra-tipoaren tamaina",
"select_language": "Hizkuntza",
"settings": "Ezarpenak",
"show_intro": "Erakutsi aurkezpena",
"toggle_theme": "Gai argia/iluna",
"zen_mode": "ZEN modua"
},
"notification": {
"favourited_post": "zure bidalketa gogoko egin du",
"followed_you": "jarraitu dizu",
"followed_you_count": "{0} pertsonak jarraitu dizute|pertsona {0}ek jarraitu dizu|{0} pertsonak jarraitu dizute",
"missing_type": "MISSING notification.type:",
"reblogged_post": "zure bidalketari bultzada eman dio",
"request_to_follow": "jarraipen-eskaera bidali dizu",
"signed_up": "izena eman du",
"update_status": "bidalketa eguneratu du"
},
"placeholder": {
"content_warning": "Idatzi oharra hemen",
"default_1": "Zer duzu buruan?",
"reply_to_account": "Erantzun {0}(r)i",
"replying": "Erantzuten",
"the_thread": "haria"
},
"pwa": {
"dismiss": "Baztertu",
"install": "Instalatu",
"install_title": "Instalatu Elk",
"title": "Eguneraketa berria eskuragai!",
"update": "Eguneratu",
"update_available_short": "Eguneratu Elk",
"webmanifest": {
"canary": {
"description": "Mastodon web-bezero arin bat (canary)",
"name": "Elk (canary)",
"short_name": "Elk (canary)"
},
"dev": {
"description": "Mastodon web-bezero arin bat (dev)",
"name": "Elk (dev)",
"short_name": "Elk (dev)"
},
"preview": {
"description": "Mastodon web-bezero arin bat (preview)",
"name": "Elk (preview)",
"short_name": "Elk (preview)"
},
"release": {
"description": "Mastodon web-bezero arin bat",
"name": "Elk",
"short_name": "Elk"
}
}
},
"search": {
"search_desc": "Bilatu pertsonak eta traolak",
"search_empty": "Ezin izan da ezer aurkitu"
},
"settings": {
"about": {
"built_at": "Biltze-data",
"label": "Honi buruz",
"meet_the_team": "Ezagutu taldea",
"sponsor_action": "Eman babesa",
"sponsor_action_desc": "Elk garatzen duen taldeari babesa emateko",
"sponsors": "Babesleak",
"sponsors_body_1": "Elk ondorengoen babes eskuzabalari eta laguntzari esker izan da posible egitea:",
"sponsors_body_2": "Baita Elk taldea eta bere kideak babesten dituzten enpresa eta norbanakoei esker.",
"sponsors_body_3": "Aplikazioa gogoko baduzu, agian babesa eman diezagukezu:",
"version": "Bertsioa"
},
"account_settings": {
"description": "Editatu zure kontuaren ezarpenak Mastodonen interfazean",
"label": "Kontuaren ezarpenak"
},
"interface": {
"color_mode": "Kolorea",
"dark_mode": "Iluna",
"default": " (defektuzkoa)",
"font_size": "Letra-tipoaren tamaina",
"label": "Interfazea",
"light_mode": "Argia",
"system_mode": "Sistemak darabilena",
"theme_color": "Gaiaren kolorea"
},
"language": {
"display_language": "Hizkuntza",
"label": "Hizkuntza",
"status": "Itzulpenaren egoera: {0}/{1} ({2}%)",
"translations": {
"add": "Gehitu",
"choose_language": "Hautatu hizkuntza",
"heading": "Itzulpenak",
"hide_specific": "Ezkutatu itzulpena ondorengo hizkuntzetarako:",
"remove": "Kendu"
}
},
"notifications": {
"label": "Jakinarazpenak",
"notifications": {
"label": "Jakinarazpenen ezarpenak"
},
"push_notifications": {
"alerts": {
"favourite": "Gogokoak",
"follow": "Jarraitzaile berriak",
"mention": "Aipamenak",
"poll": "Bozketak",
"reblog": "Zure bidalketen bultzadak",
"title": "Zer jakinarazpen jaso nahi duzu?"
},
"description": "Jaso jakinarazpenak Elk erabiltzen ari ez bazara ere.",
"instructions": "Ez ahaztu aldaketak gordetzeaz @:settings.notifications.push_notifications.save_settings botoia erabiliz!",
"label": "Push jakinarazpenen ezarpenak",
"policy": {
"all": "Edonorenak",
"followed": "Jarraitzen ditudan pertsonenak",
"follower": "Jarraitzen didaten pertsonenak",
"none": "Inorenak",
"title": "Noren jakinarazpenak jaso ditzazket?"
},
"save_settings": "Gorde ezarpenak",
"subscription_error": {
"clear_error": "Garbitu errorea",
"invalid_vapid_key": "Ez dirudi VAPID gako publikoa baliozkoa denik.",
"permission_denied": "Baimena ukatu da: gaitu jakinarazpenak zure nabigatzailean.",
"repo_link": "Elk-en biltegia GitHub-en",
"request_error": "Errorea gertatu da harpidetza eskatzerakoan, saiatu berriro eta errorea gertatuko balitz, jakinarazi Elk-en biltegian.",
"title": "Ezin izan da push jakinarazpenei harpidetu",
"too_many_registrations": "Nabigatzailearen mugak direla-eta, Elk-ek ezin ditu push jakinarazpenak zerbitzari desberdinetan dauden kontuetarako erabili. Beste kontu bateko push harpidetza utzi eta saiatu berriro.",
"vapid_not_supported": "Zure nabigatzailea webeko push jakinarazpenekin bateragarria da, baina ez dirudi VAPID protokoloa gauzatu duenik."
},
"title": "Push jakinarazpenen ezarpenak",
"undo_settings": "Desegin aldaketak",
"unsubscribe": "Ezgaitu push jakinarazpenak",
"unsupported": "Zure nabigatzailea ez da push jakinarazpenekin bateragarria.",
"warning": {
"enable_close": "Itxi",
"enable_description": "Elk irekita ez dagoenean jakinarazpenak jasotzeko, gaitu push jakinarazpenak. Zehatz mehatz kontrola dezakezu zer jakinarazpen mota jaso nahi duzun \"@:settings.notifications.show_btn{'\"'} botoiari esker aukera gaitu eta gero.",
"enable_description_desktop": "Elk irekita ez dagoenean jakinarazpenak jasotzeko, gaitu push jakinarazpenak. Zehatz mehatz kontrola dezakezu zer jakinarazpen mota jaso nahi duzun \"Settings > Notifications > Push notifications settings\" eremuan aukera gaitu eta gero.",
"enable_description_mobile": "Menuko nabigazioa ere erabil dezakezu ezarpenetara jotzeko: \"Settings > Notifications > Push notification settings\".",
"enable_description_settings": "Elk irekita ez dagoenean jakinarazpenak jasotzeko, gaitu push jakinarazpenak. Zehatz mehatz kontrola dezakezu zer jakinarazpen mota jaso nahi duzun pantaila honetan bertan aukera gaitu eta gero.",
"enable_desktop": "Gaitu push jakinarazpenak",
"enable_title": "Ez galdu ezer inoiz",
"re_auth": "Ez dirudi zure zerbitzaria push jakinarazpenekin bateragarria denik. Saiatu saioa amaitzen eta berriro hasten; mezu honek badirau, jarri harremanetan zure zerbitzariko administratzailearekin."
}
},
"show_btn": "Joan jakinarazpenen ezarpenetara",
"under_construction": "Amaitzeke"
},
"notifications_settings": "Jakinarazpenak",
"preferences": {
"enable_autoplay": "Gaitu erreproduzitze automatikoa",
"enable_data_saving": "Gaitu datu aurrezlea",
"enable_data_saving_description": "Aurreztu datuak eranskinen kargatze-automatikoa galarazten.",
"enable_pinch_to_zoom": "Gaitu atximur keinua zoom egiteko",
"github_cards": "GitHub Cards",
"grayscale_mode": "Gris modua",
"hide_account_hover_card": "Ezkutatu kontuaren geruza (Hide account hover card)",
"hide_alt_indi_on_posts": "Ezkutatu ALT adierazlea bidalketetan",
"hide_boost_count": "Ezkutatu bultzaden kopurua",
"hide_favorite_count": "Ezkutatu gogokoen kopurua",
"hide_follower_count": "Ezkutatu jarraitzaileen kopurua",
"hide_reply_count": "Ezkutatu erantzunen kopurua",
"hide_translation": "Ezkutatu itzulpenak",
"hide_username_emojis": "Ezkutatu emojiak erabiltzaile izenetan",
"hide_username_emojis_description": "Denbora-lerroetan erabiltzaile izenetako emojiak ezkutatzen ditu. Emojiak ikusgai egoten jarraituko dute euren profiletan.",
"label": "Hobespenak",
"title": "Ezaugarri esperimentalak",
"user_picker": "Erabiltzaile hautatzailea",
"virtual_scroll": "Korritze birtuala",
"wellbeing": "Ongizatea"
},
"profile": {
"appearance": {
"bio": "Bio",
"description": "Editatu abatarra, erabiltzaile izena, profila, etab.",
"display_name": "Pantaila izena",
"label": "Itxura",
"profile_metadata": "Profileko metadatuak",
"profile_metadata_desc": "Gehienez {0} item erakuts ditzakezu taula moduan profilean",
"profile_metadata_label": "Etiketa",
"profile_metadata_value": "Edukia",
"title": "Editatu profila"
},
"featured_tags": {
"description": "Jendeak traola hauek dituzten zure bidalketa publikoak ikus ditzazke.",
"label": "Nabarmendutako traolak"
},
"label": "Profila"
},
"select_a_settings": "Hautatu ezarpena",
"users": {
"export": "Esportatu erabiltzaile-tokenak",
"import": "Inportatu erabiltzaile-tokenak",
"label": "Saioa hasitako erabiltzaileak"
}
},
"share-target": {
"description": "Elk beste aplikazioetako edukia partekatu ahal izateko konfiguratu daiteke; Elk zure gailuan instalatu eta saioa hasi behar duzu, besterik ez.",
"hint": "Elk-ekin edukia partekatu nahi izatekotan, Elk instalatuta egon behar da eta saioa hasita.",
"title": "Partekatu Elk-ekin"
},
"state": {
"attachments_exceed_server_limit": "Erantsitakoen kopuruak bidalketaren muga gainditu du.",
"attachments_limit_error": "Bidalketaren muga gainditu da",
"edited": "(Editatua)",
"editing": "Editatzen",
"loading": "Kargatzen…",
"publish_failed": "Argitaratzeak huts egin du",
"publishing": "Argitaratzen",
"upload_failed": "Igotzeak huts egin du",
"uploading": "Igotzen…"
},
"status": {
"boosted_by": "Bultzatu dute:",
"edited": "Azken edizioa: {0}",
"favourited_by": "Gogoko egin dute:",
"filter_hidden_phrase": "Iragazia:",
"filter_removed_phrase": "Iragazki honek kendu du:",
"filter_show_anyway": "Erakutsi edonola ere",
"img_alt": {
"ALT": "ALT",
"desc": "Deskribapena",
"dismiss": "Baztertu",
"read": "Irakurri {0} deskribapena"
},
"poll": {
"count": "{0} boto|boto {0}|{0} boto",
"ends": "epemuga: {0}",
"finished": "amaiera: {0}"
},
"reblogged": "{0}(e)k bultzatua",
"replying_to": "{0}(r)i erantzunez",
"show_full_thread": "Erakutsi hari osoa",
"someone": "norbait",
"spoiler_show_less": "Erakutsi gutxiago",
"spoiler_show_more": "Erakutsi gehiago",
"thread": "Haria",
"try_original_site": "Probatu jatorrizko orrialdea"
},
"status_history": {
"created": "{0} sortua",
"edited": "{0} editatua"
},
"tab": {
"accounts": "Kontuak",
"for_you": "Zuretzat",
"hashtags": "Traolak",
"list": "Zerrendak",
"media": "Multimedia",
"news": "Albisteak",
"notifications_all": "Guztiak",
"notifications_mention": "Aipamenak",
"posts": "Bidalketak",
"posts_with_replies": "Bidalketak eta erantzunak"
},
"tag": {
"follow": "Jarraitu",
"follow_label": "Jarraitu {0} traola",
"unfollow": "Utzi jarraitzeari",
"unfollow_label": "Utzi {0} traola jarraitzeari"
},
"time_ago_options": {
"day_future": "0 egunetan|bihar|{n} egunetan",
"day_past": "duela 0 egun|atzo|duela {n} egun",
"hour_future": "0 ordutan|ordu batean|{n} ordutan",
"hour_past": "duela 0 ordu|duela ordubete|duela {n} ordu",
"just_now": "oraintxe bertan",
"minute_future": "0 minututan|minutu batean|{n} minututan",
"minute_past": "duela 0 minutu|duela minutu bat|duela {n} minutu",
"month_future": "0 hilabetetan|datorren hilabetean|{n} hilabetetan",
"month_past": "duela 0 hilabete|aurreko hilabetean|duela {n} hilabete",
"second_future": "oraintxe bertan|datorren segunduan|datozen {n} segundutan",
"second_past": "oraintxe bertan| duela segundu {n}|duela {n} segundu",
"short_day_future": "{n}e barru",
"short_day_past": "{n}e",
"short_hour_future": "{n}o barru",
"short_hour_past": "{n}o",
"short_minute_future": "{n}min barru",
"short_minute_past": "{n}min",
"short_month_future": "{n}h barru",
"short_month_past": "{n}h",
"short_second_future": "{n}s barru",
"short_second_past": "{n}s",
"short_week_future": "{n}a barru",
"short_week_past": "{n}a",
"short_year_future": "{n}u barru",
"short_year_past": "{n}u",
"week_future": "0 astetan|datorren astean|datozen {n} asteetan",
"week_past": "duela 0 aste|aurreko astean|duela {n} aste",
"year_future": "0 urtetan|datorren urtean|datozen {n} urteetan",
"year_past": "duela 0 urte|aurreko urtean|duela {n} urte"
},
"timeline": {
"show_new_items": "Erakutsi {v} elementu berri|Erakutsi elementu berri {v}|Erakutsi {v} elementu berri",
"view_older_posts": "Oso litekeena da beste instantziatako bidalketa zaharragoak ikusgai ez egotea."
},
"title": {
"federated_timeline": "Denbora-lerro federatua",
"local_timeline": "Denbora-lerro lokala"
},
"tooltip": {
"add_content_warning": "Gehitu edukiari buruzko oharra",
"add_emojis": "Gehitu emojiak",
"add_media": "Gehitu irudiak, bideoak edo audio fitxategi bat",
"add_publishable_content": "Gehitu argitaratzeko edukia",
"change_content_visibility": "Aldatu edukiaren ikusgaitasuna",
"change_language": "Aldatu hizkuntza",
"emoji": "Emojia",
"explore_links_intro": "Albiste hauei buruz ari dira hitz egiten sare deszentralizatutako zerbitzari honetan eta beste batzuetan.",
"explore_posts_intro": "Bidalketa hauek bogan daude deszentralizatutako zerbitzari honetan eta beste batzuetan.",
"explore_tags_intro": "Traola hauek joera dira deszentralizatutako zerbitzari honetan eta beste batzuetan.",
"open_editor_tools": "Editatzeko tresnak",
"pick_an_icon": "Hautatu ikonoa",
"publish_failed": "Itxi editorearen goikaldeko huts-egiteen mezuak bidalketa berrargitaratzeko",
"toggle_bold": "Lodia",
"toggle_code_block": "Kodea",
"toggle_italic": "Etzana"
},
"user": {
"add_existing": "Gehitu dagoeneko existitzen den kontu bat",
"server_address_label": "Mastodon zerbitzariaren helbidea",
"sign_in_desc": "Hasi saioa profil edo traolak jarraitzeko, eta bidalketak gogoko egiteko, partekatzeko, eta erantzuteko edo beste zerbitzari bateko kontuarekin elkarreragiteko.",
"sign_in_notice_title": "{0}(e)ko datu publikoak ikusten",
"sign_out_account": "Amaitu {0}(r)en saioa",
"single_instance_sign_in_desc": "Hasi saioa profil edo traolak jarraitzeko, eta bidalketak gogoko egiteko, partekatzeko, eta erantzuteko.",
"tip_no_account": "Oraindik Mastodon kontua ez badaukazu, {0}.",
"tip_register_account": "hautatu zerbitzaria eta eman izena"
},
"visibility": {
"direct": "Zuzena",
"direct_desc": "Aipatutako erabiltzaileek bakarrik ikusiko dute",
"private": "Jarraitzaileak bakarrik",
"private_desc": "Jarraitzen dizutenek bakarrik ikusiko dute",
"public": "Publikoa",
"public_desc": "Edonork ikusiko du",
"unlisted": "Zerrendatu gabea",
"unlisted_desc": "Edonork ikusiko du, baina ez da bilaketetan agertuko"
}
}

View file

@ -30,12 +30,12 @@
"mutuals": "Abonné·e·s", "mutuals": "Abonné·e·s",
"notifications_on_post_disable": "Arrêtez de me notifier lorsque {username} publie", "notifications_on_post_disable": "Arrêtez de me notifier lorsque {username} publie",
"notifications_on_post_enable": "M'avertir lorsque {username} publie", "notifications_on_post_enable": "M'avertir lorsque {username} publie",
"notify_on_post": "",
"pinned": "Épinglés", "pinned": "Épinglés",
"posts": "Messages", "posts": "Messages",
"posts_count": "{0} Messages", "posts_count": "{0} Messages",
"profile_description": "En-tête du profil de {0}", "profile_description": "En-tête du profil de {0}",
"profile_unavailable": "Profil non accessible", "profile_unavailable": "Profil non accessible",
"request_follow": "Demander à suivre",
"unblock": "Débloquer", "unblock": "Débloquer",
"unfollow": "Ne plus suivre", "unfollow": "Ne plus suivre",
"unmute": "Réafficher", "unmute": "Réafficher",
@ -69,6 +69,7 @@
"save": "Enregistrer", "save": "Enregistrer",
"save_changes": "Enregistrer les changements", "save_changes": "Enregistrer les changements",
"sign_in": "Se connecter", "sign_in": "Se connecter",
"sign_in_to": "Se connecter à {0}",
"switch_account": "Changer de compte", "switch_account": "Changer de compte",
"vote": "Voter" "vote": "Voter"
}, },
@ -94,6 +95,7 @@
"common": { "common": {
"end_of_list": "Fin de liste", "end_of_list": "Fin de liste",
"error": "ERREUR", "error": "ERREUR",
"fetching": "Récupération...",
"in": "sur", "in": "sur",
"not_found": "404 Introuvable", "not_found": "404 Introuvable",
"offline_desc": "Il semble que vous soyez hors-ligne. Vérifiez votre connexion internet." "offline_desc": "Il semble que vous soyez hors-ligne. Vérifiez votre connexion internet."
@ -115,8 +117,7 @@
}, },
"common": { "common": {
"cancel": "Non", "cancel": "Non",
"confirm": "Oui", "confirm": "Oui"
"title": "Êtes-vous sûr·e ?"
}, },
"delete_list": { "delete_list": {
"cancel": "Annuler", "cancel": "Annuler",
@ -205,9 +206,6 @@
"copy_original_link_to_post": "Copiez le lien d'origine vers ce message", "copy_original_link_to_post": "Copiez le lien d'origine vers ce message",
"delete": "Supprimer", "delete": "Supprimer",
"delete_and_redraft": "Supprimer et réécrire", "delete_and_redraft": "Supprimer et réécrire",
"delete_confirm": {
"cancel": ""
},
"direct_message_account": "Message direct à {0}", "direct_message_account": "Message direct à {0}",
"edit": "Éditer", "edit": "Éditer",
"hide_reblogs": "Cacher les partages de {0}", "hide_reblogs": "Cacher les partages de {0}",
@ -340,6 +338,7 @@
"language": { "language": {
"display_language": "Langue d'affichage", "display_language": "Langue d'affichage",
"label": "Langue", "label": "Langue",
"status": "État de la traduction : {0}/{1} ({2} %)",
"translations": { "translations": {
"add": "Ajouter", "add": "Ajouter",
"choose_language": "Choisir une langue", "choose_language": "Choisir une langue",
@ -375,10 +374,13 @@
"save_settings": "Enregistrer les paramètres", "save_settings": "Enregistrer les paramètres",
"subscription_error": { "subscription_error": {
"clear_error": "Effacer l'erreur", "clear_error": "Effacer l'erreur",
"invalid_vapid_key": "La clé publique VAPID ne semble pas être valide",
"permission_denied": "Autorisation refusée : activez les notifications dans votre navigateur.", "permission_denied": "Autorisation refusée : activez les notifications dans votre navigateur.",
"repo_link": "Dépôt Elk sur GitHub",
"request_error": "Une erreur s'est produite lors de la demande d'abonnement, réessayez et si l'erreur persiste, veuillez signaler le problème auprès du dépôt Elk.", "request_error": "Une erreur s'est produite lors de la demande d'abonnement, réessayez et si l'erreur persiste, veuillez signaler le problème auprès du dépôt Elk.",
"title": "Impossible de s'abonner aux notifications push", "title": "Impossible de s'abonner aux notifications push",
"too_many_registrations": "En raison des limitations du navigateur, Elk ne peut pas utiliser le service de notifications push pour plusieurs comptes sur différents serveurs. \nVous devez vous désabonner des notifications push sur un autre compte et réessayer." "too_many_registrations": "En raison des limitations du navigateur, Elk ne peut pas utiliser le service de notifications push pour plusieurs comptes sur différents serveurs. \nVous devez vous désabonner des notifications push sur un autre compte et réessayer.",
"vapid_not_supported": "Votre navigateur prend en charge les Notifications Push Web, mais ne semble pas implémenter le protocole VAPID."
}, },
"title": "Paramètres des notifications push", "title": "Paramètres des notifications push",
"undo_settings": "Annuler les changements de paramètres", "undo_settings": "Annuler les changements de paramètres",
@ -401,19 +403,25 @@
"notifications_settings": "Notifications", "notifications_settings": "Notifications",
"preferences": { "preferences": {
"enable_autoplay": "Activer la lecture automatique", "enable_autoplay": "Activer la lecture automatique",
"enable_data_saving": "Activer l'économie de données",
"enable_data_saving_description": "Economise les données en évitant le chargement automatique des médias.",
"enable_pinch_to_zoom": "Activer le zoom par pincement", "enable_pinch_to_zoom": "Activer le zoom par pincement",
"github_cards": "Cartes GitHub", "github_cards": "Cartes GitHub",
"grayscale_mode": "Mode niveaux de gris", "grayscale_mode": "Mode niveaux de gris",
"hide_account_hover_card": "Masquer la carte de survol du compte", "hide_account_hover_card": "Masquer la carte de survol du compte",
"hide_boost_count": "Cacher les compteurs de partages", "hide_alt_indi_on_posts": "Masquer l'indicateur alt sur les messsages",
"hide_favorite_count": "Cacher les compteurs de favoris", "hide_boost_count": "Masquer les compteurs de partages",
"hide_follower_count": "Cacher les compteurs d'abonné·e·s", "hide_favorite_count": "Masquer les compteurs de favoris",
"hide_reply_count": "Cacher les compteurs de réponses", "hide_follower_count": "Masquer les compteurs d'abonné·e·s",
"hide_translation": "Cacher traduction", "hide_reply_count": "Masquer les compteurs de réponses",
"hide_translation": "Masquer traduction",
"hide_username_emojis": "Masquer les emojis sur le nom d'utilisateur",
"hide_username_emojis_description": "Masque les emojis des noms d'utilisateur dans la timeline. \nLes emojis seront toujours visibles sur leurs profils.",
"label": "Préférences", "label": "Préférences",
"title": "Fonctionnalités expérimentales", "title": "Fonctionnalités expérimentales",
"user_picker": "User Picker", "user_picker": "User Picker",
"virtual_scroll": "Défilement virtuel" "virtual_scroll": "Défilement virtuel",
"wellbeing": "Bien-être"
}, },
"profile": { "profile": {
"appearance": { "appearance": {
@ -423,6 +431,8 @@
"label": "Apparence", "label": "Apparence",
"profile_metadata": "Métadonnées de profil", "profile_metadata": "Métadonnées de profil",
"profile_metadata_desc": "Vous pouvez avoir jusqu'à 4 éléments affichés sous forme de tableau sur votre profil", "profile_metadata_desc": "Vous pouvez avoir jusqu'à 4 éléments affichés sous forme de tableau sur votre profil",
"profile_metadata_label": "Label",
"profile_metadata_value": "Contenu",
"title": "Éditer le profil" "title": "Éditer le profil"
}, },
"featured_tags": { "featured_tags": {
@ -553,8 +563,12 @@
"explore_links_intro": "Ces actualités sont sujets à discussions sur ce serveur et sur d'autres serveurs du réseau décentralisé.", "explore_links_intro": "Ces actualités sont sujets à discussions sur ce serveur et sur d'autres serveurs du réseau décentralisé.",
"explore_posts_intro": "Ces publications de ce serveur et d'autres serveurs du réseau décentralisé gagnent du terrain sur ce serveur en ce moment.", "explore_posts_intro": "Ces publications de ce serveur et d'autres serveurs du réseau décentralisé gagnent du terrain sur ce serveur en ce moment.",
"explore_tags_intro": "Ces hashtags gagnent du terrain sur ce serveur et sur d'autres serveurs du réseau décentralisé.", "explore_tags_intro": "Ces hashtags gagnent du terrain sur ce serveur et sur d'autres serveurs du réseau décentralisé.",
"open_editor_tools": "Outils d'édition",
"pick_an_icon": "Choisir une icône",
"publish_failed": "Fermez les messages ayant échoué en haut de l'éditeur pour republier les messages", "publish_failed": "Fermez les messages ayant échoué en haut de l'éditeur pour republier les messages",
"toggle_code_block": "Ajouter un bloc de code" "toggle_bold": "Appliquer/retirer le gras",
"toggle_code_block": "Ajouter un bloc de code",
"toggle_italic": "Appliquer/retirer l'italique"
}, },
"user": { "user": {
"add_existing": "Ajouter un compte existant", "add_existing": "Ajouter un compte existant",
@ -562,6 +576,7 @@
"sign_in_desc": "Connectez-vous pour suivre des profils ou des hashtags, aimer, partagez et répondre à des messages, ou interagir à partir de votre compte d'autre serveur.", "sign_in_desc": "Connectez-vous pour suivre des profils ou des hashtags, aimer, partagez et répondre à des messages, ou interagir à partir de votre compte d'autre serveur.",
"sign_in_notice_title": "Affichage de {0} données publiques", "sign_in_notice_title": "Affichage de {0} données publiques",
"sign_out_account": "Se déconnecter de {0}", "sign_out_account": "Se déconnecter de {0}",
"single_instance_sign_in_desc": "Connectez-vous pour suivre des profils ou des hashtags, aimer, partager et répondre aux messages.",
"tip_no_account": "Si vous n'avez pas encore de compte Mastodon, {0}.", "tip_no_account": "Si vous n'avez pas encore de compte Mastodon, {0}.",
"tip_register_account": "choisissez votre serveur et enregistrez-en un" "tip_register_account": "choisissez votre serveur et enregistrez-en un"
}, },

View file

@ -19,7 +19,7 @@
"follow_requested": "Solicitado", "follow_requested": "Solicitado",
"followers": "Seguidoras", "followers": "Seguidoras",
"followers_count": "{0} Seguidoras|{0} Seguidora|{0} Seguidoras", "followers_count": "{0} Seguidoras|{0} Seguidora|{0} Seguidoras",
"following": "Seguimentos", "following": "Seguindo",
"following_count": "Seguindo a {0}", "following_count": "Seguindo a {0}",
"follows_you": "Séguete", "follows_you": "Séguete",
"go_to_profile": "Ir ao perfil", "go_to_profile": "Ir ao perfil",
@ -35,6 +35,7 @@
"posts_count": "{0} Publicacións|{0} Publicación|{0} Publicacións", "posts_count": "{0} Publicacións|{0} Publicación|{0} Publicacións",
"profile_description": "Cabeceira do perfil de {0}", "profile_description": "Cabeceira do perfil de {0}",
"profile_unavailable": "Perfil non dispoñible", "profile_unavailable": "Perfil non dispoñible",
"request_follow": "Solicitar seguimento",
"unblock": "Desbloquear", "unblock": "Desbloquear",
"unfollow": "Retirar seguimento", "unfollow": "Retirar seguimento",
"unmute": "Reactivar", "unmute": "Reactivar",
@ -68,6 +69,7 @@
"save": "Gardar", "save": "Gardar",
"save_changes": "Gardar cambios", "save_changes": "Gardar cambios",
"sign_in": "Acceder", "sign_in": "Acceder",
"sign_in_to": "Iniciar sesión en {0}",
"switch_account": "Cambiar de conta", "switch_account": "Cambiar de conta",
"vote": "Votar" "vote": "Votar"
}, },
@ -116,6 +118,11 @@
"cancel": "Non", "cancel": "Non",
"confirm": "Si" "confirm": "Si"
}, },
"delete_list": {
"cancel": "Cancelar",
"confirm": "Eliminar",
"title": "Tes a certeza de querer eliminar a lista \"{0}\"?"
},
"delete_posts": { "delete_posts": {
"cancel": "Cancelar", "cancel": "Cancelar",
"confirm": "Eliminar", "confirm": "Eliminar",
@ -169,11 +176,28 @@
"desc_para4": "Elk é Código Aberto. Se queres axudar probándoo, aportando a túa opinión, ou colaborando,", "desc_para4": "Elk é Código Aberto. Se queres axudar probándoo, aportando a túa opinión, ou colaborando,",
"desc_para5": "contacta con nós en GitHub", "desc_para5": "contacta con nós en GitHub",
"desc_para6": "e involúcrate.", "desc_para6": "e involúcrate.",
"footer_team": "O Equipo Elk",
"title": "Elk está de pre-estrea!" "title": "Elk está de pre-estrea!"
}, },
"language": { "language": {
"search": "Buscar" "search": "Buscar"
}, },
"list": {
"add_account": "Engadir conta á lista",
"cancel_edit": "Cancelar a edición",
"clear_error": "Limpar erro",
"create": "Crear",
"delete": "Elimina esta lista",
"delete_error": "Algo fallou ao querer eliminar a lista",
"edit": "Edita esta lista",
"edit_error": "Algo fallou ao querer actualizar a lista",
"error": "Houbo un fallo ao crear a lista",
"error_prefix": "Erro: ",
"list_title_placeholder": "Título da lista",
"modify_account": "Modificar listas coa conta",
"remove_account": "Retirar conta da lista",
"save": "Gardar cambios"
},
"menu": { "menu": {
"block_account": "Bloquear {0}", "block_account": "Bloquear {0}",
"block_domain": "Bloquear o dominio {0}", "block_domain": "Bloquear o dominio {0}",
@ -209,16 +233,19 @@
"blocked_domains": "Dominios bloqueados", "blocked_domains": "Dominios bloqueados",
"blocked_users": "Usuarias bloqueadas", "blocked_users": "Usuarias bloqueadas",
"bookmarks": "Marcadores", "bookmarks": "Marcadores",
"built_at": "Versión {0}", "built_at": "Publicada en {0}",
"compose": "Redactar", "compose": "Redactar",
"conversations": "Conversas", "conversations": "Conversas",
"explore": "Explorar", "explore": "Explorar",
"favourites": "Favoritas", "favourites": "Favoritas",
"federated": "Federada", "federated": "Federada",
"home": "Inicio", "home": "Inicio",
"list": "Lista",
"lists": "Listas",
"local": "Local", "local": "Local",
"muted_users": "Usuarias acaladas", "muted_users": "Usuarias acaladas",
"notifications": "Notificacións", "notifications": "Notificacións",
"privacy": "Privacidade",
"profile": "Perfil", "profile": "Perfil",
"search": "Buscar", "search": "Buscar",
"select_feature_flags": "Toggle Feature Flags", "select_feature_flags": "Toggle Feature Flags",
@ -243,11 +270,13 @@
"content_warning": "Escribe aquí o aviso", "content_warning": "Escribe aquí o aviso",
"default_1": "En que estás a pensar?", "default_1": "En que estás a pensar?",
"reply_to_account": "Responder a {0}", "reply_to_account": "Responder a {0}",
"replying": "Respondendo", "replying": "Responde aquí",
"the_thread": "a conversa" "the_thread": "a conversa"
}, },
"pwa": { "pwa": {
"dismiss": "Desbotar", "dismiss": "Desbotar",
"install": "Instalar",
"install_title": "Instalar Elk",
"title": "Dispoñible nova versión de Elk!", "title": "Dispoñible nova versión de Elk!",
"update": "Actualizar", "update": "Actualizar",
"update_available_short": "Actualizar Elk", "update_available_short": "Actualizar Elk",
@ -280,6 +309,7 @@
}, },
"settings": { "settings": {
"about": { "about": {
"built_at": "Data versión",
"label": "Acerca de", "label": "Acerca de",
"meet_the_team": "Coñece ao equipo", "meet_the_team": "Coñece ao equipo",
"sponsor_action": "Patrocínanos", "sponsor_action": "Patrocínanos",
@ -306,7 +336,15 @@
}, },
"language": { "language": {
"display_language": "Idioma da interface", "display_language": "Idioma da interface",
"label": "Idioma" "label": "Idioma",
"status": "Estado da tradución: {0}/{1} ({2}%)",
"translations": {
"add": "Engadir",
"choose_language": "Elixe idioma",
"heading": "Traducións",
"hide_specific": "Agochar determinadas traducións",
"remove": "Retirar"
}
}, },
"notifications": { "notifications": {
"label": "Notificacións", "label": "Notificacións",
@ -335,10 +373,13 @@
"save_settings": "Gardar axustes", "save_settings": "Gardar axustes",
"subscription_error": { "subscription_error": {
"clear_error": "Limpar erro", "clear_error": "Limpar erro",
"invalid_vapid_key": "A chave pública VAPID non semella válida.",
"permission_denied": "Permiso non concedido: activa as notificacións no navegador.", "permission_denied": "Permiso non concedido: activa as notificacións no navegador.",
"repo_link": "Repositorio de Elk en GitHub",
"request_error": "Algo fallou ao solicitar a subscrición, inténtao outra vez e se o erro continúa, informa do problema no repositorio de Elk.", "request_error": "Algo fallou ao solicitar a subscrición, inténtao outra vez e se o erro continúa, informa do problema no repositorio de Elk.",
"title": "Non se puido activar a subscrición a notificacións push", "title": "Non se puido activar a subscrición a notificacións push",
"too_many_registrations": "Debido a limitacións do navegador, Elk non pode usar o servizo de notificacións push para múltiples contas en diferentes servidores. Podes subscribirte ás notificacións push con outra conta e intentalo outra vez." "too_many_registrations": "Debido a limitacións do navegador, Elk non pode usar o servizo de notificacións push para múltiples contas en diferentes servidores. Podes subscribirte ás notificacións push con outra conta e intentalo outra vez.",
"vapid_not_supported": "O teu navegador ten soporte para Notificacións Web Push, pero non semella ter incluído o protocolo VAPID."
}, },
"title": "Axustes das Notificacións Push", "title": "Axustes das Notificacións Push",
"undo_settings": "Desfacer cambios", "undo_settings": "Desfacer cambios",
@ -355,22 +396,29 @@
"re_auth": "Semella que o teu servidor non ten soporte para notificacións Push. Inténtao pechando a sesión e volvendo a acceder, se esta mensaxe continúa aparecendo contacta coa administración do teu servidor." "re_auth": "Semella que o teu servidor non ten soporte para notificacións Push. Inténtao pechando a sesión e volvendo a acceder, se esta mensaxe continúa aparecendo contacta coa administración do teu servidor."
} }
}, },
"show_btn": "Ir aos axustes de notificacións" "show_btn": "Ir aos axustes de notificacións",
"under_construction": "En desenvolvemento"
}, },
"notifications_settings": "Notificacións", "notifications_settings": "Notificacións",
"preferences": { "preferences": {
"enable_autoplay": "Activa reprodución auto.", "enable_autoplay": "Activar reprodución auto.",
"enable_pinch_to_zoom": "Activar belisco para aumentar",
"github_cards": "GitHub Cards", "github_cards": "GitHub Cards",
"grayscale_mode": "Modo en escala de grises", "grayscale_mode": "Modo en escala de grises",
"hide_account_hover_card": "Non mostrar tarxetas emerxentes", "hide_account_hover_card": "Non mostrar tarxetas emerxentes",
"hide_alt_indi_on_posts": "Agochar indicador ALT nas publicacións",
"hide_boost_count": "Agochar conta de promocións", "hide_boost_count": "Agochar conta de promocións",
"hide_favorite_count": "Agochar conta de favoritos", "hide_favorite_count": "Agochar conta de favoritos",
"hide_follower_count": "Agochar conta de seguimentos", "hide_follower_count": "Agochar conta de seguimentos",
"hide_reply_count": "Agochar conta de respostas",
"hide_translation": "Agochar tradución", "hide_translation": "Agochar tradución",
"hide_username_emojis": "Agochar emojis nos nomes",
"hide_username_emojis_description": "Agocha nas cronoloxías os emojis nos nomes de usuaria. Emojis seguirán visibles na páxina de perfil da usuaria.",
"label": "Preferencias", "label": "Preferencias",
"title": "Características experimentais", "title": "Características experimentais",
"user_picker": "Selector de usuarias", "user_picker": "Selector de conta",
"virtual_scroll": "Desprazamento virtual" "virtual_scroll": "Desprazamento virtual",
"wellbeing": "Benestar"
}, },
"profile": { "profile": {
"appearance": { "appearance": {
@ -419,8 +467,10 @@
"filter_removed_phrase": "Eliminada polo filtro", "filter_removed_phrase": "Eliminada polo filtro",
"filter_show_anyway": "Mostrar igualmente", "filter_show_anyway": "Mostrar igualmente",
"img_alt": { "img_alt": {
"ALT": "Texto Alt",
"desc": "Descrición", "desc": "Descrición",
"dismiss": "Desbotar" "dismiss": "Desbotar",
"read": "Ler a descrición de {0}"
}, },
"poll": { "poll": {
"count": "{0} votos|{0} voto|{0} votos", "count": "{0} votos|{0} voto|{0} votos",
@ -441,8 +491,10 @@
"edited": "editada o {0}" "edited": "editada o {0}"
}, },
"tab": { "tab": {
"accounts": "Contas",
"for_you": "Para ti", "for_you": "Para ti",
"hashtags": "Cancelos", "hashtags": "Cancelos",
"list": "Lista",
"media": "Multimedia", "media": "Multimedia",
"news": "Novas", "news": "Novas",
"notifications_all": "Todo", "notifications_all": "Todo",
@ -506,8 +558,11 @@
"explore_links_intro": "Estes son os temas sobre os que están a conversar agora mesmo as persoas deste servidor e as dos outros servidores da rede descentralizada.", "explore_links_intro": "Estes son os temas sobre os que están a conversar agora mesmo as persoas deste servidor e as dos outros servidores da rede descentralizada.",
"explore_posts_intro": "Estas publicacións deste e outros servidores da rede descentralizada están aumentando a súa popularidade.", "explore_posts_intro": "Estas publicacións deste e outros servidores da rede descentralizada están aumentando a súa popularidade.",
"explore_tags_intro": "Está aumentando a popularidade destes cancelos entre as persoas deste e outros servidores da rede descentralizada.", "explore_tags_intro": "Está aumentando a popularidade destes cancelos entre as persoas deste e outros servidores da rede descentralizada.",
"open_editor_tools": "Ferramentas de edición",
"publish_failed": "Close failed messages at the top of editor to republish posts", "publish_failed": "Close failed messages at the top of editor to republish posts",
"toggle_code_block": "Activar bloque de código" "toggle_bold": "Activar grosa",
"toggle_code_block": "Activar bloque de código",
"toggle_italic": "Activar cursiva"
}, },
"user": { "user": {
"add_existing": "Engadir unha conta existente.", "add_existing": "Engadir unha conta existente.",
@ -515,6 +570,7 @@
"sign_in_desc": "Inicia sesión para seguir perfís e cancelos, favorecer, compartir ou responder a mensaxes, ou interactuar coa túa conta noutro servidor.", "sign_in_desc": "Inicia sesión para seguir perfís e cancelos, favorecer, compartir ou responder a mensaxes, ou interactuar coa túa conta noutro servidor.",
"sign_in_notice_title": "Vendo {0} datos públicos", "sign_in_notice_title": "Vendo {0} datos públicos",
"sign_out_account": "Pechar sesión {0}", "sign_out_account": "Pechar sesión {0}",
"single_instance_sign_in_desc": "Inicia sesión para seguir perfís e cancelos, favorecer, compartir ou responder a mensaxes.",
"tip_no_account": "Se aínda non tes unha conta Mastodon, {0}.", "tip_no_account": "Se aínda non tes unha conta Mastodon, {0}.",
"tip_register_account": "elixe un servidor para crear unha" "tip_register_account": "elixe un servidor para crear unha"
}, },

View file

@ -35,6 +35,7 @@
"posts_count": "{0} 投稿", "posts_count": "{0} 投稿",
"profile_description": "{0}さんのプロフィールヘッダー", "profile_description": "{0}さんのプロフィールヘッダー",
"profile_unavailable": "プロフィールが利用できません", "profile_unavailable": "プロフィールが利用できません",
"request_follow": "フォローをリクエスト",
"unblock": "ブロック解除", "unblock": "ブロック解除",
"unfollow": "フォロー解除", "unfollow": "フォロー解除",
"unmute": "ミュート解除", "unmute": "ミュート解除",
@ -68,6 +69,7 @@
"save": "保存する", "save": "保存する",
"save_changes": "変更を保存", "save_changes": "変更を保存",
"sign_in": "サインイン", "sign_in": "サインイン",
"sign_in_to": "{0}にサインイン",
"switch_account": "アカウントの切り替え", "switch_account": "アカウントの切り替え",
"vote": "投票" "vote": "投票"
}, },
@ -93,6 +95,7 @@
"common": { "common": {
"end_of_list": "リストの最後です", "end_of_list": "リストの最後です",
"error": "ERROR", "error": "ERROR",
"fetching": "取得中...",
"in": "in", "in": "in",
"not_found": "404 Not Found", "not_found": "404 Not Found",
"offline_desc": "現在オフラインのようです。ネットワーク接続を確認してください。" "offline_desc": "現在オフラインのようです。ネットワーク接続を確認してください。"
@ -183,9 +186,14 @@
"list": { "list": {
"add_account": "アカウントをリストに追加", "add_account": "アカウントをリストに追加",
"cancel_edit": "編集をキャンセル", "cancel_edit": "編集をキャンセル",
"clear_error": "エラーをクリア",
"create": "作成", "create": "作成",
"delete": "リストを削除", "delete": "リストを削除",
"delete_error": "リストの削除中にエラーが発生しました",
"edit": "リストを編集", "edit": "リストを編集",
"edit_error": "リストの更新中にエラーが発生しました",
"error": "リストの作成中にエラーが発生しました",
"error_prefix": "エラー: ",
"list_title_placeholder": "リストのタイトル", "list_title_placeholder": "リストのタイトル",
"modify_account": "このアカウントでリストを編集", "modify_account": "このアカウントでリストを編集",
"remove_account": "アカウントをリストから削除", "remove_account": "アカウントをリストから削除",
@ -330,6 +338,7 @@
"language": { "language": {
"display_language": "表示言語", "display_language": "表示言語",
"label": "言語", "label": "言語",
"status": "翻訳状況: {0}/{1} ({2}%)",
"translations": { "translations": {
"add": "追加", "add": "追加",
"choose_language": "言語を選択", "choose_language": "言語を選択",
@ -365,10 +374,14 @@
"save_settings": "設定を保存", "save_settings": "設定を保存",
"subscription_error": { "subscription_error": {
"clear_error": "エラーのクリア", "clear_error": "エラーのクリア",
"error_hint": "問題を解決するために、よくある質問の一覧を参照できます: {0}",
"invalid_vapid_key": "VAPID公開鍵が無効なようです。",
"permission_denied": "パーミッション拒否: ブラウザで通知を有効にしてください。", "permission_denied": "パーミッション拒否: ブラウザで通知を有効にしてください。",
"repo_link": "ElkのGitHubリポジトリ",
"request_error": "購読のリクエスト中にエラーが発生しました。再試行してエラーが続くかどうか確認してください。もしエラーが解消しない場合、Elkリポジトリにissueを報告してください。", "request_error": "購読のリクエスト中にエラーが発生しました。再試行してエラーが続くかどうか確認してください。もしエラーが解消しない場合、Elkリポジトリにissueを報告してください。",
"title": "プッシュ通知を購読できませんでした", "title": "プッシュ通知を購読できませんでした",
"too_many_registrations": "ブラウザーの制限により、Elkは異なるサーバー上の複数アカウントに対してプッシュ通知サービスを使用できません。他のアカウントのプッシュ通知の購読を解除して、再購読する必要があります。" "too_many_registrations": "ブラウザーの制限により、Elkは異なるサーバー上の複数アカウントに対してプッシュ通知サービスを使用できません。他のアカウントのプッシュ通知の購読を解除して、再購読する必要があります。",
"vapid_not_supported": "このブラウザはWeb Push通知をサポートしていませんが、VAPIDプロトコルをサポートしているようです。"
}, },
"title": "プッシュ通知の設定", "title": "プッシュ通知の設定",
"undo_settings": "変更を元に戻す", "undo_settings": "変更を元に戻す",
@ -391,19 +404,25 @@
"notifications_settings": "通知", "notifications_settings": "通知",
"preferences": { "preferences": {
"enable_autoplay": "自動再生を有効化", "enable_autoplay": "自動再生を有効化",
"enable_data_saving": "データセーバーを有効にする",
"enable_data_saving_description": "添付の自動読み込みを防止してデータを節約します。",
"enable_pinch_to_zoom": "ピンチによるズームを有効にする", "enable_pinch_to_zoom": "ピンチによるズームを有効にする",
"github_cards": "GitHubカード", "github_cards": "GitHubカード",
"grayscale_mode": "グレースケールモード", "grayscale_mode": "グレースケールモード",
"hide_account_hover_card": "アカウントのホバーカードを隠す", "hide_account_hover_card": "アカウントのホバーカードを隠す",
"hide_alt_indi_on_posts": "投稿上のalt表示を隠す",
"hide_boost_count": "ブーストの数を隠す", "hide_boost_count": "ブーストの数を隠す",
"hide_favorite_count": "お気に入りの数を隠す", "hide_favorite_count": "お気に入りの数を隠す",
"hide_follower_count": "フォロワーの数を隠す", "hide_follower_count": "フォロワーの数を隠す",
"hide_reply_count": "リプライの数を隠す", "hide_reply_count": "リプライの数を隠す",
"hide_translation": "翻訳を隠す", "hide_translation": "翻訳を隠す",
"hide_username_emojis": "ユーザー名の絵文字を隠す",
"hide_username_emojis_description": "タイムライン上でユーザー名に含まれる絵文字を隠します。プロフィールでは絵文字は引き続き表示されます。",
"label": "環境設定", "label": "環境設定",
"title": "実験的機能", "title": "実験的機能",
"user_picker": "ユーザーピッカー", "user_picker": "ユーザーピッカー",
"virtual_scroll": "仮想スクロール" "virtual_scroll": "仮想スクロール",
"wellbeing": "ウェルビーイング"
}, },
"profile": { "profile": {
"appearance": { "appearance": {
@ -413,6 +432,8 @@
"label": "外観", "label": "外観",
"profile_metadata": "プロフィールのメタデータ", "profile_metadata": "プロフィールのメタデータ",
"profile_metadata_desc": "プロフィールのテーブルには最大{0}項目まで表示できます", "profile_metadata_desc": "プロフィールのテーブルには最大{0}項目まで表示できます",
"profile_metadata_label": "ラベル",
"profile_metadata_value": "コンテンツ",
"title": "プロフィールを編集" "title": "プロフィールを編集"
}, },
"featured_tags": { "featured_tags": {
@ -543,7 +564,12 @@
"explore_links_intro": "これらのニュース記事は、分散型ネットワーク上のこのサーバーや他のサーバー上で現在話題になっています。", "explore_links_intro": "これらのニュース記事は、分散型ネットワーク上のこのサーバーや他のサーバー上で現在話題になっています。",
"explore_posts_intro": "これらの投稿は、分散型ネットワーク上のこのサーバーや他のサーバー上にあり、現在このサーバー上で注目を集めています。", "explore_posts_intro": "これらの投稿は、分散型ネットワーク上のこのサーバーや他のサーバー上にあり、現在このサーバー上で注目を集めています。",
"explore_tags_intro": "これらのハッシュタグは、分散型ネットワーク上のこのサーバーや他のサーバーにいるユーザーの間で現在注目を集めています。", "explore_tags_intro": "これらのハッシュタグは、分散型ネットワーク上のこのサーバーや他のサーバーにいるユーザーの間で現在注目を集めています。",
"toggle_code_block": "コードブロックを切り替え" "open_editor_tools": "エディタツール",
"pick_an_icon": "アイコンの選択",
"publish_failed": "投稿の再公開時にエディタ上部のエラーメッセージを閉じる",
"toggle_bold": "太字を切り替える",
"toggle_code_block": "コードブロックを切り替える",
"toggle_italic": "イタリックを切り替える"
}, },
"user": { "user": {
"add_existing": "既存のアカウントを追加する", "add_existing": "既存のアカウントを追加する",
@ -551,6 +577,7 @@
"sign_in_desc": "サインインすると、アカウントやハッシュタグのフォロー、お気に入りの追加、投稿の共有、投稿への返信のほか、異なるサーバー上のあなたのアカウントから交流できるようになります。", "sign_in_desc": "サインインすると、アカウントやハッシュタグのフォロー、お気に入りの追加、投稿の共有、投稿への返信のほか、異なるサーバー上のあなたのアカウントから交流できるようになります。",
"sign_in_notice_title": "{0}の公開データを表示しています", "sign_in_notice_title": "{0}の公開データを表示しています",
"sign_out_account": "{0}からサインアウト", "sign_out_account": "{0}からサインアウト",
"single_instance_sign_in_desc": "サインインすると、プロファイルやハッシュタグのフォロー、投稿のお気に入り、共有、返信ができるようになります。",
"tip_no_account": "まだMastodonアカウントを持っていない場合は、{0}しましょう。", "tip_no_account": "まだMastodonアカウントを持っていない場合は、{0}しましょう。",
"tip_register_account": "サーバーを選んでアカウントを登録" "tip_register_account": "サーバーを選んでアカウントを登録"
}, },

View file

@ -3,7 +3,7 @@
"loading_page": "Pagina laden, even wachten", "loading_page": "Pagina laden, even wachten",
"loading_titled_page": "Pagina {0} laden, even wachten", "loading_titled_page": "Pagina {0} laden, even wachten",
"locale_changed": "Taal veranderd naar {0}", "locale_changed": "Taal veranderd naar {0}",
"locale_changing": "Taal veranderen, even wachten", "locale_changing": "Taal veranderen, even geduld",
"route_loaded": "Pagina {0} geladen" "route_loaded": "Pagina {0} geladen"
}, },
"account": { "account": {
@ -99,7 +99,7 @@
"account_not_found": "Account {0} niet gevonden", "account_not_found": "Account {0} niet gevonden",
"explore-list-empty": "Er is nu niets trending. Kom later terug!", "explore-list-empty": "Er is nu niets trending. Kom later terug!",
"file_size_cannot_exceed_n_mb": "Bestandsgrootte mag niet groter zijn dan {0}MB", "file_size_cannot_exceed_n_mb": "Bestandsgrootte mag niet groter zijn dan {0}MB",
"sign_in_error": "Kan geen connectie maken met de server.", "sign_in_error": "Kan geen verbinding maken met de server.",
"status_not_found": "Post niet gevonden", "status_not_found": "Post niet gevonden",
"unsupported_file_format": "Bestandstype niet ondersteund" "unsupported_file_format": "Bestandstype niet ondersteund"
}, },
@ -130,7 +130,7 @@
"open_in_original_site": "Open in originele site", "open_in_original_site": "Open in originele site",
"pin_on_profile": "Pin op profiel", "pin_on_profile": "Pin op profiel",
"share_post": "Deel deze post", "share_post": "Deel deze post",
"show_untranslated": "Laat onvertaalde zien", "show_untranslated": "Laat onvertaalde versie zien",
"toggle_theme": { "toggle_theme": {
"dark": "Donkere modus wisselen", "dark": "Donkere modus wisselen",
"light": "Lichte modus wisselen" "light": "Lichte modus wisselen"
@ -163,7 +163,7 @@
"zen_mode": "Zen Modus" "zen_mode": "Zen Modus"
}, },
"notification": { "notification": {
"favourited_post": "vindt jou post favoriet", "favourited_post": "vindt jouw post favoriet",
"followed_you": "volgt jou", "followed_you": "volgt jou",
"followed_you_count": "{0} mensen hebben je gevolgd|{0} persoon heeft je gevold|{0} mensen hebben je gevolgd", "followed_you_count": "{0} mensen hebben je gevolgd|{0} persoon heeft je gevold|{0} mensen hebben je gevolgd",
"missing_type": "MISSEND notificatie.type:", "missing_type": "MISSEND notificatie.type:",
@ -196,14 +196,14 @@
"feature_flags": { "feature_flags": {
"github_cards": "GitHub Cards", "github_cards": "GitHub Cards",
"title": "Experimentele Functies", "title": "Experimentele Functies",
"user_picker": "Gebruiker Picker", "user_picker": "Gebruiker Kiezer",
"virtual_scroll": "Virtueel Scrollen" "virtual_scroll": "Virtueel Scrollen"
}, },
"interface": { "interface": {
"color_mode": "Kleur Modus", "color_mode": "Kleur Modus",
"dark_mode": "Donkere Modus", "dark_mode": "Donkere Modus",
"default": " (standaard)", "default": " (standaard)",
"font_size": "Font Grootte", "font_size": "Letter Grootte",
"label": "Interface", "label": "Interface",
"light_mode": "Lichte Modus" "light_mode": "Lichte Modus"
}, },
@ -225,9 +225,9 @@
"reblog": "Herblogt jou post", "reblog": "Herblogt jou post",
"title": "Welke notificaties wil je krijgen?" "title": "Welke notificaties wil je krijgen?"
}, },
"description": "Ontvang notificaties zelfs wanneer je elk niet gebruikt.", "description": "Ontvang meldingen zelfs wanneer je Elk niet gebruikt.",
"instructions": "Vergeet niet om je wijzigingen op te slaatn met de @:settings.notifications.push_notifications.save_settings knop!", "instructions": "Vergeet niet om je wijzigingen op te slaan d.m.v. de @:settings.notifications.push_notifications.save_settings knop!",
"label": "Push notificaties instellingen", "label": "Pushnotificatie instellingen",
"policy": { "policy": {
"all": "Van iedereen", "all": "Van iedereen",
"followed": "Van mensen die ik volg", "followed": "Van mensen die ik volg",
@ -238,23 +238,23 @@
"save_settings": "Instellingen aanpassingen opslaan", "save_settings": "Instellingen aanpassingen opslaan",
"subscription_error": { "subscription_error": {
"clear_error": "Wis error", "clear_error": "Wis error",
"permission_denied": "Geen toestemming: schakel notificaties in in je browser.", "permission_denied": "Geen toestemming: zet notificaties aan in je browser.",
"request_error": "Er is een error tijdens het ophalen van de subscriptie, probeer opnieuw en als de error blijft, raporteer het probleem naar de Elk repository.", "request_error": "Er is een error tijdens het ophalen van het abonnement, probeer opnieuw en als de error blijft, raporteer het probleem naar de Elk repository.",
"title": "Kon niet subscriben aan push notificaties" "title": "Kon niet abonneren op de pushnotificaties"
}, },
"undo_settings": "Veranderde instellingen ongedaan maken", "undo_settings": "Veranderde instellingen ongedaan maken",
"unsubscribe": "Zet pushnotificaties uit", "unsubscribe": "Zet pushnotificaties uit",
"unsupported": "Je browser ondersteunt geen pushnotificaties.", "unsupported": "Je browser ondersteunt geen pushnotificaties.",
"warning": { "warning": {
"enable_close": "Sluit", "enable_close": "Sluit",
"enable_description": "Om notificaties te krijgen terwijl Elk niet actief is, zet push notificaties aan. Je kan precies instellen wat voor typen interacties push notificaties genereren via de \"@:notification.settings.show_btn{'\"'} knop hierboven wanneer ze aan staan.", "enable_description": "Om notificaties te krijgen terwijl Elk niet actief is, zet pushnotificaties aan. Je kan precies instellen wat voor type interacties pushnotificaties genereren via de \"@:notification.settings.show_btn{'\"'} knop hierboven wanneer ze aan staan.",
"enable_description_settings": "Om notificaties te ontvangen wanneer Elk niet actief is, schakel push notificaties in. Je kunt precies instellen welke typen interacties een push notificatie activeren in dit venster, wanneer je ze hebt ingeschakeld.", "enable_description_settings": "Om notificaties te ontvangen wanneer Elk niet actief is, schakel pushnotificaties in. Je kunt precies instellen welke soorten interacties pushnotificaties genereren in dit venster zodra je ze hebt inschakelt.",
"enable_desktop": "Zet pushnotificaties aan", "enable_desktop": "Zet pushnotificaties aan",
"enable_title": "Mis niets", "enable_title": "Mis niets",
"re_auth": "Het lijkt er op dat je server geen push notificaties ondersteunt. Probeer uitloggen en opnieuw inloggen. Als deze boodschap nog steeds verschijnt, neem dan contact op met je server administrator." "re_auth": "Het lijkt erop dat je server geen pushnotificaties ondersteunt. Probeer uitloggen en opnieuw inloggen. Neem contact op met de serverbeheerder als dit bericht nog steeds verschijnt."
} }
}, },
"show_btn": "Ga naar notificaties instellingen" "show_btn": "Ga naar notificatie instellingen"
}, },
"notifications_settings": "Notificaties", "notifications_settings": "Notificaties",
"preferences": { "preferences": {
@ -267,7 +267,7 @@
"display_name": "Zichtbare naam", "display_name": "Zichtbare naam",
"label": "Uiterlijk", "label": "Uiterlijk",
"profile_metadata": "Profiel metadata", "profile_metadata": "Profiel metadata",
"profile_metadata_desc": "Je kan tot en met {0} elementen als table zetten op je profiel zetten", "profile_metadata_desc": "Je kan tot en met {0} elementen als tabel op je profiel zetten",
"title": "Aanpassen profiel" "title": "Aanpassen profiel"
}, },
"featured_tags": { "featured_tags": {
@ -284,6 +284,7 @@
} }
}, },
"state": { "state": {
"attachments_exceed_server_limit": "De hoeveelheid bijlagen is meer dan het limiet per post.", "attachments_exceed_server_limit": "De hoeveelheid bijlagen is meer dan het limiet per post.",
"attachments_limit_error": "Limiet per post overschreden", "attachments_limit_error": "Limiet per post overschreden",
"edited": "(Aangepast)", "edited": "(Aangepast)",
@ -383,7 +384,7 @@
"user": { "user": {
"add_existing": "Voeg een bestaand account toe", "add_existing": "Voeg een bestaand account toe",
"server_address_label": "Mastodon Server Address", "server_address_label": "Mastodon Server Address",
"sign_in_desc": "Inloggen om profielen of hashtags te volgen, markeer posts als favoriet, deel en reageer op posts, of communiceer vanaf je account op een andere server.", "sign_in_desc": "Log in om profielen te volgen of hashtags, markeer posts als favoriet, deel en reageer op posts, of reageer vanaf je account op een andere server.",
"sign_in_notice_title": "Je bekijkt {0} publieke data", "sign_in_notice_title": "Je bekijkt {0} publieke data",
"sign_out_account": "Uitloggen {0}", "sign_out_account": "Uitloggen {0}",
"tip_no_account": "Als je nog geen Mastodon account hebt, {0}.", "tip_no_account": "Als je nog geen Mastodon account hebt, {0}.",
@ -391,7 +392,7 @@
}, },
"visibility": { "visibility": {
"direct": "Direct", "direct": "Direct",
"direct_desc": "Alleen zichtbaar voor vermeldde gebruikers", "direct_desc": "Alleen zichtbaar voor vermelde gebruikers",
"private": "Alleen volgers", "private": "Alleen volgers",
"private_desc": "Alleen zichtbaar voor volgers", "private_desc": "Alleen zichtbaar voor volgers",
"public": "Publiek", "public": "Publiek",

View file

@ -16,7 +16,7 @@
"favourites": "Ulubione", "favourites": "Ulubione",
"follow": "Obserwuj", "follow": "Obserwuj",
"follow_back": "Przestań obserwować", "follow_back": "Przestań obserwować",
"follow_requested": "Wniosek", "follow_requested": "Prośba",
"followers": "Obserwujący", "followers": "Obserwujący",
"followers_count": "{0} Obserwujących|{0} Obserwujący|{0} Obserwujących|{0} Obserwujących", "followers_count": "{0} Obserwujących|{0} Obserwujący|{0} Obserwujących|{0} Obserwujących",
"following": "Obserwujesz", "following": "Obserwujesz",
@ -35,6 +35,7 @@
"posts_count": "{0} Wpisów|{0} Wpis|{0} Wpisy|{0} Wpisów", "posts_count": "{0} Wpisów|{0} Wpis|{0} Wpisy|{0} Wpisów",
"profile_description": "nagłówek profilu {0}", "profile_description": "nagłówek profilu {0}",
"profile_unavailable": "Profil niedostępny", "profile_unavailable": "Profil niedostępny",
"request_follow": "Prośba o śledzenie",
"unblock": "Odblokuj", "unblock": "Odblokuj",
"unfollow": "Przestań obserwować", "unfollow": "Przestań obserwować",
"unmute": "Wyłącz wyciszenie", "unmute": "Wyłącz wyciszenie",
@ -68,6 +69,7 @@
"save": "Zapisz", "save": "Zapisz",
"save_changes": "Zapisz zmiany", "save_changes": "Zapisz zmiany",
"sign_in": "Zaloguj się", "sign_in": "Zaloguj się",
"sign_in_to": "Zaloguj się do {0}",
"switch_account": "Przełącz konto", "switch_account": "Przełącz konto",
"vote": "Zagłosuj" "vote": "Zagłosuj"
}, },
@ -93,6 +95,7 @@
"common": { "common": {
"end_of_list": "Koniec listy", "end_of_list": "Koniec listy",
"error": "BŁĄD", "error": "BŁĄD",
"fetching": "Pobieranie...",
"in": "w", "in": "w",
"not_found": "404 Nie Znaleziono", "not_found": "404 Nie Znaleziono",
"offline_desc": "Wygląda na to, że jesteś offline. Sprawdź połączenie sieciowe." "offline_desc": "Wygląda na to, że jesteś offline. Sprawdź połączenie sieciowe."
@ -116,6 +119,11 @@
"cancel": "Nie", "cancel": "Nie",
"confirm": "Tak" "confirm": "Tak"
}, },
"delete_list": {
"cancel": "Anuluj",
"confirm": "Usuń",
"title": "Czy na pewno chcesz usunąć listę „{0}”?"
},
"delete_posts": { "delete_posts": {
"cancel": "Anuluj", "cancel": "Anuluj",
"confirm": "Usuń", "confirm": "Usuń",
@ -140,6 +148,13 @@
"conversation": { "conversation": {
"with": " " "with": " "
}, },
"custom_cards": {
"stackblitz": {
"lines": "Lines {0}",
"open": "Otwórz",
"snippet_from": "Snippet from {0}"
}
},
"error": { "error": {
"account_not_found": "Nie znaleziono konta {0}", "account_not_found": "Nie znaleziono konta {0}",
"explore-list-empty": "Nic nie jest w tej chwili popularne. Sprawdź później!", "explore-list-empty": "Nic nie jest w tej chwili popularne. Sprawdź później!",
@ -162,6 +177,7 @@
"desc_para4": "Elk jest Open Source. Jeśli chcesz pomóc w testowaniu, przekazać opinię lub wnieść swój wkład,", "desc_para4": "Elk jest Open Source. Jeśli chcesz pomóc w testowaniu, przekazać opinię lub wnieść swój wkład,",
"desc_para5": "skontaktuj się z nami na GitHub", "desc_para5": "skontaktuj się z nami na GitHub",
"desc_para6": "i zaangażuj się.", "desc_para6": "i zaangażuj się.",
"footer_team": "Zespół Elk",
"title": "Elk w wersji Preview!" "title": "Elk w wersji Preview!"
}, },
"language": { "language": {
@ -169,8 +185,19 @@
}, },
"list": { "list": {
"add_account": "Dodaj konto do listy", "add_account": "Dodaj konto do listy",
"cancel_edit": "Anuluj edycję",
"clear_error": "Usuń błąd",
"create": "Utwórz",
"delete": "Usuń listę",
"delete_error": "Podczas usuwania listy wystąpił błąd",
"edit": "Edytuj listę",
"edit_error": "Podczas aktualizowania listy wystąpił błąd",
"error": "Podczas tworzenia listy wystąpił błąd",
"error_prefix": "Błąd:",
"list_title_placeholder": "Tytuł listy",
"modify_account": "Modyfikacja listy", "modify_account": "Modyfikacja listy",
"remove_account": "Usuń konto z listy" "remove_account": "Usuń konto z listy",
"save": "Zapisz zmiany"
}, },
"menu": { "menu": {
"block_account": "Zablokuj {0}", "block_account": "Zablokuj {0}",
@ -283,6 +310,7 @@
}, },
"settings": { "settings": {
"about": { "about": {
"built_at": "Kompilacja",
"label": "Informacje", "label": "Informacje",
"meet_the_team": "Poznaj zespół", "meet_the_team": "Poznaj zespół",
"sponsor_action": "Wspomóż nas", "sponsor_action": "Wspomóż nas",
@ -310,6 +338,7 @@
"language": { "language": {
"display_language": "Język aplikacji", "display_language": "Język aplikacji",
"label": "Język", "label": "Język",
"status": "Stan tłumaczenia: {0}/{1} ({2}%)",
"translations": { "translations": {
"add": "Dodaj", "add": "Dodaj",
"choose_language": "Wybierz język", "choose_language": "Wybierz język",
@ -345,10 +374,14 @@
"save_settings": "Zapisz ustawienia", "save_settings": "Zapisz ustawienia",
"subscription_error": { "subscription_error": {
"clear_error": "Usuń błąd", "clear_error": "Usuń błąd",
"error_hint": "Możesz zapoznać się z listą najczęściej zadawanych pytań, aby spróbować rozwiązać problem: {0}.",
"invalid_vapid_key": "Wydaje się, że klucz publiczny VAPID jest nieprawidłowy.",
"permission_denied": "Brak uprawnień: włącz powiadomienia w przeglądarce.", "permission_denied": "Brak uprawnień: włącz powiadomienia w przeglądarce.",
"repo_link": "Repozytorium Elk w Github",
"request_error": "Wystąpił błąd podczas żądania subskrypcji, spróbuj ponownie, a jeśli błąd będzie się powtarzał, zgłoś problem do repozytorium Elk.", "request_error": "Wystąpił błąd podczas żądania subskrypcji, spróbuj ponownie, a jeśli błąd będzie się powtarzał, zgłoś problem do repozytorium Elk.",
"title": "Nie można zasubskrybować powiadomień push", "title": "Nie można zasubskrybować powiadomień push",
"too_many_registrations": "Ze względu na ograniczenia przeglądarki Elk nie może korzystać z usługi powiadomień push dla wielu kont na różnych serwerach. Powinieneś anulować subskrypcję powiadomień push na innym koncie i spróbować ponownie." "too_many_registrations": "Ze względu na ograniczenia przeglądarki Elk nie może korzystać z usługi powiadomień push dla wielu kont na różnych serwerach. Powinieneś anulować subskrypcję powiadomień push na innym koncie i spróbować ponownie.",
"vapid_not_supported": "Twoja przeglądarka obsługuje powiadomienia Web Push, ale wydaje się, że nie implementuje protokołu VAPID."
}, },
"title": "Ustawienia powiadomień push", "title": "Ustawienia powiadomień push",
"undo_settings": "Cofnij zmiany", "undo_settings": "Cofnij zmiany",
@ -365,23 +398,31 @@
"re_auth": "Wygląda na to, że Twój serwer nie obsługuje powiadomień push. Spróbuj wylogować się i zalogować ponownie, jeśli ten komunikat nadal się pojawia, skontaktuj się z administratorem serwera." "re_auth": "Wygląda na to, że Twój serwer nie obsługuje powiadomień push. Spróbuj wylogować się i zalogować ponownie, jeśli ten komunikat nadal się pojawia, skontaktuj się z administratorem serwera."
} }
}, },
"show_btn": "Przejdź do ustawień powiadomień" "show_btn": "Przejdź do ustawień powiadomień",
"under_construction": "W budowie"
}, },
"notifications_settings": "Powiadomienia", "notifications_settings": "Powiadomienia",
"preferences": { "preferences": {
"enable_autoplay": "Włącz autoodtwarzanie", "enable_autoplay": "Włącz autoodtwarzanie",
"enable_data_saving": "Włącz oszczędzanie danych",
"enable_data_saving_description": "Oszczędzaj dane, zapobiegając automatycznemu ładowaniu załączników.",
"enable_pinch_to_zoom": "Włącz powiększanie za pomocą gestów",
"github_cards": "GitHub Cards", "github_cards": "GitHub Cards",
"grayscale_mode": "Tryb skali szarości", "grayscale_mode": "Wpisy w odcieniach szarości",
"hide_account_hover_card": "Ukryj wizytówkę konta", "hide_account_hover_card": "Ukryj wizytówkę konta",
"hide_alt_indi_on_posts": "Ukryj wskaźnik ALT przy wpisach",
"hide_boost_count": "Ukryj liczbę podbić", "hide_boost_count": "Ukryj liczbę podbić",
"hide_favorite_count": "Ukryj liczbę polubień", "hide_favorite_count": "Ukryj liczbę polubień",
"hide_follower_count": "Ukryj liczbę obserwujących", "hide_follower_count": "Ukryj liczbę obserwujących",
"hide_reply_count": "Ukryj liczbę odpowiedzi", "hide_reply_count": "Ukryj liczbę odpowiedzi",
"hide_translation": "Ukryj funkcję tłumaczenia", "hide_translation": "Ukryj funkcję tłumaczenia",
"hide_username_emojis": "Ukryj emotikony w nazwie użytkownika",
"hide_username_emojis_description": "Ukrywa emotikony przed nazwami użytkowników na osi czasu. Emotikony będą nadal widoczne w ich profilach.",
"label": "Preferencje", "label": "Preferencje",
"title": "Funkcje eksperymentalne", "title": "Funkcje eksperymentalne",
"user_picker": "User Picker", "user_picker": "User Picker",
"virtual_scroll": "Virtual Scrolling" "virtual_scroll": "Virtual Scrolling",
"wellbeing": "Dla dobrego samopoczucia"
}, },
"profile": { "profile": {
"appearance": { "appearance": {
@ -391,6 +432,8 @@
"label": "Wygląd", "label": "Wygląd",
"profile_metadata": "Metadane profilu", "profile_metadata": "Metadane profilu",
"profile_metadata_desc": "Możesz mieć maksymalnie {0} elementy wyświetlane jako tabela w swoim profilu", "profile_metadata_desc": "Możesz mieć maksymalnie {0} elementy wyświetlane jako tabela w swoim profilu",
"profile_metadata_label": "Nazwa",
"profile_metadata_value": "Zawartość",
"title": "Edytuj profil" "title": "Edytuj profil"
}, },
"featured_tags": { "featured_tags": {
@ -430,8 +473,10 @@
"filter_removed_phrase": "Usunięto przez filtr", "filter_removed_phrase": "Usunięto przez filtr",
"filter_show_anyway": "Pokaż mimo wszystko", "filter_show_anyway": "Pokaż mimo wszystko",
"img_alt": { "img_alt": {
"ALT": "ALT",
"desc": "Opis", "desc": "Opis",
"dismiss": "Zamknij" "dismiss": "Zamknij",
"read": "Przeczytaj opis {0}"
}, },
"poll": { "poll": {
"count": "{0} głosów|{0} głos|{0} głosy|{0} głosów", "count": "{0} głosów|{0} głos|{0} głosy|{0} głosów",
@ -519,8 +564,12 @@
"explore_links_intro": "Te wiadomości obecnie są komentowane przez osoby z tego serwera i pozostałych w zdecentralizowanej sieci.", "explore_links_intro": "Te wiadomości obecnie są komentowane przez osoby z tego serwera i pozostałych w zdecentralizowanej sieci.",
"explore_posts_intro": "Te wpisy z tego i innych serwerów w zdecentralizowanej sieci zyskują teraz popularność na tym serwerze.", "explore_posts_intro": "Te wpisy z tego i innych serwerów w zdecentralizowanej sieci zyskują teraz popularność na tym serwerze.",
"explore_tags_intro": "Te hasztagi zyskują obecnie na popularności wśród osób na tym i innych serwerach zdecentralizowanej sieci.", "explore_tags_intro": "Te hasztagi zyskują obecnie na popularności wśród osób na tym i innych serwerach zdecentralizowanej sieci.",
"open_editor_tools": "Narzędzia edycji",
"pick_an_icon": "Wybierz ikonę",
"publish_failed": "Zamknij komunikaty o błędzie u góry edytora, aby ponownie opublikować wpisy", "publish_failed": "Zamknij komunikaty o błędzie u góry edytora, aby ponownie opublikować wpisy",
"toggle_code_block": "Przełączenie do trybu kodowania" "toggle_bold": "Zmień na pogrubienie",
"toggle_code_block": "Przełączenie do trybu kodowania",
"toggle_italic": "Zmień na kursywę"
}, },
"user": { "user": {
"add_existing": "Dodaj istniejące konto", "add_existing": "Dodaj istniejące konto",
@ -528,6 +577,7 @@
"sign_in_desc": "Zaloguj się, aby obserwować profile lub hasztagi, dodawać do ulubionych, udostępniać i odpowiadać na wpisy lub wchodzić w interakcje ze swojego konta na innym serwerze.", "sign_in_desc": "Zaloguj się, aby obserwować profile lub hasztagi, dodawać do ulubionych, udostępniać i odpowiadać na wpisy lub wchodzić w interakcje ze swojego konta na innym serwerze.",
"sign_in_notice_title": "Dane publiczne {0}", "sign_in_notice_title": "Dane publiczne {0}",
"sign_out_account": "Wyloguj {0}", "sign_out_account": "Wyloguj {0}",
"single_instance_sign_in_desc": "Zaloguj się, aby obserwować profile lub hashtagi, dodawać do ulubionych, udostępniać i odpowiadać na posty.",
"tip_no_account": "Jeśli nie masz jeszcze konta Mastodon, {0}.", "tip_no_account": "Jeśli nie masz jeszcze konta Mastodon, {0}.",
"tip_register_account": "wybierz swój serwer i zarejestruj się" "tip_register_account": "wybierz swój serwer i zarejestruj się"
}, },

View file

@ -35,6 +35,7 @@
"posts_count": "{0} Publicações|{0} Publicação|{0} Publicações", "posts_count": "{0} Publicações|{0} Publicação|{0} Publicações",
"profile_description": "Descrição de perfil de {0}", "profile_description": "Descrição de perfil de {0}",
"profile_unavailable": "Perfil indisponível", "profile_unavailable": "Perfil indisponível",
"request_follow": "Pedir para seguir",
"unblock": "Desbloquear", "unblock": "Desbloquear",
"unfollow": "Deixar de seguir", "unfollow": "Deixar de seguir",
"unmute": "Deixar de silenciar", "unmute": "Deixar de silenciar",
@ -68,6 +69,7 @@
"save": "Guardar", "save": "Guardar",
"save_changes": "Guardar alterações", "save_changes": "Guardar alterações",
"sign_in": "Entrar", "sign_in": "Entrar",
"sign_in_to": "Entrar em {0}",
"switch_account": "Mudar de conta", "switch_account": "Mudar de conta",
"vote": "Votar" "vote": "Votar"
}, },
@ -93,6 +95,7 @@
"common": { "common": {
"end_of_list": "Fim da lista", "end_of_list": "Fim da lista",
"error": "ERRO", "error": "ERRO",
"fetching": "A carregar...",
"in": "em", "in": "em",
"not_found": "404 Não Encontrado", "not_found": "404 Não Encontrado",
"offline_desc": "Parece que está offline. Por favor, confirme a sua conexão à internet." "offline_desc": "Parece que está offline. Por favor, confirme a sua conexão à internet."
@ -196,6 +199,31 @@
"remove_account": "Remover conta da lista", "remove_account": "Remover conta da lista",
"save": "Salvar alterações" "save": "Salvar alterações"
}, },
"magic_keys": {
"dialog_header": "Atalhos de teclado",
"groups": {
"actions": {
"boost": "Partilhar",
"command_mode": "Menu de comandos",
"compose": "Compor",
"favourite": "Adcionar aos favoritos",
"title": "Ações",
"zen_mode": "Modo Zen"
},
"media": {
"title": "Media"
},
"navigation": {
"go_to_home": "Início",
"go_to_notifications": "Notificações",
"next_status": "Publicação seguinte",
"previous_status": "Publicação anterior",
"shortcut_help": "Lista de atalhos",
"title": "Navegação"
}
},
"sequence_then": "seguido de"
},
"menu": { "menu": {
"block_account": "Bloquear {0}", "block_account": "Bloquear {0}",
"block_domain": "Bloquear domínio {0}", "block_domain": "Bloquear domínio {0}",
@ -226,6 +254,9 @@
"unmute_conversation": "Deixar de silenciar esta publicação", "unmute_conversation": "Deixar de silenciar esta publicação",
"unpin_on_profile": "Desafixar do perfil" "unpin_on_profile": "Desafixar do perfil"
}, },
"modals": {
"aria_label_close": "Fechar"
},
"nav": { "nav": {
"back": "Voltar", "back": "Voltar",
"blocked_domains": "Domínios bloqueados", "blocked_domains": "Domínios bloqueados",
@ -335,6 +366,7 @@
"language": { "language": {
"display_language": "Idioma de Apresentação", "display_language": "Idioma de Apresentação",
"label": "Idioma", "label": "Idioma",
"status": "Estado da tradução: {0}/{1} ({2}%)",
"translations": { "translations": {
"add": "Adicionar", "add": "Adicionar",
"choose_language": "Selecionar idioma", "choose_language": "Selecionar idioma",
@ -370,6 +402,7 @@
"save_settings": "Salvar configurações", "save_settings": "Salvar configurações",
"subscription_error": { "subscription_error": {
"clear_error": "Limpar erro", "clear_error": "Limpar erro",
"error_hint": "Pode consultar uma lista de perguntas frequentes para tentar solucionar o problema: {0}.",
"invalid_vapid_key": "A chave pública VAPID parece ser inválida.", "invalid_vapid_key": "A chave pública VAPID parece ser inválida.",
"permission_denied": "Permissão negada: habilite as notificações no seu browser.", "permission_denied": "Permissão negada: habilite as notificações no seu browser.",
"repo_link": "Repositório do Elk no GitHub", "repo_link": "Repositório do Elk no GitHub",
@ -399,6 +432,8 @@
"notifications_settings": "Notificações", "notifications_settings": "Notificações",
"preferences": { "preferences": {
"enable_autoplay": "Habilitar Reprodução Automática", "enable_autoplay": "Habilitar Reprodução Automática",
"enable_data_saving": "Habilitar poupança de dados",
"enable_data_saving_description": "Poupar dados, impedindo que os anexos sejam carregados automaticamente.",
"enable_pinch_to_zoom": "Habilitar afastar/aproximar dedos para fazer zoom", "enable_pinch_to_zoom": "Habilitar afastar/aproximar dedos para fazer zoom",
"github_cards": "Cartões do GitHub", "github_cards": "Cartões do GitHub",
"grayscale_mode": "Modo tons de cinza", "grayscale_mode": "Modo tons de cinza",
@ -409,7 +444,8 @@
"hide_follower_count": "Esconder contagem de seguidores", "hide_follower_count": "Esconder contagem de seguidores",
"hide_reply_count": "Esconder contagem de respostas", "hide_reply_count": "Esconder contagem de respostas",
"hide_translation": "Esconder botão de tradução", "hide_translation": "Esconder botão de tradução",
"hide_username_emojis": "Esconder emojis no nome de utilizador", "hide_username_emojis": "Esconder emojis do nome de utilizador",
"hide_username_emojis_description": "Esconde os emojis do nome de utilizador nas cronologias. Os Emojis continuarão a ser visíveis nos seus perfis.",
"label": "Preferências", "label": "Preferências",
"title": "Funcionalidades Experimentais", "title": "Funcionalidades Experimentais",
"user_picker": "Selecionador de Utilizador", "user_picker": "Selecionador de Utilizador",
@ -424,6 +460,8 @@
"label": "Aspeto", "label": "Aspeto",
"profile_metadata": "Metadados de perfil", "profile_metadata": "Metadados de perfil",
"profile_metadata_desc": "Pode ter até {0} itens expostos, em forma de tabela, no seu perfil", "profile_metadata_desc": "Pode ter até {0} itens expostos, em forma de tabela, no seu perfil",
"profile_metadata_label": "Rótulo",
"profile_metadata_value": "Conteúdo",
"title": "Editar perfil" "title": "Editar perfil"
}, },
"featured_tags": { "featured_tags": {
@ -554,15 +592,20 @@
"explore_links_intro": "Estas notícias estão, neste momento, a ser faladas por pessoas neste e noutros servidores da rede descentralizada.", "explore_links_intro": "Estas notícias estão, neste momento, a ser faladas por pessoas neste e noutros servidores da rede descentralizada.",
"explore_posts_intro": "Estas publicações deste e de outros servidores na rede descentralizada estão, neste momento, a ganhar popularidade neste servidor.", "explore_posts_intro": "Estas publicações deste e de outros servidores na rede descentralizada estão, neste momento, a ganhar popularidade neste servidor.",
"explore_tags_intro": "Estes hashtags estão, neste momento, a ganhar popularidade entre as pessoas neste e noutros servidores da rede descentralizada.", "explore_tags_intro": "Estes hashtags estão, neste momento, a ganhar popularidade entre as pessoas neste e noutros servidores da rede descentralizada.",
"open_editor_tools": "Ferramentas de edição",
"pick_an_icon": "Escolher um ícone",
"publish_failed": "Fechar mensagens de falha no topo do editor para republicar publicações", "publish_failed": "Fechar mensagens de falha no topo do editor para republicar publicações",
"toggle_code_block": "Alternar bloco de código" "toggle_bold": "Alternar negrito",
"toggle_code_block": "Alternar bloco de código",
"toggle_italic": "Alternar itálico"
}, },
"user": { "user": {
"add_existing": "Adicionar uma conta existente", "add_existing": "Adicionar uma conta existente",
"server_address_label": "Endereço do Servidor Mastodon", "server_address_label": "Endereço do Servidor Mastodon",
"sign_in_desc": "Entre, para seguir pessoas ou hashtags, adicionar aos favoritos, partilhar e responder a publicações, ou interagir a partir da sua conta de outro servidor.", "sign_in_desc": "Inicie sessão, para seguir pessoas ou hashtags, adicionar aos favoritos, partilhar e responder a publicações, ou interagir a partir da sua conta de outro servidor.",
"sign_in_notice_title": "A visualizar os dados públicos de {0}", "sign_in_notice_title": "A visualizar os dados públicos de {0}",
"sign_out_account": "Desconectar {0}", "sign_out_account": "Desconectar {0}",
"single_instance_sign_in_desc": "Inicie sessão, para seguir pessoas ou hashtags, adicionar aos favoritos, partilhar e responder a publicações.",
"tip_no_account": "Se ainda não tem uma conta Mastodon, {0}.", "tip_no_account": "Se ainda não tem uma conta Mastodon, {0}.",
"tip_register_account": "escolha um servidor e inscreva-se" "tip_register_account": "escolha um servidor e inscreva-se"
}, },

View file

@ -337,7 +337,7 @@
"subscription_error": { "subscription_error": {
"clear_error": "Очистить ошибки", "clear_error": "Очистить ошибки",
"permission_denied": "Ошибка: включите уведомления в вашем браузере.", "permission_denied": "Ошибка: включите уведомления в вашем браузере.",
"request_error": "При запросе подписки произошла ошибка, попробуйте еще раз, и если ошибка повторится, пожалуйста, сообщите о проблеме в Github репозиторий Elk.", "request_error": "При запросе подписки произошла ошибка, попробуйте еще раз, и если ошибка повторится, пожалуйста, сообщите о проблеме в GitHub репозиторий Elk.",
"title": "Не удалось подписаться на push-уведомления", "title": "Не удалось подписаться на push-уведомления",
"too_many_registrations": "Из-за ограничений браузера, Elk не может использовать службу push-уведомлений для нескольких учетных записей на разных серверах. Вам необходимо отказаться от подписки на push-уведомления в других аккаунтах и повторить попытку." "too_many_registrations": "Из-за ограничений браузера, Elk не может использовать службу push-уведомлений для нескольких учетных записей на разных серверах. Вам необходимо отказаться от подписки на push-уведомления в других аккаунтах и повторить попытку."
}, },

View file

@ -123,7 +123,7 @@
"desc_highlight": "Orada burada bir kaç hata ve eksik özellik bekleyin.", "desc_highlight": "Orada burada bir kaç hata ve eksik özellik bekleyin.",
"desc_para1": "Elk'i, bizim çalışması devam eden Mastodon web istemcimizi, denemedeki ilginiz için teşekkürler!", "desc_para1": "Elk'i, bizim çalışması devam eden Mastodon web istemcimizi, denemedeki ilginiz için teşekkürler!",
"desc_para2": "Zaman içinde geliştirmek ve iyileştirmek için çok çalışıyoruz.", "desc_para2": "Zaman içinde geliştirmek ve iyileştirmek için çok çalışıyoruz.",
"desc_para3": "Geliştirmemizi hızlandırmak için takıma Github Sponsors üzerinden sponsor olabilirsinizi. Umarız Elk'i beğenirsiniz!", "desc_para3": "Geliştirmemizi hızlandırmak için takıma GitHub Sponsors üzerinden sponsor olabilirsinizi. Umarız Elk'i beğenirsiniz!",
"desc_para4": "Elk açık kaynaklıdır. Test etmek, geri dönüş vermek veya katkıda bulunmak isterseniz,", "desc_para4": "Elk açık kaynaklıdır. Test etmek, geri dönüş vermek veya katkıda bulunmak isterseniz,",
"desc_para5": "GitHub'da bize ulaşın", "desc_para5": "GitHub'da bize ulaşın",
"desc_para6": "ve dahil olun.", "desc_para6": "ve dahil olun.",

View file

@ -35,6 +35,7 @@
"posts_count": "{0} 条帖文", "posts_count": "{0} 条帖文",
"profile_description": "{0} 的个人资料头图", "profile_description": "{0} 的个人资料头图",
"profile_unavailable": "个人资料不可见", "profile_unavailable": "个人资料不可见",
"request_follow": "请求关注",
"unblock": "取消拉黑", "unblock": "取消拉黑",
"unfollow": "取消关注", "unfollow": "取消关注",
"unmute": "取消屏蔽", "unmute": "取消屏蔽",
@ -68,6 +69,7 @@
"save": "保存", "save": "保存",
"save_changes": "保存更改", "save_changes": "保存更改",
"sign_in": "登鹿", "sign_in": "登鹿",
"sign_in_to": "登鹿 {0}",
"switch_account": "切换帐号", "switch_account": "切换帐号",
"vote": "投票" "vote": "投票"
}, },
@ -116,6 +118,11 @@
"cancel": "否", "cancel": "否",
"confirm": "是" "confirm": "是"
}, },
"delete_list": {
"cancel": "取消",
"confirm": "删除",
"title": "你确定要删除 \"{0}\" 列表吗?"
},
"delete_posts": { "delete_posts": {
"cancel": "取消", "cancel": "取消",
"confirm": "删除", "confirm": "删除",
@ -177,8 +184,19 @@
}, },
"list": { "list": {
"add_account": "向列表中添加用户", "add_account": "向列表中添加用户",
"cancel_edit": "取消编辑",
"clear_error": "清空错误",
"create": "创建",
"delete": "删除列表",
"delete_error": "删除列表时出现了一个错误",
"edit": "编辑列表",
"edit_error": "更新列表时出现了一个错误",
"error": "创建列表时出现了一个错误",
"error_prefix": "错误:",
"list_title_placeholder": "列表标题",
"modify_account": "修改列表中的用户", "modify_account": "修改列表中的用户",
"remove_account": "移除列表中的用户" "remove_account": "移除列表中的用户",
"save": "保存更改"
}, },
"menu": { "menu": {
"block_account": "拉黑 {0}", "block_account": "拉黑 {0}",
@ -319,6 +337,7 @@
"language": { "language": {
"display_language": "首选语言", "display_language": "首选语言",
"label": "语言", "label": "语言",
"status": "翻译进度: {0}/{1} ({2}%)",
"translations": { "translations": {
"add": "添加", "add": "添加",
"choose_language": "选择语言", "choose_language": "选择语言",
@ -354,10 +373,13 @@
"save_settings": "保存设置改动", "save_settings": "保存设置改动",
"subscription_error": { "subscription_error": {
"clear_error": "清除错误", "clear_error": "清除错误",
"invalid_vapid_key": "VAPID 密钥无效。",
"permission_denied": "权限不足:请在你的浏览器中打开通知权限。", "permission_denied": "权限不足:请在你的浏览器中打开通知权限。",
"repo_link": "鹿鸣在 Github 上的仓库",
"request_error": "请求订阅时发生了一个错误,请再次尝试。如错误仍然存在,请到鹿鸣代码仓库中报告这一问题。", "request_error": "请求订阅时发生了一个错误,请再次尝试。如错误仍然存在,请到鹿鸣代码仓库中报告这一问题。",
"title": "无法订阅推送通知。", "title": "无法订阅推送通知。",
"too_many_registrations": "由于浏览器限制,鹿鸣无法为不同服务器上的多个帐户使用推送通知服务。请取消订阅另一个帐户的推送通知,然后重试。" "too_many_registrations": "由于浏览器限制,鹿鸣无法为不同服务器上的多个帐户使用推送通知服务。请取消订阅另一个帐户的推送通知,然后重试。",
"vapid_not_supported": "你的浏览器支持推送通知功能,但似乎没有实现 VAPID 协议。"
}, },
"title": "推送通知设置", "title": "推送通知设置",
"undo_settings": "撤销设置改动", "undo_settings": "撤销设置改动",
@ -384,15 +406,19 @@
"github_cards": "GitHub 卡片", "github_cards": "GitHub 卡片",
"grayscale_mode": "灰色模式", "grayscale_mode": "灰色模式",
"hide_account_hover_card": "隐藏用户悬浮卡", "hide_account_hover_card": "隐藏用户悬浮卡",
"hide_alt_indi_on_posts": "隐藏帖文上的描述按钮",
"hide_boost_count": "隐藏转发数", "hide_boost_count": "隐藏转发数",
"hide_favorite_count": "隐藏收藏数", "hide_favorite_count": "隐藏收藏数",
"hide_follower_count": "隐藏关注者数", "hide_follower_count": "隐藏关注者数",
"hide_reply_count": "隐藏回复数", "hide_reply_count": "隐藏回复数",
"hide_translation": "隐藏翻译", "hide_translation": "隐藏翻译",
"hide_username_emojis": "隐藏用户昵称的表情符号",
"hide_username_emojis_description": "隐藏时间线上用户的表情符号。表情符号仍然会在用户个人资料中展示。",
"label": "首选项", "label": "首选项",
"title": "实验功能", "title": "实验功能",
"user_picker": "用户选择器", "user_picker": "用户选择器",
"virtual_scroll": "虚拟滚动" "virtual_scroll": "虚拟滚动",
"wellbeing": "社交偏好"
}, },
"profile": { "profile": {
"appearance": { "appearance": {
@ -402,6 +428,8 @@
"label": "个人资料", "label": "个人资料",
"profile_metadata": "个人资料附加信息", "profile_metadata": "个人资料附加信息",
"profile_metadata_desc": "这将会在个人资料页上以表格的形式展示,最多 {0} 个项目", "profile_metadata_desc": "这将会在个人资料页上以表格的形式展示,最多 {0} 个项目",
"profile_metadata_label": "描述",
"profile_metadata_value": "内容",
"title": "编辑个人资料" "title": "编辑个人资料"
}, },
"featured_tags": { "featured_tags": {
@ -441,8 +469,10 @@
"filter_removed_phrase": "从筛选中移除", "filter_removed_phrase": "从筛选中移除",
"filter_show_anyway": "仍然展示", "filter_show_anyway": "仍然展示",
"img_alt": { "img_alt": {
"ALT": "ALT",
"desc": "描述", "desc": "描述",
"dismiss": "关闭" "dismiss": "关闭",
"read": "查看 {0} 的描述"
}, },
"poll": { "poll": {
"count": "{0} 次投票", "count": "{0} 次投票",
@ -530,8 +560,11 @@
"explore_links_intro": "这些新闻故事正被本站和分布式网络上其他站点的用户谈论。", "explore_links_intro": "这些新闻故事正被本站和分布式网络上其他站点的用户谈论。",
"explore_posts_intro": "来自本站和分布式网络上其他站点的这些嘟文正在本站引起关注。", "explore_posts_intro": "来自本站和分布式网络上其他站点的这些嘟文正在本站引起关注。",
"explore_tags_intro": "这些标签正在本站和分布式网络上其他站点的用户中引起关注。", "explore_tags_intro": "这些标签正在本站和分布式网络上其他站点的用户中引起关注。",
"open_editor_tools": "编辑器工具",
"publish_failed": "关闭编辑器上方的错误信息以重新发布帖文。", "publish_failed": "关闭编辑器上方的错误信息以重新发布帖文。",
"toggle_code_block": "切换代码块" "toggle_bold": "切换加粗",
"toggle_code_block": "切换代码块",
"toggle_italic": "切换斜体"
}, },
"user": { "user": {
"add_existing": "添加现有帐户", "add_existing": "添加现有帐户",
@ -539,6 +572,7 @@
"sign_in_desc": "登录后可关注其他人或标签、点赞、分享和回复帖文,或与不同服务器上的账号交互。", "sign_in_desc": "登录后可关注其他人或标签、点赞、分享和回复帖文,或与不同服务器上的账号交互。",
"sign_in_notice_title": "正在查看 {0} 的公共数据", "sign_in_notice_title": "正在查看 {0} 的公共数据",
"sign_out_account": "登出 {0}", "sign_out_account": "登出 {0}",
"single_instance_sign_in_desc": "登录后可关注其他人或标签、收藏、分享和回复帖文。",
"tip_no_account": "如果您还没有 Mastodon 账户,{0}。", "tip_no_account": "如果您还没有 Mastodon 账户,{0}。",
"tip_register_account": "选择您的服务器并注册一个" "tip_register_account": "选择您的服务器并注册一个"
}, },

Some files were not shown because too many files have changed in this diff Show more