mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-01-06 05:18:32 -05:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65c35adc9d | ||
|
|
83b4846f0c | ||
|
|
6bc7ada20a | ||
|
|
8ce6f9038a | ||
|
|
e3c6d4c66c | ||
|
|
381a698220 | ||
|
|
c866557d58 | ||
|
|
bb5da2cb54 | ||
|
|
0fed5f54f6 | ||
|
|
f4bde93960 | ||
|
|
62300deea0 | ||
|
|
87f4b23711 | ||
|
|
8983745106 | ||
|
|
8872fd52cd | ||
|
|
b81b97d934 | ||
|
|
f798fafb3e | ||
|
|
dbbbe06a23 | ||
|
|
4b9eb5077a | ||
|
|
ff6db2374d | ||
|
|
3e69ea94d5 | ||
|
|
2e114cfa69 | ||
|
|
eb34ef0156 | ||
|
|
446755f678 | ||
|
|
08fe2d32b0 | ||
|
|
fb653ee2f6 | ||
|
|
a326a8c717 | ||
|
|
6e7cb5fb86 | ||
|
|
9289bd8e05 | ||
|
|
985b5634b7 | ||
|
|
2b2bc041bd | ||
|
|
6e16d4cc91 | ||
|
|
53a566d08a | ||
|
|
fb0a747549 | ||
|
|
6e045bf0c3 | ||
|
|
8d1ce5c190 | ||
|
|
3bf6840cbc | ||
|
|
0053f76531 | ||
|
|
05ac18f00b | ||
|
|
8b6c75877d | ||
|
|
0e25c7485d | ||
|
|
ea0d2ece6a | ||
|
|
f7e595b404 | ||
|
|
d48320f0a5 | ||
|
|
2240ab01d2 | ||
|
|
ae9276b55c | ||
|
|
b5643a9399 | ||
|
|
702180aeda | ||
|
|
458d2bb61b | ||
|
|
5a83f55a00 | ||
|
|
04ef4037b7 | ||
|
|
fdb5ff9ec0 | ||
|
|
302002d630 | ||
|
|
2305438423 | ||
|
|
34bd4a74c2 | ||
|
|
dacd0acff6 | ||
|
|
010c6d8eb2 | ||
|
|
3eac3e6648 | ||
|
|
3dd61f7742 | ||
|
|
99fec90288 | ||
|
|
d05f27dfe5 | ||
|
|
4c84f48e81 | ||
|
|
441b51a6e7 | ||
|
|
bf2a69735d | ||
|
|
61511d17d3 | ||
|
|
248e560a5c | ||
|
|
09beac24c8 |
@@ -24,6 +24,8 @@ jobs:
|
||||
image-ref: "mealie"
|
||||
format: "sarif"
|
||||
output: "trivy-results.sarif"
|
||||
env:
|
||||
TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
|
||||
26
.github/workflows/scheduled-checks.yml
vendored
26
.github/workflows/scheduled-checks.yml
vendored
@@ -15,8 +15,30 @@ jobs:
|
||||
- name: Checkout 🛎
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Update pre-commit Hooks
|
||||
uses: vrslev/pre-commit-autoupdate@v1.0.0
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Set PY
|
||||
shell: bash
|
||||
run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pre-commit
|
||||
~/.cache/pip
|
||||
key: pre-commit-${{ env.PY }}-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
|
||||
- name: Install pre-commit
|
||||
shell: bash
|
||||
run: pip install -U pre-commit
|
||||
|
||||
- name: Run `pre-commit autoupdate`
|
||||
shell: bash
|
||||
run: pre-commit autoupdate --color=always
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
|
||||
@@ -12,7 +12,7 @@ repos:
|
||||
exclude: ^tests/data/
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.7.0
|
||||
rev: v0.7.2
|
||||
hooks:
|
||||
- id: ruff
|
||||
- id: ruff-format
|
||||
|
||||
@@ -5,6 +5,7 @@ vars:
|
||||
GREETING: Hello, World!
|
||||
env:
|
||||
DEFAULT_GROUP: Home
|
||||
DEFAULT_HOUSEHOLD: Family
|
||||
PRODUCTION: false
|
||||
API_PORT: 9000
|
||||
API_DOCS: True
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
"""'Add summary to recipe instructions'
|
||||
|
||||
Revision ID: 3897397b4631
|
||||
Revises: 86054b40fd06
|
||||
Create Date: 2024-10-20 09:47:46.844436
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
import mealie.db.migration_types
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "3897397b4631"
|
||||
down_revision: str | None = "86054b40fd06"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("recipe_instructions", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("summary", sa.String(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("recipe_instructions", schema=None) as batch_op:
|
||||
batch_op.drop_column("summary")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -131,7 +131,6 @@ HEALTHCHECK CMD python $MEALIE_HOME/mealie/scripts/healthcheck.py || exit 1
|
||||
# ----------------------------------
|
||||
# Copy Frontend
|
||||
|
||||
# copying caddy into image
|
||||
ENV STATIC_FILES=/spa/static
|
||||
COPY --from=builder /app/dist ${STATIC_FILES}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ Before you can start using OIDC Authentication, you must first configure a new c
|
||||
The redirect URI(s) that are needed:
|
||||
|
||||
1. `http(s)://DOMAIN:PORT/login`
|
||||
2. `https(s)://DOMAIN:PORT/login?direct=1`
|
||||
2. `http(s)://DOMAIN:PORT/login?direct=1`
|
||||
1. This URI is only required if your IdP supports [RP-Initiated Logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html) such as Keycloak. You may also be able to combine this into the previous URI by using a wildcard: `http(s)://DOMAIN:PORT/login*`
|
||||
|
||||
The redirect URI(s) should include any URL that Mealie is accessible from. Some examples include
|
||||
|
||||
@@ -28,7 +28,7 @@ Before you can start using OIDC Authentication, you must first configure a new c
|
||||
The redirect URI(s) that are needed:
|
||||
|
||||
1. `http(s)://DOMAIN:PORT/login`
|
||||
2. `https(s)://DOMAIN:PORT/login?direct=1`
|
||||
2. `http(s)://DOMAIN:PORT/login?direct=1`
|
||||
1. This URI is only required if your IdP supports [RP-Initiated Logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html) such as Keycloak. You may also be able to combine this into the previous URI by using a wildcard: `http(s)://DOMAIN:PORT/login*`
|
||||
|
||||
The redirect URI(s) should include any URL that Mealie is accessible from. Some examples include
|
||||
|
||||
@@ -4,20 +4,21 @@
|
||||
|
||||
### General
|
||||
|
||||
| Variables | Default | Description |
|
||||
| ----------------------------- | :-------------------: | --------------------------------------------------------------------------------------------------------- |
|
||||
| PUID | 911 | UserID permissions between host OS and container |
|
||||
| PGID | 911 | GroupID permissions between host OS and container |
|
||||
| DEFAULT_GROUP | Home | The default group for users |
|
||||
| BASE_URL | http://localhost:8080 | Used for Notifications |
|
||||
| TOKEN_TIME | 48 | The time in hours that a login/auth token is valid |
|
||||
| API_PORT | 9000 | The port exposed by backend API. **Do not change this if you're running in Docker** |
|
||||
| API_DOCS | True | Turns on/off access to the API documentation locally |
|
||||
| TZ | UTC | Must be set to get correct date/time on the server |
|
||||
| ALLOW_SIGNUP<super>\*</super> | false | Allow user sign-up without token |
|
||||
| LOG_CONFIG_OVERRIDE | | Override the config for logging with a custom path |
|
||||
| LOG_LEVEL | info | Logging level (e.g. critical, error, warning, info, debug) |
|
||||
| DAILY_SCHEDULE_TIME | 23:45 | The time of day to run daily server tasks, in HH:MM format. Use the server's local time, *not* UTC |
|
||||
| Variables | Default | Description |
|
||||
| ----------------------------- | :-------------------: | -------------------------------------------------------------------------------------------------- |
|
||||
| PUID | 911 | UserID permissions between host OS and container |
|
||||
| PGID | 911 | GroupID permissions between host OS and container |
|
||||
| DEFAULT_GROUP | Home | The default group for users |
|
||||
| DEFAULT_HOUSEHOLD | Family | The default household for users in each group |
|
||||
| BASE_URL | http://localhost:8080 | Used for Notifications |
|
||||
| TOKEN_TIME | 48 | The time in hours that a login/auth token is valid |
|
||||
| API_PORT | 9000 | The port exposed by backend API. **Do not change this if you're running in Docker** |
|
||||
| API_DOCS | True | Turns on/off access to the API documentation locally |
|
||||
| TZ | UTC | Must be set to get correct date/time on the server |
|
||||
| ALLOW_SIGNUP<super>\*</super> | false | Allow user sign-up without token |
|
||||
| LOG_CONFIG_OVERRIDE | | Override the config for logging with a custom path |
|
||||
| LOG_LEVEL | info | Logging level (e.g. critical, error, warning, info, debug) |
|
||||
| DAILY_SCHEDULE_TIME | 23:45 | The time of day to run daily server tasks, in HH:MM format. Use the server's local time, *not* UTC |
|
||||
|
||||
<super>\*</super> Starting in v1.4.0 this was changed to default to `false` as part of a security review of the application.
|
||||
|
||||
@@ -56,10 +57,19 @@
|
||||
|
||||
Changing the webworker settings may cause unforeseen memory leak issues with Mealie. It's best to leave these at the defaults unless you begin to experience issues with multiple users. Exercise caution when changing these settings
|
||||
|
||||
| Variables | Default | Description |
|
||||
| --------------- | :-----: | ----------------------------------------------------------------------------- |
|
||||
| Variables | Default | Description |
|
||||
| --------------- | :-----: | -------------------------------------------------------------------------------- |
|
||||
| UVICORN_WORKERS | 1 | Sets the number of workers for the web server. [More info here][unicorn_workers] |
|
||||
|
||||
### TLS
|
||||
|
||||
Use this only when mealie is run without a webserver or reverse proxy.
|
||||
|
||||
| Variables | Default | Description |
|
||||
| -------------------- | :-----: | ------------------------ |
|
||||
| TLS_CERTIFICATE_PATH | None | File path to Certificate |
|
||||
| TLS_PRIVATE_KEY_PATH | None | File path to private key |
|
||||
|
||||
### LDAP
|
||||
|
||||
| Variables | Default | Description |
|
||||
@@ -84,21 +94,22 @@ Changing the webworker settings may cause unforeseen memory leak issues with Mea
|
||||
|
||||
For usage, see [Usage - OpenID Connect](../authentication/oidc-v2.md)
|
||||
|
||||
| Variables | Default | Description |
|
||||
| ---------------------- | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| OIDC_AUTH_ENABLED | False | Enables authentication via OpenID Connect |
|
||||
| OIDC_SIGNUP_ENABLED | True | Enables new users to be created when signing in for the first time with OIDC |
|
||||
| OIDC_CONFIGURATION_URL | None | The URL to the OIDC configuration of your provider. This is usually something like https://auth.example.com/.well-known/openid-configuration |
|
||||
| OIDC_CLIENT_ID | None | The client id of your configured client in your provider |
|
||||
| OIDC_CLIENT_SECRET <br/> :octicons-tag-24: v2.0.0 | None | The client secret of your configured client in your provider|
|
||||
| OIDC_USER_GROUP | None | If specified, only users belonging to this group will be able to successfully authenticate. For more information see [this page](../authentication/oidc-v2.md#groups) |
|
||||
| OIDC_ADMIN_GROUP | None | If specified, users belonging to this group will be able to successfully authenticate *and* be made an admin. For more information see [this page](../authentication/oidc-v2.md#groups) |
|
||||
| OIDC_AUTO_REDIRECT | False | If `True`, then the login page will be bypassed an you will be sent directly to your Identity Provider. You can still get to the login page by adding `?direct=1` to the login URL |
|
||||
| OIDC_PROVIDER_NAME | OAuth | The provider name is shown in SSO login button. "Login with <OIDC_PROVIDER_NAME\>" |
|
||||
| OIDC_REMEMBER_ME | False | Because redirects bypass the login screen, you cant extend your session by clicking the "Remember Me" checkbox. By setting this value to true, a session will be extended as if "Remember Me" was checked |
|
||||
| OIDC_USER_CLAIM | email | This is the claim which Mealie will use to look up an existing user by (e.g. "email", "preferred_username") |
|
||||
| OIDC_GROUPS_CLAIM | groups | Optional if not using `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP`. This is the claim Mealie will request from your IdP and will use to compare to `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP` to allow the user to log in to Mealie or is set as an admin. **Your IdP must be configured to grant this claim** |
|
||||
| OIDC_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) |
|
||||
| Variables | Default | Description |
|
||||
| ------------------------------------------------- | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| OIDC_AUTH_ENABLED | False | Enables authentication via OpenID Connect |
|
||||
| OIDC_SIGNUP_ENABLED | True | Enables new users to be created when signing in for the first time with OIDC |
|
||||
| OIDC_CONFIGURATION_URL | None | The URL to the OIDC configuration of your provider. This is usually something like https://auth.example.com/.well-known/openid-configuration |
|
||||
| OIDC_CLIENT_ID | None | The client id of your configured client in your provider |
|
||||
| OIDC_CLIENT_SECRET <br/> :octicons-tag-24: v2.0.0 | None | The client secret of your configured client in your provider |
|
||||
| OIDC_USER_GROUP | None | If specified, only users belonging to this group will be able to successfully authenticate. For more information see [this page](../authentication/oidc-v2.md#groups) |
|
||||
| OIDC_ADMIN_GROUP | None | If specified, users belonging to this group will be able to successfully authenticate *and* be made an admin. For more information see [this page](../authentication/oidc-v2.md#groups) |
|
||||
| OIDC_AUTO_REDIRECT | False | If `True`, then the login page will be bypassed and you will be sent directly to your Identity Provider. You can still get to the login page by adding `?direct=1` to the login URL |
|
||||
| OIDC_PROVIDER_NAME | OAuth | The provider name is shown in SSO login button. "Login with <OIDC_PROVIDER_NAME\>" |
|
||||
| OIDC_REMEMBER_ME | False | Because redirects bypass the login screen, you cant extend your session by clicking the "Remember Me" checkbox. By setting this value to true, a session will be extended as if "Remember Me" was checked |
|
||||
| OIDC_USER_CLAIM | email | This is the claim which Mealie will use to look up an existing user by (e.g. "email", "preferred_username") |
|
||||
| OIDC_GROUPS_CLAIM | groups | Optional if not using `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP`. This is the claim Mealie will request from your IdP and will use to compare to `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP` to allow the user to log in to Mealie or is set as an admin. **Your IdP must be configured to grant this claim** |
|
||||
| OIDC_SCOPES_OVERRIDE | None | Advanced configuration used to override the scopes requested from the IdP. **Most users won't need to change this**. At a minimum, 'openid profile email' are required. |
|
||||
| OIDC_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) |
|
||||
|
||||
### OpenAI
|
||||
|
||||
@@ -117,7 +128,7 @@ For custom mapping variables (e.g. OPENAI_CUSTOM_HEADERS) you should pass values
|
||||
| OPENAI_ENABLE_IMAGE_SERVICES | True | Whether to enable OpenAI image services, such as creating recipes via image. Leave this enabled unless your custom model doesn't support it, or you want to reduce costs |
|
||||
| OPENAI_WORKERS | 2 | Number of OpenAI workers per request. Higher values may increase processing speed, but will incur additional API costs |
|
||||
| OPENAI_SEND_DATABASE_DATA | True | Whether to send Mealie data to OpenAI to improve request accuracy. This will incur additional API costs |
|
||||
| OPENAI_REQUEST_TIMEOUT | 60 | The number of seconds to wait for an OpenAI request to complete before cancelling the request. Leave this empty unless you're running into timeout issues on slower hardware |
|
||||
| OPENAI_REQUEST_TIMEOUT | 60 | The number of seconds to wait for an OpenAI request to complete before cancelling the request. Leave this empty unless you're running into timeout issues on slower hardware |
|
||||
|
||||
### Theming
|
||||
|
||||
@@ -147,7 +158,7 @@ This can be used to avoid leaking passwords through compose files, environment v
|
||||
For example, to configure the Postgres database password in Docker compose, create a file on the host that contains only the password, and expose that file to the Mealie service as a secret with the correct name.
|
||||
Note that environment variables take priority over secrets, so any previously defined environment variables should be removed when migrating to secrets.
|
||||
|
||||
```
|
||||
```yaml
|
||||
services:
|
||||
mealie:
|
||||
...
|
||||
|
||||
@@ -31,7 +31,7 @@ To deploy mealie on your local network, it is highly recommended to use Docker t
|
||||
We've gone through a few versions of Mealie v1 deployment targets. We have settled on a single container deployment, and we've begun publishing the nightly container on github containers. If you're looking to move from the old nightly (split containers _or_ the omni image) to the new nightly, there are a few things you need to do:
|
||||
|
||||
1. Take a backup just in case!
|
||||
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v1.12.0`
|
||||
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v2.1.0`
|
||||
3. Take the external port from the frontend container and set that as the port mapped to port `9000` on the new container. The frontend is now served on port 9000 from the new container, so it will need to be mapped for you to have access.
|
||||
4. Restart the container
|
||||
|
||||
@@ -65,7 +65,7 @@ After you've decided setup the files it's important to set a few ENV variables t
|
||||
- [x] You've configured the relevant ENV variables for your database selection in the `docker-compose.yaml` files.
|
||||
- [x] You've configured the [SMTP server settings](./backend-config.md#email) (used for invitations, password resets, etc). You can setup a [google app password](https://support.google.com/accounts/answer/185833?hl=en) if you want to send email via gmail.
|
||||
- [x] You've set the [`BASE_URL`](./backend-config.md#general) variable.
|
||||
- [x] You've set the `DEFAULT_EMAIL` and `DEFAULT_GROUP` variable.
|
||||
- [x] You've set the `DEFAULT_EMAIL`, `DEFAULT_GROUP`, and `DEFAULT_HOUSEHOLD` variables.
|
||||
|
||||
## Step 4: Startup
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ PostgreSQL might be considered if you need to support many concurrent users. In
|
||||
```yaml
|
||||
services:
|
||||
mealie:
|
||||
image: ghcr.io/mealie-recipes/mealie:v1.12.0 # (3)
|
||||
image: ghcr.io/mealie-recipes/mealie:v2.1.0 # (3)
|
||||
container_name: mealie
|
||||
restart: always
|
||||
ports:
|
||||
|
||||
@@ -11,7 +11,7 @@ SQLite is a popular, open source, self-contained, zero-configuration database th
|
||||
```yaml
|
||||
services:
|
||||
mealie:
|
||||
image: ghcr.io/mealie-recipes/mealie:v1.12.0 # (3)
|
||||
image: ghcr.io/mealie-recipes/mealie:v2.1.0 # (3)
|
||||
container_name: mealie
|
||||
restart: always
|
||||
ports:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
site_name: Mealie
|
||||
demo_url: https://demo.mealie.io
|
||||
site_url: https://hay-kot.github.io/mealie/
|
||||
site_url: https://docs.mealie.io
|
||||
use_directory_urls: true
|
||||
theme:
|
||||
palette:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
width="100%"
|
||||
max-width="1100px"
|
||||
:icon="$globals.icons.pages"
|
||||
:title="$t('general.edit')"
|
||||
:title="$tc('general.edit')"
|
||||
:submit-icon="$globals.icons.save"
|
||||
:submit-text="$tc('general.save')"
|
||||
:submit-disabled="!editTarget.queryFilterString"
|
||||
@@ -25,7 +25,7 @@
|
||||
<v-toolbar-title class="headline"> {{ book.name }} </v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<BaseButton
|
||||
v-if="isOwnGroup"
|
||||
v-if="canEdit"
|
||||
class="mx-1"
|
||||
:edit="true"
|
||||
@click="handleEditCookbook"
|
||||
@@ -79,6 +79,15 @@
|
||||
const tab = ref(null);
|
||||
const book = getOne(slug);
|
||||
|
||||
const isOwnHousehold = computed(() => {
|
||||
if (!($auth.user && book.value?.householdId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $auth.user.householdId === book.value.householdId;
|
||||
})
|
||||
const canEdit = computed(() => isOwnGroup.value && isOwnHousehold.value);
|
||||
|
||||
const dialogStates = reactive({
|
||||
edit: false,
|
||||
});
|
||||
@@ -118,7 +127,7 @@
|
||||
recipes,
|
||||
removeRecipe,
|
||||
replaceRecipes,
|
||||
isOwnGroup,
|
||||
canEdit,
|
||||
dialogStates,
|
||||
editTarget,
|
||||
handleEditCookbook,
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
<draggable
|
||||
:value="fields"
|
||||
handle=".handle"
|
||||
delay="250"
|
||||
:delay-on-touch-only="true"
|
||||
v-bind="{
|
||||
animation: 200,
|
||||
group: 'recipe-instructions',
|
||||
|
||||
@@ -205,23 +205,14 @@ export default defineComponent({
|
||||
const route = useRoute();
|
||||
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
|
||||
|
||||
const router = useRouter();
|
||||
function navigateRandom() {
|
||||
if (props.recipes.length > 0) {
|
||||
const recipe = props.recipes[Math.floor(Math.random() * props.recipes.length)];
|
||||
if (recipe.slug !== undefined) {
|
||||
router.push(`/g/${groupSlug.value}/r/${recipe.slug}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const page = ref(1);
|
||||
const perPage = 32;
|
||||
const hasMore = ref(true);
|
||||
const ready = ref(false);
|
||||
const loading = ref(false);
|
||||
|
||||
const { fetchMore } = useLazyRecipes(isOwnGroup.value ? null : groupSlug.value);
|
||||
const { fetchMore, getRandom } = useLazyRecipes(isOwnGroup.value ? null : groupSlug.value);
|
||||
const router = useRouter();
|
||||
|
||||
const queryFilter = computed(() => {
|
||||
const orderBy = props.query?.orderBy || preferences.value.orderBy;
|
||||
@@ -383,6 +374,15 @@ export default defineComponent({
|
||||
}, useAsyncKey());
|
||||
}
|
||||
|
||||
async function navigateRandom() {
|
||||
const recipe = await getRandom(props.query, queryFilter.value);
|
||||
if (!recipe?.slug) {
|
||||
return;
|
||||
}
|
||||
|
||||
router.push(`/g/${groupSlug.value}/r/${recipe.slug}`);
|
||||
}
|
||||
|
||||
function toggleMobileCards() {
|
||||
preferences.value.useMobileCards = !preferences.value.useMobileCards;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,13 @@ import RecipePageTitleContent from "./RecipePageParts/RecipePageTitleContent.vue
|
||||
import RecipePageComments from "./RecipePageParts/RecipePageComments.vue";
|
||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||
import RecipePrintContainer from "~/components/Domain/Recipe/RecipePrintContainer.vue";
|
||||
import { EditorMode, PageMode, usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
||||
import {
|
||||
clearPageState,
|
||||
EditorMode,
|
||||
PageMode,
|
||||
usePageState,
|
||||
usePageUser,
|
||||
} from "~/composables/recipe-page/shared-state";
|
||||
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import { useRouteQuery } from "~/composables/use-router";
|
||||
@@ -170,6 +176,9 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
deactivateNavigationWarning();
|
||||
|
||||
clearPageState(props.recipe.slug || "");
|
||||
console.debug("reset RecipePage state during unmount");
|
||||
});
|
||||
|
||||
/** =============================================================
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onUnmounted } from "@nuxtjs/composition-api";
|
||||
import { clearPageState, usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
||||
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
||||
import { usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
|
||||
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
@@ -75,10 +75,6 @@ export default defineComponent({
|
||||
return households.value.find((h) => h.id === owner.householdId);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
clearPageState(props.recipe.slug);
|
||||
console.debug("reset RecipePage state during unmount");
|
||||
});
|
||||
async function uploadImage(fileObject: File) {
|
||||
if (!props.recipe || !props.recipe.slug) {
|
||||
return;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
v-if="recipe.recipeIngredient.length > 0"
|
||||
v-model="recipe.recipeIngredient"
|
||||
handle=".handle"
|
||||
delay="250"
|
||||
:delay-on-touch-only="true"
|
||||
v-bind="{
|
||||
animation: 200,
|
||||
group: 'recipe-ingredients',
|
||||
|
||||
@@ -77,6 +77,8 @@
|
||||
:disabled="!isEditForm"
|
||||
:value="value"
|
||||
handle=".handle"
|
||||
delay="250"
|
||||
:delay-on-touch-only="true"
|
||||
v-bind="{
|
||||
animation: 200,
|
||||
group: 'recipe-instructions',
|
||||
@@ -121,9 +123,22 @@
|
||||
@click="toggleDisabled(index)"
|
||||
>
|
||||
<v-card-title :class="{ 'pb-0': !isChecked(index) }">
|
||||
<span :class="isEditForm ? 'handle' : ''">
|
||||
<v-icon v-if="isEditForm" size="26" class="pb-1">{{ $globals.icons.arrowUpDown }}</v-icon>
|
||||
{{ $t("recipe.step-index", { step: index + 1 }) }}
|
||||
<v-text-field
|
||||
v-if="isEditForm"
|
||||
v-model="step.summary"
|
||||
class="headline handle"
|
||||
hide-details
|
||||
dense
|
||||
solo
|
||||
flat
|
||||
:placeholder="$t('recipe.step-index', { step: index + 1 })"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon size="26">{{ $globals.icons.arrowUpDown }}</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<span v-else>
|
||||
{{ step.summary ? step.summary : $t("recipe.step-index", { step: index + 1 }) }}
|
||||
</span>
|
||||
<template v-if="isEditForm">
|
||||
<div class="ml-auto">
|
||||
@@ -339,7 +354,7 @@ export default defineComponent({
|
||||
// ===============================================================
|
||||
// UI State Helpers
|
||||
|
||||
function validateTitle(title: string | undefined) {
|
||||
function hasSectionTitle(title: string | undefined) {
|
||||
return !(title === null || title === "" || title === undefined);
|
||||
}
|
||||
|
||||
@@ -348,7 +363,7 @@ export default defineComponent({
|
||||
|
||||
v.forEach((element: RecipeStep) => {
|
||||
if (element.id !== undefined) {
|
||||
showTitleEditor.value[element.id] = validateTitle(element.title);
|
||||
showTitleEditor.value[element.id] = hasSectionTitle(element.title);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -359,7 +374,7 @@ export default defineComponent({
|
||||
onMounted(() => {
|
||||
props.value.forEach((element: RecipeStep) => {
|
||||
if (element.id !== undefined) {
|
||||
showTitleEditor.value[element.id] = validateTitle(element.title);
|
||||
showTitleEditor.value[element.id] = hasSectionTitle(element.title);
|
||||
}
|
||||
|
||||
// showCookMode.value = false;
|
||||
@@ -576,7 +591,7 @@ export default defineComponent({
|
||||
const sectionSteps: number[] = [];
|
||||
|
||||
for (let i = index; i < props.value.length; i++) {
|
||||
if (!(i === index) && validateTitle(props.value[i].title)) {
|
||||
if (!(i === index) && hasSectionTitle(props.value[i].title)) {
|
||||
break;
|
||||
} else {
|
||||
sectionSteps.push(i);
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<h4 v-if="step.title" :key="`instruction-title-${stepIndex}`" class="instruction-title mb-2">
|
||||
{{ step.title }}
|
||||
</h4>
|
||||
<h5>{{ $t("recipe.step-index", { step: stepIndex + instructionSection.stepOffset + 1 }) }}</h5>
|
||||
<h5>{{ step.summary ? step.summary : $t("recipe.step-index", { step: stepIndex + instructionSection.stepOffset + 1 }) }}</h5>
|
||||
<SafeMarkdown :source="step.text" class="recipe-step-body" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,7 +98,7 @@
|
||||
<tr v-for="(value, key) in recipe.nutrition" :key="key">
|
||||
<template v-if="value">
|
||||
<td>{{ labels[key].label }}</td>
|
||||
<td>{{ value || '-' }}</td>
|
||||
<td>{{ value ? (labels[key].suffix ? `${value} ${labels[key].suffix}` : value) : '-' }}</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -322,10 +322,32 @@ li {
|
||||
}
|
||||
|
||||
.nutrition-table {
|
||||
width: 25%;
|
||||
max-width: 80%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.nutrition-table th,
|
||||
.nutrition-table td {
|
||||
padding: 6px 10px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.nutrition-table th {
|
||||
font-weight: bold;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.nutrition-table td:first-child {
|
||||
width: 70%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nutrition-table td:last-child {
|
||||
width: 30%;
|
||||
text-align: right;
|
||||
}
|
||||
.nutrition-table td {
|
||||
padding: 2px;
|
||||
text-align: left;
|
||||
|
||||
@@ -97,8 +97,8 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
|
||||
const numerator = ref<number>(parseFloat(props.basicYieldNum.toFixed(3)) ?? 1);
|
||||
const denominator = parseFloat(props.basicYieldNum.toFixed(32)) ?? 1;
|
||||
const numerator = ref<number>(props.basicYieldNum != null ? parseFloat(props.basicYieldNum.toFixed(3)) : 1);
|
||||
const denominator = props.basicYieldNum != null ? parseFloat(props.basicYieldNum.toFixed(32)) : 1;
|
||||
const numberParsed = !!props.basicYieldNum;
|
||||
|
||||
watch(() => numerator.value, () => {
|
||||
|
||||
@@ -17,25 +17,15 @@
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list dense>
|
||||
<v-list-item v-for="action in contextMenu" :key="action.event" dense @click="contextHandler(action.event)">
|
||||
<v-list-item-title>{{ action.text }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import { defineComponent, ref } from "@nuxtjs/composition-api";
|
||||
import { ShoppingListMultiPurposeLabelOut } from "~/lib/api/types/household";
|
||||
|
||||
interface actions {
|
||||
text: string;
|
||||
event: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
@@ -48,23 +38,14 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props, context) {
|
||||
const { i18n } = useContext();
|
||||
const labelColor = ref<string | undefined>(props.useColor ? props.value.label.color : undefined);
|
||||
|
||||
const contextMenu: actions[] = [
|
||||
{
|
||||
text: i18n.t("general.transfer") as string,
|
||||
event: "transfer",
|
||||
},
|
||||
];
|
||||
|
||||
function contextHandler(event: string) {
|
||||
context.emit(event);
|
||||
}
|
||||
|
||||
return {
|
||||
contextHandler,
|
||||
contextMenu,
|
||||
labelColor,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -151,10 +151,6 @@ export default defineComponent({
|
||||
text: i18n.t("general.delete") as string,
|
||||
event: "delete",
|
||||
},
|
||||
{
|
||||
text: i18n.t("general.transfer") as string,
|
||||
event: "transfer",
|
||||
},
|
||||
];
|
||||
|
||||
// copy prop value so a refresh doesn't interrupt the user
|
||||
|
||||
@@ -82,12 +82,17 @@ import { computed, defineComponent, onMounted, ref, useContext, useRoute } from
|
||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||
import AppHeader from "@/components/Layout/LayoutParts/AppHeader.vue";
|
||||
import AppSidebar from "@/components/Layout/LayoutParts/AppSidebar.vue";
|
||||
import { SidebarLinks } from "~/types/application-types";
|
||||
import { SideBarLink } from "~/types/application-types";
|
||||
import LanguageDialog from "~/components/global/LanguageDialog.vue";
|
||||
import TheSnackbar from "@/components/Layout/LayoutParts/TheSnackbar.vue";
|
||||
import { useAppInfo } from "~/composables/api";
|
||||
import { useCookbooks, usePublicCookbooks } from "~/composables/use-group-cookbooks";
|
||||
import { useCookbookPreferences } from "~/composables/use-users/preferences";
|
||||
import { useHouseholdStore, usePublicHouseholdStore } from "~/composables/store/use-household-store";
|
||||
import { useToggleDarkMode } from "~/composables/use-utils";
|
||||
import { ReadCookBook } from "~/lib/api/types/cookbook";
|
||||
import { HouseholdSummary } from "~/lib/api/types/household";
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: { AppHeader, AppSidebar, LanguageDialog, TheSnackbar },
|
||||
@@ -99,6 +104,15 @@ export default defineComponent({
|
||||
const route = useRoute();
|
||||
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
|
||||
const { cookbooks } = isOwnGroup.value ? useCookbooks() : usePublicCookbooks(groupSlug.value || "");
|
||||
const cookbookPreferences = useCookbookPreferences();
|
||||
const { store: households } = isOwnGroup.value ? useHouseholdStore() : usePublicHouseholdStore(groupSlug.value || "");
|
||||
|
||||
const householdsById = computed(() => {
|
||||
return households.value.reduce((acc, household) => {
|
||||
acc[household.id] = household;
|
||||
return acc;
|
||||
}, {} as { [key: string]: HouseholdSummary });
|
||||
});
|
||||
|
||||
const appInfo = useAppInfo();
|
||||
const showImageImport = computed(() => appInfo.value?.enableOpenaiImageServices);
|
||||
@@ -113,29 +127,57 @@ export default defineComponent({
|
||||
sidebar.value = !$vuetify.breakpoint.md;
|
||||
});
|
||||
|
||||
const cookbookLinks = computed(() => {
|
||||
if (!cookbooks.value) return [];
|
||||
return cookbooks.value.map((cookbook) => {
|
||||
return {
|
||||
key: cookbook.slug,
|
||||
icon: $globals.icons.pages,
|
||||
title: cookbook.name,
|
||||
to: `/g/${groupSlug.value}/cookbooks/${cookbook.slug as string}`,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
interface Link {
|
||||
insertDivider: boolean;
|
||||
icon: string;
|
||||
title: string;
|
||||
subtitle: string | null;
|
||||
to: string;
|
||||
restricted: boolean;
|
||||
hide: boolean;
|
||||
function cookbookAsLink(cookbook: ReadCookBook): SideBarLink {
|
||||
return {
|
||||
key: cookbook.slug || "",
|
||||
icon: $globals.icons.pages,
|
||||
title: cookbook.name,
|
||||
to: `/g/${groupSlug.value}/cookbooks/${cookbook.slug || ""}`,
|
||||
restricted: false,
|
||||
};
|
||||
}
|
||||
|
||||
const createLinks = computed<Link[]>(() => [
|
||||
const currentUserHouseholdId = computed(() => $auth.user?.householdId);
|
||||
const cookbookLinks = computed<SideBarLink[]>(() => {
|
||||
if (!cookbooks.value) {
|
||||
return [];
|
||||
}
|
||||
cookbooks.value.sort((a, b) => (a.position || 0) - (b.position || 0));
|
||||
|
||||
const ownLinks: SideBarLink[] = [];
|
||||
const links: SideBarLink[] = [];
|
||||
const cookbooksByHousehold = cookbooks.value.reduce((acc, cookbook) => {
|
||||
const householdName = householdsById.value[cookbook.householdId]?.name || "";
|
||||
if (!acc[householdName]) {
|
||||
acc[householdName] = [];
|
||||
}
|
||||
acc[householdName].push(cookbook);
|
||||
return acc;
|
||||
}, {} as Record<string, ReadCookBook[]>);
|
||||
|
||||
Object.entries(cookbooksByHousehold).forEach(([householdName, cookbooks]) => {
|
||||
if (cookbooks[0].householdId === currentUserHouseholdId.value) {
|
||||
ownLinks.push(...cookbooks.map(cookbookAsLink));
|
||||
} else {
|
||||
links.push({
|
||||
key: householdName,
|
||||
icon: $globals.icons.book,
|
||||
title: householdName,
|
||||
children: cookbooks.map(cookbookAsLink),
|
||||
restricted: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
links.sort((a, b) => a.title.localeCompare(b.title));
|
||||
if ($auth.user && cookbookPreferences.value.hideOtherHouseholds) {
|
||||
return ownLinks;
|
||||
} else {
|
||||
return [...ownLinks, ...links];
|
||||
}
|
||||
});
|
||||
|
||||
const createLinks = computed<SideBarLink[]>(() => [
|
||||
{
|
||||
insertDivider: false,
|
||||
icon: $globals.icons.link,
|
||||
@@ -165,7 +207,7 @@ export default defineComponent({
|
||||
},
|
||||
]);
|
||||
|
||||
const bottomLinks = computed<SidebarLinks>(() => [
|
||||
const bottomLinks = computed<SideBarLink[]>(() => [
|
||||
{
|
||||
icon: $globals.icons.cog,
|
||||
title: i18n.tc("general.settings"),
|
||||
@@ -174,7 +216,7 @@ export default defineComponent({
|
||||
},
|
||||
]);
|
||||
|
||||
const topLinks = computed<SidebarLinks>(() => [
|
||||
const topLinks = computed<SideBarLink[]>(() => [
|
||||
{
|
||||
icon: $globals.icons.silverwareForkKnife,
|
||||
to: `/g/${groupSlug.value}`,
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, reactive, toRefs, useContext } from "@nuxtjs/composition-api";
|
||||
import { computed, defineComponent, reactive, toRefs, useContext, watch } from "@nuxtjs/composition-api";
|
||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||
import { SidebarLinks } from "~/types/application-types";
|
||||
import UserAvatar from "~/components/Domain/User/UserAvatar.vue";
|
||||
@@ -192,13 +192,29 @@ export default defineComponent({
|
||||
const userProfileLink = computed(() => $auth.user ? "/user/profile" : undefined);
|
||||
|
||||
const state = reactive({
|
||||
dropDowns: {},
|
||||
dropDowns: {} as Record<string, boolean>,
|
||||
topSelected: null as string[] | null,
|
||||
secondarySelected: null as string[] | null,
|
||||
bottomSelected: null as string[] | null,
|
||||
hasOpenedBefore: false as boolean,
|
||||
});
|
||||
|
||||
const allLinks = computed(() => [...props.topLink, ...(props.secondaryLinks || []), ...(props.bottomLinks || [])]);
|
||||
function initDropdowns() {
|
||||
allLinks.value.forEach((link) => {
|
||||
state.dropDowns[link.title] = link.childrenStartExpanded || false;
|
||||
})
|
||||
}
|
||||
watch(
|
||||
() => allLinks,
|
||||
() => {
|
||||
initDropdowns();
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
userFavoritesLink,
|
||||
|
||||
@@ -54,7 +54,7 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
|
||||
// casting to number is required as sometimes quantity is a string
|
||||
if (quantity && Number(quantity) !== 0) {
|
||||
if (unit && !unit.fraction) {
|
||||
returnQty = (quantity * scale).toString();
|
||||
returnQty = Number((quantity * scale).toPrecision(3)).toString();
|
||||
} else {
|
||||
const fraction = frac(quantity * scale, 10, true);
|
||||
if (fraction[0] !== undefined && fraction[0] > 0) {
|
||||
|
||||
@@ -8,6 +8,32 @@ import { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
|
||||
export const allRecipes = ref<Recipe[]>([]);
|
||||
export const recentRecipes = ref<Recipe[]>([]);
|
||||
|
||||
function getParams(
|
||||
orderBy: string | null = null,
|
||||
orderDirection = "desc",
|
||||
query: RecipeSearchQuery | null = null,
|
||||
queryFilter: string | null = null
|
||||
) {
|
||||
return {
|
||||
orderBy,
|
||||
orderDirection,
|
||||
paginationSeed: query?._searchSeed, // propagate searchSeed to stabilize random order pagination
|
||||
searchSeed: query?._searchSeed, // unused, but pass it along for completeness of data
|
||||
search: query?.search,
|
||||
cookbook: query?.cookbook,
|
||||
households: query?.households,
|
||||
categories: query?.categories,
|
||||
requireAllCategories: query?.requireAllCategories,
|
||||
tags: query?.tags,
|
||||
requireAllTags: query?.requireAllTags,
|
||||
tools: query?.tools,
|
||||
requireAllTools: query?.requireAllTools,
|
||||
foods: query?.foods,
|
||||
requireAllFoods: query?.requireAllFoods,
|
||||
queryFilter,
|
||||
};
|
||||
};
|
||||
|
||||
export const useLazyRecipes = function (publicGroupSlug: string | null = null) {
|
||||
const router = useRouter();
|
||||
|
||||
@@ -25,24 +51,11 @@ export const useLazyRecipes = function (publicGroupSlug: string | null = null) {
|
||||
queryFilter: string | null = null,
|
||||
) {
|
||||
|
||||
const { data, error } = await api.recipes.getAll(page, perPage, {
|
||||
orderBy,
|
||||
orderDirection,
|
||||
paginationSeed: query?._searchSeed, // propagate searchSeed to stabilize random order pagination
|
||||
searchSeed: query?._searchSeed, // unused, but pass it along for completeness of data
|
||||
search: query?.search,
|
||||
cookbook: query?.cookbook,
|
||||
households: query?.households,
|
||||
categories: query?.categories,
|
||||
requireAllCategories: query?.requireAllCategories,
|
||||
tags: query?.tags,
|
||||
requireAllTags: query?.requireAllTags,
|
||||
tools: query?.tools,
|
||||
requireAllTools: query?.requireAllTools,
|
||||
foods: query?.foods,
|
||||
requireAllFoods: query?.requireAllFoods,
|
||||
queryFilter,
|
||||
});
|
||||
const { data, error } = await api.recipes.getAll(
|
||||
page,
|
||||
perPage,
|
||||
getParams(orderBy, orderDirection, query, queryFilter),
|
||||
);
|
||||
|
||||
if (error?.response?.status === 404) {
|
||||
router.push("/login");
|
||||
@@ -74,6 +87,13 @@ export const useLazyRecipes = function (publicGroupSlug: string | null = null) {
|
||||
recipes.value = val;
|
||||
}
|
||||
|
||||
async function getRandom(query: RecipeSearchQuery | null = null, queryFilter: string | null = null) {
|
||||
const { data } = await api.recipes.getAll(1, 1, getParams("random", "desc", query, queryFilter));
|
||||
if (data?.items.length) {
|
||||
return data.items[0];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
recipes,
|
||||
fetchMore,
|
||||
@@ -81,6 +101,7 @@ export const useLazyRecipes = function (publicGroupSlug: string | null = null) {
|
||||
assignSorted,
|
||||
removeRecipe,
|
||||
replaceRecipes,
|
||||
getRandom,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -99,10 +99,10 @@ export const useCookbooks = function () {
|
||||
|
||||
loading.value = false;
|
||||
},
|
||||
async createOne() {
|
||||
async createOne(name: string | null = null) {
|
||||
loading.value = true;
|
||||
const { data } = await api.cookbooks.createOne({
|
||||
name: i18n.t("cookbook.household-cookbook-name", [household.value?.name || "", String((cookbookStore?.value?.length ?? 0) + 1)]) as string,
|
||||
name: name || i18n.t("cookbook.household-cookbook-name", [household.value?.name || "", String((cookbookStore?.value?.length ?? 0) + 1)]) as string,
|
||||
position: (cookbookStore?.value?.length ?? 0) + 1,
|
||||
queryFilterString: "",
|
||||
});
|
||||
@@ -129,18 +129,18 @@ export const useCookbooks = function () {
|
||||
return data;
|
||||
},
|
||||
|
||||
async updateOrder() {
|
||||
if (!cookbookStore?.value) {
|
||||
async updateOrder(cookbooks: ReadCookBook[]) {
|
||||
if (!cookbooks?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
|
||||
cookbookStore.value.forEach((element, index) => {
|
||||
cookbooks.forEach((element, index) => {
|
||||
element.position = index + 1;
|
||||
});
|
||||
|
||||
const { data } = await api.cookbooks.updateAll(cookbookStore.value);
|
||||
const { data } = await api.cookbooks.updateAll(cookbooks);
|
||||
|
||||
if (data && cookbookStore?.value) {
|
||||
this.refreshAll();
|
||||
|
||||
@@ -45,6 +45,10 @@ export interface UserParsingPreferences {
|
||||
parser: RegisteredParser;
|
||||
}
|
||||
|
||||
export interface UserCookbooksPreferences {
|
||||
hideOtherHouseholds: boolean;
|
||||
}
|
||||
|
||||
export function useUserMealPlanPreferences(): Ref<UserMealPlanPreferences> {
|
||||
const fromStorage = useLocalStorage(
|
||||
"meal-planner-preferences",
|
||||
@@ -153,3 +157,17 @@ export function useParsingPreferences(): Ref<UserParsingPreferences> {
|
||||
|
||||
return fromStorage;
|
||||
}
|
||||
|
||||
export function useCookbookPreferences(): Ref<UserCookbooksPreferences> {
|
||||
const fromStorage = useLocalStorage(
|
||||
"cookbook-preferences",
|
||||
{
|
||||
hideOtherHouseholds: false,
|
||||
},
|
||||
{ mergeDefaults: true }
|
||||
// we cast to a Ref because by default it will return an optional type ref
|
||||
// but since we pass defaults we know all properties are set.
|
||||
) as unknown as Ref<UserCookbooksPreferences>;
|
||||
|
||||
return fromStorage;
|
||||
}
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"database-type": "Тип на база данни",
|
||||
"database-url": "URL адрес база данни",
|
||||
"default-group": "Група по подразбиране",
|
||||
"default-household": "Default Household",
|
||||
"default-household": "Домакинство по подразбиране",
|
||||
"demo": "Демо",
|
||||
"demo-status": "Статус на Демото",
|
||||
"development": "Разработване",
|
||||
@@ -65,7 +65,7 @@
|
||||
"something-went-wrong": "Нещо се обърка!",
|
||||
"subscribed-events": "Планирани събития",
|
||||
"test-message-sent": "Тестово съобщение е изпратено",
|
||||
"message-sent": "Message Sent",
|
||||
"message-sent": "Съобщението е изпратено",
|
||||
"new-notification": "Ново известие",
|
||||
"event-notifiers": "Известия за събитие",
|
||||
"apprise-url-skipped-if-blank": "URL за известяване (пропуска се ако е празно)",
|
||||
@@ -162,7 +162,7 @@
|
||||
"test": "Тест",
|
||||
"themes": "Теми",
|
||||
"thursday": "четвъртък",
|
||||
"title": "Title",
|
||||
"title": "Заглавие",
|
||||
"token": "Токън",
|
||||
"tuesday": "Вторник",
|
||||
"type": "Тип",
|
||||
@@ -182,7 +182,7 @@
|
||||
"date": "Дата",
|
||||
"id": "Id",
|
||||
"owner": "Собственик",
|
||||
"change-owner": "Change Owner",
|
||||
"change-owner": "Промени собственика",
|
||||
"date-added": "Дата на добавяне",
|
||||
"none": "Няма",
|
||||
"run": "Старт",
|
||||
@@ -213,11 +213,11 @@
|
||||
"clipboard-copy-failure": "Линкът към рецептата е копиран в клипборда.",
|
||||
"confirm-delete-generic-items": "Сигурни ли сте, че желаете да изтриете следните елементи?",
|
||||
"organizers": "Органайзер",
|
||||
"caution": "Caution",
|
||||
"show-advanced": "Show Advanced",
|
||||
"add-field": "Add Field",
|
||||
"date-created": "Date Created",
|
||||
"date-updated": "Date Updated"
|
||||
"caution": "Внимание",
|
||||
"show-advanced": "Покажи разширени",
|
||||
"add-field": "Добави поле",
|
||||
"date-created": "Дата на създаване",
|
||||
"date-updated": "Дата на актуализация"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Сигурни ли сте, че искате да изтриете <b>{groupName}<b/>?",
|
||||
@@ -244,16 +244,16 @@
|
||||
"keep-my-recipes-private-description": "Задай групата и всичките рецепти като лични. Винаги може да промените това по-късно."
|
||||
},
|
||||
"manage-members": "Управление на потребителите",
|
||||
"manage-members-description": "Manage the permissions of the members in your household. {manage} allows the user to access the data-management page, and {invite} allows the user to generate invitation links for other users. Group owners cannot change their own permissions.",
|
||||
"manage-members-description": "Управлявайте правата на членовете на домакинството ви. {manage} позволява на потребителите да постъпват страницата за управление на данните, а {invite} позволява на потребителите да генерират покани към други потребители. Собствениците на групи немогат да променят собствените си права.",
|
||||
"manage": "Управление",
|
||||
"manage-household": "Manage Household",
|
||||
"manage-household": "Управлявай домакинство",
|
||||
"invite": "Покани",
|
||||
"looking-to-update-your-profile": "Търсите да обновите собствения си профил?",
|
||||
"default-recipe-preferences-description": "Това са настройките по подразбиране когато нова рецепта е създадена в твоя група. Тези настройки могат да бъдат променени са всяка рецепта в менюто за настройки на рецептата.",
|
||||
"default-recipe-preferences": "Стандартни настройки за рецептите",
|
||||
"group-preferences": "Настройки на групите",
|
||||
"private-group": "Частна група",
|
||||
"private-group-description": "Setting your group to private will disable all public view options. This overrides any individual public view settings",
|
||||
"private-group-description": "Конфигуриране на групата като лична ще изключи опциите за публичен достъп. Това презаписва всякакви настройки за публично показване.",
|
||||
"enable-public-access": "Включи публичния достъп",
|
||||
"enable-public-access-description": "Направи груповите рецепти публични по подразбиране и позволи на посетителите да преглеждат рецептите без да се вписват",
|
||||
"allow-users-outside-of-your-group-to-see-your-recipes": "Разрешете на потребители извън вашата група да виждат рецептите Ви",
|
||||
@@ -267,7 +267,7 @@
|
||||
"disable-users-from-commenting-on-recipes": "Забрани коментирането на рецепти от потребителите",
|
||||
"disable-users-from-commenting-on-recipes-description": "Скрива раздела за коментари към рецептата и забранява коментирането",
|
||||
"disable-organizing-recipe-ingredients-by-units-and-food": "Изключи организирането на съставките по мерни единици и продукти",
|
||||
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Hides the Food, Unit, and Amount fields for ingredients and treats ingredients as plain text fields",
|
||||
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Скрива полетата за храна, единица и количество за съставките и третира съставките като полета със свободен текст",
|
||||
"general-preferences": "Общи предпочитания",
|
||||
"group-recipe-preferences": "Предпочитания за рецепта по група",
|
||||
"report": "Сигнал",
|
||||
@@ -276,27 +276,27 @@
|
||||
"admin-group-management": "Административно управление на групите",
|
||||
"admin-group-management-text": "Промените по тази група ще бъдат отразени моментално.",
|
||||
"group-id-value": "ID на Групата: {0}",
|
||||
"total-households": "Total Households"
|
||||
"total-households": "Общ брой домакинства"
|
||||
},
|
||||
"household": {
|
||||
"household": "Household",
|
||||
"households": "Households",
|
||||
"user-household": "User Household",
|
||||
"create-household": "Create Household",
|
||||
"household-name": "Household Name",
|
||||
"household-group": "Household Group",
|
||||
"household-management": "Household Management",
|
||||
"manage-households": "Manage Households",
|
||||
"admin-household-management": "Admin Household Management",
|
||||
"admin-household-management-text": "Changes to this household will be reflected immediately.",
|
||||
"household-id-value": "Household Id: {0}",
|
||||
"private-household": "Private Household",
|
||||
"private-household-description": "Setting your household to private will disable all public view options. This overrides any individual public view settings",
|
||||
"lock-recipe-edits-from-other-households": "Lock recipe edits from other households",
|
||||
"lock-recipe-edits-from-other-households-description": "When enabled only users in your household can edit recipes created by your household",
|
||||
"household-recipe-preferences": "Household Recipe Preferences",
|
||||
"default-recipe-preferences-description": "These are the default settings when a new recipe is created in your household. These can be changed for individual recipes in the recipe settings menu.",
|
||||
"allow-users-outside-of-your-household-to-see-your-recipes": "Allow users outside of your household to see your recipes",
|
||||
"household": "Домакинство",
|
||||
"households": "Домакинства",
|
||||
"user-household": "Домакинство на потребителя",
|
||||
"create-household": "Създай домакинство",
|
||||
"household-name": "Име на домакинството",
|
||||
"household-group": "Група на домакинството",
|
||||
"household-management": "Управление на домакинството",
|
||||
"manage-households": "Управлявай домакинствата",
|
||||
"admin-household-management": "Административно управление на домакинствата",
|
||||
"admin-household-management-text": "Промените към това домакинство ще бъдат отразени незабавно.",
|
||||
"household-id-value": "Идентификатор на домакинство: {0}",
|
||||
"private-household": "Лично Домакинство",
|
||||
"private-household-description": "Конфигуриране на домакинството като лично ще изключи опциите за публичен достъп. Това презаписва всякакви настройки за публично показване.",
|
||||
"lock-recipe-edits-from-other-households": "Забравени редакция на рецептата от други членове на домакинството",
|
||||
"lock-recipe-edits-from-other-households-description": "Когато е включено, само членове на Вашето домакинство ще могат да редактират рецепти създадени от Вашето домакинство.",
|
||||
"household-recipe-preferences": "Предпочитания за рецептите на домакинството",
|
||||
"default-recipe-preferences-description": "Това са настройките по подразбиране когато нова рецепта е създадена в домакинството ви. Тези настройки могат да бъдат променени за всяка рецепта в менюто за настройки на рецептата.",
|
||||
"allow-users-outside-of-your-household-to-see-your-recipes": "Разреши на потребители от други домакинства да виждат рецептите ми",
|
||||
"allow-users-outside-of-your-household-to-see-your-recipes-description": "When enabled you can use a public share link to share specific recipes without authorizing the user. When disabled, you can only share recipes with users who are in your household or with a pre-generated private link",
|
||||
"household-preferences": "Household Preferences"
|
||||
},
|
||||
@@ -321,8 +321,8 @@
|
||||
"mealplan-update-failed": "Неуспешно обновяване на седмичното меню",
|
||||
"mealplan-updated": "Седмичното меню бе обновено",
|
||||
"mealplan-households-description": "If no household is selected, recipes can be added from any household",
|
||||
"any-category": "Any Category",
|
||||
"any-tag": "Any Tag",
|
||||
"any-category": "Всяка категория",
|
||||
"any-tag": "Всеки етикет",
|
||||
"any-household": "Any Household",
|
||||
"no-meal-plan-defined-yet": "Все още няма създадено седмично меню",
|
||||
"no-meal-planned-for-today": "За днес няма планирано меню",
|
||||
@@ -466,7 +466,7 @@
|
||||
"calories-suffix": "калории",
|
||||
"carbohydrate-content": "Въглехидрати",
|
||||
"categories": "Категории",
|
||||
"cholesterol-content": "Cholesterol",
|
||||
"cholesterol-content": "Холестерол",
|
||||
"comment-action": "Коментирай",
|
||||
"comment": "Коментар",
|
||||
"comments": "Коментари",
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Групите от потребители или домакинства немогат да бъдат изтривани",
|
||||
"household-delete-note": "Домакинства с потребители не могат да бъдат изтривани"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Добре дошъл(а), {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Benvingut/Benvinguda, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Velkommen, {0}!",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"date": "Datum",
|
||||
"id": "ID",
|
||||
"owner": "Besitzer",
|
||||
"change-owner": "Change Owner",
|
||||
"change-owner": "Besitzer ändern",
|
||||
"date-added": "Hinzugefügt am",
|
||||
"none": "Nichts",
|
||||
"run": "Starten",
|
||||
@@ -214,10 +214,10 @@
|
||||
"confirm-delete-generic-items": "Bist du dir sicher, dass du die folgenden Einträge löschen möchtest?",
|
||||
"organizers": "Organisieren",
|
||||
"caution": "Vorsicht",
|
||||
"show-advanced": "Show Advanced",
|
||||
"add-field": "Add Field",
|
||||
"date-created": "Date Created",
|
||||
"date-updated": "Date Updated"
|
||||
"show-advanced": "Erweiterte Optionen anzeigen",
|
||||
"add-field": "Feld Hinzufügen",
|
||||
"date-created": "Erstellungsdatum",
|
||||
"date-updated": "Aktualisiert am"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Bist du dir sicher, dass du die Gruppe <b>{groupName}<b/> löschen möchtest?",
|
||||
@@ -431,7 +431,7 @@
|
||||
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Füge deine Rezeptdaten ein. Jede Zeile wird als Eintrag in einer Liste dargestellt",
|
||||
"recipe-markup-specification": "Rezept Markup Spezifikation",
|
||||
"recipe-url": "Rezept URL",
|
||||
"recipe-html-or-json": "Recipe HTML or JSON",
|
||||
"recipe-html-or-json": "Rezept HTML oder JSON",
|
||||
"upload-a-recipe": "Rezept hochladen",
|
||||
"upload-individual-zip-file": "Lade eine individuelle .zip-Datei hoch, die von einer anderen Mealie-Instanz exportiert wird.",
|
||||
"url-form-hint": "Kopiere einen Link von deiner Lieblingsrezept-Website und füge ihn ein",
|
||||
@@ -466,7 +466,7 @@
|
||||
"calories-suffix": "Kalorien",
|
||||
"carbohydrate-content": "Kohlenhydrate",
|
||||
"categories": "Kategorien",
|
||||
"cholesterol-content": "Cholesterol",
|
||||
"cholesterol-content": "Cholesterin",
|
||||
"comment-action": "Kommentieren",
|
||||
"comment": "Kommentar",
|
||||
"comments": "Kommentare",
|
||||
@@ -513,7 +513,7 @@
|
||||
"recipe-updated": "Rezept aktualisiert",
|
||||
"remove-from-favorites": "Von Favoriten entfernen",
|
||||
"remove-section": "Abschnitt entfernen",
|
||||
"saturated-fat-content": "Saturated fat",
|
||||
"saturated-fat-content": "Gesättigte Fettsäuren",
|
||||
"save-recipe-before-use": "Rezept vor Verwendung speichern",
|
||||
"section-title": "Titel des Abschnitts",
|
||||
"servings": "Portionen",
|
||||
@@ -526,7 +526,7 @@
|
||||
"total-time": "Gesamtzeit",
|
||||
"trans-fat-content": "Trans-fat",
|
||||
"unable-to-delete-recipe": "Rezept kann nicht gelöscht werden",
|
||||
"unsaturated-fat-content": "Unsaturated fat",
|
||||
"unsaturated-fat-content": "Ungesättigte Fettsäuren",
|
||||
"no-recipe": "Kein Rezept",
|
||||
"locked-by-owner": "Vom Besitzer gesperrt",
|
||||
"join-the-conversation": "Beteilige dich an der Unterhaltung",
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Willkommen, {0}!",
|
||||
@@ -1348,17 +1350,17 @@
|
||||
"does-not-equal": "does not equal",
|
||||
"is-greater-than": "is greater than",
|
||||
"is-greater-than-or-equal-to": "is greater than or equal to",
|
||||
"is-less-than": "is less than",
|
||||
"is-less-than": "ist weniger als",
|
||||
"is-less-than-or-equal-to": "is less than or equal to"
|
||||
},
|
||||
"relational-keywords": {
|
||||
"is": "is",
|
||||
"is-not": "is not",
|
||||
"is-one-of": "is one of",
|
||||
"is-not-one-of": "is not one of",
|
||||
"contains-all-of": "contains all of",
|
||||
"is-like": "is like",
|
||||
"is-not-like": "is not like"
|
||||
"is": "ist",
|
||||
"is-not": "ist nicht",
|
||||
"is-one-of": "ist eines von",
|
||||
"is-not-one-of": "ist nicht einer von",
|
||||
"contains-all-of": "enthält alle",
|
||||
"is-like": "ist wie",
|
||||
"is-not-like": "ist nicht wie"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@
|
||||
"for-type-meal-types": "για γεύματα τύπου {0}",
|
||||
"meal-plan-rules": "Κανόνες Προγράμματος Γευμάτων",
|
||||
"new-rule": "Νέος κανόνας",
|
||||
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the rule filters will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
|
||||
"meal-plan-rules-description": "Μπορείτε να δημιουργήσετε κανόνες για την αυτόματη επιλογή συνταγών για τα προγράμματα γευμάτων. Αυτοί οι κανόνες χρησιμοποιούνται από το διακομιστή για τον προσδιορισμό της τυχαίας δεξαμενής συνταγών από τις οποίες μπορείτε να επιλέξετε κατά τη δημιουργία προγραμμάτων γευμάτων. Σημειώστε ότι αν οι κανόνες έχουν τους ίδιους περιορισμούς ημέρας/τύπου τότε τα φίλτρα κανόνων θα συγχωνευθούν. Στην πράξη, είναι περιττή η δημιουργία διπλότυπων κανόνων, είναι όμως εφικτή.",
|
||||
"new-rule-description": "Κατά τη δημιουργία ενός νέου κανόνα για ένα σχέδιο γεύματος, μπορείτε να περιορίσετε τον κανόνα ώστε να ισχύει για μια συγκεκριμένη ημέρα της εβδομάδας ή/και ένα συγκεκριμένο τύπο γεύματος. Για να εφαρμόσετε έναν κανόνα σε όλες τις ημέρες ή σε όλους τους τύπους γεύματος μπορείτε να ορίσετε τον κανόνα σε \"Ολα\" που θα τον εφαρμόσει σε όλες τις πιθανές τιμές για την ημέρα ή/και τον τύπο γεύματος.",
|
||||
"recipe-rules": "Κανόνες Συνταγής",
|
||||
"applies-to-all-days": "Εφαρμόζεται για όλες τις ημέρες",
|
||||
@@ -440,7 +440,7 @@
|
||||
"trim-prefix-description": "Περικοπή πρώτου χαρακτήρα από κάθε γραμμή",
|
||||
"split-by-numbered-line-description": "Προσπάθεια για χωρισμό μιας παραγράφου ταιριάζοντας μοτίβα '1)' ή '1.'",
|
||||
"import-by-url": "Εισαγωγή μιας συνταγής από URL",
|
||||
"create-manually": "Δημιούργησε μια συνταγή χειροκίνητα",
|
||||
"create-manually": "Δημιουργήστε μια συνταγή χειροκίνητα",
|
||||
"make-recipe-image": "Ορισμός ως εικόνας συνταγής"
|
||||
},
|
||||
"page": {
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Εντοπισμός Σφαλμάτων Υπηρεσιών OpenAI",
|
||||
"debug-openai-services-description": "Χρησιμοποιήστε αυτή τη σελίδα για να αποσφαλματώσετε τις υπηρεσίες OpenAI. Μπορείτε να ελέγξετε τη σύνδεσή σας στην OpenAI και να δείτε τα αποτελέσματα εδώ. Αν έχετε ενεργοποιημένες τις υπηρεσίες εικόνας, μπορείτε επίσης να παρέχετε μια εικόνα.",
|
||||
"run-test": "Εκτέλεση δοκιμής",
|
||||
"test-results": "Αποτελέσματα δοκιμής"
|
||||
"test-results": "Αποτελέσματα δοκιμής",
|
||||
"group-delete-note": "Ομάδες με χρήστες ή νοικοκυριά δεν μπορούν να διαγραφούν",
|
||||
"household-delete-note": "Τα νοικοκυριά με χρήστες δεν μπορούν να διαγραφούν"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Καλώς ορίσατε, {0}!",
|
||||
@@ -1324,7 +1326,7 @@
|
||||
},
|
||||
"cookbook": {
|
||||
"cookbooks": "Βιβλία Μαγειρικής",
|
||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
||||
"description": "Τα βιβλία μαγειρικής είναι ένας άλλος τρόπος για να οργανώσετε τις συνταγές δημιουργώντας τμήματα συνταγών, οργανωτών και άλλων φίλτρων. Η δημιουργία ενός βιβλίου μαγειρικής θα προσθέσει μια καταχώρηση στην πλευρική μπάρα και όλες οι συνταγές με τα φίλτρα που έχουν επιλεγεί θα εμφανιστούν στο βιβλίο μαγειρικών.",
|
||||
"public-cookbook": "Δημόσιο Βιβλίο Μαγειρικής",
|
||||
"public-cookbook-description": "Τα δημόσια βιβλία μαγειρικής μπορούν να μοιραστούν με χρήστες εκτός του mealie και θα εμφανιστούν στη σελίδα των ομάδων σας.",
|
||||
"filter-options": "Επιλογές φίλτρου",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
@@ -1325,6 +1327,8 @@
|
||||
"cookbook": {
|
||||
"cookbooks": "Cookbooks",
|
||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
||||
"hide-cookbooks-from-other-households": "Hide Cookbooks from Other Households",
|
||||
"hide-cookbooks-from-other-households-description": "When enabled, only cookbooks from your household will appear on the sidebar",
|
||||
"public-cookbook": "Public Cookbook",
|
||||
"public-cookbook-description": "Public Cookbooks can be shared with non-mealie users and will be displayed on your groups page.",
|
||||
"filter-options": "Filter Options",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"date": "Fecha",
|
||||
"id": "Id",
|
||||
"owner": "Propietario",
|
||||
"change-owner": "Change Owner",
|
||||
"change-owner": "Cambiar dueño",
|
||||
"date-added": "Fecha creación",
|
||||
"none": "Nada",
|
||||
"run": "Ejecutar",
|
||||
@@ -214,10 +214,10 @@
|
||||
"confirm-delete-generic-items": "¿Estás seguro que quieres eliminar los siguientes elementos?",
|
||||
"organizers": "Organizadores",
|
||||
"caution": "Precaución",
|
||||
"show-advanced": "Show Advanced",
|
||||
"add-field": "Add Field",
|
||||
"date-created": "Date Created",
|
||||
"date-updated": "Date Updated"
|
||||
"show-advanced": "Mostrar Avanzado",
|
||||
"add-field": "Añadir campo",
|
||||
"date-created": "Fecha de creación",
|
||||
"date-updated": "Fecha de actualización"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Por favor, confirma que deseas eliminar <b>{groupName}<b/>",
|
||||
@@ -246,14 +246,14 @@
|
||||
"manage-members": "Gestionar miembros",
|
||||
"manage-members-description": "Gestione los permisos de los miembros de su hogar. {manage} permite al usuario acceder a la página de administración de datos, y {invite} permite al usuario generar enlaces de invitación para otros usuarios. Los propietarios de los grupos no pueden cambiar sus propios permisos.",
|
||||
"manage": "Gestionar",
|
||||
"manage-household": "Manage Household",
|
||||
"manage-household": "Administrar Casa",
|
||||
"invite": "Invitar",
|
||||
"looking-to-update-your-profile": "¿Buscas actualizar tu perfil?",
|
||||
"default-recipe-preferences-description": "Estas son las opciones por defecto cuando se crea una nueva receta en tu grupo. Éstas se pueden cambiar para recetas individuales en el menú de ajustes de recetas.",
|
||||
"default-recipe-preferences": "Preferencias de receta predeterminadas",
|
||||
"group-preferences": "Preferencias de grupo",
|
||||
"private-group": "Grupo privado",
|
||||
"private-group-description": "Setting your group to private will disable all public view options. This overrides any individual public view settings",
|
||||
"private-group-description": "Establecer tu grupo como privado, desactivará todas las opciones de vista pública. Esto sobreescribe cualquier configuración individual de vista pública",
|
||||
"enable-public-access": "Habilitar acceso público",
|
||||
"enable-public-access-description": "Habilitar recetas públicas por defecto y permitir que usuarios anónimos vean recetas",
|
||||
"allow-users-outside-of-your-group-to-see-your-recipes": "Permite a los usuarios fuera de tu grupo ver tus recetas",
|
||||
@@ -288,12 +288,12 @@
|
||||
"household-management": "Gestión de la Casa",
|
||||
"manage-households": "Administrar Casas",
|
||||
"admin-household-management": "Administración de la Casa",
|
||||
"admin-household-management-text": "Changes to this household will be reflected immediately.",
|
||||
"admin-household-management-text": "Los cambios en esta casa se reflejarán inmediatamente.",
|
||||
"household-id-value": "Id del hogar: {0}",
|
||||
"private-household": "Casa Privada",
|
||||
"private-household-description": "Setting your household to private will disable all public view options. This overrides any individual public view settings",
|
||||
"lock-recipe-edits-from-other-households": "Lock recipe edits from other households",
|
||||
"lock-recipe-edits-from-other-households-description": "When enabled only users in your household can edit recipes created by your household",
|
||||
"private-household-description": "Establecer tu hogar como privado, desactivará todas las opciones de vista pública. Esto sobreescribe cualquier configuración individual de vista pública",
|
||||
"lock-recipe-edits-from-other-households": "Bloquear la edición de receta de otros hogares",
|
||||
"lock-recipe-edits-from-other-households-description": "Cuando está habilitado, sólo los usuarios de tu hogar pueden editar las recetas creadas por tu hogar",
|
||||
"household-recipe-preferences": "Preferencias de la Receta de la Casa",
|
||||
"default-recipe-preferences-description": "Estas son las configuraciones por defecto cuando se crea una nueva receta en su hogar. Estos se pueden cambiar para recetas individuales en el menú de ajustes de receta.",
|
||||
"allow-users-outside-of-your-household-to-see-your-recipes": "Permite a los usuarios fuera de tu hogar ver tus recetas",
|
||||
@@ -320,10 +320,10 @@
|
||||
"mealplan-settings": "Ajustes del menú",
|
||||
"mealplan-update-failed": "Actualización del menú fallida",
|
||||
"mealplan-updated": "Menú actualizado",
|
||||
"mealplan-households-description": "If no household is selected, recipes can be added from any household",
|
||||
"any-category": "Any Category",
|
||||
"any-tag": "Any Tag",
|
||||
"any-household": "Any Household",
|
||||
"mealplan-households-description": "Si no se selecciona un hogar, se pueden añadir recetas de cualquier hogar",
|
||||
"any-category": "Cualquier categoría",
|
||||
"any-tag": "Cualquier etiqueta",
|
||||
"any-household": "Cualquier casa",
|
||||
"no-meal-plan-defined-yet": "Todavía no hay ningún menú",
|
||||
"no-meal-planned-for-today": "No hay ningún menú para hoy",
|
||||
"numberOfDays-hint": "Número de días al cargar la página",
|
||||
@@ -356,7 +356,7 @@
|
||||
"for-type-meal-types": "para {0} tipos de comida",
|
||||
"meal-plan-rules": "Reglas del Plan de Comida",
|
||||
"new-rule": "Nueva Regla",
|
||||
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the rule filters will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
|
||||
"meal-plan-rules-description": "Puedes crear reglas para la selección automática de recetas para tus planes de comida. Estas reglas son usadas por el servidor para determinar el conjunto aleatorio de recetas para crear planes de comidas. Ten en cuenta que si las reglas tienen las mismas restricciones diarias/tipos, las categorías de las reglas se fusionaran. En la práctica, no es necesario crear reglas duplicadas, pero es posible.",
|
||||
"new-rule-description": "Al crear una nueva regla para un plan de comidas, puede restringir la regla para que se aplique a un día específico de la semana y/o un tipo específico de comida. Para aplicar una regla a todos los días o a todos los tipos de comida, puede establecer la regla en \"Cualquiera\", lo que la aplicará a todos los valores posibles para el día y/o el tipo de comida.",
|
||||
"recipe-rules": "Reglas de Recetas",
|
||||
"applies-to-all-days": "Aplica para todos los días",
|
||||
@@ -431,7 +431,7 @@
|
||||
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Pegar los datos de tu receta. Cada línea será tratada como un elemento de una lista",
|
||||
"recipe-markup-specification": "Definición de Markup de la receta",
|
||||
"recipe-url": "URL de la receta",
|
||||
"recipe-html-or-json": "Recipe HTML or JSON",
|
||||
"recipe-html-or-json": "Receta HTML o JSON",
|
||||
"upload-a-recipe": "Subir una receta",
|
||||
"upload-individual-zip-file": "Sube un archivo .zip individual exportado desde otra instancia de Mealie.",
|
||||
"url-form-hint": "Copia y pega un enlace desde tu página web favorita",
|
||||
@@ -466,7 +466,7 @@
|
||||
"calories-suffix": "calorías",
|
||||
"carbohydrate-content": "Hidratos de carbono",
|
||||
"categories": "Categorías",
|
||||
"cholesterol-content": "Cholesterol",
|
||||
"cholesterol-content": "Colesterol",
|
||||
"comment-action": "Comentar",
|
||||
"comment": "Comentar",
|
||||
"comments": "Comentarios",
|
||||
@@ -513,7 +513,7 @@
|
||||
"recipe-updated": "Receta actualizada",
|
||||
"remove-from-favorites": "Eliminar de favoritos",
|
||||
"remove-section": "Eliminar sección",
|
||||
"saturated-fat-content": "Saturated fat",
|
||||
"saturated-fat-content": "Grasas saturadas",
|
||||
"save-recipe-before-use": "Guardar la receta antes de usar",
|
||||
"section-title": "Título de la sección",
|
||||
"servings": "Porciones",
|
||||
@@ -524,9 +524,9 @@
|
||||
"sugar-content": "Azúcares",
|
||||
"title": "Título",
|
||||
"total-time": "Tiempo total",
|
||||
"trans-fat-content": "Trans-fat",
|
||||
"trans-fat-content": "Grasas trans",
|
||||
"unable-to-delete-recipe": "No se puede eliminar la receta",
|
||||
"unsaturated-fat-content": "Unsaturated fat",
|
||||
"unsaturated-fat-content": "Grasas no saturadas",
|
||||
"no-recipe": "No hay receta",
|
||||
"locked-by-owner": "Bloqueada por el propietario",
|
||||
"join-the-conversation": "Únete a la conversación",
|
||||
@@ -614,16 +614,16 @@
|
||||
"scrape-recipe-description": "Importa una receta por URL. Proporcione la URL para el sitio que desea importar, y Mealie intentará importar la receta de ese sitio y añadirla a su colección.",
|
||||
"scrape-recipe-have-a-lot-of-recipes": "¿Tienes muchas recetas que quieres raspar a la vez?",
|
||||
"scrape-recipe-suggest-bulk-importer": "Prueba el importador masivo",
|
||||
"scrape-recipe-have-raw-html-or-json-data": "Have raw HTML or JSON data?",
|
||||
"scrape-recipe-you-can-import-from-raw-data-directly": "You can import from raw data directly",
|
||||
"scrape-recipe-have-raw-html-or-json-data": "¿Tiene datos HTML o JSON?",
|
||||
"scrape-recipe-you-can-import-from-raw-data-directly": "Puede importar directamente desde datos brutos",
|
||||
"import-original-keywords-as-tags": "Importar palabras clave originales como etiquetas",
|
||||
"stay-in-edit-mode": "Permanecer en modo edición",
|
||||
"import-from-zip": "Importar desde zip",
|
||||
"import-from-zip-description": "Importa una receta única que fue exportada desde otra instancia de Mealie.",
|
||||
"import-from-html-or-json": "Import from HTML or JSON",
|
||||
"import-from-html-or-json-description": "Import a single recipe from raw HTML or JSON. This is useful if you have a recipe from a site that Mealie can't scrape normally, or from some other external source.",
|
||||
"json-import-format-description-colon": "To import via JSON, it must be in valid format:",
|
||||
"json-editor": "JSON Editor",
|
||||
"import-from-html-or-json": "Importar desde HTML o JSON",
|
||||
"import-from-html-or-json-description": "Importar una sola receta de HTML crudo o JSON. Esto es útil si tienes una receta de un sitio que Meale no puede extraer normalmente, o de alguna otra fuente externa.",
|
||||
"json-import-format-description-colon": "Para importar a través de JSON, debe tener un formato válido:",
|
||||
"json-editor": "Editor de JSON",
|
||||
"zip-files-must-have-been-exported-from-mealie": "Los archivos .zip deben haber sido exportados desde Mealie",
|
||||
"create-a-recipe-by-uploading-a-scan": "Crea una receta subiendo una escaneada.",
|
||||
"upload-a-png-image-from-a-recipe-book": "Suba una imagen png de un libro de recetas",
|
||||
@@ -662,7 +662,7 @@
|
||||
"missing-food": "Crear comida faltante: {food}",
|
||||
"no-food": "Sin Comida"
|
||||
},
|
||||
"reset-servings-count": "Reset Servings Count"
|
||||
"reset-servings-count": "Restablecer contador de porciones"
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Búsqueda avanzada",
|
||||
@@ -673,7 +673,7 @@
|
||||
"or": "O",
|
||||
"has-any": "Tiene alguna",
|
||||
"has-all": "Tiene todo",
|
||||
"clear-selection": "Clear Selection",
|
||||
"clear-selection": "Borrar selección",
|
||||
"results": "Resultados",
|
||||
"search": "Buscar",
|
||||
"search-mealie": "Buscar Mealie (presione /)",
|
||||
@@ -1012,7 +1012,7 @@
|
||||
"administrator": "Administrador",
|
||||
"user-can-invite-other-to-group": "El usuario puede invitar a otros al grupo",
|
||||
"user-can-manage-group": "El usuario puede administrar el grupo",
|
||||
"user-can-manage-household": "User can manage household",
|
||||
"user-can-manage-household": "El usuario puede administrar el hogar",
|
||||
"user-can-organize-group-data": "El usuario puede organizar los datos del grupo",
|
||||
"enable-advanced-features": "Habilitar Características Avanzadas",
|
||||
"it-looks-like-this-is-your-first-time-logging-in": "Parece que esta es la primera vez que inicias sesión.",
|
||||
@@ -1268,10 +1268,12 @@
|
||||
"restore-from-v1-backup": "¿Tienes una copia de seguridad de Mealie v1? Puedes restaurarla aquí.",
|
||||
"manage-profile-or-get-invite-link": "Gestiona tu perfil, o usa un enlace de invitación para compartir con otros."
|
||||
},
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"debug-openai-services": "Depurar servicios OpenAI",
|
||||
"debug-openai-services-description": "Utilice esta página para depurar servicios OpenAI. Puede probar su conexión OpenAI y ver los resultados aquí. Si tiene activados los servicios de imágenes, también puede proporcionar una imagen.",
|
||||
"run-test": "Iniciar prueba",
|
||||
"test-results": "Resultados de la Prueba",
|
||||
"group-delete-note": "Los grupos con usuarios u hogares no pueden ser eliminados",
|
||||
"household-delete-note": "No se pueden eliminar los hogares con usuarios"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 ¡Bienvenido, {0}!",
|
||||
@@ -1324,7 +1326,7 @@
|
||||
},
|
||||
"cookbook": {
|
||||
"cookbooks": "Recetarios",
|
||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
||||
"description": "Los recetarios son otra forma de organizar recetas creando secciones cruzadas de recetas y etiquetas. Crear un recetario añadirá una entrada a la barra lateral y todas las recetas con las etiquetas y categorías elegidas se mostrarán en el recetario.",
|
||||
"public-cookbook": "Recetario público",
|
||||
"public-cookbook-description": "Los recetarios públicos se pueden compartir con usuarios externos y se mostrarán en su página de grupos.",
|
||||
"filter-options": "Opciones de filtro",
|
||||
@@ -1340,25 +1342,25 @@
|
||||
},
|
||||
"query-filter": {
|
||||
"logical-operators": {
|
||||
"and": "AND",
|
||||
"or": "OR"
|
||||
"and": "Y",
|
||||
"or": "O"
|
||||
},
|
||||
"relational-operators": {
|
||||
"equals": "equals",
|
||||
"does-not-equal": "does not equal",
|
||||
"is-greater-than": "is greater than",
|
||||
"is-greater-than-or-equal-to": "is greater than or equal to",
|
||||
"is-less-than": "is less than",
|
||||
"is-less-than-or-equal-to": "is less than or equal to"
|
||||
"equals": "es igual a",
|
||||
"does-not-equal": "no se corresponde",
|
||||
"is-greater-than": "es mayor que",
|
||||
"is-greater-than-or-equal-to": "es mayor que o igual a",
|
||||
"is-less-than": "es menor que",
|
||||
"is-less-than-or-equal-to": "es menor que o igual a"
|
||||
},
|
||||
"relational-keywords": {
|
||||
"is": "is",
|
||||
"is-not": "is not",
|
||||
"is-one-of": "is one of",
|
||||
"is-not-one-of": "is not one of",
|
||||
"contains-all-of": "contains all of",
|
||||
"is-like": "is like",
|
||||
"is-not-like": "is not like"
|
||||
"is": "es",
|
||||
"is-not": "no es",
|
||||
"is-one-of": "es uno de",
|
||||
"is-not-one-of": "no es uno de",
|
||||
"contains-all-of": "contiene todo de",
|
||||
"is-like": "es como",
|
||||
"is-not-like": "no es como"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Tervetuloa, {0}!",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"date": "Date",
|
||||
"id": "Identifiant",
|
||||
"owner": "Propriétaire",
|
||||
"change-owner": "Change Owner",
|
||||
"change-owner": "Modifier le propriétaire",
|
||||
"date-added": "Date d’ajout",
|
||||
"none": "Aucun",
|
||||
"run": "Exécuter",
|
||||
@@ -214,10 +214,10 @@
|
||||
"confirm-delete-generic-items": "Êtes-vous sûr de vouloir supprimer les éléments suivants ?",
|
||||
"organizers": "Classification",
|
||||
"caution": "Avertissement",
|
||||
"show-advanced": "Show Advanced",
|
||||
"add-field": "Add Field",
|
||||
"date-created": "Date Created",
|
||||
"date-updated": "Date Updated"
|
||||
"show-advanced": "Afficher les paramètres avancés",
|
||||
"add-field": "Ajouter un champ",
|
||||
"date-created": "Date de création",
|
||||
"date-updated": "Date de mise à jour"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Voulez-vous vraiment supprimer <b>{groupName}<b/> ?",
|
||||
@@ -356,7 +356,7 @@
|
||||
"for-type-meal-types": "pour les types de repas {0}",
|
||||
"meal-plan-rules": "Règles du menu",
|
||||
"new-rule": "Nouvelle règle",
|
||||
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the rule filters will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
|
||||
"meal-plan-rules-description": "Vous pouvez créer des règles pour ajouter automatiquement des recettes à votre menu. Ces règles sont utilisées par le serveur pour établir la liste aléatoire de recettes utilisée lors de la création de menus. Notez que si les règles ont les mêmes contraintes jour/type, alors les filtres des règles seront fusionnées. Dans la pratique, il est inutile de créer des règles en double, mais il est possible de le faire.",
|
||||
"new-rule-description": "Lorsque vous créez une nouvelle règle pour un menu, vous pouvez restreindre la règle à un jour précis de la semaine et/ou un type de repas particulier. Pour appliquer une règle à tous les jours ou à tous les types de repas, vous pouvez définir la règle à \"Tous\".",
|
||||
"recipe-rules": "Règles de recette",
|
||||
"applies-to-all-days": "S'applique à tous les jours",
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Déboguer les services OpenAI",
|
||||
"debug-openai-services-description": "Utilisez cette page pour déboguer les services OpenAI. Vous pouvez tester votre connexion OpenAI et voir les résultats ici. Si vous avez activé les services d'image, vous pouvez également fournir une image.",
|
||||
"run-test": "Lancer le test",
|
||||
"test-results": "Résultats du test"
|
||||
"test-results": "Résultats du test",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Bienvenue, {0} !",
|
||||
@@ -1324,7 +1326,7 @@
|
||||
},
|
||||
"cookbook": {
|
||||
"cookbooks": "Livres de recettes",
|
||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
||||
"description": "Les livres de recettes sont un autre moyen d’organiser des recettes en sélectionnant un ensemble précis de recettes, de classification et de filtres. La création d'un livre de recettes ajoute une entrée à la barre latérale et toutes les recettes avec les filtres choisies seront affichées dans le livre de recettes.",
|
||||
"public-cookbook": "Livre de recettes public",
|
||||
"public-cookbook-description": "Les livres de recettes publics peuvent être partagés avec des personnes qui n'ont pas Mealie et seront affichés sur la page de vos groupes.",
|
||||
"filter-options": "Options de filtres",
|
||||
@@ -1340,8 +1342,8 @@
|
||||
},
|
||||
"query-filter": {
|
||||
"logical-operators": {
|
||||
"and": "AND",
|
||||
"or": "OR"
|
||||
"and": "ET",
|
||||
"or": "OU"
|
||||
},
|
||||
"relational-operators": {
|
||||
"equals": "equals",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"date": "Date",
|
||||
"id": "Identifiant",
|
||||
"owner": "Propriétaire",
|
||||
"change-owner": "Change Owner",
|
||||
"change-owner": "Modifier le propriétaire",
|
||||
"date-added": "Date d’ajout",
|
||||
"none": "Aucun",
|
||||
"run": "Exécuter",
|
||||
@@ -214,10 +214,10 @@
|
||||
"confirm-delete-generic-items": "Êtes-vous sûr de vouloir supprimer les éléments suivants ?",
|
||||
"organizers": "Classification",
|
||||
"caution": "Avertissement",
|
||||
"show-advanced": "Show Advanced",
|
||||
"add-field": "Add Field",
|
||||
"date-created": "Date Created",
|
||||
"date-updated": "Date Updated"
|
||||
"show-advanced": "Afficher les paramètres avancés",
|
||||
"add-field": "Ajouter un champ",
|
||||
"date-created": "Date de création",
|
||||
"date-updated": "Date de mise à jour"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Êtes-vous certain de vouloir supprimer <b>{groupName}<b/>?",
|
||||
@@ -356,7 +356,7 @@
|
||||
"for-type-meal-types": "pour les types de repas {0}",
|
||||
"meal-plan-rules": "Règles du menu",
|
||||
"new-rule": "Nouvelle règle",
|
||||
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the rule filters will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
|
||||
"meal-plan-rules-description": "Vous pouvez créer des règles pour ajouter automatiquement des recettes à votre menu. Ces règles sont utilisées par le serveur pour établir la liste aléatoire de recettes utilisée lors de la création de menus. Notez que si les règles ont les mêmes contraintes jour/type, alors les filtres des règles seront fusionnées. Dans la pratique, il est inutile de créer des règles en double, mais il est possible de le faire.",
|
||||
"new-rule-description": "Lorsque vous créez une nouvelle règle pour un menu, vous pouvez restreindre la règle à un jour précis de la semaine et/ou un type de repas particulier. Pour appliquer une règle à tous les jours ou à tous les types de repas, vous pouvez définir la règle à \"Tous\".",
|
||||
"recipe-rules": "Règles de recette",
|
||||
"applies-to-all-days": "S'applique à tous les jours",
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Déboguer les services OpenAI",
|
||||
"debug-openai-services-description": "Utilisez cette page pour déboguer les services OpenAI. Vous pouvez tester votre connexion OpenAI et voir les résultats ici. Si vous avez activé les services d'image, vous pouvez également fournir une image.",
|
||||
"run-test": "Lancer le test",
|
||||
"test-results": "Résultats du test"
|
||||
"test-results": "Résultats du test",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Bienvenue, {0}!",
|
||||
@@ -1324,7 +1326,7 @@
|
||||
},
|
||||
"cookbook": {
|
||||
"cookbooks": "Livres de recettes",
|
||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
||||
"description": "Les livres de recettes sont un autre moyen d’organiser des recettes en sélectionnant un ensemble précis de recettes, de classification et de filtres. La création d'un livre de recettes ajoute une entrée à la barre latérale et toutes les recettes avec les filtres choisies seront affichées dans le livre de recettes.",
|
||||
"public-cookbook": "Livre de recettes public",
|
||||
"public-cookbook-description": "Les livres de recettes publics peuvent être partagés avec des personnes qui n'ont pas Mealie et seront affichés sur la page de vos groupes.",
|
||||
"filter-options": "Options de filtres",
|
||||
@@ -1340,8 +1342,8 @@
|
||||
},
|
||||
"query-filter": {
|
||||
"logical-operators": {
|
||||
"and": "AND",
|
||||
"or": "OR"
|
||||
"and": "ET",
|
||||
"or": "OU"
|
||||
},
|
||||
"relational-operators": {
|
||||
"equals": "equals",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Déboguer les services OpenAI",
|
||||
"debug-openai-services-description": "Utilisez cette page pour déboguer les services OpenAI. Vous pouvez tester votre connexion OpenAI et voir les résultats ici. Si vous avez activé les services d'image, vous pouvez également fournir une image.",
|
||||
"run-test": "Lancer le test",
|
||||
"test-results": "Résultats du test"
|
||||
"test-results": "Résultats du test",
|
||||
"group-delete-note": "Les groupes avec des utilisateurs ou des foyers ne peuvent être supprimés",
|
||||
"household-delete-note": "Les foyers avec des utilisateurs ne peuvent être supprimés"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Bienvenue, {0} !",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"development": "פיתוח",
|
||||
"docs": "תיעוד",
|
||||
"download-log": "הורדת לוגים",
|
||||
"download-recipe-json": "הJSON האחרון שנקרא",
|
||||
"download-recipe-json": "ה-JSON האחרון שנקרא",
|
||||
"github": "גיטהאב",
|
||||
"log-lines": "שורות לוג",
|
||||
"not-demo": "לא בהדגמה",
|
||||
@@ -54,10 +54,10 @@
|
||||
"apprise-url": "קישור להודעה",
|
||||
"database": "מסד נתונים",
|
||||
"delete-event": "מחיקת אירוע",
|
||||
"event-delete-confirmation": "האם את/ה בטוח/ה שברצונך למחוק את האירוע?",
|
||||
"event-delete-confirmation": "למחוק את האירוע?",
|
||||
"event-deleted": "אירוע נמחק",
|
||||
"event-updated": "האירוע עודכן",
|
||||
"new-notification-form-description": "מילי עושה שימוש בספריה בשם Apprise לשליחת התראות. Apprise מציעה אפשרויות רבות עבור התראות לבעלי שירותים. פנה לאתר הWiki של Apprise להסבר מלא על יצירת לינקים לשירות שלך.",
|
||||
"new-notification-form-description": "Mקשךןק עושה שימוש בספריה בשם Apprise לשליחת התראות. Apprise מציעה אפשרויות רבות עבור התראות לבעלי שירותים. פנה לאתר הWiki של Apprise להסבר מלא על יצירת לינקים לשירות שלך.",
|
||||
"new-version": "גרסה חדשה זמינה!",
|
||||
"notification": "התראה",
|
||||
"refresh": "רענן",
|
||||
@@ -88,7 +88,7 @@
|
||||
"close": "סגירה",
|
||||
"confirm": "אישור",
|
||||
"confirm-how-does-everything-look": "איך הכל נראה?",
|
||||
"confirm-delete-generic": "האם את/ה בטוח/ה שברצונך למחוק את זה?",
|
||||
"confirm-delete-generic": "למחוק את זה?",
|
||||
"copied_message": "הועתק!",
|
||||
"create": "יצירה",
|
||||
"created": "נוצר",
|
||||
@@ -189,7 +189,7 @@
|
||||
"menu": "תפריט",
|
||||
"a-name-is-required": "נדרש שם",
|
||||
"delete-with-name": "מחיקת {name}",
|
||||
"confirm-delete-generic-with-name": "האם את/ה בטוח/ה שברצונך למחוק את {name}?",
|
||||
"confirm-delete-generic-with-name": "למחוק את {name}?",
|
||||
"confirm-delete-own-admin-account": "אזהרה! אתה עומד למחוק את חשבון המנהל שלך. פעולה זו לא ניתנת לביטול ותמחק לחלוטין את החשבון שלך. האם להמשיך?",
|
||||
"organizer": "מארגן",
|
||||
"transfer": "העברה",
|
||||
@@ -211,7 +211,7 @@
|
||||
"created-on-date": "נוצר ב-{0}",
|
||||
"unsaved-changes": "יש שינויים שלא נשמרו. לצאת לפני שמירה? אשר לשמירה, בטל למחיקת שינויים.",
|
||||
"clipboard-copy-failure": "כשלון בהעתקה ללוח ההדבקה.",
|
||||
"confirm-delete-generic-items": "האם אתה בטוח שברצונך למחוק את הפריטים הנבחרים?",
|
||||
"confirm-delete-generic-items": "למחוק את הפריטים שנבחרו?",
|
||||
"organizers": "מארגנים",
|
||||
"caution": "זהירות",
|
||||
"show-advanced": "הצג הגדרות מתקדמות",
|
||||
@@ -220,7 +220,7 @@
|
||||
"date-updated": "תאריך עדכון"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "האם את/ה בטוח/ה שברצונך למחוק את <b>{groupName}<b/>?",
|
||||
"are-you-sure-you-want-to-delete-the-group": "למחוק את <b>{groupName}<b/>?",
|
||||
"cannot-delete-default-group": "לא ניתן למחוק את קבוצת ברירת המחדל",
|
||||
"cannot-delete-group-with-users": "לא ניתן למחוק קבוצה עם משתמשים",
|
||||
"confirm-group-deletion": "אשר/י מחיקת קבוצה",
|
||||
@@ -322,7 +322,7 @@
|
||||
"mealplan-updated": "תכנית ארוחה עודכנה",
|
||||
"mealplan-households-description": "אם לא נבחר משק בית, ניתן להוסיף מתכונים מכל משק בית",
|
||||
"any-category": "כל קטגוריה",
|
||||
"any-tag": "כל תג",
|
||||
"any-tag": "כל תגית",
|
||||
"any-household": "כל משק בית",
|
||||
"no-meal-plan-defined-yet": "עדיין לא הוגדרה תכנית ארוחה",
|
||||
"no-meal-planned-for-today": "לא מתוכננת ארוחה להיום",
|
||||
@@ -377,7 +377,7 @@
|
||||
},
|
||||
"nextcloud": {
|
||||
"description": "ייבא מידע מספר המתכונים ב- Nextcould",
|
||||
"description-long": "ייבוא מתכונים מ- Nextcould יכול להתבצע בקובץ zip שמכיל את המידע מ- Nextcloud. ראה את דוגמת מבנה התיקיות למטה כדי לוודא שניתן לייבא את המתכון.",
|
||||
"description-long": "ייבוא מתכונים מ-Nextcould יכול להתבצע בקובץ zip שמכיל את המידע. המבנה צריך להתאים לתיקייה לדוגמא למטה כדי להבטיח שניתן לייבא את המתכון.",
|
||||
"title": "ספר בישול של Nextcloud"
|
||||
},
|
||||
"copymethat": {
|
||||
@@ -400,8 +400,8 @@
|
||||
"recipe-data-migrations-explanation": "ניתן לייבא מתכונים מאפליקציות תומכות אחרות אל Mealie. זו דרך מעולה להתחיל במילי.",
|
||||
"coming-from-another-application-or-an-even-older-version-of-mealie": "הגעת מתוכנה אחרת או גרסה ישנה יותר של Mealie? מומלץ לבדוק מיגרציות ולראות אם ניתן לייבא את המידע שלך.",
|
||||
"choose-migration-type": "בחר סוג מיגרציה",
|
||||
"tag-all-recipes": "תייג את כל המתכונים עם תגית {tag-name}",
|
||||
"nextcloud-text": "ייבוא מתכונים מ- Nextcould יכול להתבצע בקובץ zip שמכיל את המידע מ- Nextcloud. ראה את דוגמת מבנה התיקיות למטה כדי לוודא שניתן לייבא את המתכון.",
|
||||
"tag-all-recipes": "תיוג את כל המתכונים עם תגית {tag-name}",
|
||||
"nextcloud-text": "ייבוא מתכונים מ-Nextcould יכול להתבצע בקובץ zip שמכיל את המידע. המבנה צריך להתאים לתיקייה לדוגמא למטה כדי להבטיח שניתן לייבא את המתכון.",
|
||||
"chowdown-text": "Mealie תומכת באופן טבעי בפורמט Chowdown. יש להוריד את רפוזיטורי הקוד כ-zip ולהעלות אותו כאן.",
|
||||
"recipe-1": "מתכון 1",
|
||||
"recipe-2": "מתכון 2",
|
||||
@@ -470,7 +470,7 @@
|
||||
"comment-action": "הערה",
|
||||
"comment": "הערה",
|
||||
"comments": "הערות",
|
||||
"delete-confirmation": "האם את/ה בטוח/ה שברצונך למחוק את המתכון הזה?",
|
||||
"delete-confirmation": "למחוק את המתכון הזה?",
|
||||
"delete-recipe": "מחיקת מתכון",
|
||||
"description": "תיאור",
|
||||
"disable-amount": "ביטול כמויות מרכיבים",
|
||||
@@ -607,7 +607,7 @@
|
||||
"should-translate-description": "תרגום המתכון לשפה שלי",
|
||||
"please-wait-image-procesing": "נה להמתין, התמונה עוברת עיבוץ. זה יכול לקחת זמן.",
|
||||
"bulk-url-import": "ייבוא מספר לינקים",
|
||||
"debug-scraper": "סורק דיבוג",
|
||||
"debug-scraper": "סורק לניפוי שגיאות",
|
||||
"create-a-recipe-by-providing-the-name-all-recipes-must-have-unique-names": "יצירת מתכון באמצעות שם. כל שמות המתכונים צריכים להיות שונים.",
|
||||
"new-recipe-names-must-be-unique": "שם מתכון חדש חייב להיות ייחודי",
|
||||
"scrape-recipe": "קריאת מתכון",
|
||||
@@ -616,7 +616,7 @@
|
||||
"scrape-recipe-suggest-bulk-importer": "נסה את יכולת קריאת רשימה",
|
||||
"scrape-recipe-have-raw-html-or-json-data": "יש לך מידע גולמי ב-HTML או JSON?",
|
||||
"scrape-recipe-you-can-import-from-raw-data-directly": "ניתן לייבא ישירות ממידע גולמי",
|
||||
"import-original-keywords-as-tags": "ייבא שמות מפתח מקוריות כתגיות",
|
||||
"import-original-keywords-as-tags": "ייבוא שמות מפתח מקוריות כתגיות",
|
||||
"stay-in-edit-mode": "השאר במצב עריכה",
|
||||
"import-from-zip": "ייבא מקובץ",
|
||||
"import-from-zip-description": "ייבוא מתכון בודד שיוצא ממילי אחרת.",
|
||||
@@ -634,11 +634,11 @@
|
||||
"bulk-import-process-has-started": "ייבוא קבוצה התחיל",
|
||||
"bulk-import-process-has-failed": "יבוא קבוצתי נכשל",
|
||||
"report-deletion-failed": "מחיקת דוח נכשלה",
|
||||
"recipe-debugger": "דיבאגר למתכון",
|
||||
"recipe-debugger-description": "ניתן להדביק פה קישור למתכון שברצונך לדבג. הכתובת תיסרק ע\"י סורק המתכונים והתוצאות יוצגו. אם לא חוזרות תוצאות, האתר לא נתמך ע\"י Mealie או ספריית הסריקה.",
|
||||
"recipe-debugger": "מנפה שגיאות למתכון",
|
||||
"recipe-debugger-description": "ניתן להדביק פה קישור למתכון שברצונך לנפות שגיאות עבורו. הכתובת תיסרק ע\"י סורק המתכונים והתוצאות יוצגו. אם לא חוזרות תוצאות, האתר לא נתמך ע\"י Mealie או ספריית הסריקה.",
|
||||
"use-openai": "השתמש ב-OpenAI",
|
||||
"recipe-debugger-use-openai-description": "ניתן להשתמש ב-OpenAI כדי לפענח את התוצאות במקום להסתמך על ספריית הסריקה. כאשר מייצרים מתכון באמצעות כתובת, זה נעשה אוטומטית אם ספריית הסריקה נכשלת, אך ניתן לבדוק זאת ידנית כאן.",
|
||||
"debug": "דיבאג",
|
||||
"debug": "ניפוי שגיאות",
|
||||
"tree-view": "תצוגת עץ",
|
||||
"recipe-yield": "תשואת מתכון",
|
||||
"unit": "יחידה",
|
||||
@@ -692,7 +692,7 @@
|
||||
"backup-created-at-response-export_path": "גיבוי נוצר ב {path}",
|
||||
"backup-deleted": "גיבוי נמחק",
|
||||
"restore-success": "השחזור הצליח",
|
||||
"restore-fail": "שחזור נכשל. נא לבדוק את יומני השרת לעוד מידע",
|
||||
"restore-fail": "שחזור נכשל. נא לבדוק את הלוגים של השרת לעוד מידע",
|
||||
"backup-tag": "תגית גיבוי",
|
||||
"create-heading": "Create a Backup",
|
||||
"delete-backup": "מחיקת גיבוי",
|
||||
@@ -701,7 +701,7 @@
|
||||
"import-summary": "ייבא תקציר",
|
||||
"partial-backup": "גיבוי חלקי",
|
||||
"unable-to-delete-backup": "לא ניתן למחוק גיבוי.",
|
||||
"experimental-description": "Backups are total snapshots of the database and data directory of the site. This includes all data and cannot be set to exclude subsets of data. You can think of this as a snapshot of Mealie at a specific time. These serve as a database agnostic way to export and import data, or back up the site to an external location.",
|
||||
"experimental-description": "גיבויים הם תמונת מצב של מסד הנתונים ותיקיית המידע של האתר. זה כולל את כל המידע ולא ניתן להוריד חלק מהמידע. ניתן להתייחס אליהם כתמונת מצב של Mealie בזמן מסוים. הגיבויים הם דרך לייבוא ולייצוא מידע ללא תלות במסד הנתונים עצמו, או לגיבוי האתר למיקום חיצוני.",
|
||||
"backup-restore": "גיבוי / שחזור",
|
||||
"back-restore-description": "שחזור מגיבוי זה ידרוס את המידע הקיים במסד הנתונים ובספריות האתר ויחליף אותם בזה הקיים בגיבוי. {cannot-be-undone} אם השחזור יצליח, המשתמש ינותק מהמערכת.",
|
||||
"cannot-be-undone": "פעולה זו לא בלתי הפיכה - השתמש בזהירות.",
|
||||
@@ -800,7 +800,7 @@
|
||||
"tracker": "טראקר",
|
||||
"configuration": "הגדרות",
|
||||
"docker-volume": "Docker Volume",
|
||||
"docker-volume-help": "מילי דורשת שקונטיינר הקצה הקדמי והקצה האחורי חולקים את באותו כונן / שטח אחסון בדוקר.\nכל זאת כל מנת לוודא שלקונטיינר תהיה גישה לתמונות הגיבוי או לנכסים בדיסק.",
|
||||
"docker-volume-help": "Mealie דורשת שקונטיינר הקצה הקדמי והקצה האחורי יחלקו את אותו כונן / נפח אחסון. זה יבטיח שהקצה הקדמי יוכל לגשת לתמונות ונכסים ששמורים בדיסק.",
|
||||
"volumes-are-misconfigured": "כוננים אינם מוגדרים בצורה תקינה.",
|
||||
"volumes-are-configured-correctly": "ה-Volumeים מוגדרים תקין.",
|
||||
"status-unknown-try-running-a-validation": "מצב לא ידוע. נסה להריץ אימות.",
|
||||
@@ -809,13 +809,13 @@
|
||||
"email-configured": "דואר אלקטרוני הוגדר",
|
||||
"email-test-results": "תוצאות בדיקת דואר אלקטרוני",
|
||||
"ready": "מוכן",
|
||||
"not-ready": "לא מוכן - בדוק משתני סביבה",
|
||||
"not-ready": "לא מוכן - נא לבדוק משתני סביבה",
|
||||
"succeeded": "הצליח",
|
||||
"failed": "נכשל",
|
||||
"general-about": "מידע כללי",
|
||||
"application-version": "גרסת אפליקציה",
|
||||
"application-version-error-text": "הגרסה הנוכחית ({0}) לא מתאימה לגרסה האחרונה. יש לשקול שדרוג לגרסה האחרונה ({1}).",
|
||||
"mealie-is-up-to-date": "מילי מעודכנת לגרסה האחרונה",
|
||||
"mealie-is-up-to-date": "Mealie מעודכנת",
|
||||
"secure-site": "אבטוח אתר",
|
||||
"secure-site-error-text": "שלח באמצעות כתובת המערכת בלופבאק או בצורה מאובטחת בעזרת https. לוח ההעתקה ו- API נוספים בדפדפן עשויים לא לעבוד.",
|
||||
"secure-site-success-text": "האתר נגיש דרך כתובת המערכת בלופבאק או בצורה מאובטחת בעזרת https",
|
||||
@@ -823,16 +823,16 @@
|
||||
"server-side-base-url-error-text": "'כתובת בסיס' היא עדיין הערך ברירת המחדל בשרת ה- API. הדבר יגרום לבעיות עם קישורי התראות בשרת עבור אימייל וכו'.",
|
||||
"server-side-base-url-success-text": "קישור צד שרת אינו תואם לברירת המחדל",
|
||||
"ldap-ready": "LDAP מוכן",
|
||||
"ldap-ready-error-text": "לא כל ערכי ה- LDAP מוגדרים. ניתן להתעלם אם אינך משתמשת באימות LDAP.",
|
||||
"ldap-ready-success-text": "כל משתני ה- LDAP הנחוצים מוגדרים.",
|
||||
"ldap-ready-error-text": "לא כל הערכים הנחוצים ל-LDAP מוגדרים. ניתן להתעלם אם לא נעשה שימוש באימות LDAP.",
|
||||
"ldap-ready-success-text": "כל המשתנים הנחוצים ל-LDAP מוגדרים.",
|
||||
"build": "בניה",
|
||||
"recipe-scraper-version": "גרסת סורק המתכונים",
|
||||
"oidc-ready": "OIDC Ready",
|
||||
"oidc-ready-error-text": "Not all OIDC Values are configured. This can be ignored if you are not using OIDC Authentication.",
|
||||
"oidc-ready-success-text": "Required OIDC variables are all set.",
|
||||
"openai-ready": "OpenAI Ready",
|
||||
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
|
||||
"openai-ready-success-text": "Required OpenAI variables are all set."
|
||||
"oidc-ready": "OIDC מוכן",
|
||||
"oidc-ready-error-text": "לא כל הערכים הנחוצים ל-OIDC מוגדרים. ניתן להתעלם אם לא נעשה שימוש באימות OIDC.",
|
||||
"oidc-ready-success-text": "כל המשתנים הנחוצים ל-OIDC מוגדרים.",
|
||||
"openai-ready": "OpenAI מוכן",
|
||||
"openai-ready-error-text": "לא כל הערכים הנחוצים ל-OpenAI מוגדרים. ניתן להתעלם אם לא נעשה שימוש ב-OpenAI.",
|
||||
"openai-ready-success-text": "כל המשתנים הנחוצים ל-OpenAI מוגדרים."
|
||||
},
|
||||
"shopping-list": {
|
||||
"all-lists": "כל הרשימות",
|
||||
@@ -850,7 +850,7 @@
|
||||
"linked-item-warning": "האובייקט הזה מקושר לאחד או יותר מתכונים. שינוי היחידות או האוכל יוביל לתוצאות בלתי צפויות בהוספה או הסרת מתכונים מהרשימה.",
|
||||
"toggle-food": "הצג/הסתר אוכל",
|
||||
"manage-labels": "ניהול תוויות",
|
||||
"are-you-sure-you-want-to-delete-this-item": "האם אתה בטוח שברצונך למחוק פריט זה?",
|
||||
"are-you-sure-you-want-to-delete-this-item": "למחוק פריט זה?",
|
||||
"copy-as-text": "העתק כטקסט",
|
||||
"copy-as-markdown": "העתק כ-Markdown",
|
||||
"delete-checked": "מחק מסומנים",
|
||||
@@ -862,11 +862,11 @@
|
||||
"items-checked-count": "לא נבחרו פריטים|פריט אחד נבחר|{count} פריטים נבחרו",
|
||||
"no-label": "ללא תווית",
|
||||
"completed-on": "הושלם ב- {date}",
|
||||
"you-are-offline": "You are offline",
|
||||
"you-are-offline-description": "Not all features are available while offline. You can still add, modify, and remove items, but you will not be able to sync your changes to the server until you are back online.",
|
||||
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
|
||||
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
|
||||
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?"
|
||||
"you-are-offline": "במצב לא מקוון",
|
||||
"you-are-offline-description": "חלק מהיכולות אינן זמינות במצב לא מקוון. עדיין ניתן להוסיף, לשנות, ולהוריד פריטים, אך לא ניתן לסנכרן את השינויים שלך לשרת עד לחידוש החיבור.",
|
||||
"are-you-sure-you-want-to-check-all-items": "לסמן את כל הפריטים?",
|
||||
"are-you-sure-you-want-to-uncheck-all-items": "לבטל את סימון כל הפריטים?",
|
||||
"are-you-sure-you-want-to-delete-checked-items": "למחוק את כל הפריטים המסומנים?"
|
||||
},
|
||||
"sidebar": {
|
||||
"all-recipes": "כל המתכונים",
|
||||
@@ -909,7 +909,7 @@
|
||||
"tag-updated": "תגית עודכנה",
|
||||
"tags": "תגיות",
|
||||
"untagged-count": "לא מתוייג {count}",
|
||||
"create-a-tag": "צור תגית",
|
||||
"create-a-tag": "יצירת תגית",
|
||||
"tag-name": "שם תגית",
|
||||
"tag": "תגית"
|
||||
},
|
||||
@@ -925,8 +925,8 @@
|
||||
},
|
||||
"user": {
|
||||
"admin": "אדמין",
|
||||
"are-you-sure-you-want-to-delete-the-link": "האם את/ה בטוח/ה שברצונך למחוק את הלינק <b>{link}<b/>?",
|
||||
"are-you-sure-you-want-to-delete-the-user": "האם את/ה בטוח/ה שברצונך למחוק את המשתמש <b>{activeName} ID: {activeId}<b/>?",
|
||||
"are-you-sure-you-want-to-delete-the-link": "למחוק את הלינק <b>{link}<b/>?",
|
||||
"are-you-sure-you-want-to-delete-the-user": "למחוק את המשתמש <b>{activeName} מזהה: {activeId}<b/>?",
|
||||
"auth-method": "שיטת אימות",
|
||||
"confirm-link-deletion": "אשר מחיקת לינק",
|
||||
"confirm-password": "אימות סיסמה",
|
||||
@@ -1016,7 +1016,7 @@
|
||||
"user-can-organize-group-data": "משתמש יכול לשנות מידע של קבוצה",
|
||||
"enable-advanced-features": "אפשר אפשרויות מתקדמות",
|
||||
"it-looks-like-this-is-your-first-time-logging-in": "נראה שזו ההתחברות הראשונה שלך.",
|
||||
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "לא רוצה לראות את זה יותר? דאג לשנות את המייל של בהגדרות המשתמש!",
|
||||
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "לא רוצה לראות את זה יותר? ניתן לשנות את המייל שלך בהגדרות המשתמש!",
|
||||
"forgot-password": "שכחתי סיסמא",
|
||||
"forgot-password-text": "נא לספק כתובת דוא\"ל. אנו נשלח לך הודעת דוא\"ל לצורך איפוס הסיסמה שלך.",
|
||||
"changes-reflected-immediately": "שינויים למשתמש זה ישתקפו מיידית."
|
||||
@@ -1024,7 +1024,7 @@
|
||||
"language-dialog": {
|
||||
"translated": "תורגם",
|
||||
"choose-language": "בחר שפה",
|
||||
"select-description": "בחר את שפת הממשק של מילי. הגדרה זו היא עבורך בלבד ולא תחול על משתמשים אחרים.",
|
||||
"select-description": "בחר את שפת הממשק של Mealie. הגדרה זו היא עבורך בלבד ולא תחול על משתמשים אחרים.",
|
||||
"how-to-contribute-description": "האם משהו לא תורגם עדיין, תרגום שגוי או שהשפה שלך חסרה ברשימה? {read-the-docs-link} על איך לתרום!",
|
||||
"read-the-docs": "קרא את ההוראות"
|
||||
},
|
||||
@@ -1077,8 +1077,8 @@
|
||||
},
|
||||
"recipes": {
|
||||
"purge-exports": "נקה ייצואים",
|
||||
"are-you-sure-you-want-to-delete-all-export-data": "האם אתה בטוח שאתה רוצה למחוק נתוני הייצוא?",
|
||||
"confirm-delete-recipes": "האם אתה בטוח שאתה רוצה למחוק את המתכונים הבאים? פעולה זו בלתי הפיכה.",
|
||||
"are-you-sure-you-want-to-delete-all-export-data": "למחוק את כל נתוני הייצוא?",
|
||||
"confirm-delete-recipes": "למחוק את המתכונים שנבחרו? פעולה זו בלתי הפיכה.",
|
||||
"the-following-recipes-selected-length-will-be-exported": "המתכונים הבאים ({0}) ייוצאו.",
|
||||
"settings-chosen-explanation": "הגדרות שנבחרו מלבד האפשרויות הנעולות, יופעלו על כל המתכונים שנבחרו.",
|
||||
"selected-length-recipe-s-settings-will-be-updated": "הגדרות של {count} מתכונים יעודכנו.",
|
||||
@@ -1097,10 +1097,10 @@
|
||||
"source-unit-will-be-deleted": "יחידת המקור תמחק"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
"recipe-actions-data": "מידע על הפעולות במתכון",
|
||||
"new-recipe-action": "פעולת-מתכון חדשה",
|
||||
"edit-recipe-action": "עריכת פעולת-מתכון",
|
||||
"action-type": "סוג פעולה"
|
||||
},
|
||||
"create-alias": "יצירת שם נרדף",
|
||||
"manage-aliases": "נהל שמות נרדפים",
|
||||
@@ -1118,9 +1118,9 @@
|
||||
"category-data": "נתוני קטגוריה"
|
||||
},
|
||||
"tags": {
|
||||
"new-tag": "טאג חדש",
|
||||
"edit-tag": "ערוך טאג",
|
||||
"tag-data": "נתוני טאגים"
|
||||
"new-tag": "תגית חדשה",
|
||||
"edit-tag": "עריכת תגית",
|
||||
"tag-data": "נתוני תגיות"
|
||||
},
|
||||
"tools": {
|
||||
"new-tool": "כלי חדש",
|
||||
@@ -1137,7 +1137,7 @@
|
||||
"group-details": "פרטי הקבוצה",
|
||||
"group-details-description": "לפני יצירת חשבון יש צורך ליצור קבוצה. הקבוצה תכיל רק אותך אבל תוכל להזמין אחרים בשלב מאוחר יותר. חברים בקבוצה יכולים לשתף תוכנית ארוחות, רשימות קניות, מתכונים ועוד!",
|
||||
"use-seed-data": "השתמש בנתוני האכלוס",
|
||||
"use-seed-data-description": "מילי מגיעה עם אוסף של מאכלים, יחידות מדידה ותוויות שניתן להשתמש לאכלוס הקבוצות עם מידע שימושי לארגון המתכונים.",
|
||||
"use-seed-data-description": "Mealie מגיעה עם אוסף של מאכלים, יחידות מדידה ותוויות שניתן להשתמש לאכלוס הקבוצות עם מידע שימושי לארגון המתכונים.",
|
||||
"account-details": "פרטי חשבון"
|
||||
},
|
||||
"validation": {
|
||||
@@ -1237,14 +1237,14 @@
|
||||
"actions-description-destructive": "הרסני",
|
||||
"actions-description-irreversible": "בלתי הפיך",
|
||||
"logs-action-refresh": "רענן לוגים",
|
||||
"logs-page-title": "לוגים של מילי",
|
||||
"logs-page-title": "לוגים של Mealie",
|
||||
"logs-tail-lines-label": "קווים סופיים"
|
||||
},
|
||||
"mainentance": {
|
||||
"actions-title": "פעולות"
|
||||
},
|
||||
"ingredients-natural-language-processor": "מעבד שפה טבעי לרכיבים",
|
||||
"ingredients-natural-language-processor-explanation": "מילי עושה שימוש בתנאי שדות רנדומליים (CRFs) עבור ניתוח ועיבוד מרכיבים. שימוש במודל זה עבור מרכיבים מסתמך על נתונים של מעל ל- 100 אלף מרכיבים מרשימה של ה- New York Times. מכיוון שמודל זה אומן בשפה האנגלית בלבד, ייתכן ותחווה תוצאות שונות בשפות אחרות. עמוד זה הוא סביבה לבדיקת המודל.",
|
||||
"ingredients-natural-language-processor-explanation": "Mealie עושה שימוש בשדות מותנים אקראית (CRFs) עבור ניתוח ועיבוד מרכיבים. שימוש במודל זה עבור מרכיבים מסתמך על נתונים של מעל ל- 100 אלף מרכיבים מרשימה של ה- New York Times. מכיוון שמודל זה אומן בשפה האנגלית בלבד, ייתכן ותחווה תוצאות שונות בשפות אחרות. עמוד זה הוא סביבה לבדיקת המודל.",
|
||||
"ingredients-natural-language-processor-explanation-2": "זה לא מושלם, אבל מספק תוצאות טובות באופן כללי ומספק נקודת התחלה טובה עבור עיבוד ידני של מרכיבים לשדות נפרדים. לחילופין ניתן להשתמש בעיבוד \"נוקשה\" לזיהוי המרכיבים.",
|
||||
"nlp": "NLP",
|
||||
"brute": "נוקשה",
|
||||
@@ -1261,52 +1261,54 @@
|
||||
"setup": {
|
||||
"first-time-setup": "הגדרה ראשונית",
|
||||
"welcome-to-mealie-get-started": "ברוכים הבאים ל-Mealie! בואו נתחיל",
|
||||
"already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage",
|
||||
"common-settings-for-new-sites": "Here are some common settings for new sites",
|
||||
"setup-complete": "Setup Complete!",
|
||||
"here-are-a-few-things-to-help-you-get-started": "Here are a few things to help you get started with Mealie",
|
||||
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.",
|
||||
"manage-profile-or-get-invite-link": "Manage your own profile, or grab an invite link to share with others."
|
||||
"already-set-up-bring-to-homepage": "כבר הגדרתי הכל, תעבירו אותי לעמוד הבית",
|
||||
"common-settings-for-new-sites": "הגדרות נפוצות לאתרים חדשים יופיעו כאן",
|
||||
"setup-complete": "ההגדרה הושלמה!",
|
||||
"here-are-a-few-things-to-help-you-get-started": "כמה דברים שיעזרו לך להתחיל להשתמש ב-Mealie",
|
||||
"restore-from-v1-backup": "יש לך גיבוי משרת Mealie v1? ניתן לשחזר אותו כאן.",
|
||||
"manage-profile-or-get-invite-link": "ניתן לנהל את הפרופיל שלך, או לשתף את לינק ההזמנה לאחרים."
|
||||
},
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"debug-openai-services": "ניפוי שגיאות לשירותי OpenAI",
|
||||
"debug-openai-services-description": "עמוד זה ישמש לניפוי שגיאות בשירותי OpenAI. ניתן לבדוק חיבור ל-OpenAI ולראות את התוצאות כאן. אם איפשרת את שירותי התמונה, ניתן גם לספק תמונה.",
|
||||
"run-test": "הרצת בדיקה",
|
||||
"test-results": "תוצאות הבדיקה",
|
||||
"group-delete-note": "לא ניתן למחוק קבוצות עם משתמשים או משקי בית",
|
||||
"household-delete-note": "לא ניתן למחוק משקי בית עם משתמשים"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
"welcome-user": "👋 שלום, {0}!",
|
||||
"description": "ניהול פרופיל, מתכונים והגדרות קבוצה.",
|
||||
"get-invite-link": "קבלת קישור להזמנה",
|
||||
"get-public-link": "כתובת פומבית",
|
||||
"account-summary": "פירוט משתמש",
|
||||
"account-summary-description": "Here's a summary of your group's information.",
|
||||
"account-summary-description": "הנה סיכום המידע של הקבוצה שלך.",
|
||||
"group-statistics": "נתונים סטטיסטיים של קבוצה",
|
||||
"group-statistics-description": "סטטיסטיקות על הקבוצה שלך.",
|
||||
"household-statistics": "Household Statistics",
|
||||
"household-statistics-description": "Your Household Statistics provide some insight how you're using Mealie.",
|
||||
"household-statistics": "סטטיסטיקות משק הבית",
|
||||
"household-statistics-description": "סטטיסטיקות משק הבית שלך מספקות תובנות לאיך אתם משתמשים ב-Mealie.",
|
||||
"storage-capacity": "מקום אחסון",
|
||||
"storage-capacity-description": "מקום האחסון מחושב ע\"י סיכום התמונות והקבצים שהעלת.",
|
||||
"personal": "אישי",
|
||||
"personal-description": "These are settings that are personal to you. Changes here won't affect other users.",
|
||||
"personal-description": "הגדרות אלה אישיות עבורך. שינויים לא ישפיעו על משתמשים אחרים.",
|
||||
"user-settings": "הגדרות משתמש",
|
||||
"user-settings-description": "Manage your preferences, change your password, and update your email.",
|
||||
"api-tokens-description": "Manage your API Tokens for access from external applications.",
|
||||
"user-settings-description": "ניהול ההעדפות שלך, שינוי סיסמא, ועדכון כתובת מייל.",
|
||||
"api-tokens-description": "ניהול אסימוני API יאפשר גישה ליישומים חיצוניים.",
|
||||
"group-description": "פריטים אלו שותפו עם קבוצתך. עריכתם תבצע שינוי עבור כל הקבוצה!",
|
||||
"group-settings": "הגדרות קבוצה",
|
||||
"group-settings-description": "Manage your common group settings, like privacy settings.",
|
||||
"household-description": "These items are shared within your household. Editing one of them will change it for the whole household!",
|
||||
"household-settings": "Household Settings",
|
||||
"household-settings-description": "Manage your household settings, like mealplan and privacy settings.",
|
||||
"group-settings-description": "ניהול הגדרות הקבוצות שלך, כגון הגדרות פרטיות.",
|
||||
"household-description": "פריטים אלה משותפים עם משק הבית שלך. עריכה תשנה אותם עבור משק הבית כולו!",
|
||||
"household-settings": "הגדרות משק הבית",
|
||||
"household-settings-description": "ניהול הגדרות משק הבית שלך, כגון תכנון ארוחות והגדרות פרטיות.",
|
||||
"cookbooks-description": "נהל אוסף של מתכונים וצור עבורם עמודים.",
|
||||
"members": "חברים",
|
||||
"members-description": "See who's in your household and manage their permissions.",
|
||||
"members-description": "צפייה בחברי משק הביתה שלך ובהרשאות שלהם.",
|
||||
"webhooks-description": "הגדר webhooks אשר יופעלו בימים בהם יש לך תוכניות ארוחות.",
|
||||
"notifiers": "מתריעים",
|
||||
"notifiers-description": "הגדרת הודעות דואל והודעות בדחיפה אשר יופעלו עם אירועים ספציפיים.",
|
||||
"manage-data": "נהל נתונים",
|
||||
"manage-data-description": "Manage your Mealie data; Foods, Units, Categories, Tags and more.",
|
||||
"manage-data-description": "ניהול המידע שלך ב-Mealie; אוכל, יחידות מידה, קטגוריות, תגיות ועוד.",
|
||||
"data-migrations": "ניוד נתונים",
|
||||
"data-migrations-description": "Migrate your existing data from other applications like Nextcloud Recipes and Chowdown.",
|
||||
"data-migrations-description": "העברת המידע שלך מאפליקציות אחרות כגון Nextcloud Recipes ו-Chowdown.",
|
||||
"email-sent": "דוא\"ל נשלח",
|
||||
"error-sending-email": "שגיאה בשליחת דוא\"ל",
|
||||
"personal-information": "פרטים אישיים",
|
||||
@@ -1324,9 +1326,9 @@
|
||||
},
|
||||
"cookbook": {
|
||||
"cookbooks": "ספרי בישול",
|
||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
||||
"description": "ספרי בישול הם דרך נוספת לסידור מתכונים על ידי חיתוך של מתכונים, מארגנים, ומסננים נוספים. יצירת ספר בישול תוסיף רשומה לתפריט הצדדי וכל המתכונים שעונים לסינון יוצגו באופן אוטומטי.",
|
||||
"public-cookbook": "ספר בישול פומבי",
|
||||
"public-cookbook-description": "ספרי בישול ניתנים לשיתוף עם משתמשים מחוץ למילי ויופיעו בתוך עמוד הקבוצות.",
|
||||
"public-cookbook-description": "ספרי בישול ניתנים לשיתוף עם משתמשים מחוץ ל-Mealie ויופיעו בתוך עמוד הקבוצות.",
|
||||
"filter-options": "אפשרויות סינון",
|
||||
"filter-options-description": "כאשר מסומן 'זקוק לכל' ספר הבישול יכיל רק את המתכונים שמכילים את כל הפריטים שנבחרו. זה מתייחס לכל תתי הבחירות ולא לנושאים של האפשרויות שסומנו.",
|
||||
"require-all-categories": "זקוק לכל הקטגוריות",
|
||||
@@ -1334,31 +1336,31 @@
|
||||
"require-all-tools": "זקוק לכל הכלים",
|
||||
"cookbook-name": "שם ספר בישול",
|
||||
"cookbook-with-name": "ספר בישול {0}",
|
||||
"household-cookbook-name": "{0} Cookbook {1}",
|
||||
"household-cookbook-name": "{0} ספר בישול {1}",
|
||||
"create-a-cookbook": "צור ספר בישול חדש",
|
||||
"cookbook": "ספר בישול"
|
||||
},
|
||||
"query-filter": {
|
||||
"logical-operators": {
|
||||
"and": "AND",
|
||||
"or": "OR"
|
||||
"and": "וגם",
|
||||
"or": "או"
|
||||
},
|
||||
"relational-operators": {
|
||||
"equals": "equals",
|
||||
"does-not-equal": "does not equal",
|
||||
"is-greater-than": "is greater than",
|
||||
"is-greater-than-or-equal-to": "is greater than or equal to",
|
||||
"is-less-than": "is less than",
|
||||
"is-less-than-or-equal-to": "is less than or equal to"
|
||||
"equals": "שווה",
|
||||
"does-not-equal": "לא שווה",
|
||||
"is-greater-than": "גדול מ-",
|
||||
"is-greater-than-or-equal-to": "גדול או שווה ל-",
|
||||
"is-less-than": "קטן מ-",
|
||||
"is-less-than-or-equal-to": "קטן או שווה ל-"
|
||||
},
|
||||
"relational-keywords": {
|
||||
"is": "is",
|
||||
"is-not": "is not",
|
||||
"is-one-of": "is one of",
|
||||
"is-not-one-of": "is not one of",
|
||||
"contains-all-of": "contains all of",
|
||||
"is-like": "is like",
|
||||
"is-not-like": "is not like"
|
||||
"is": "זהה ל-",
|
||||
"is-not": "לא זהה ל-",
|
||||
"is-one-of": "אחד מ-",
|
||||
"is-not-one-of": "לא אחד מ-",
|
||||
"contains-all-of": "מכיל הכל מתוך",
|
||||
"is-like": "דומה ל-",
|
||||
"is-not-like": "לא דומה לא-"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"database-type": "Vrsta baze podataka",
|
||||
"database-url": "URL baze podataka",
|
||||
"default-group": "Zadana grupa",
|
||||
"default-household": "Default Household",
|
||||
"default-household": "Zadano kućanstvo",
|
||||
"demo": "Demo",
|
||||
"demo-status": "Demo Status",
|
||||
"development": "Razvoj",
|
||||
@@ -65,7 +65,7 @@
|
||||
"something-went-wrong": "Nešto nije bilo u redu!",
|
||||
"subscribed-events": "Pretplaćeni Događaji",
|
||||
"test-message-sent": "Testna Poruka je Poslana",
|
||||
"message-sent": "Message Sent",
|
||||
"message-sent": "Poruka je poslana",
|
||||
"new-notification": "Nova Obavijest",
|
||||
"event-notifiers": "Obavještavatelji Događaja",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (preskočeno ako je prazno)",
|
||||
@@ -79,15 +79,15 @@
|
||||
"tag-events": "Događaji Oznaka",
|
||||
"category-events": "Događaji Kategorija",
|
||||
"when-a-new-user-joins-your-group": "Kada se novi korisnik pridruži vašoj grupi",
|
||||
"recipe-events": "Recipe Events"
|
||||
"recipe-events": "Događaji recepta"
|
||||
},
|
||||
"general": {
|
||||
"add": "Add",
|
||||
"add": "Dodaj",
|
||||
"cancel": "Odustani",
|
||||
"clear": "Očisti",
|
||||
"close": "Zatvori",
|
||||
"confirm": "Potvrdi",
|
||||
"confirm-how-does-everything-look": "How does everything look?",
|
||||
"confirm-how-does-everything-look": "Kako sve izgleda?",
|
||||
"confirm-delete-generic": "Jeste li sigurni da želite izbrisati ovo?",
|
||||
"copied_message": "Kopirano!",
|
||||
"create": "Kreiraj",
|
||||
@@ -118,10 +118,10 @@
|
||||
"json": "JSON",
|
||||
"keyword": "Ključna riječ",
|
||||
"link-copied": "Poveznica kopirana",
|
||||
"loading": "Loading",
|
||||
"loading": "Učitavanje",
|
||||
"loading-events": "Učitavanje događaja",
|
||||
"loading-recipe": "Loading recipe...",
|
||||
"loading-ocr-data": "Loading OCR data...",
|
||||
"loading-recipe": "Učitavanje recepta...",
|
||||
"loading-ocr-data": "Učitavanje OCR podataka...",
|
||||
"loading-recipes": "Učitavanje Recepata",
|
||||
"message": "Poruka",
|
||||
"monday": "Ponedjeljak",
|
||||
@@ -132,7 +132,7 @@
|
||||
"no-recipe-found": "Nije Pronađen Recept",
|
||||
"ok": "OK",
|
||||
"options": "Opcije:",
|
||||
"plural-name": "Plural Name",
|
||||
"plural-name": "Ime u množini",
|
||||
"print": "Ispiši",
|
||||
"print-preferences": "Postavke ispisa",
|
||||
"random": "Nasumično",
|
||||
@@ -146,23 +146,23 @@
|
||||
"save": "Spremi",
|
||||
"settings": "Postavke",
|
||||
"share": "Podijeli",
|
||||
"show-all": "Show All",
|
||||
"show-all": "Prikaži sve",
|
||||
"shuffle": "Nasumično",
|
||||
"sort": "Sortiraj",
|
||||
"sort-ascending": "Sort Ascending",
|
||||
"sort-descending": "Sort Descending",
|
||||
"sort-ascending": "Poredaj uzlazno",
|
||||
"sort-descending": "Poredaj silazno",
|
||||
"sort-alphabetically": "Abecedno",
|
||||
"status": "Status",
|
||||
"subject": "Tema",
|
||||
"submit": "Pošalji",
|
||||
"success-count": "Uspješno: {count}",
|
||||
"sunday": "Nedjelja",
|
||||
"system": "System",
|
||||
"system": "Sistem",
|
||||
"templates": "Predlošci:",
|
||||
"test": "Test",
|
||||
"themes": "Teme",
|
||||
"thursday": "Četvrtak",
|
||||
"title": "Title",
|
||||
"title": "Naslov",
|
||||
"token": "Token",
|
||||
"tuesday": "Utorak",
|
||||
"type": "Tip",
|
||||
@@ -177,12 +177,12 @@
|
||||
"units": "Mjerne jedinice",
|
||||
"back": "Nazad",
|
||||
"next": "Nastavi",
|
||||
"start": "Start",
|
||||
"start": "Započni",
|
||||
"toggle-view": "Promijeni Prikaz",
|
||||
"date": "Datum",
|
||||
"id": "Id",
|
||||
"owner": "Vlasnik",
|
||||
"change-owner": "Change Owner",
|
||||
"change-owner": "Promjena vlasnika",
|
||||
"date-added": "Datum Dodavanja",
|
||||
"none": "Ništa",
|
||||
"run": "Pokreni",
|
||||
@@ -209,15 +209,15 @@
|
||||
"refresh": "Osvježi",
|
||||
"upload-file": "Prenesi Datoteku",
|
||||
"created-on-date": "Kreirano dana: {0}",
|
||||
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
|
||||
"clipboard-copy-failure": "Failed to copy to the clipboard.",
|
||||
"confirm-delete-generic-items": "Are you sure you want to delete the following items?",
|
||||
"organizers": "Organizers",
|
||||
"caution": "Caution",
|
||||
"show-advanced": "Show Advanced",
|
||||
"add-field": "Add Field",
|
||||
"date-created": "Date Created",
|
||||
"date-updated": "Date Updated"
|
||||
"unsaved-changes": "Imate promjene koje nisu spremljene. Želite li ih spremiti prije odlaska? Ok za spremanje, Odustani za odbaciti promjene.",
|
||||
"clipboard-copy-failure": "Pogreška prilikom spremanja u međuspremnik.",
|
||||
"confirm-delete-generic-items": "Jeste li sigurni da želite izbrisati ove stavke?",
|
||||
"organizers": "Organizatori",
|
||||
"caution": "Pažnja",
|
||||
"show-advanced": "Prikaži napredno",
|
||||
"add-field": "Dodaj polje",
|
||||
"date-created": "Datum kreiranja",
|
||||
"date-updated": "Datum ažuriranja"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Jeste li sigurni da želite izbrisati <b>{groupName}<b/>?",
|
||||
@@ -232,7 +232,7 @@
|
||||
"group-id-with-value": "ID grupe: {groupID}",
|
||||
"group-name": "Naziv Grupe",
|
||||
"group-not-found": "Grupa nije pronađena",
|
||||
"group-token": "Group Token",
|
||||
"group-token": "Token grupe",
|
||||
"group-with-value": "Grupa: {groupID}",
|
||||
"groups": "Grupe",
|
||||
"manage-groups": "Upravljaj Grupama",
|
||||
@@ -244,9 +244,9 @@
|
||||
"keep-my-recipes-private-description": "Postavlja vašu grupu i zadane postavke svih recepata na privatne. Uvijek možete to kasnije promijeniti."
|
||||
},
|
||||
"manage-members": "Upravljanje Korisnicima",
|
||||
"manage-members-description": "Manage the permissions of the members in your household. {manage} allows the user to access the data-management page, and {invite} allows the user to generate invitation links for other users. Group owners cannot change their own permissions.",
|
||||
"manage-members-description": "Upravljajte dozvolama članova u vašem kućanstvu. {manage} omogućuje korisniku pristup stranici za upravljanje podacima, i {invite} omogućuje korisniku generiranje pozivnih veza za druge korisnike. Vlasnici grupe ne mogu mijenjati vlastite dozvole.",
|
||||
"manage": "Upravljaj",
|
||||
"manage-household": "Manage Household",
|
||||
"manage-household": "Upravljaj kućanstvom",
|
||||
"invite": "Pozovi",
|
||||
"looking-to-update-your-profile": "Želite ažurirati svoj profil?",
|
||||
"default-recipe-preferences-description": "Ovo su zadane postavke prilikom stvaranja novog recepta u vašoj grupi. Ove postavke mogu se promijeniti za pojedinačne recepte u izborniku postavki recepta.",
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"date": "Dátum",
|
||||
"id": "Azonosító",
|
||||
"owner": "Tulajdonos",
|
||||
"change-owner": "Change Owner",
|
||||
"change-owner": "Tulajdonos módosítása",
|
||||
"date-added": "Hozzáadás Dátuma",
|
||||
"none": "Nincs",
|
||||
"run": "Futtatás",
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "OpenAI-szolgáltatások hibakeresése",
|
||||
"debug-openai-services-description": "Ezt az oldalt az OpenAI szolgáltatások hibakeresésére használhatja. Itt tesztelheti az OpenAI-kapcsolatot, és láthatja az eredményeket. Ha engedélyezte a képszolgáltatásokat, akkor képet is megadhat.",
|
||||
"run-test": "Teszt futtatása",
|
||||
"test-results": "Teszt eredmények"
|
||||
"test-results": "Teszt eredmények",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Üdvözöljük, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug Dei Servizi OpenAI",
|
||||
"debug-openai-services-description": "Usa questa pagina per fare debug dei servizi OpenAI. Puoi testare la tua connessione OpenAI e vedere i risultati qui. Se si dispone di servizi di immagine abilitati, è anche possibile fornire un'immagine.",
|
||||
"run-test": "Esegui test",
|
||||
"test-results": "Risultati dei test"
|
||||
"test-results": "Risultati dei test",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Benvenutǝ, {0}!",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"date": "日付",
|
||||
"id": "Id",
|
||||
"owner": "所有者",
|
||||
"change-owner": "Change Owner",
|
||||
"change-owner": "所有者の変更",
|
||||
"date-added": "追加日",
|
||||
"none": "なし",
|
||||
"run": "実行",
|
||||
@@ -214,10 +214,10 @@
|
||||
"confirm-delete-generic-items": "次のアイテムを本当に削除しますか?",
|
||||
"organizers": "収納",
|
||||
"caution": "注意",
|
||||
"show-advanced": "Show Advanced",
|
||||
"add-field": "Add Field",
|
||||
"date-created": "Date Created",
|
||||
"date-updated": "Date Updated"
|
||||
"show-advanced": "詳細を表示",
|
||||
"add-field": "フィールドを追加",
|
||||
"date-created": "作成日",
|
||||
"date-updated": "更新日"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "<b>{groupName}<b/> を削除しますか?",
|
||||
@@ -356,7 +356,7 @@
|
||||
"for-type-meal-types": "{0} の食事の種類",
|
||||
"meal-plan-rules": "食事プランのルール",
|
||||
"new-rule": "新しいルール",
|
||||
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the rule filters will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
|
||||
"meal-plan-rules-description": "食事プランのレシピを自動選択するためのルールを作成できます。これらのルールは、食事プランを作成するときに選択するレシピのランダムプールを決定するためにサーバーによって使用されます。ルールに同じ日/タイプの制約がある場合は、ルールフィルターが結合されることに注意してください。実際には、重複したルールを作成する必要はありませんが、重複して作成することは可能です。",
|
||||
"new-rule-description": "食事プランの新しいルールを作成する場合は、特定の曜日および/または特定の種類の食事に適用されるルールを制限できます。 すべての日またはすべての食事タイプにルールを適用するには、その日または食事タイプのすべての可能な値に適用されるルールを \"すべて\" に設定できます。",
|
||||
"recipe-rules": "レシピのルール",
|
||||
"applies-to-all-days": "すべての日に適用",
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "OpenAIサービスのデバッグ",
|
||||
"debug-openai-services-description": "このページを使用して、OpenAIサービスをデバッグします。ここでOpenAI接続をテストし、結果を確認できます。画像サービスが有効になっている場合は、画像を提供することもできます。",
|
||||
"run-test": "テスト実行",
|
||||
"test-results": "テスト結果"
|
||||
"test-results": "テスト結果",
|
||||
"group-delete-note": "ユーザーまたは世帯を含むグループは削除できません",
|
||||
"household-delete-note": "ユーザーがいる世帯は削除できません"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 ようこそ, {0}!",
|
||||
@@ -1324,7 +1326,7 @@
|
||||
},
|
||||
"cookbook": {
|
||||
"cookbooks": "料理本",
|
||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
||||
"description": "料理本は、レシピのクロスセクション、オーガナイザー、その他のフィルターを作成してレシピを整理するもう 1 つの方法です。料理本を作成するとサイドバーにエントリが追加され、選択したフィルターが適用されたすべてのレシピが料理本に表示されます。",
|
||||
"public-cookbook": "公開料理本",
|
||||
"public-cookbook-description": "公開料理本は非Mealieユーザーと共有でき、グループページに表示されます。",
|
||||
"filter-options": "フィルタオプション",
|
||||
@@ -1344,21 +1346,21 @@
|
||||
"or": "OR"
|
||||
},
|
||||
"relational-operators": {
|
||||
"equals": "equals",
|
||||
"does-not-equal": "does not equal",
|
||||
"is-greater-than": "is greater than",
|
||||
"is-greater-than-or-equal-to": "is greater than or equal to",
|
||||
"is-less-than": "is less than",
|
||||
"is-less-than-or-equal-to": "is less than or equal to"
|
||||
"equals": "等しい",
|
||||
"does-not-equal": "等しくない",
|
||||
"is-greater-than": "より大きい",
|
||||
"is-greater-than-or-equal-to": "以上",
|
||||
"is-less-than": "より小さい",
|
||||
"is-less-than-or-equal-to": "以下"
|
||||
},
|
||||
"relational-keywords": {
|
||||
"is": "is",
|
||||
"is-not": "is not",
|
||||
"is-one-of": "is one of",
|
||||
"is-not-one-of": "is not one of",
|
||||
"contains-all-of": "contains all of",
|
||||
"is-like": "is like",
|
||||
"is-not-like": "is not like"
|
||||
"is": "は",
|
||||
"is-not": "ではない",
|
||||
"is-one-of": "のうちの1つです",
|
||||
"is-not-one-of": "のひとつではない",
|
||||
"contains-all-of": "すべてを含む",
|
||||
"is-like": "次のようなものです",
|
||||
"is-not-like": "というわけではありません"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
"new-version": "새로운 버전 사용 가능",
|
||||
"notification": "알림",
|
||||
"refresh": "새로고침",
|
||||
"scheduled": "Scheduled",
|
||||
"scheduled": "예약됨",
|
||||
"something-went-wrong": "문제가 발생했습니다!",
|
||||
"subscribed-events": "구독한 이벤트",
|
||||
"test-message-sent": "테스트 메시지가 전송됐습니다.",
|
||||
@@ -76,7 +76,7 @@
|
||||
"when-a-user-in-your-group-creates-a-new-mealplan": "When a user in your group creates a new mealplan",
|
||||
"shopping-list-events": "Shopping List Events",
|
||||
"cookbook-events": "Cookbook Events",
|
||||
"tag-events": "Tag Events",
|
||||
"tag-events": "Tag 이벤트",
|
||||
"category-events": "Category Events",
|
||||
"when-a-new-user-joins-your-group": "When a new user joins your group",
|
||||
"recipe-events": "Recipe Events"
|
||||
@@ -118,7 +118,7 @@
|
||||
"json": "JSON",
|
||||
"keyword": "키워드",
|
||||
"link-copied": "링크 복사됨",
|
||||
"loading": "Loading",
|
||||
"loading": "불러오는 중",
|
||||
"loading-events": "이벤트를 불러오는 중",
|
||||
"loading-recipe": "레시피 로딩 중...",
|
||||
"loading-ocr-data": "Loading OCR data...",
|
||||
@@ -146,49 +146,49 @@
|
||||
"save": "저장",
|
||||
"settings": "설정",
|
||||
"share": "공유",
|
||||
"show-all": "Show All",
|
||||
"show-all": "모두 보기",
|
||||
"shuffle": "섞기",
|
||||
"sort": "정렬",
|
||||
"sort-ascending": "Sort Ascending",
|
||||
"sort-descending": "Sort Descending",
|
||||
"sort-ascending": "오름차순 정렬",
|
||||
"sort-descending": "내림차순으로 정렬",
|
||||
"sort-alphabetically": "알파벳순",
|
||||
"status": "상태",
|
||||
"subject": "제목",
|
||||
"submit": "제출",
|
||||
"success-count": "성공: {count}",
|
||||
"sunday": "일요일",
|
||||
"system": "System",
|
||||
"templates": "Templates:",
|
||||
"test": "Test",
|
||||
"themes": "Themes",
|
||||
"system": "시스템",
|
||||
"templates": "템플릿:",
|
||||
"test": "테스트",
|
||||
"themes": "테마",
|
||||
"thursday": "목요일",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"title": "제목",
|
||||
"token": "토큰",
|
||||
"tuesday": "화요일",
|
||||
"type": "Type",
|
||||
"update": "Update",
|
||||
"updated": "Updated",
|
||||
"upload": "Upload",
|
||||
"update": "업데이트",
|
||||
"updated": "업데이트됨",
|
||||
"upload": "업로드",
|
||||
"url": "URL",
|
||||
"view": "View",
|
||||
"wednesday": "수요일",
|
||||
"yes": "Yes",
|
||||
"foods": "Foods",
|
||||
"yes": "예",
|
||||
"foods": "음식",
|
||||
"units": "Units",
|
||||
"back": "Back",
|
||||
"next": "Next",
|
||||
"start": "Start",
|
||||
"back": "뒤로",
|
||||
"next": "다음",
|
||||
"start": "시작",
|
||||
"toggle-view": "Toggle View",
|
||||
"date": "Date",
|
||||
"date": "날짜",
|
||||
"id": "Id",
|
||||
"owner": "작성자",
|
||||
"change-owner": "Change Owner",
|
||||
"date-added": "Date Added",
|
||||
"date-added": "추가된 날짜",
|
||||
"none": "없음",
|
||||
"run": "실행",
|
||||
"menu": "메뉴",
|
||||
"a-name-is-required": "이름은 필수 항목 입니다.",
|
||||
"delete-with-name": "Delete {name}",
|
||||
"delete-with-name": "{name} 삭제",
|
||||
"confirm-delete-generic-with-name": "Are you sure you want to delete this {name}?",
|
||||
"confirm-delete-own-admin-account": "Please note that you are trying to delete your own admin account! This action cannot be undone and will permanently delete your account?",
|
||||
"organizer": "Organizer",
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI diensten",
|
||||
"debug-openai-services-description": "Gebruik deze pagina om de OpenAI-diensten te debuggen. U kunt uw OpenAI-verbinding testen en de resultaten hier bekijken. Als u afbeeldingsdiensten hebt ingeschakeld, kunt u ook een afbeelding geven.",
|
||||
"run-test": "Test starten",
|
||||
"test-results": "Resultaten van de test"
|
||||
"test-results": "Resultaten van de test",
|
||||
"group-delete-note": "Groepen met gebruikers of huishoudens kunnen niet worden verwijderd",
|
||||
"household-delete-note": "Huishoudens met gebruikers kunnen niet worden verwijderd"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welkom, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "Velkommen, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Bem-vindo(a), {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Bem-vindo, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Добро пожаловать, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Vitajte, {0}!",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"date": "Datum",
|
||||
"id": "Id",
|
||||
"owner": "Lastnik",
|
||||
"change-owner": "Change Owner",
|
||||
"change-owner": "Zamenjava lastnika",
|
||||
"date-added": "Dodano dne",
|
||||
"none": "Brez",
|
||||
"run": "Zaženi",
|
||||
@@ -214,10 +214,10 @@
|
||||
"confirm-delete-generic-items": "Ali ste prepričani, da želite izbrisati izbrane elemente?",
|
||||
"organizers": "Organizatorji",
|
||||
"caution": "Pozor",
|
||||
"show-advanced": "Show Advanced",
|
||||
"add-field": "Add Field",
|
||||
"date-created": "Date Created",
|
||||
"date-updated": "Date Updated"
|
||||
"show-advanced": "Prikaži napredno",
|
||||
"add-field": "Dodaj polje",
|
||||
"date-created": "Datum nastanka",
|
||||
"date-updated": "Datum posodobitve"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Ste prepričani, da želite izbrisati <b>{groupName}<b/>?",
|
||||
@@ -320,10 +320,10 @@
|
||||
"mealplan-settings": "Nastavitve jedilnika",
|
||||
"mealplan-update-failed": "Napaka pri posodobitvi jedilnika",
|
||||
"mealplan-updated": "Jedilnik je posodobljen",
|
||||
"mealplan-households-description": "If no household is selected, recipes can be added from any household",
|
||||
"any-category": "Any Category",
|
||||
"any-tag": "Any Tag",
|
||||
"any-household": "Any Household",
|
||||
"mealplan-households-description": "Recepti iz gospodinjstev ne morejo biti dodani, če ni izbranega nobenega gospodinjstva",
|
||||
"any-category": "Vse kategorije",
|
||||
"any-tag": "Vse oznake",
|
||||
"any-household": "Vsa gospodinjstva",
|
||||
"no-meal-plan-defined-yet": "Za danes ni definiranjega načrta obroka",
|
||||
"no-meal-planned-for-today": "Za danes ni planiranega načrta obroka",
|
||||
"numberOfDays-hint": "Število dni na začetni strani",
|
||||
@@ -356,7 +356,7 @@
|
||||
"for-type-meal-types": "za obrok {0}",
|
||||
"meal-plan-rules": "Pravila za jedilnik",
|
||||
"new-rule": "Novo pravilo",
|
||||
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the rule filters will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
|
||||
"meal-plan-rules-description": "Ustvariš lahko pravila za avtomatično izbiro receptov za tvoje jedilnike. Ta pravila strežnik uporablja, da izbere nabor receptov iz katerih potem naključno izbere recept za jedilnik. Upoštevaj, da če imata dve pravili ista določila za dan in tip, potem bodo kategorije pravil združene. V praksi ni potrebno podvajati pravil, je pa to možno.",
|
||||
"new-rule-description": "Ko dodajaš novo pravilo za jedilnik lahko pravilo omejiš na določen dan v tednu in/ali na določen tip obroka. Če želiš pravilo nastaviti za vse dni ali za vse tipe obrokov, potem izberi \"Katerikoli\".",
|
||||
"recipe-rules": "Pravila za recepte",
|
||||
"applies-to-all-days": "Velja za vse dneve",
|
||||
@@ -431,7 +431,7 @@
|
||||
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Prilepi podatke recepta. Vsaka vrsta bo obravnavana kot element v seznamu",
|
||||
"recipe-markup-specification": "Markup specifikacija recepta",
|
||||
"recipe-url": "URL recepta",
|
||||
"recipe-html-or-json": "Recipe HTML or JSON",
|
||||
"recipe-html-or-json": "Recept HTML ali JSON",
|
||||
"upload-a-recipe": "Naloži recept",
|
||||
"upload-individual-zip-file": "Naloži posamezno .zip datoteko, izvoženo iz druge Mealie namestitve.",
|
||||
"url-form-hint": "Kopiraj in prilepi povezavo iz vaše priljubljene strani z recepti",
|
||||
@@ -466,7 +466,7 @@
|
||||
"calories-suffix": "kalorije",
|
||||
"carbohydrate-content": "Ogljikovi hidrati",
|
||||
"categories": "Kategorije",
|
||||
"cholesterol-content": "Cholesterol",
|
||||
"cholesterol-content": "Holesterol",
|
||||
"comment-action": "Pripomba",
|
||||
"comment": "Komentar",
|
||||
"comments": "Pripombe",
|
||||
@@ -513,7 +513,7 @@
|
||||
"recipe-updated": "Recept je posodobljen",
|
||||
"remove-from-favorites": "Odstrani iz priljubljenih",
|
||||
"remove-section": "Odstrani odsek",
|
||||
"saturated-fat-content": "Saturated fat",
|
||||
"saturated-fat-content": "Nasičena maščoba",
|
||||
"save-recipe-before-use": "Shrani recept pred uporabo",
|
||||
"section-title": "Naslov odseka",
|
||||
"servings": "Porcija",
|
||||
@@ -524,9 +524,9 @@
|
||||
"sugar-content": "Sladkor",
|
||||
"title": "Naslov",
|
||||
"total-time": "Skupni čas",
|
||||
"trans-fat-content": "Trans-fat",
|
||||
"trans-fat-content": "Transmaščoba",
|
||||
"unable-to-delete-recipe": "Ne morem izbrisati recepta",
|
||||
"unsaturated-fat-content": "Unsaturated fat",
|
||||
"unsaturated-fat-content": "Nenasičena maščoba",
|
||||
"no-recipe": "Ni recepta",
|
||||
"locked-by-owner": "Zakljenjeno od lastnika",
|
||||
"join-the-conversation": "Pridruži se pogovoru",
|
||||
@@ -614,16 +614,16 @@
|
||||
"scrape-recipe-description": "Postrgaj recept preko povezave. Priskrbi povezavo do strani, ki jo želiš postrgati in Mealie bo poskusil uvoziti recept iz strani in ga dodati v tvojo zbirko.",
|
||||
"scrape-recipe-have-a-lot-of-recipes": "Imaš veliko receptov, ki jih želiš naenkrat postrgati s spleta?",
|
||||
"scrape-recipe-suggest-bulk-importer": "Preizkusi masovni uvoz",
|
||||
"scrape-recipe-have-raw-html-or-json-data": "Have raw HTML or JSON data?",
|
||||
"scrape-recipe-you-can-import-from-raw-data-directly": "You can import from raw data directly",
|
||||
"scrape-recipe-have-raw-html-or-json-data": "Imaš surove HTML ali JSON podatke?",
|
||||
"scrape-recipe-you-can-import-from-raw-data-directly": "Uvoziš lahko neposredno iz surovih podatkov",
|
||||
"import-original-keywords-as-tags": "Uvozi izvorne ključne besede kot značke",
|
||||
"stay-in-edit-mode": "Urejaj naprej",
|
||||
"import-from-zip": "Uvozi z Zip",
|
||||
"import-from-zip-description": "Uvozi posamezen recept, ki je bil izvožen iz druge instance Mealie aplikacije.",
|
||||
"import-from-html-or-json": "Import from HTML or JSON",
|
||||
"import-from-html-or-json-description": "Import a single recipe from raw HTML or JSON. This is useful if you have a recipe from a site that Mealie can't scrape normally, or from some other external source.",
|
||||
"json-import-format-description-colon": "To import via JSON, it must be in valid format:",
|
||||
"json-editor": "JSON Editor",
|
||||
"import-from-html-or-json": "Uvozi iz HTML ali JSON",
|
||||
"import-from-html-or-json-description": "Uvozi posamezen recept iz surovega HTML ali JSON. To je uporabno, če je recept iz spletne strani, ki jo Mealie ne more normalno postrgati, ali pa iz ostalih zunanjih virov.",
|
||||
"json-import-format-description-colon": "Za uvoz preko JSON je zahtevan veljaven format:",
|
||||
"json-editor": "JSON urejevalnik",
|
||||
"zip-files-must-have-been-exported-from-mealie": ".zip datoteka mora biti izvožena iz Mealie aplikacije",
|
||||
"create-a-recipe-by-uploading-a-scan": "Ustvari recept z nalaganjem skenirane datoteke.",
|
||||
"upload-a-png-image-from-a-recipe-book": "Naloži png sliko iz kuharske knjige",
|
||||
@@ -662,7 +662,7 @@
|
||||
"missing-food": "Ustvari manjkajoče živilo: {food}",
|
||||
"no-food": "Ni živila"
|
||||
},
|
||||
"reset-servings-count": "Reset Servings Count"
|
||||
"reset-servings-count": "Ponastavi števec obrokov"
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Napredno iskanje",
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Odpravljanje napak v storitvah OpenAI",
|
||||
"debug-openai-services-description": "Uporabite to stran za odpravljanje napak v storitvah OpenAI. Tukaj lahko preizkusite svojo povezavo OpenAI in si ogledate rezultate. Če imate omogočene slikovne storitve, lahko zagotovite tudi sliko.",
|
||||
"run-test": "Zaženi test",
|
||||
"test-results": "Rezultati testa"
|
||||
"test-results": "Rezultati testa",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Živjo, {0}!",
|
||||
@@ -1324,7 +1326,7 @@
|
||||
},
|
||||
"cookbook": {
|
||||
"cookbooks": "Kuharske knjige",
|
||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
||||
"description": "Kuharske knjige so še en način za organizacijo receptov z ustvarjanjem presekov receptov in značk. Ustvarjanje kuharske knjige bo dodalo povezavo v stranski meni, ki bo prikazala vse recepte z izbranimi značkami in kategorijami.",
|
||||
"public-cookbook": "Javna kuharska knjiga",
|
||||
"public-cookbook-description": "Javne kuharske knjige lahko deliš z zunanjimi uporabniki, prav tako bodo prikazane tudi na strani tvoje skupine.",
|
||||
"filter-options": "Nastavitve filtrov",
|
||||
@@ -1340,25 +1342,25 @@
|
||||
},
|
||||
"query-filter": {
|
||||
"logical-operators": {
|
||||
"and": "AND",
|
||||
"or": "OR"
|
||||
"and": "IN",
|
||||
"or": "ALI"
|
||||
},
|
||||
"relational-operators": {
|
||||
"equals": "equals",
|
||||
"does-not-equal": "does not equal",
|
||||
"is-greater-than": "is greater than",
|
||||
"is-greater-than-or-equal-to": "is greater than or equal to",
|
||||
"is-less-than": "is less than",
|
||||
"is-less-than-or-equal-to": "is less than or equal to"
|
||||
"equals": "je enako",
|
||||
"does-not-equal": "ni enako",
|
||||
"is-greater-than": "je več kot",
|
||||
"is-greater-than-or-equal-to": "je več kot ali enako kot",
|
||||
"is-less-than": "je manj kot",
|
||||
"is-less-than-or-equal-to": "je manj kot ali enako kot"
|
||||
},
|
||||
"relational-keywords": {
|
||||
"is": "is",
|
||||
"is-not": "is not",
|
||||
"is-one-of": "is one of",
|
||||
"is-not-one-of": "is not one of",
|
||||
"contains-all-of": "contains all of",
|
||||
"is-like": "is like",
|
||||
"is-not-like": "is not like"
|
||||
"is": "je",
|
||||
"is-not": "ni",
|
||||
"is-one-of": "je en/a od",
|
||||
"is-not-one-of": "ni en/a od",
|
||||
"contains-all-of": "vsebuje vse",
|
||||
"is-like": "je kot",
|
||||
"is-not-like": "ni kot"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
"date": "Datum",
|
||||
"id": "ID",
|
||||
"owner": "Ägare",
|
||||
"change-owner": "Change Owner",
|
||||
"change-owner": "Ändra ägare",
|
||||
"date-added": "Skapad",
|
||||
"none": "Inga",
|
||||
"run": "Kör",
|
||||
@@ -214,10 +214,10 @@
|
||||
"confirm-delete-generic-items": "Är du säker på att du vill radera följande objekt?",
|
||||
"organizers": "Organisatörer",
|
||||
"caution": "Varning",
|
||||
"show-advanced": "Show Advanced",
|
||||
"add-field": "Add Field",
|
||||
"date-created": "Date Created",
|
||||
"date-updated": "Date Updated"
|
||||
"show-advanced": "Visa avancerat",
|
||||
"add-field": "Lägg till fält",
|
||||
"date-created": "Datum skapad",
|
||||
"date-updated": "Datum uppdaterat"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Är du säker på att du vill radera <b>{groupName}<b/>?",
|
||||
@@ -320,10 +320,10 @@
|
||||
"mealplan-settings": "Måltidsplan inställningar",
|
||||
"mealplan-update-failed": "Måltidsplan gick inte att uppdatera",
|
||||
"mealplan-updated": "Måltidsplan uppdaterad",
|
||||
"mealplan-households-description": "If no household is selected, recipes can be added from any household",
|
||||
"any-category": "Any Category",
|
||||
"any-tag": "Any Tag",
|
||||
"any-household": "Any Household",
|
||||
"mealplan-households-description": "Om inget hushåll har valts kan recept läggas till från vilket hushåll som helst",
|
||||
"any-category": "Valfri kategori",
|
||||
"any-tag": "Valfri tagg",
|
||||
"any-household": "Valfritt hushåll",
|
||||
"no-meal-plan-defined-yet": "Ingen måltidsplan definierad ännu",
|
||||
"no-meal-planned-for-today": "Ingen måltidsplan för idag",
|
||||
"numberOfDays-hint": "Antal dagar vid sidhämtning",
|
||||
@@ -356,7 +356,7 @@
|
||||
"for-type-meal-types": "för {0} måltidstyper",
|
||||
"meal-plan-rules": "Regler för måltidsplan",
|
||||
"new-rule": "Ny Regel",
|
||||
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the rule filters will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
|
||||
"meal-plan-rules-description": "Du kan skapa regler för att automatisk väja recept för din måltidsplanerare. Dessa regler används av servern för att bestämma den slumpmässiga poolen av recept att välja från när du skapar måltidsplaner. Observera att om reglerna har samma dag/typ-begränsningar så kommer kategorierna av reglerna att slås samman. I praktiken är det onödigt att skapa dubbla regler, men det går att göra det.",
|
||||
"new-rule-description": "När du skapar en ny regel för en måltidsplan kan du begränsa regeln för en viss veckodag och/eller en viss typ av måltid. För att tillämpa en regel på alla dagar eller alla måltidstyper kan du ställa in regeln till \"Any\" som kommer att tillämpa den på alla möjliga värden för dagen och/eller måltidstypen.",
|
||||
"recipe-rules": "Recept regler",
|
||||
"applies-to-all-days": "Gäller för alla dagar",
|
||||
@@ -431,7 +431,7 @@
|
||||
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Klistra in din receptdata, varje rad kommer att hanteras som ett listelement",
|
||||
"recipe-markup-specification": "Specifikation för receptmärkning",
|
||||
"recipe-url": "Recept URL",
|
||||
"recipe-html-or-json": "Recipe HTML or JSON",
|
||||
"recipe-html-or-json": "Recept HTML eller JSON",
|
||||
"upload-a-recipe": "Ladda upp ett recept",
|
||||
"upload-individual-zip-file": "Ladda upp en individuell .zip-fil som exporteras från en annan Mealie-instans.",
|
||||
"url-form-hint": "Kopiera och klistra in en länk från din favorit recept webbplats",
|
||||
@@ -466,7 +466,7 @@
|
||||
"calories-suffix": "kalorier",
|
||||
"carbohydrate-content": "Kolhydrat",
|
||||
"categories": "Kategorier",
|
||||
"cholesterol-content": "Cholesterol",
|
||||
"cholesterol-content": "Kolesterol",
|
||||
"comment-action": "Kommentar",
|
||||
"comment": "Kommentar",
|
||||
"comments": "Kommentarer",
|
||||
@@ -513,7 +513,7 @@
|
||||
"recipe-updated": "Recept uppdaterad",
|
||||
"remove-from-favorites": "Ta bort från favoriter",
|
||||
"remove-section": "Ta bort avdelning",
|
||||
"saturated-fat-content": "Saturated fat",
|
||||
"saturated-fat-content": "Mättat fett",
|
||||
"save-recipe-before-use": "Spara recept innan användning",
|
||||
"section-title": "Avdelningens rubrik",
|
||||
"servings": "Portioner",
|
||||
@@ -524,9 +524,9 @@
|
||||
"sugar-content": "Socker",
|
||||
"title": "Titel",
|
||||
"total-time": "Total tid",
|
||||
"trans-fat-content": "Trans-fat",
|
||||
"trans-fat-content": "Transfett",
|
||||
"unable-to-delete-recipe": "Gick inte att radera receptet",
|
||||
"unsaturated-fat-content": "Unsaturated fat",
|
||||
"unsaturated-fat-content": "Omättat fett",
|
||||
"no-recipe": "Inget recept",
|
||||
"locked-by-owner": "Låst av ägaren",
|
||||
"join-the-conversation": "Delta i diskussionen",
|
||||
@@ -614,16 +614,16 @@
|
||||
"scrape-recipe-description": "Hämta ett recept med webbadress. Ange URL:en för webbplatsen du vill hämta, och Mealie kommer att försöka hämta receptet från den webbplatsen och lägga till det i din samling.",
|
||||
"scrape-recipe-have-a-lot-of-recipes": "Har du många recept som du vill skrapa på en gång?",
|
||||
"scrape-recipe-suggest-bulk-importer": "Testa massimportören",
|
||||
"scrape-recipe-have-raw-html-or-json-data": "Have raw HTML or JSON data?",
|
||||
"scrape-recipe-you-can-import-from-raw-data-directly": "You can import from raw data directly",
|
||||
"scrape-recipe-have-raw-html-or-json-data": "Har rå HTML eller JSON-data?",
|
||||
"scrape-recipe-you-can-import-from-raw-data-directly": "Du kan importera från rådata direkt",
|
||||
"import-original-keywords-as-tags": "Importera ursprungliga sökord som taggar",
|
||||
"stay-in-edit-mode": "Stanna kvar i redigeringsläge",
|
||||
"import-from-zip": "Importera från zip",
|
||||
"import-from-zip-description": "Importera ett enstaka recept som exporterades från en annan Mealie-instans.",
|
||||
"import-from-html-or-json": "Import from HTML or JSON",
|
||||
"import-from-html-or-json-description": "Import a single recipe from raw HTML or JSON. This is useful if you have a recipe from a site that Mealie can't scrape normally, or from some other external source.",
|
||||
"json-import-format-description-colon": "To import via JSON, it must be in valid format:",
|
||||
"json-editor": "JSON Editor",
|
||||
"import-from-html-or-json": "Importera från HTML eller JSON",
|
||||
"import-from-html-or-json-description": "Importera ett recept från rå HTML eller JSON. Detta är användbart om du har ett recept från en webbplats som Mealie inte kan skrapa normalt, eller från någon annan extern källa.",
|
||||
"json-import-format-description-colon": "För att importera via JSON måste det vara i giltigt format:",
|
||||
"json-editor": "JSON-redigerare",
|
||||
"zip-files-must-have-been-exported-from-mealie": ".zip-filer måste ha exporterats från Mealie",
|
||||
"create-a-recipe-by-uploading-a-scan": "Skapa ett recept genom att ladda upp en skanning.",
|
||||
"upload-a-png-image-from-a-recipe-book": "Ladda upp en png bild från en receptbok",
|
||||
@@ -662,7 +662,7 @@
|
||||
"missing-food": "Skapa saknad ingrediens: {food}",
|
||||
"no-food": "Ingen mat"
|
||||
},
|
||||
"reset-servings-count": "Reset Servings Count"
|
||||
"reset-servings-count": "Nollställ antal serveringar"
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Avancerad sökning",
|
||||
@@ -1271,12 +1271,14 @@
|
||||
"debug-openai-services": "Felsök OpenAI-tjänster",
|
||||
"debug-openai-services-description": "Använd denna sida för att felsöka OpenAI-tjänster. Du kan testa din OpenAI-anslutning och se resultaten här. Om du har bildtjänster aktiverade, kan du också ge en bild.",
|
||||
"run-test": "Kör test",
|
||||
"test-results": "Testresultat"
|
||||
"test-results": "Testresultat",
|
||||
"group-delete-note": "Grupper med användare eller hushåll kan inte raderas",
|
||||
"household-delete-note": "Hushåll med användare kan inte tas bort"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Välkommen, {0}!",
|
||||
"description": "Hantera dina profil, recept och gruppinställningar.",
|
||||
"get-invite-link": "Få inbjudningslänk",
|
||||
"get-invite-link": "Skapa inbjudningslänk",
|
||||
"get-public-link": "Få offentlig länk",
|
||||
"account-summary": "Kontosammanfattning",
|
||||
"account-summary-description": "Här är en sammanfattning av din grupps information.",
|
||||
@@ -1324,7 +1326,7 @@
|
||||
},
|
||||
"cookbook": {
|
||||
"cookbooks": "Kokböcker",
|
||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
||||
"description": "Kokböcker är ett annat sätt att organisera recept genom att skapa tvärsnitt av recept, organisatörer och andra filter. När du skapar en kokbok kommer den att lägga till en post i sidofältet och alla recept med de taggar och kategorier som valts kommer att visas i kokboken.",
|
||||
"public-cookbook": "Offentlig kokbok",
|
||||
"public-cookbook-description": "Offentliga kokböcker kan delas med icke-mealie användare och kommer att visas på din gruppsida.",
|
||||
"filter-options": "Filterinställningar",
|
||||
@@ -1340,25 +1342,25 @@
|
||||
},
|
||||
"query-filter": {
|
||||
"logical-operators": {
|
||||
"and": "AND",
|
||||
"or": "OR"
|
||||
"and": "OCH",
|
||||
"or": "ELLER"
|
||||
},
|
||||
"relational-operators": {
|
||||
"equals": "equals",
|
||||
"does-not-equal": "does not equal",
|
||||
"is-greater-than": "is greater than",
|
||||
"is-greater-than-or-equal-to": "is greater than or equal to",
|
||||
"is-less-than": "is less than",
|
||||
"is-less-than-or-equal-to": "is less than or equal to"
|
||||
"equals": "lika med",
|
||||
"does-not-equal": "inte lika med",
|
||||
"is-greater-than": "är större än",
|
||||
"is-greater-than-or-equal-to": "är större än eller lika med",
|
||||
"is-less-than": "är mindre än",
|
||||
"is-less-than-or-equal-to": "är mindre eller lika med"
|
||||
},
|
||||
"relational-keywords": {
|
||||
"is": "is",
|
||||
"is-not": "is not",
|
||||
"is-one-of": "is one of",
|
||||
"is-not-one-of": "is not one of",
|
||||
"contains-all-of": "contains all of",
|
||||
"is-like": "is like",
|
||||
"is-not-like": "is not like"
|
||||
"is": "är",
|
||||
"is-not": "är inte",
|
||||
"is-one-of": "är en av",
|
||||
"is-not-one-of": "är inte en av",
|
||||
"contains-all-of": "innehåller alla",
|
||||
"is-like": "är som",
|
||||
"is-not-like": "är inte som"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Hoşgeldin, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Налагодження служб OpenAI",
|
||||
"debug-openai-services-description": "Використовуйте цю сторінку, щоб налагодити служби OpenAI. Ви можете перевірити ваше з'єднання з OpenAI й побачити результати тут. Якщо ввімкнено служби зображень, ви також можете надати зображення.",
|
||||
"run-test": "Запустити перевірку",
|
||||
"test-results": "Результати перевірки"
|
||||
"test-results": "Результати перевірки",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Ласкаво просимо, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 欢迎,{0}!",
|
||||
|
||||
@@ -1271,7 +1271,9 @@
|
||||
"debug-openai-services": "Debug OpenAI Services",
|
||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||
"run-test": "Run Test",
|
||||
"test-results": "Test Results"
|
||||
"test-results": "Test Results",
|
||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||
"household-delete-note": "Households with users cannot be deleted"
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mealie",
|
||||
"version": "1.12.0",
|
||||
"version": "2.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
|
||||
@@ -47,20 +47,27 @@
|
||||
{{ item.users.length }}
|
||||
</template>
|
||||
<template #item.actions="{ item }">
|
||||
<v-btn
|
||||
:disabled="item && (item.households.length > 0 || item.users.length > 0)"
|
||||
class="mr-1"
|
||||
icon
|
||||
color="error"
|
||||
@click.stop="
|
||||
confirmDialog = true;
|
||||
deleteTarget = item.id;
|
||||
"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.delete }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-tooltip bottom :disabled="!(item && (item.households.length > 0 || item.users.length > 0))">
|
||||
<template #activator="{ on, attrs }">
|
||||
<div v-bind="attrs" v-on="on" >
|
||||
<v-btn
|
||||
:disabled="item && (item.households.length > 0 || item.users.length > 0)"
|
||||
class="mr-1"
|
||||
icon
|
||||
color="error"
|
||||
@click.stop="
|
||||
confirmDialog = true;
|
||||
deleteTarget = item.id;
|
||||
"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.delete }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<span>{{ $tc("admin.group-delete-note") }}</span>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<v-divider></v-divider>
|
||||
|
||||
@@ -63,20 +63,27 @@
|
||||
{{ item.webhooks.length > 0 ? $t("general.yes") : $t("general.no") }}
|
||||
</template>
|
||||
<template #item.actions="{ item }">
|
||||
<v-btn
|
||||
:disabled="item && item.users.length > 0"
|
||||
class="mr-1"
|
||||
icon
|
||||
color="error"
|
||||
@click.stop="
|
||||
confirmDialog = true;
|
||||
deleteTarget = item.id;
|
||||
"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.delete }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-tooltip bottom :disabled="!(item && item.users.length > 0)">
|
||||
<template #activator="{ on, attrs }">
|
||||
<div v-bind="attrs" v-on="on" >
|
||||
<v-btn
|
||||
:disabled="item && item.users.length > 0"
|
||||
class="mr-1"
|
||||
icon
|
||||
color="error"
|
||||
@click.stop="
|
||||
confirmDialog = true;
|
||||
deleteTarget = item.id;
|
||||
"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.delete }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<span>{{ $tc("admin.household-delete-note") }}</span>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<v-divider></v-divider>
|
||||
|
||||
@@ -48,13 +48,33 @@
|
||||
{{ $t('cookbook.description') }}
|
||||
</BasePageTitle>
|
||||
|
||||
<div class="my-6">
|
||||
<v-checkbox
|
||||
v-model="cookbookPreferences.hideOtherHouseholds"
|
||||
:label="$tc('cookbook.hide-cookbooks-from-other-households')"
|
||||
hide-details
|
||||
/>
|
||||
<div class="ml-8">
|
||||
<p class="text-subtitle-2 my-0 py-0">
|
||||
{{ $tc("cookbook.hide-cookbooks-from-other-households-description") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create New -->
|
||||
<BaseButton create @click="createCookbook" />
|
||||
|
||||
<!-- Cookbook List -->
|
||||
<v-expansion-panels class="mt-2">
|
||||
<draggable v-model="cookbooks" handle=".handle" style="width: 100%" @change="actions.updateOrder()">
|
||||
<v-expansion-panel v-for="cookbook in cookbooks" :key="cookbook.id" class="my-2 left-border rounded">
|
||||
<draggable
|
||||
v-model="myCookbooks"
|
||||
handle=".handle"
|
||||
delay="250"
|
||||
:delay-on-touch-only="true"
|
||||
style="width: 100%"
|
||||
@change="actions.updateOrder(myCookbooks)"
|
||||
>
|
||||
<v-expansion-panel v-for="cookbook in myCookbooks" :key="cookbook.id" class="my-2 left-border rounded">
|
||||
<v-expansion-panel-header disable-icon-rotate class="headline">
|
||||
<div class="d-flex align-center">
|
||||
<v-icon large left>
|
||||
@@ -103,11 +123,13 @@
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
import { defineComponent, onBeforeUnmount, onMounted, reactive, ref } from "@nuxtjs/composition-api";
|
||||
import { computed, defineComponent, onBeforeUnmount, onMounted, reactive, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import draggable from "vuedraggable";
|
||||
import { useCookbooks } from "@/composables/use-group-cookbooks";
|
||||
import { useHouseholdSelf } from "@/composables/use-households";
|
||||
import CookbookEditor from "~/components/Domain/Cookbook/CookbookEditor.vue";
|
||||
import { ReadCookBook } from "~/lib/api/types/cookbook";
|
||||
import { useCookbookPreferences } from "~/composables/use-users/preferences";
|
||||
|
||||
export default defineComponent({
|
||||
components: { CookbookEditor, draggable },
|
||||
@@ -117,13 +139,28 @@ export default defineComponent({
|
||||
create: false,
|
||||
delete: false,
|
||||
});
|
||||
const { cookbooks, actions } = useCookbooks();
|
||||
|
||||
const { $auth, i18n } = useContext();
|
||||
const { cookbooks: allCookbooks, actions } = useCookbooks();
|
||||
const myCookbooks = computed<ReadCookBook[]>({
|
||||
get: () => {
|
||||
return allCookbooks.value?.filter((cookbook) => {
|
||||
return cookbook.householdId === $auth.user?.householdId;
|
||||
}) || [];
|
||||
},
|
||||
set: (value: ReadCookBook[]) => {
|
||||
actions.updateOrder(value);
|
||||
},
|
||||
});
|
||||
const { household } = useHouseholdSelf();
|
||||
const cookbookPreferences = useCookbookPreferences()
|
||||
|
||||
// create
|
||||
const createTargetKey = ref(0);
|
||||
const createTarget = ref<ReadCookBook | null>(null);
|
||||
async function createCookbook() {
|
||||
await actions.createOne().then((cookbook) => {
|
||||
const name = i18n.t("cookbook.household-cookbook-name", [household.value?.name || "", String((myCookbooks.value?.length ?? 0) + 1)]) as string
|
||||
await actions.createOne(name).then((cookbook) => {
|
||||
createTarget.value = cookbook as ReadCookBook;
|
||||
createTargetKey.value++;
|
||||
});
|
||||
@@ -170,7 +207,8 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
return {
|
||||
cookbooks,
|
||||
myCookbooks,
|
||||
cookbookPreferences,
|
||||
actions,
|
||||
dialogStates,
|
||||
// create
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
v-if="parsedIng.length > 0"
|
||||
v-model="parsedIng"
|
||||
handle=".handle"
|
||||
delay="250"
|
||||
:delay-on-touch-only="true"
|
||||
:style="{ width: '100%' }"
|
||||
ghost-class="ghost"
|
||||
>
|
||||
|
||||
@@ -323,7 +323,7 @@ export default defineComponent({
|
||||
// we explicitly set booleans to false since forms don't POST unchecked boxes
|
||||
const createTarget = ref<CreateIngredientUnit>({
|
||||
name: "",
|
||||
fraction: false,
|
||||
fraction: true,
|
||||
useAbbreviation: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -100,6 +100,8 @@
|
||||
<draggable
|
||||
tag="div"
|
||||
handle=".handle"
|
||||
delay="250"
|
||||
:delay-on-touch-only="true"
|
||||
:value="plan.meals"
|
||||
group="meals"
|
||||
:data-index="index"
|
||||
|
||||
@@ -56,34 +56,40 @@
|
||||
|
||||
<!-- View By Label -->
|
||||
<div v-else>
|
||||
<div v-for="(value, key) in itemsByLabel" :key="key" class="mb-6">
|
||||
<div class="text-left">
|
||||
<v-btn
|
||||
:color="getLabelColor(value[0]) ? getLabelColor(value[0]) : '#959595'"
|
||||
:style="{
|
||||
<div v-for="(value, key) in itemsByLabel" :key="key" class="pb-4">
|
||||
<v-btn
|
||||
:color="getLabelColor(value[0]) ? getLabelColor(value[0]) : '#959595'"
|
||||
:style="{
|
||||
'color': getTextColor(getLabelColor(value[0])),
|
||||
'letter-spacing': 'normal',
|
||||
}"
|
||||
>
|
||||
{{ key }}
|
||||
@click="toggleShowLabel(key)"
|
||||
>
|
||||
<v-icon>
|
||||
{{ labelOpenState[key] ? $globals.icons.chevronDown : $globals.icons.chevronRight }}
|
||||
</v-icon>
|
||||
{{ key }}
|
||||
</v-btn>
|
||||
<v-divider/>
|
||||
<v-expand-transition group>
|
||||
<div v-show="labelOpenState[key]">
|
||||
<draggable :value="value" handle=".handle" delay="250" :delay-on-touch-only="true" @start="loadingCounter += 1" @end="loadingCounter -= 1" @input="updateIndexUncheckedByLabel(key, $event)">
|
||||
<v-lazy v-for="(item, index) in value" :key="item.id" class="ml-2 my-2">
|
||||
<ShoppingListItem
|
||||
v-model="value[index]"
|
||||
:show-label=false
|
||||
:labels="allLabels || []"
|
||||
:units="allUnits || []"
|
||||
:foods="allFoods || []"
|
||||
:recipes="recipeMap"
|
||||
@checked="saveListItem"
|
||||
@save="saveListItem"
|
||||
@delete="deleteListItem(item)"
|
||||
/>
|
||||
</v-lazy>
|
||||
</draggable>
|
||||
</div>
|
||||
<v-divider/>
|
||||
<draggable :value="value" handle=".handle" delay="250" :delay-on-touch-only="true" @start="loadingCounter += 1" @end="loadingCounter -= 1" @input="updateIndexUncheckedByLabel(key, $event)">
|
||||
<v-lazy v-for="(item, index) in value" :key="item.id" class="ml-2 my-2">
|
||||
<ShoppingListItem
|
||||
v-model="value[index]"
|
||||
:show-label=false
|
||||
:labels="allLabels || []"
|
||||
:units="allUnits || []"
|
||||
:foods="allFoods || []"
|
||||
:recipes="recipeMap"
|
||||
@checked="saveListItem"
|
||||
@save="saveListItem"
|
||||
@delete="deleteListItem(item)"
|
||||
/>
|
||||
</v-lazy>
|
||||
</draggable>
|
||||
</v-expand-transition>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -97,7 +103,15 @@
|
||||
@submit="saveLabelOrder"
|
||||
@close="cancelLabelOrder">
|
||||
<v-card height="fit-content" max-height="70vh" style="overflow-y: auto;">
|
||||
<draggable v-if="localLabels" :value="localLabels" handle=".handle" class="my-2" @input="updateLabelOrder">
|
||||
<draggable
|
||||
v-if="localLabels"
|
||||
:value="localLabels"
|
||||
handle=".handle"
|
||||
delay="250"
|
||||
:delay-on-touch-only="true"
|
||||
class="my-2"
|
||||
@input="updateLabelOrder"
|
||||
>
|
||||
<div v-for="(labelSetting, index) in localLabels" :key="labelSetting.id">
|
||||
<MultiPurposeLabelSection v-model="localLabels[index]" use-color />
|
||||
</div>
|
||||
@@ -293,7 +307,7 @@
|
||||
<script lang="ts">
|
||||
import draggable from "vuedraggable";
|
||||
|
||||
import { defineComponent, useRoute, computed, ref, toRefs, onUnmounted, useContext, reactive } from "@nuxtjs/composition-api";
|
||||
import { defineComponent, useRoute, computed, ref, toRefs, onUnmounted, useContext, reactive, watch } from "@nuxtjs/composition-api";
|
||||
import { useIdle, useToggle } from "@vueuse/core";
|
||||
import { useCopyList } from "~/composables/use-copy";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
@@ -455,6 +469,43 @@ export default defineComponent({
|
||||
};
|
||||
});
|
||||
|
||||
// =====================================
|
||||
// Collapsable Labels
|
||||
const labelOpenState = ref<{ [key: string]: boolean }>({});
|
||||
|
||||
const initializeLabelOpenStates = () => {
|
||||
if (!shoppingList.value?.listItems) return;
|
||||
|
||||
const existingLabels = new Set(Object.keys(labelOpenState.value));
|
||||
let hasChanges = false;
|
||||
|
||||
for (const item of shoppingList.value.listItems) {
|
||||
const labelName = item.label?.name || i18n.tc("shopping-list.no-label");
|
||||
if (!existingLabels.has(labelName) && !(labelName in labelOpenState.value)) {
|
||||
labelOpenState.value[labelName] = true;
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChanges) {
|
||||
labelOpenState.value = { ...labelOpenState.value };
|
||||
}
|
||||
};
|
||||
|
||||
const labelNames = computed(() => {
|
||||
return new Set(
|
||||
shoppingList.value?.listItems
|
||||
?.map(item => item.label?.name || i18n.tc("shopping-list.no-label"))
|
||||
.filter(Boolean) ?? []
|
||||
);
|
||||
});
|
||||
|
||||
watch(labelNames, initializeLabelOpenStates, { immediate: true });
|
||||
|
||||
function toggleShowLabel(key: string) {
|
||||
labelOpenState.value[key] = !labelOpenState.value[key];
|
||||
}
|
||||
|
||||
const [showChecked, toggleShowChecked] = useToggle(false);
|
||||
|
||||
// =====================================
|
||||
@@ -1082,6 +1133,8 @@ export default defineComponent({
|
||||
shoppingList,
|
||||
showChecked,
|
||||
sortByLabels,
|
||||
labelOpenState,
|
||||
toggleShowLabel,
|
||||
toggleShowChecked,
|
||||
uncheckAll,
|
||||
openUncheckAll,
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface SideBarLink {
|
||||
href?: string;
|
||||
title: string;
|
||||
children?: SideBarLink[];
|
||||
childrenStartExpanded?: boolean;
|
||||
restricted: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,20 +64,23 @@ async def lifespan_fn(_: FastAPI) -> AsyncGenerator[None, None]:
|
||||
settings.model_dump_json(
|
||||
indent=4,
|
||||
exclude={
|
||||
"LDAP_QUERY_PASSWORD",
|
||||
"OPENAI_API_KEY",
|
||||
"SECRET",
|
||||
"SESSION_SECRET",
|
||||
"SFTP_PASSWORD",
|
||||
"SFTP_USERNAME",
|
||||
"DB_URL", # replace by DB_URL_PUBLIC for logs
|
||||
"DB_PROVIDER",
|
||||
"SMTP_USER",
|
||||
"SMTP_PASSWORD",
|
||||
"OIDC_CLIENT_SECRET",
|
||||
},
|
||||
)
|
||||
)
|
||||
logger.info("------APP FEATURES------")
|
||||
logger.info("--------==SMTP==--------")
|
||||
logger.info(settings.SMTP_FEATURE)
|
||||
logger.info("--------==LDAP==--------")
|
||||
logger.info(settings.LDAP_FEATURE)
|
||||
logger.info("--------==OIDC==--------")
|
||||
logger.info(settings.OIDC_FEATURE)
|
||||
logger.info("-------==OPENAI==-------")
|
||||
logger.info(settings.OPENAI_FEATURE)
|
||||
logger.info("------------------------")
|
||||
|
||||
yield
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ import os
|
||||
import secrets
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any, NamedTuple
|
||||
from typing import Annotated, Any, NamedTuple
|
||||
|
||||
from dateutil.tz import tzlocal
|
||||
from pydantic import field_validator
|
||||
from pydantic import PlainSerializer, field_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
from mealie.core.settings.themes import Theme
|
||||
@@ -19,6 +19,29 @@ class ScheduleTime(NamedTuple):
|
||||
minute: int
|
||||
|
||||
|
||||
class FeatureDetails(NamedTuple):
|
||||
enabled: bool
|
||||
"""Indicates if the feature is enabled or not"""
|
||||
description: str | None
|
||||
"""Short description describing why the feature is not ready"""
|
||||
|
||||
def __str__(self):
|
||||
s = f"Enabled: {self.enabled}"
|
||||
if not self.enabled and self.description:
|
||||
s += f"\nReason: {self.description}"
|
||||
return s
|
||||
|
||||
|
||||
MaskedNoneString = Annotated[
|
||||
str | None,
|
||||
PlainSerializer(lambda x: None if x is None else "*****", return_type=str | None),
|
||||
]
|
||||
"""
|
||||
Custom serializer for sensitive settings. If the setting is None, then will serialize as null, otherwise,
|
||||
the secret will be serialized as '*****'
|
||||
"""
|
||||
|
||||
|
||||
def determine_secrets(data_dir: Path, secret: str, production: bool) -> str:
|
||||
if not production:
|
||||
return "shh-secret-test-key"
|
||||
@@ -200,12 +223,16 @@ class AppSettings(AppLoggingSettings):
|
||||
SMTP_PORT: str | None = "587"
|
||||
SMTP_FROM_NAME: str | None = "Mealie"
|
||||
SMTP_FROM_EMAIL: str | None = None
|
||||
SMTP_USER: str | None = None
|
||||
SMTP_PASSWORD: str | None = None
|
||||
SMTP_USER: MaskedNoneString = None
|
||||
SMTP_PASSWORD: MaskedNoneString = None
|
||||
SMTP_AUTH_STRATEGY: str | None = "TLS" # Options: 'TLS', 'SSL', 'NONE'
|
||||
|
||||
@property
|
||||
def SMTP_ENABLE(self) -> bool:
|
||||
return self.SMTP_FEATURE.enabled
|
||||
|
||||
@property
|
||||
def SMTP_FEATURE(self) -> FeatureDetails:
|
||||
return AppSettings.validate_smtp(
|
||||
self.SMTP_HOST,
|
||||
self.SMTP_PORT,
|
||||
@@ -225,15 +252,30 @@ class AppSettings(AppLoggingSettings):
|
||||
strategy: str | None = None,
|
||||
user: str | None = None,
|
||||
password: str | None = None,
|
||||
) -> bool:
|
||||
) -> FeatureDetails:
|
||||
"""Validates all SMTP variables are set"""
|
||||
required = {host, port, from_name, from_email, strategy}
|
||||
description = None
|
||||
required = {
|
||||
"SMTP_HOST": host,
|
||||
"SMTP_PORT": port,
|
||||
"SMTP_FROM_NAME": from_name,
|
||||
"SMTP_FROM_EMAIL": from_email,
|
||||
"SMTP_AUTH_STRATEGY": strategy,
|
||||
}
|
||||
missing_values = [key for (key, value) in required.items() if value is None]
|
||||
if missing_values:
|
||||
description = f"Missing required values for {missing_values}"
|
||||
|
||||
if strategy and strategy.upper() in {"TLS", "SSL"}:
|
||||
required.add(user)
|
||||
required.add(password)
|
||||
required["SMTP_USER"] = user
|
||||
required["SMTP_PASSWORD"] = password
|
||||
if not description:
|
||||
missing_values = [key for (key, value) in required.items() if value is None]
|
||||
description = f"Missing required values for {missing_values} because SMTP_AUTH_STRATEGY is not None"
|
||||
|
||||
return "" not in required and None not in required
|
||||
not_none = "" not in required.values() and None not in required.values()
|
||||
|
||||
return FeatureDetails(enabled=not_none, description=description)
|
||||
|
||||
# ===============================================
|
||||
# LDAP Configuration
|
||||
@@ -245,31 +287,43 @@ class AppSettings(AppLoggingSettings):
|
||||
LDAP_ENABLE_STARTTLS: bool = False
|
||||
LDAP_BASE_DN: str | None = None
|
||||
LDAP_QUERY_BIND: str | None = None
|
||||
LDAP_QUERY_PASSWORD: str | None = None
|
||||
LDAP_QUERY_PASSWORD: MaskedNoneString = None
|
||||
LDAP_USER_FILTER: str | None = None
|
||||
LDAP_ADMIN_FILTER: str | None = None
|
||||
LDAP_ID_ATTRIBUTE: str = "uid"
|
||||
LDAP_MAIL_ATTRIBUTE: str = "mail"
|
||||
LDAP_NAME_ATTRIBUTE: str = "name"
|
||||
|
||||
@property
|
||||
def LDAP_FEATURE(self) -> FeatureDetails:
|
||||
description = None if self.LDAP_AUTH_ENABLED else "LDAP_AUTH_ENABLED is false"
|
||||
required = {
|
||||
"LDAP_SERVER_URL": self.LDAP_SERVER_URL,
|
||||
"LDAP_BASE_DN": self.LDAP_BASE_DN,
|
||||
"LDAP_ID_ATTRIBUTE": self.LDAP_ID_ATTRIBUTE,
|
||||
"LDAP_MAIL_ATTRIBUTE": self.LDAP_MAIL_ATTRIBUTE,
|
||||
"LDAP_NAME_ATTRIBUTE": self.LDAP_NAME_ATTRIBUTE,
|
||||
}
|
||||
not_none = None not in required.values()
|
||||
if not not_none and not description:
|
||||
missing_values = [key for (key, value) in required.items() if value is None]
|
||||
description = f"Missing required values for {missing_values}"
|
||||
|
||||
return FeatureDetails(
|
||||
enabled=self.LDAP_AUTH_ENABLED and not_none,
|
||||
description=description,
|
||||
)
|
||||
|
||||
@property
|
||||
def LDAP_ENABLED(self) -> bool:
|
||||
"""Validates LDAP settings are all set"""
|
||||
required = {
|
||||
self.LDAP_SERVER_URL,
|
||||
self.LDAP_BASE_DN,
|
||||
self.LDAP_ID_ATTRIBUTE,
|
||||
self.LDAP_MAIL_ATTRIBUTE,
|
||||
self.LDAP_NAME_ATTRIBUTE,
|
||||
}
|
||||
not_none = None not in required
|
||||
return self.LDAP_AUTH_ENABLED and not_none
|
||||
return self.LDAP_FEATURE.enabled
|
||||
|
||||
# ===============================================
|
||||
# OIDC Configuration
|
||||
OIDC_AUTH_ENABLED: bool = False
|
||||
OIDC_CLIENT_ID: str | None = None
|
||||
OIDC_CLIENT_SECRET: str | None = None
|
||||
OIDC_CLIENT_SECRET: MaskedNoneString = None
|
||||
OIDC_CONFIGURATION_URL: str | None = None
|
||||
OIDC_SIGNUP_ENABLED: bool = True
|
||||
OIDC_USER_GROUP: str | None = None
|
||||
@@ -279,6 +333,7 @@ class AppSettings(AppLoggingSettings):
|
||||
OIDC_REMEMBER_ME: bool = False
|
||||
OIDC_USER_CLAIM: str = "email"
|
||||
OIDC_GROUPS_CLAIM: str | None = "groups"
|
||||
OIDC_SCOPES_OVERRIDE: str | None = None
|
||||
OIDC_TLS_CACERTFILE: str | None = None
|
||||
|
||||
@property
|
||||
@@ -286,29 +341,41 @@ class AppSettings(AppLoggingSettings):
|
||||
return self.OIDC_USER_GROUP is not None or self.OIDC_ADMIN_GROUP is not None
|
||||
|
||||
@property
|
||||
def OIDC_READY(self) -> bool:
|
||||
"""Validates OIDC settings are all set"""
|
||||
|
||||
def OIDC_FEATURE(self) -> FeatureDetails:
|
||||
description = None if self.OIDC_AUTH_ENABLED else "OIDC_AUTH_ENABLED is false"
|
||||
required = {
|
||||
self.OIDC_CLIENT_ID,
|
||||
self.OIDC_CLIENT_SECRET,
|
||||
self.OIDC_CONFIGURATION_URL,
|
||||
self.OIDC_USER_CLAIM,
|
||||
"OIDC_CLIENT_ID": self.OIDC_CLIENT_ID,
|
||||
"OIDC_CLIENT_SECRET": self.OIDC_CLIENT_SECRET,
|
||||
"OIDC_CONFIGURATION_URL": self.OIDC_CONFIGURATION_URL,
|
||||
"OIDC_USER_CLAIM": self.OIDC_USER_CLAIM,
|
||||
}
|
||||
not_none = None not in required
|
||||
valid_group_claim = True
|
||||
not_none = None not in required.values()
|
||||
if not not_none and not description:
|
||||
missing_values = [key for (key, value) in required.items() if value is None]
|
||||
description = f"Missing required values for {missing_values}"
|
||||
|
||||
valid_group_claim = True
|
||||
if self.OIDC_REQUIRES_GROUP_CLAIM and self.OIDC_GROUPS_CLAIM is None:
|
||||
if not description:
|
||||
description = "OIDC_GROUPS_CLAIM is required when OIDC_USER_GROUP or OIDC_ADMIN_GROUP are provided"
|
||||
valid_group_claim = False
|
||||
|
||||
return self.OIDC_AUTH_ENABLED and not_none and valid_group_claim
|
||||
return FeatureDetails(
|
||||
enabled=self.OIDC_AUTH_ENABLED and not_none and valid_group_claim,
|
||||
description=description,
|
||||
)
|
||||
|
||||
@property
|
||||
def OIDC_READY(self) -> bool:
|
||||
"""Validates OIDC settings are all set"""
|
||||
return self.OIDC_FEATURE.enabled
|
||||
|
||||
# ===============================================
|
||||
# OpenAI Configuration
|
||||
|
||||
OPENAI_BASE_URL: str | None = None
|
||||
"""The base URL for the OpenAI API. Leave this unset for most usecases"""
|
||||
OPENAI_API_KEY: str | None = None
|
||||
OPENAI_API_KEY: MaskedNoneString = None
|
||||
"""Your OpenAI API key. Required to enable OpenAI features"""
|
||||
OPENAI_MODEL: str = "gpt-4o"
|
||||
"""Which OpenAI model to send requests to. Leave this unset for most usecases"""
|
||||
@@ -333,6 +400,24 @@ class AppSettings(AppLoggingSettings):
|
||||
The number of seconds to wait for an OpenAI request to complete before cancelling the request
|
||||
"""
|
||||
|
||||
@property
|
||||
def OPENAI_FEATURE(self) -> FeatureDetails:
|
||||
description = None
|
||||
if not self.OPENAI_API_KEY:
|
||||
description = "OPENAI_API_KEY is not set"
|
||||
elif self.OPENAI_MODEL:
|
||||
description = "OPENAI_MODEL is not set"
|
||||
|
||||
return FeatureDetails(
|
||||
enabled=bool(self.OPENAI_API_KEY and self.OPENAI_MODEL),
|
||||
description=description,
|
||||
)
|
||||
|
||||
@property
|
||||
def OPENAI_ENABLED(self) -> bool:
|
||||
"""Validates OpenAI settings are all set"""
|
||||
return self.OPENAI_FEATURE.enabled
|
||||
|
||||
# ===============================================
|
||||
# Web Concurrency
|
||||
|
||||
@@ -346,13 +431,17 @@ class AppSettings(AppLoggingSettings):
|
||||
def WORKERS(self) -> int:
|
||||
return max(1, self.WORKER_PER_CORE * self.UVICORN_WORKERS)
|
||||
|
||||
@property
|
||||
def OPENAI_ENABLED(self) -> bool:
|
||||
"""Validates OpenAI settings are all set"""
|
||||
return bool(self.OPENAI_API_KEY and self.OPENAI_MODEL)
|
||||
|
||||
model_config = SettingsConfigDict(arbitrary_types_allowed=True, extra="allow")
|
||||
|
||||
# ===============================================
|
||||
# TLS
|
||||
|
||||
TLS_CERTIFICATE_PATH: str | os.PathLike[str] | None = None
|
||||
"""Path where the certificate resides."""
|
||||
|
||||
TLS_PRIVATE_KEY_PATH: str | os.PathLike[str] | None = None
|
||||
"""Path where the private key resides."""
|
||||
|
||||
|
||||
def app_settings_constructor(data_dir: Path, production: bool, env_file: Path, env_encoding="utf-8") -> AppSettings:
|
||||
"""
|
||||
|
||||
@@ -23,8 +23,9 @@ class RecipeInstruction(SqlAlchemyBase):
|
||||
recipe_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("recipes.id"), index=True)
|
||||
position: Mapped[int | None] = mapped_column(Integer, index=True)
|
||||
type: Mapped[str | None] = mapped_column(String, default="")
|
||||
title: Mapped[str | None] = mapped_column(String)
|
||||
title: Mapped[str | None] = mapped_column(String) # This is the section title!!!
|
||||
text: Mapped[str | None] = mapped_column(String, index=True)
|
||||
summary: Mapped[str | None] = mapped_column(String)
|
||||
|
||||
ingredient_references: Mapped[list[RecipeIngredientRefLink]] = orm.relationship(
|
||||
RecipeIngredientRefLink, cascade="all, delete-orphan"
|
||||
|
||||
@@ -151,6 +151,14 @@ class User(SqlAlchemyBase, BaseMixins):
|
||||
else:
|
||||
self.household = None
|
||||
|
||||
if self.group is None:
|
||||
raise ValueError(f"Group {group} does not exist; cannot create user")
|
||||
if self.household is None:
|
||||
raise ValueError(
|
||||
f'Household "{household}" does not exist on group '
|
||||
f'"{self.group.name}" ({self.group.id}); cannot create user'
|
||||
)
|
||||
|
||||
self.rated_recipes = []
|
||||
|
||||
self.password = password
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"no-recipes-match-your-rules": "규칙과 일치하는 Recipe가 없습니다"
|
||||
},
|
||||
"user": {
|
||||
"user-updated": "User updated",
|
||||
"user-updated": "사용자 업데이트됨",
|
||||
"password-updated": "비밀번호가 변경되었습니다",
|
||||
"invalid-current-password": "현재 비밀번호가 잘못되었습니다",
|
||||
"ldap-update-password-unavailable": "사용자가 LDAP으로 제어되기 때문에 비밀번호를 변경할 수 없습니다"
|
||||
@@ -22,11 +22,11 @@
|
||||
"report-deleted": "Report가 삭제됐습니다"
|
||||
},
|
||||
"exceptions": {
|
||||
"permission_denied": "You do not have permission to perform this action",
|
||||
"no-entry-found": "The requested resource was not found",
|
||||
"permission_denied": "이 작업을 수행할 권한이 없습니다.",
|
||||
"no-entry-found": "요청한 페이지를 찾을 수 없습니다. 주소를 정확히 입력했는지 확인해보세요.",
|
||||
"integrity-error": "Database integrity error",
|
||||
"username-conflict-error": "This username is already taken",
|
||||
"email-conflict-error": "This email is already in use"
|
||||
"username-conflict-error": "이미 사용 중인 사용자 이름입니다.",
|
||||
"email-conflict-error": "이 이메일은 이미 사용중입니다"
|
||||
},
|
||||
"notifications": {
|
||||
"generic-created": "{name} was created",
|
||||
@@ -48,23 +48,23 @@
|
||||
"emails": {
|
||||
"password": {
|
||||
"subject": "Mealie Forgot Password",
|
||||
"header_text": "Forgot Password",
|
||||
"message_top": "You have requested to reset your password.",
|
||||
"message_bottom": "Please click the button above to reset your password.",
|
||||
"button_text": "Reset Password"
|
||||
"header_text": "비밀번호를 분실",
|
||||
"message_top": "비밀번호 재설정을 요청하셨습니다",
|
||||
"message_bottom": "비밀번호를 변경하려면 아래 버튼을 클릭하십시오.",
|
||||
"button_text": "비밀번호 재설정"
|
||||
},
|
||||
"invitation": {
|
||||
"subject": "Invitation to join Mealie",
|
||||
"header_text": "You're Invited!",
|
||||
"header_text": "귀하는 초대되었습니다!",
|
||||
"message_top": "You have been invited to join Mealie.",
|
||||
"message_bottom": "Please click the button above to accept the invitation.",
|
||||
"button_text": "Accept Invitation"
|
||||
"button_text": "초대 수락"
|
||||
},
|
||||
"test": {
|
||||
"subject": "Mealie Test Email",
|
||||
"header_text": "Test Email",
|
||||
"message_top": "This is a test email.",
|
||||
"message_bottom": "Please click the button above to test the email.",
|
||||
"header_text": "테스트 이메일",
|
||||
"message_top": "이것은 테스트용 이메일입니다.",
|
||||
"message_bottom": "비밀번호를 변경하려면 아래 버튼을 클릭하십시오.",
|
||||
"button_text": "Open Mealie"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ def main():
|
||||
log_config=log_config(),
|
||||
workers=settings.WORKERS,
|
||||
forwarded_allow_ips=settings.HOST_IP,
|
||||
ssl_keyfile=settings.TLS_PRIVATE_KEY_PATH,
|
||||
ssl_certfile=settings.TLS_CERTIFICATE_PATH,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
"name": "brown sugar"
|
||||
},
|
||||
"brussels-sprouts": {
|
||||
"name": "brussels sprouts"
|
||||
"name": "brussel sprouts"
|
||||
},
|
||||
"butter": {
|
||||
"name": "butter"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"apples": {
|
||||
"name": "manzanas",
|
||||
"plural_name": "apples"
|
||||
"plural_name": "manzanas"
|
||||
},
|
||||
"artichoke": {
|
||||
"name": "alcachofa"
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"avocado": {
|
||||
"name": "aguacate",
|
||||
"plural_name": "avocado"
|
||||
"plural_name": "palta"
|
||||
},
|
||||
"bacon": {
|
||||
"name": "panceta"
|
||||
@@ -48,7 +48,7 @@
|
||||
},
|
||||
"bell-peppers": {
|
||||
"name": "pimiento morrón",
|
||||
"plural_name": "bell peppers"
|
||||
"plural_name": "pimiento morrón"
|
||||
},
|
||||
"blackberries": {
|
||||
"name": "moras"
|
||||
@@ -94,7 +94,7 @@
|
||||
},
|
||||
"cabbage": {
|
||||
"name": "repollo",
|
||||
"plural_name": "cabbages"
|
||||
"plural_name": "repollos"
|
||||
},
|
||||
"cactus-edible": {
|
||||
"name": "higo chumbo"
|
||||
@@ -116,7 +116,7 @@
|
||||
},
|
||||
"carrot": {
|
||||
"name": "zanahoria",
|
||||
"plural_name": "carrots"
|
||||
"plural_name": "zanahorias"
|
||||
},
|
||||
"caster-sugar": {
|
||||
"name": "azúcar de caña"
|
||||
@@ -129,7 +129,7 @@
|
||||
},
|
||||
"cauliflower": {
|
||||
"name": "coliflor",
|
||||
"plural_name": "cauliflowers"
|
||||
"plural_name": "coliflor"
|
||||
},
|
||||
"cayenne-pepper": {
|
||||
"name": "pimienta de cayena"
|
||||
@@ -154,7 +154,7 @@
|
||||
},
|
||||
"chilli-peppers": {
|
||||
"name": "chile/guindilla",
|
||||
"plural_name": "chilli peppers"
|
||||
"plural_name": "chile/guindilla"
|
||||
},
|
||||
"chinese-leaves": {
|
||||
"name": "col china"
|
||||
@@ -176,7 +176,7 @@
|
||||
},
|
||||
"coconut": {
|
||||
"name": "coco",
|
||||
"plural_name": "coconuts"
|
||||
"plural_name": "cocos"
|
||||
},
|
||||
"coconut-milk": {
|
||||
"name": "leche de coco"
|
||||
@@ -198,7 +198,7 @@
|
||||
},
|
||||
"corn": {
|
||||
"name": "maíz",
|
||||
"plural_name": "corns"
|
||||
"plural_name": "maíz"
|
||||
},
|
||||
"corn-syrup": {
|
||||
"name": "jarabe de maíz"
|
||||
@@ -214,7 +214,7 @@
|
||||
},
|
||||
"cucumber": {
|
||||
"name": "pepino",
|
||||
"plural_name": "cucumbers"
|
||||
"plural_name": "pepino"
|
||||
},
|
||||
"cumin": {
|
||||
"name": "comino"
|
||||
@@ -240,11 +240,11 @@
|
||||
},
|
||||
"eggplant": {
|
||||
"name": "berenjena",
|
||||
"plural_name": "eggplants"
|
||||
"plural_name": "berenjenas"
|
||||
},
|
||||
"eggs": {
|
||||
"name": "huevos",
|
||||
"plural_name": "eggs"
|
||||
"plural_name": "huevos"
|
||||
},
|
||||
"endive": {
|
||||
"name": "endibia",
|
||||
@@ -292,7 +292,7 @@
|
||||
},
|
||||
"garlic": {
|
||||
"name": "ajo",
|
||||
"plural_name": "garlics"
|
||||
"plural_name": "ajos"
|
||||
},
|
||||
"gem-squash": {
|
||||
"name": "calabaza gem"
|
||||
@@ -317,7 +317,7 @@
|
||||
},
|
||||
"green-onion": {
|
||||
"name": "cebolleta",
|
||||
"plural_name": "green onions"
|
||||
"plural_name": "cebolletas"
|
||||
},
|
||||
"heart-of-palm": {
|
||||
"name": "palmito",
|
||||
@@ -368,7 +368,7 @@
|
||||
},
|
||||
"leek": {
|
||||
"name": "puerro",
|
||||
"plural_name": "leeks"
|
||||
"plural_name": "puerros"
|
||||
},
|
||||
"legumes": {
|
||||
"name": "legumbres"
|
||||
@@ -384,7 +384,7 @@
|
||||
},
|
||||
"liver": {
|
||||
"name": "hígado",
|
||||
"plural_name": "livers"
|
||||
"plural_name": "hígados"
|
||||
},
|
||||
"maize": {
|
||||
"name": "maíz"
|
||||
@@ -403,7 +403,7 @@
|
||||
},
|
||||
"mushroom": {
|
||||
"name": "champiñón",
|
||||
"plural_name": "mushrooms"
|
||||
"plural_name": "setas"
|
||||
},
|
||||
"mussels": {
|
||||
"name": "mejillones"
|
||||
@@ -425,7 +425,7 @@
|
||||
},
|
||||
"octopuses": {
|
||||
"name": "pulpo",
|
||||
"plural_name": "octopuses"
|
||||
"plural_name": "pulpos"
|
||||
},
|
||||
"oils": {
|
||||
"name": "aceites"
|
||||
@@ -450,7 +450,7 @@
|
||||
},
|
||||
"oranges": {
|
||||
"name": "naranjas",
|
||||
"plural_name": "oranges"
|
||||
"plural_name": "naranjas"
|
||||
},
|
||||
"oregano": {
|
||||
"name": "orégano"
|
||||
@@ -473,29 +473,29 @@
|
||||
},
|
||||
"pear": {
|
||||
"name": "pera",
|
||||
"plural_name": "pears"
|
||||
"plural_name": "peras"
|
||||
},
|
||||
"peas": {
|
||||
"name": "guisantes/chícharos"
|
||||
},
|
||||
"pepper": {
|
||||
"name": "pimiento",
|
||||
"plural_name": "peppers"
|
||||
"plural_name": "pimientos"
|
||||
},
|
||||
"pineapple": {
|
||||
"name": "piña",
|
||||
"plural_name": "pineapples"
|
||||
"plural_name": "piñas"
|
||||
},
|
||||
"plantain": {
|
||||
"name": "plátano macho",
|
||||
"plural_name": "plantains"
|
||||
"plural_name": "plátanos"
|
||||
},
|
||||
"poppy-seeds": {
|
||||
"name": "semillas de amapola"
|
||||
},
|
||||
"potato": {
|
||||
"name": "papa/patata",
|
||||
"plural_name": "potatoes"
|
||||
"plural_name": "patatas"
|
||||
},
|
||||
"poultry": {
|
||||
"name": "aves de corral"
|
||||
@@ -505,14 +505,14 @@
|
||||
},
|
||||
"pumpkin": {
|
||||
"name": "calabaza",
|
||||
"plural_name": "pumpkins"
|
||||
"plural_name": "calabazas"
|
||||
},
|
||||
"pumpkin-seeds": {
|
||||
"name": "semillas de calabaza"
|
||||
},
|
||||
"radish": {
|
||||
"name": "rábano",
|
||||
"plural_name": "radishes"
|
||||
"plural_name": "rábanos"
|
||||
},
|
||||
"raw-sugar": {
|
||||
"name": "azúcar integral"
|
||||
@@ -543,7 +543,7 @@
|
||||
},
|
||||
"scallion": {
|
||||
"name": "cebollita china",
|
||||
"plural_name": "scallions"
|
||||
"plural_name": "cebolletas"
|
||||
},
|
||||
"seafood": {
|
||||
"name": "marisco"
|
||||
@@ -556,7 +556,7 @@
|
||||
},
|
||||
"shallot": {
|
||||
"name": "chalote",
|
||||
"plural_name": "shallots"
|
||||
"plural_name": "chalotes"
|
||||
},
|
||||
"skate": {
|
||||
"name": "raya"
|
||||
@@ -589,7 +589,7 @@
|
||||
},
|
||||
"squash": {
|
||||
"name": "calabaza dulce",
|
||||
"plural_name": "squashes"
|
||||
"plural_name": "calabazas"
|
||||
},
|
||||
"squash-family": {
|
||||
"name": "calabazas"
|
||||
@@ -612,11 +612,11 @@
|
||||
},
|
||||
"sweet-potato": {
|
||||
"name": "batata",
|
||||
"plural_name": "sweet potatoes"
|
||||
"plural_name": "batatas"
|
||||
},
|
||||
"sweetcorn": {
|
||||
"name": "maíz dulce",
|
||||
"plural_name": "sweetcorns"
|
||||
"plural_name": "maíz dulce"
|
||||
},
|
||||
"sweeteners": {
|
||||
"name": "edulcorantes"
|
||||
@@ -633,7 +633,7 @@
|
||||
},
|
||||
"tomato": {
|
||||
"name": "tomate",
|
||||
"plural_name": "tomatoes"
|
||||
"plural_name": "tomates"
|
||||
},
|
||||
"trout": {
|
||||
"name": "trucha"
|
||||
@@ -666,7 +666,7 @@
|
||||
},
|
||||
"watermelon": {
|
||||
"name": "sandía/melón de agua",
|
||||
"plural_name": "watermelons"
|
||||
"plural_name": "sandías"
|
||||
},
|
||||
"white-mushroom": {
|
||||
"name": "champiñón blanco",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"apples": {
|
||||
"name": "jabolka",
|
||||
"plural_name": "apples"
|
||||
"plural_name": "jabolka"
|
||||
},
|
||||
"artichoke": {
|
||||
"name": "artičoke"
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"avocado": {
|
||||
"name": "avokado",
|
||||
"plural_name": "avocado"
|
||||
"plural_name": "avokado"
|
||||
},
|
||||
"bacon": {
|
||||
"name": "slanina"
|
||||
@@ -48,13 +48,13 @@
|
||||
},
|
||||
"bell-peppers": {
|
||||
"name": "podolgovate paprike",
|
||||
"plural_name": "bell peppers"
|
||||
"plural_name": "podolgovate paprike"
|
||||
},
|
||||
"blackberries": {
|
||||
"name": "robide"
|
||||
},
|
||||
"bok-choy": {
|
||||
"name": "bok choy"
|
||||
"name": "kitajsko zelje"
|
||||
},
|
||||
"brassicas": {
|
||||
"name": "repa"
|
||||
@@ -94,7 +94,7 @@
|
||||
},
|
||||
"cabbage": {
|
||||
"name": "zelje",
|
||||
"plural_name": "cabbages"
|
||||
"plural_name": "zelje"
|
||||
},
|
||||
"cactus-edible": {
|
||||
"name": "jedilni kaktus"
|
||||
@@ -116,7 +116,7 @@
|
||||
},
|
||||
"carrot": {
|
||||
"name": "korenje",
|
||||
"plural_name": "carrots"
|
||||
"plural_name": "korenje"
|
||||
},
|
||||
"caster-sugar": {
|
||||
"name": "trsni sladkor"
|
||||
@@ -129,7 +129,7 @@
|
||||
},
|
||||
"cauliflower": {
|
||||
"name": "cvetača",
|
||||
"plural_name": "cauliflowers"
|
||||
"plural_name": "cvetača"
|
||||
},
|
||||
"cayenne-pepper": {
|
||||
"name": "kajenski poper"
|
||||
@@ -154,13 +154,13 @@
|
||||
},
|
||||
"chilli-peppers": {
|
||||
"name": "čili paprika",
|
||||
"plural_name": "chilli peppers"
|
||||
"plural_name": "čili paprika"
|
||||
},
|
||||
"chinese-leaves": {
|
||||
"name": "kitajski listi"
|
||||
},
|
||||
"chives": {
|
||||
"name": "chives"
|
||||
"name": "drobnjak"
|
||||
},
|
||||
"chocolate": {
|
||||
"name": "čokolada"
|
||||
@@ -176,7 +176,7 @@
|
||||
},
|
||||
"coconut": {
|
||||
"name": "kokos",
|
||||
"plural_name": "coconuts"
|
||||
"plural_name": "kokos"
|
||||
},
|
||||
"coconut-milk": {
|
||||
"name": "kokosovo mleko"
|
||||
@@ -198,7 +198,7 @@
|
||||
},
|
||||
"corn": {
|
||||
"name": "koruza",
|
||||
"plural_name": "corns"
|
||||
"plural_name": "koruza"
|
||||
},
|
||||
"corn-syrup": {
|
||||
"name": "koruzni sirup"
|
||||
@@ -214,14 +214,14 @@
|
||||
},
|
||||
"cucumber": {
|
||||
"name": "kumare",
|
||||
"plural_name": "cucumbers"
|
||||
"plural_name": "kumare"
|
||||
},
|
||||
"cumin": {
|
||||
"name": "kumina"
|
||||
},
|
||||
"daikon": {
|
||||
"name": "daikon redkev",
|
||||
"plural_name": "daikons"
|
||||
"plural_name": "daikon redkev"
|
||||
},
|
||||
"dairy-products-and-dairy-substitutes": {
|
||||
"name": "mlečni izdelki in mlečni nadomestki"
|
||||
@@ -240,15 +240,15 @@
|
||||
},
|
||||
"eggplant": {
|
||||
"name": "jajčevec",
|
||||
"plural_name": "eggplants"
|
||||
"plural_name": "jajčevec"
|
||||
},
|
||||
"eggs": {
|
||||
"name": "jajca",
|
||||
"plural_name": "eggs"
|
||||
"plural_name": "jajca"
|
||||
},
|
||||
"endive": {
|
||||
"name": "endivija",
|
||||
"plural_name": "endives"
|
||||
"plural_name": "endivija"
|
||||
},
|
||||
"fats": {
|
||||
"name": "maščobe"
|
||||
@@ -261,7 +261,7 @@
|
||||
},
|
||||
"fiddlehead-fern": {
|
||||
"name": "gosličasta praprot",
|
||||
"plural_name": "fiddlehead ferns"
|
||||
"plural_name": "gosličasta praprot"
|
||||
},
|
||||
"fish": {
|
||||
"name": "ribe"
|
||||
@@ -292,10 +292,10 @@
|
||||
},
|
||||
"garlic": {
|
||||
"name": "česen",
|
||||
"plural_name": "garlics"
|
||||
"plural_name": "česen"
|
||||
},
|
||||
"gem-squash": {
|
||||
"name": "gem squash"
|
||||
"name": "poletna buča"
|
||||
},
|
||||
"ghee": {
|
||||
"name": "ghee"
|
||||
@@ -317,11 +317,11 @@
|
||||
},
|
||||
"green-onion": {
|
||||
"name": "zelena čebula",
|
||||
"plural_name": "green onions"
|
||||
"plural_name": "zelena čebula"
|
||||
},
|
||||
"heart-of-palm": {
|
||||
"name": "srce palme",
|
||||
"plural_name": "heart of palms"
|
||||
"plural_name": "srce palme"
|
||||
},
|
||||
"hemp": {
|
||||
"name": "konoplja"
|
||||
@@ -337,7 +337,7 @@
|
||||
},
|
||||
"jackfruit": {
|
||||
"name": "nangka",
|
||||
"plural_name": "jackfruits"
|
||||
"plural_name": "nangka"
|
||||
},
|
||||
"jaggery": {
|
||||
"name": "trsni sladkor"
|
||||
@@ -368,7 +368,7 @@
|
||||
},
|
||||
"leek": {
|
||||
"name": "por",
|
||||
"plural_name": "leeks"
|
||||
"plural_name": "por"
|
||||
},
|
||||
"legumes": {
|
||||
"name": "stročnice"
|
||||
@@ -384,7 +384,7 @@
|
||||
},
|
||||
"liver": {
|
||||
"name": "jetra",
|
||||
"plural_name": "livers"
|
||||
"plural_name": "jetra"
|
||||
},
|
||||
"maize": {
|
||||
"name": "koruza"
|
||||
@@ -403,7 +403,7 @@
|
||||
},
|
||||
"mushroom": {
|
||||
"name": "gobe",
|
||||
"plural_name": "mushrooms"
|
||||
"plural_name": "gobe"
|
||||
},
|
||||
"mussels": {
|
||||
"name": "školjke"
|
||||
@@ -425,7 +425,7 @@
|
||||
},
|
||||
"octopuses": {
|
||||
"name": "hobotnice",
|
||||
"plural_name": "octopuses"
|
||||
"plural_name": "hobotnice"
|
||||
},
|
||||
"oils": {
|
||||
"name": "olja"
|
||||
@@ -450,7 +450,7 @@
|
||||
},
|
||||
"oranges": {
|
||||
"name": "pomaranče",
|
||||
"plural_name": "oranges"
|
||||
"plural_name": "pomaranče"
|
||||
},
|
||||
"oregano": {
|
||||
"name": "origano"
|
||||
@@ -469,33 +469,33 @@
|
||||
},
|
||||
"parsnip": {
|
||||
"name": "pastinak",
|
||||
"plural_name": "parsnips"
|
||||
"plural_name": "pastinak"
|
||||
},
|
||||
"pear": {
|
||||
"name": "hruška",
|
||||
"plural_name": "pears"
|
||||
"plural_name": "hruške"
|
||||
},
|
||||
"peas": {
|
||||
"name": "grah"
|
||||
},
|
||||
"pepper": {
|
||||
"name": "poper",
|
||||
"plural_name": "peppers"
|
||||
"plural_name": "popri"
|
||||
},
|
||||
"pineapple": {
|
||||
"name": "ananas",
|
||||
"plural_name": "pineapples"
|
||||
"plural_name": "ananas"
|
||||
},
|
||||
"plantain": {
|
||||
"name": "trpotec",
|
||||
"plural_name": "plantains"
|
||||
"plural_name": "trpotec"
|
||||
},
|
||||
"poppy-seeds": {
|
||||
"name": "makova semena"
|
||||
},
|
||||
"potato": {
|
||||
"name": "krompir",
|
||||
"plural_name": "potatoes"
|
||||
"plural_name": "krompir"
|
||||
},
|
||||
"poultry": {
|
||||
"name": "perutnina"
|
||||
@@ -505,14 +505,14 @@
|
||||
},
|
||||
"pumpkin": {
|
||||
"name": "buča",
|
||||
"plural_name": "pumpkins"
|
||||
"plural_name": "buča"
|
||||
},
|
||||
"pumpkin-seeds": {
|
||||
"name": "bučna semena"
|
||||
},
|
||||
"radish": {
|
||||
"name": "redkev",
|
||||
"plural_name": "radishes"
|
||||
"plural_name": "redkev"
|
||||
},
|
||||
"raw-sugar": {
|
||||
"name": "surovi sladkor"
|
||||
@@ -543,7 +543,7 @@
|
||||
},
|
||||
"scallion": {
|
||||
"name": "česen",
|
||||
"plural_name": "scallions"
|
||||
"plural_name": "mlada čebula"
|
||||
},
|
||||
"seafood": {
|
||||
"name": "morska hrana"
|
||||
@@ -556,7 +556,7 @@
|
||||
},
|
||||
"shallot": {
|
||||
"name": "šalotka",
|
||||
"plural_name": "shallots"
|
||||
"plural_name": "šalotka"
|
||||
},
|
||||
"skate": {
|
||||
"name": "skat"
|
||||
@@ -572,7 +572,7 @@
|
||||
},
|
||||
"spaghetti-squash": {
|
||||
"name": "špageti buča",
|
||||
"plural_name": "spaghetti squashes"
|
||||
"plural_name": "buča špagetarica"
|
||||
},
|
||||
"speck": {
|
||||
"name": "špeh"
|
||||
@@ -585,11 +585,11 @@
|
||||
},
|
||||
"spring-onion": {
|
||||
"name": "mlada čebulica",
|
||||
"plural_name": "spring onions"
|
||||
"plural_name": "mlada čebulica"
|
||||
},
|
||||
"squash": {
|
||||
"name": "buča",
|
||||
"plural_name": "squashes"
|
||||
"plural_name": "buča"
|
||||
},
|
||||
"squash-family": {
|
||||
"name": "družina buč"
|
||||
@@ -602,7 +602,7 @@
|
||||
},
|
||||
"sunchoke": {
|
||||
"name": "topinambur",
|
||||
"plural_name": "sunchokes"
|
||||
"plural_name": "topinambur"
|
||||
},
|
||||
"sunflower-seeds": {
|
||||
"name": "sončnična semena"
|
||||
@@ -612,11 +612,11 @@
|
||||
},
|
||||
"sweet-potato": {
|
||||
"name": "sladki krompir",
|
||||
"plural_name": "sweet potatoes"
|
||||
"plural_name": "sladki krompir"
|
||||
},
|
||||
"sweetcorn": {
|
||||
"name": "sladka koruza",
|
||||
"plural_name": "sweetcorns"
|
||||
"plural_name": "sladka koruza"
|
||||
},
|
||||
"sweeteners": {
|
||||
"name": "sladilo"
|
||||
@@ -626,21 +626,21 @@
|
||||
},
|
||||
"taro": {
|
||||
"name": "taro",
|
||||
"plural_name": "taroes"
|
||||
"plural_name": "taroji"
|
||||
},
|
||||
"teff": {
|
||||
"name": "tef"
|
||||
},
|
||||
"tomato": {
|
||||
"name": "paradižnik",
|
||||
"plural_name": "tomatoes"
|
||||
"plural_name": "paradižnik"
|
||||
},
|
||||
"trout": {
|
||||
"name": "postrv"
|
||||
},
|
||||
"tubers": {
|
||||
"name": "gomolji",
|
||||
"plural_name": "tubers"
|
||||
"plural_name": "gomolji"
|
||||
},
|
||||
"tuna": {
|
||||
"name": "tuna"
|
||||
@@ -650,7 +650,7 @@
|
||||
},
|
||||
"turnip": {
|
||||
"name": "repa",
|
||||
"plural_name": "turnips"
|
||||
"plural_name": "repa"
|
||||
},
|
||||
"unrefined-sugar": {
|
||||
"name": "nerafiniran sladkor"
|
||||
@@ -666,11 +666,11 @@
|
||||
},
|
||||
"watermelon": {
|
||||
"name": "lubenica",
|
||||
"plural_name": "watermelons"
|
||||
"plural_name": "lubenica"
|
||||
},
|
||||
"white-mushroom": {
|
||||
"name": "bele gobe",
|
||||
"plural_name": "white mushrooms"
|
||||
"plural_name": "bele gobe"
|
||||
},
|
||||
"white-sugar": {
|
||||
"name": "beli sladkor"
|
||||
@@ -680,13 +680,13 @@
|
||||
},
|
||||
"yam": {
|
||||
"name": "marmelada",
|
||||
"plural_name": "yams"
|
||||
"plural_name": "marmelada"
|
||||
},
|
||||
"yeast": {
|
||||
"name": "kvas"
|
||||
},
|
||||
"zucchini": {
|
||||
"name": "bučke",
|
||||
"plural_name": "zucchinis"
|
||||
"plural_name": "bučke"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"apples": {
|
||||
"name": "äpplen",
|
||||
"plural_name": "apples"
|
||||
"plural_name": "äpplen"
|
||||
},
|
||||
"artichoke": {
|
||||
"name": "kronärtskocka"
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"avocado": {
|
||||
"name": "avokado",
|
||||
"plural_name": "avocado"
|
||||
"plural_name": "avokado"
|
||||
},
|
||||
"bacon": {
|
||||
"name": "bacon"
|
||||
@@ -48,7 +48,7 @@
|
||||
},
|
||||
"bell-peppers": {
|
||||
"name": "paprika",
|
||||
"plural_name": "bell peppers"
|
||||
"plural_name": "paprika"
|
||||
},
|
||||
"blackberries": {
|
||||
"name": "björnbär"
|
||||
@@ -94,7 +94,7 @@
|
||||
},
|
||||
"cabbage": {
|
||||
"name": "vitkål",
|
||||
"plural_name": "cabbages"
|
||||
"plural_name": "kål"
|
||||
},
|
||||
"cactus-edible": {
|
||||
"name": "kaktus, ätbar"
|
||||
@@ -116,7 +116,7 @@
|
||||
},
|
||||
"carrot": {
|
||||
"name": "morot",
|
||||
"plural_name": "carrots"
|
||||
"plural_name": "morötter"
|
||||
},
|
||||
"caster-sugar": {
|
||||
"name": "strösocker"
|
||||
@@ -129,7 +129,7 @@
|
||||
},
|
||||
"cauliflower": {
|
||||
"name": "blomkål",
|
||||
"plural_name": "cauliflowers"
|
||||
"plural_name": "blomkål"
|
||||
},
|
||||
"cayenne-pepper": {
|
||||
"name": "cayennepeppar"
|
||||
@@ -154,7 +154,7 @@
|
||||
},
|
||||
"chilli-peppers": {
|
||||
"name": "chilipeppar",
|
||||
"plural_name": "chilli peppers"
|
||||
"plural_name": "chilipeppar"
|
||||
},
|
||||
"chinese-leaves": {
|
||||
"name": "salladskål"
|
||||
@@ -176,7 +176,7 @@
|
||||
},
|
||||
"coconut": {
|
||||
"name": "kokos",
|
||||
"plural_name": "coconuts"
|
||||
"plural_name": "kokosnötter"
|
||||
},
|
||||
"coconut-milk": {
|
||||
"name": "kokosmjölk"
|
||||
@@ -198,7 +198,7 @@
|
||||
},
|
||||
"corn": {
|
||||
"name": "majs",
|
||||
"plural_name": "corns"
|
||||
"plural_name": "majs"
|
||||
},
|
||||
"corn-syrup": {
|
||||
"name": "majssirap"
|
||||
@@ -214,14 +214,14 @@
|
||||
},
|
||||
"cucumber": {
|
||||
"name": "gurka",
|
||||
"plural_name": "cucumbers"
|
||||
"plural_name": "gurkor"
|
||||
},
|
||||
"cumin": {
|
||||
"name": "spiskummin"
|
||||
},
|
||||
"daikon": {
|
||||
"name": "rättika",
|
||||
"plural_name": "daikons"
|
||||
"plural_name": "rättikor"
|
||||
},
|
||||
"dairy-products-and-dairy-substitutes": {
|
||||
"name": "mejeriprodukter och mejerisubstitut"
|
||||
@@ -240,11 +240,11 @@
|
||||
},
|
||||
"eggplant": {
|
||||
"name": "äggplanta",
|
||||
"plural_name": "eggplants"
|
||||
"plural_name": "äggplantor"
|
||||
},
|
||||
"eggs": {
|
||||
"name": "ägg",
|
||||
"plural_name": "eggs"
|
||||
"plural_name": "ägg"
|
||||
},
|
||||
"endive": {
|
||||
"name": "endiv",
|
||||
@@ -292,7 +292,7 @@
|
||||
},
|
||||
"garlic": {
|
||||
"name": "vitlök",
|
||||
"plural_name": "garlics"
|
||||
"plural_name": "vitlökar"
|
||||
},
|
||||
"gem-squash": {
|
||||
"name": "gem squash"
|
||||
@@ -317,11 +317,11 @@
|
||||
},
|
||||
"green-onion": {
|
||||
"name": "salladslök",
|
||||
"plural_name": "green onions"
|
||||
"plural_name": "salladslökar"
|
||||
},
|
||||
"heart-of-palm": {
|
||||
"name": "palmhjärta",
|
||||
"plural_name": "heart of palms"
|
||||
"plural_name": "palmhjärta"
|
||||
},
|
||||
"hemp": {
|
||||
"name": "hampa"
|
||||
@@ -337,7 +337,7 @@
|
||||
},
|
||||
"jackfruit": {
|
||||
"name": "jackfrukt",
|
||||
"plural_name": "jackfruits"
|
||||
"plural_name": "jackfrukter"
|
||||
},
|
||||
"jaggery": {
|
||||
"name": "jaggery"
|
||||
@@ -368,7 +368,7 @@
|
||||
},
|
||||
"leek": {
|
||||
"name": "purjolök",
|
||||
"plural_name": "leeks"
|
||||
"plural_name": "purjolökar"
|
||||
},
|
||||
"legumes": {
|
||||
"name": "baljväxter"
|
||||
@@ -403,7 +403,7 @@
|
||||
},
|
||||
"mushroom": {
|
||||
"name": "svamp",
|
||||
"plural_name": "mushrooms"
|
||||
"plural_name": "svampar"
|
||||
},
|
||||
"mussels": {
|
||||
"name": "musslor"
|
||||
@@ -425,7 +425,7 @@
|
||||
},
|
||||
"octopuses": {
|
||||
"name": "bläckfiskar",
|
||||
"plural_name": "octopuses"
|
||||
"plural_name": "bläckfiskar"
|
||||
},
|
||||
"oils": {
|
||||
"name": "oljor"
|
||||
@@ -450,7 +450,7 @@
|
||||
},
|
||||
"oranges": {
|
||||
"name": "apelsiner",
|
||||
"plural_name": "oranges"
|
||||
"plural_name": "apelsiner"
|
||||
},
|
||||
"oregano": {
|
||||
"name": "oregano"
|
||||
@@ -469,11 +469,11 @@
|
||||
},
|
||||
"parsnip": {
|
||||
"name": "palsternacka",
|
||||
"plural_name": "parsnips"
|
||||
"plural_name": "palsternackor"
|
||||
},
|
||||
"pear": {
|
||||
"name": "päron",
|
||||
"plural_name": "pears"
|
||||
"plural_name": "päron"
|
||||
},
|
||||
"peas": {
|
||||
"name": "ärtor"
|
||||
@@ -484,7 +484,7 @@
|
||||
},
|
||||
"pineapple": {
|
||||
"name": "ananas",
|
||||
"plural_name": "pineapples"
|
||||
"plural_name": "ananas"
|
||||
},
|
||||
"plantain": {
|
||||
"name": "kokbanan",
|
||||
@@ -495,7 +495,7 @@
|
||||
},
|
||||
"potato": {
|
||||
"name": "potatis",
|
||||
"plural_name": "potatoes"
|
||||
"plural_name": "potatis"
|
||||
},
|
||||
"poultry": {
|
||||
"name": "kyckling"
|
||||
@@ -505,14 +505,14 @@
|
||||
},
|
||||
"pumpkin": {
|
||||
"name": "pumpa",
|
||||
"plural_name": "pumpkins"
|
||||
"plural_name": "pumpor"
|
||||
},
|
||||
"pumpkin-seeds": {
|
||||
"name": "pumpafrön"
|
||||
},
|
||||
"radish": {
|
||||
"name": "rädisa",
|
||||
"plural_name": "radishes"
|
||||
"plural_name": "rädisor"
|
||||
},
|
||||
"raw-sugar": {
|
||||
"name": "brunt socker"
|
||||
@@ -543,7 +543,7 @@
|
||||
},
|
||||
"scallion": {
|
||||
"name": "salladslök",
|
||||
"plural_name": "scallions"
|
||||
"plural_name": "salladslökar"
|
||||
},
|
||||
"seafood": {
|
||||
"name": "fisk och skaldjur"
|
||||
@@ -556,7 +556,7 @@
|
||||
},
|
||||
"shallot": {
|
||||
"name": "schalottenlök",
|
||||
"plural_name": "shallots"
|
||||
"plural_name": "schalottenlökar"
|
||||
},
|
||||
"skate": {
|
||||
"name": "skate"
|
||||
@@ -585,11 +585,11 @@
|
||||
},
|
||||
"spring-onion": {
|
||||
"name": "vårlök",
|
||||
"plural_name": "spring onions"
|
||||
"plural_name": "vårlökar"
|
||||
},
|
||||
"squash": {
|
||||
"name": "squash",
|
||||
"plural_name": "squashes"
|
||||
"plural_name": "squash"
|
||||
},
|
||||
"squash-family": {
|
||||
"name": "squash-familj"
|
||||
@@ -612,7 +612,7 @@
|
||||
},
|
||||
"sweet-potato": {
|
||||
"name": "sötpotatis",
|
||||
"plural_name": "sweet potatoes"
|
||||
"plural_name": "sötpotatisar"
|
||||
},
|
||||
"sweetcorn": {
|
||||
"name": "sockermajs",
|
||||
@@ -633,7 +633,7 @@
|
||||
},
|
||||
"tomato": {
|
||||
"name": "tomat",
|
||||
"plural_name": "tomatoes"
|
||||
"plural_name": "tomater"
|
||||
},
|
||||
"trout": {
|
||||
"name": "öring"
|
||||
@@ -650,7 +650,7 @@
|
||||
},
|
||||
"turnip": {
|
||||
"name": "majrova",
|
||||
"plural_name": "turnips"
|
||||
"plural_name": "majrovor"
|
||||
},
|
||||
"unrefined-sugar": {
|
||||
"name": "råsocker"
|
||||
@@ -666,11 +666,11 @@
|
||||
},
|
||||
"watermelon": {
|
||||
"name": "vattenmelon",
|
||||
"plural_name": "watermelons"
|
||||
"plural_name": "vattenmeloner"
|
||||
},
|
||||
"white-mushroom": {
|
||||
"name": "schampinjon",
|
||||
"plural_name": "white mushrooms"
|
||||
"plural_name": "champinjoner"
|
||||
},
|
||||
"white-sugar": {
|
||||
"name": "strösocker"
|
||||
@@ -687,6 +687,6 @@
|
||||
},
|
||||
"zucchini": {
|
||||
"name": "zucchini",
|
||||
"plural_name": "zucchinis"
|
||||
"plural_name": "zucchinins"
|
||||
}
|
||||
}
|
||||
@@ -260,8 +260,8 @@
|
||||
"name": "рахіси папороті"
|
||||
},
|
||||
"fiddlehead-fern": {
|
||||
"name": "рахіси папороті",
|
||||
"plural_name": "fiddlehead ferns"
|
||||
"name": "рахіс папороті",
|
||||
"plural_name": "рахіси папороті"
|
||||
},
|
||||
"fish": {
|
||||
"name": "риба"
|
||||
@@ -626,7 +626,7 @@
|
||||
},
|
||||
"taro": {
|
||||
"name": "таро",
|
||||
"plural_name": "taroes"
|
||||
"plural_name": "таро"
|
||||
},
|
||||
"teff": {
|
||||
"name": "тефф"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"teaspoon": {
|
||||
"name": "cucharadita/s de café",
|
||||
"plural_name": "teaspoons",
|
||||
"plural_name": "cucharillas de té",
|
||||
"description": "",
|
||||
"abbreviation": "cc"
|
||||
"abbreviation": "cucharadita"
|
||||
},
|
||||
"tablespoon": {
|
||||
"name": "cucharada/s sopera/s",
|
||||
"plural_name": "tablespoons",
|
||||
"plural_name": "cucharadas",
|
||||
"description": "",
|
||||
"abbreviation": "cs"
|
||||
},
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"fluid-ounce": {
|
||||
"name": "onzas líquidas",
|
||||
"plural_name": "fluid ounces",
|
||||
"plural_name": "onzas líquidas",
|
||||
"description": "",
|
||||
"abbreviation": "liq onz"
|
||||
},
|
||||
@@ -31,7 +31,7 @@
|
||||
},
|
||||
"quart": {
|
||||
"name": "cuarto",
|
||||
"plural_name": "quarts",
|
||||
"plural_name": "cuartos de galón",
|
||||
"description": "",
|
||||
"abbreviation": "qt"
|
||||
},
|
||||
@@ -43,50 +43,50 @@
|
||||
},
|
||||
"milliliter": {
|
||||
"name": "mililitro/s",
|
||||
"plural_name": "milliliters",
|
||||
"plural_name": "mililitros",
|
||||
"description": "",
|
||||
"abbreviation": "ml"
|
||||
},
|
||||
"liter": {
|
||||
"name": "litro/s",
|
||||
"plural_name": "liters",
|
||||
"plural_name": "litros",
|
||||
"description": "",
|
||||
"abbreviation": "l"
|
||||
},
|
||||
"pound": {
|
||||
"name": "libra/s",
|
||||
"plural_name": "pounds",
|
||||
"plural_name": "libras",
|
||||
"description": "",
|
||||
"abbreviation": "lb",
|
||||
"plural_abbreviation": "lbs"
|
||||
"plural_abbreviation": "libras"
|
||||
},
|
||||
"ounce": {
|
||||
"name": "onza/s",
|
||||
"plural_name": "ounces",
|
||||
"plural_name": "onzas",
|
||||
"description": "",
|
||||
"abbreviation": "onz"
|
||||
},
|
||||
"gram": {
|
||||
"name": "gramo/s",
|
||||
"plural_name": "grams",
|
||||
"plural_name": "gramos",
|
||||
"description": "",
|
||||
"abbreviation": "g"
|
||||
},
|
||||
"kilogram": {
|
||||
"name": "kilogramo/s",
|
||||
"plural_name": "kilograms",
|
||||
"plural_name": "kilogramos",
|
||||
"description": "",
|
||||
"abbreviation": "kg"
|
||||
},
|
||||
"milligram": {
|
||||
"name": "miligramo/s",
|
||||
"plural_name": "milligrams",
|
||||
"plural_name": "miligramos",
|
||||
"description": "",
|
||||
"abbreviation": "mg"
|
||||
},
|
||||
"splash": {
|
||||
"name": "gota/s",
|
||||
"plural_name": "splashes",
|
||||
"plural_name": "gota/s",
|
||||
"description": "",
|
||||
"abbreviation": ""
|
||||
},
|
||||
@@ -98,7 +98,7 @@
|
||||
},
|
||||
"serving": {
|
||||
"name": "porción/es",
|
||||
"plural_name": "servings",
|
||||
"plural_name": "porciones",
|
||||
"description": "",
|
||||
"abbreviation": ""
|
||||
},
|
||||
@@ -110,31 +110,31 @@
|
||||
},
|
||||
"clove": {
|
||||
"name": "diente/s",
|
||||
"plural_name": "cloves",
|
||||
"plural_name": "clavos de olor",
|
||||
"description": "",
|
||||
"abbreviation": ""
|
||||
},
|
||||
"can": {
|
||||
"name": "lata",
|
||||
"plural_name": "cans",
|
||||
"plural_name": "latas",
|
||||
"description": "",
|
||||
"abbreviation": ""
|
||||
},
|
||||
"bunch": {
|
||||
"name": "bunch",
|
||||
"plural_name": "bunches",
|
||||
"name": "puñado",
|
||||
"plural_name": "puñados",
|
||||
"description": "",
|
||||
"abbreviation": ""
|
||||
},
|
||||
"pack": {
|
||||
"name": "pack",
|
||||
"plural_name": "packs",
|
||||
"name": "paquete",
|
||||
"plural_name": "paquetes",
|
||||
"description": "",
|
||||
"abbreviation": ""
|
||||
},
|
||||
"pinch": {
|
||||
"name": "pinch",
|
||||
"plural_name": "pinches",
|
||||
"name": "pellizco",
|
||||
"plural_name": "pellizcos",
|
||||
"description": "",
|
||||
"abbreviation": ""
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ from collections.abc import Callable
|
||||
from logging import Logger
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
import sqlalchemy.exc
|
||||
from fastapi import HTTPException, status
|
||||
from pydantic import UUID4, BaseModel
|
||||
|
||||
@@ -57,10 +58,16 @@ class HttpRepo(Generic[C, R, U]):
|
||||
# Respond
|
||||
msg = self.get_exception_message(ex)
|
||||
|
||||
raise HTTPException(
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
detail=ErrorResponse.respond(message=msg, exception=str(ex)),
|
||||
)
|
||||
if isinstance(ex, sqlalchemy.exc.NoResultFound):
|
||||
raise HTTPException(
|
||||
status.HTTP_404_NOT_FOUND,
|
||||
detail=ErrorResponse.respond(message=msg, exception=str(ex)),
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
detail=ErrorResponse.respond(message=msg, exception=str(ex)),
|
||||
)
|
||||
|
||||
def create_one(self, data: C) -> R | None:
|
||||
item: R | None = None
|
||||
|
||||
@@ -28,14 +28,22 @@ remember_me_duration = timedelta(days=14)
|
||||
settings = get_app_settings()
|
||||
if settings.OIDC_READY:
|
||||
oauth = OAuth()
|
||||
groups_claim = settings.OIDC_GROUPS_CLAIM if settings.OIDC_REQUIRES_GROUP_CLAIM else ""
|
||||
scope = f"openid email profile {groups_claim}"
|
||||
scope = None
|
||||
if settings.OIDC_SCOPES_OVERRIDE:
|
||||
scope = settings.OIDC_SCOPES_OVERRIDE
|
||||
else:
|
||||
groups_claim = settings.OIDC_GROUPS_CLAIM if settings.OIDC_REQUIRES_GROUP_CLAIM else ""
|
||||
scope = f"openid email profile {groups_claim}"
|
||||
client_args = {"scope": scope.rstrip()}
|
||||
if settings.OIDC_TLS_CACERTFILE:
|
||||
client_args["verify"] = settings.OIDC_TLS_CACERTFILE
|
||||
|
||||
oauth.register(
|
||||
"oidc",
|
||||
client_id=settings.OIDC_CLIENT_ID,
|
||||
client_secret=settings.OIDC_CLIENT_SECRET,
|
||||
server_metadata_url=settings.OIDC_CONFIGURATION_URL,
|
||||
client_kwargs={"scope": scope.rstrip()},
|
||||
client_kwargs=client_args,
|
||||
code_challenge_method="S256",
|
||||
)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.core.exceptions import mealie_registered_exceptions
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.routes._base import BaseCrudController, controller
|
||||
from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.routes._base.routers import MealieCrudRoute
|
||||
@@ -26,9 +27,13 @@ router = APIRouter(prefix="/households/cookbooks", tags=["Households: Cookbooks"
|
||||
@controller(router)
|
||||
class GroupCookbookController(BaseCrudController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
def cookbooks(self):
|
||||
return self.repos.cookbooks
|
||||
|
||||
@cached_property
|
||||
def group_cookbooks(self):
|
||||
return get_repositories(self.session, group_id=self.group_id, household_id=None).cookbooks
|
||||
|
||||
def registered_exceptions(self, ex: type[Exception]) -> str:
|
||||
registered = {
|
||||
**mealie_registered_exceptions(self.translator),
|
||||
@@ -38,14 +43,15 @@ class GroupCookbookController(BaseCrudController):
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
return HttpRepo[CreateCookBook, ReadCookBook, UpdateCookBook](
|
||||
self.repo,
|
||||
self.cookbooks,
|
||||
self.logger,
|
||||
self.registered_exceptions,
|
||||
)
|
||||
|
||||
@router.get("", response_model=CookBookPagination)
|
||||
def get_all(self, q: PaginationQuery = Depends(PaginationQuery)):
|
||||
response = self.repo.page_all(
|
||||
# Fetch all cookbooks for the group, rather than the household
|
||||
response = self.group_cookbooks.page_all(
|
||||
pagination=q,
|
||||
override=ReadCookBook,
|
||||
)
|
||||
@@ -106,7 +112,8 @@ class GroupCookbookController(BaseCrudController):
|
||||
except ValueError:
|
||||
match_attr = "slug"
|
||||
|
||||
cookbook = self.repo.get_one(item_id, match_attr)
|
||||
# Allow fetching other households' cookbooks
|
||||
cookbook = self.group_cookbooks.get_one(item_id, match_attr)
|
||||
|
||||
if cookbook is None:
|
||||
raise HTTPException(status_code=404)
|
||||
|
||||
@@ -66,37 +66,6 @@ class GroupMealplanController(BaseCrudController):
|
||||
)
|
||||
return recipes_data.items
|
||||
|
||||
@router.get("/today")
|
||||
def get_todays_meals(self):
|
||||
return self.repo.get_today()
|
||||
|
||||
@router.post("/random", response_model=ReadPlanEntry)
|
||||
def create_random_meal(self, data: CreateRandomEntry):
|
||||
"""
|
||||
`create_random_meal` is a route that provides the randomized functionality for mealplaners.
|
||||
It operates by following the rules set out in the household's mealplan settings. If no settings
|
||||
are set, it will return any random meal.
|
||||
|
||||
Refer to the mealplan settings routes for more information on how rules can be applied
|
||||
to the random meal selector.
|
||||
"""
|
||||
random_recipes = self._get_random_recipes_from_mealplan(data.date, data.entry_type)
|
||||
if not random_recipes:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=ErrorResponse.respond(message=self.t("mealplan.no-recipes-match-your-rules"))
|
||||
)
|
||||
|
||||
recipe = random_recipes[0]
|
||||
return self.mixins.create_one(
|
||||
SavePlanEntry(
|
||||
date=data.date,
|
||||
entry_type=data.entry_type,
|
||||
recipe_id=recipe.id,
|
||||
group_id=self.group_id,
|
||||
user_id=self.user.id,
|
||||
)
|
||||
)
|
||||
|
||||
@router.get("", response_model=PlanEntryPagination)
|
||||
def get_all(
|
||||
self,
|
||||
@@ -144,6 +113,37 @@ class GroupMealplanController(BaseCrudController):
|
||||
|
||||
return result
|
||||
|
||||
@router.get("/today")
|
||||
def get_todays_meals(self):
|
||||
return self.repo.get_today()
|
||||
|
||||
@router.post("/random", response_model=ReadPlanEntry)
|
||||
def create_random_meal(self, data: CreateRandomEntry):
|
||||
"""
|
||||
`create_random_meal` is a route that provides the randomized functionality for mealplaners.
|
||||
It operates by following the rules set out in the household's mealplan settings. If no settings
|
||||
are set, it will return any random meal.
|
||||
|
||||
Refer to the mealplan settings routes for more information on how rules can be applied
|
||||
to the random meal selector.
|
||||
"""
|
||||
random_recipes = self._get_random_recipes_from_mealplan(data.date, data.entry_type)
|
||||
if not random_recipes:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=ErrorResponse.respond(message=self.t("mealplan.no-recipes-match-your-rules"))
|
||||
)
|
||||
|
||||
recipe = random_recipes[0]
|
||||
return self.mixins.create_one(
|
||||
SavePlanEntry(
|
||||
date=data.date,
|
||||
entry_type=data.entry_type,
|
||||
recipe_id=recipe.id,
|
||||
group_id=self.group_id,
|
||||
user_id=self.user.id,
|
||||
)
|
||||
)
|
||||
|
||||
@router.get("/{item_id}", response_model=ReadPlanEntry)
|
||||
def get_one(self, item_id: int):
|
||||
return self.mixins.get_one(item_id)
|
||||
|
||||
@@ -230,28 +230,6 @@ class ShoppingListController(BaseCrudController):
|
||||
# =======================================================================
|
||||
# Other Operations
|
||||
|
||||
@router.post("/{item_id}/recipe/{recipe_id}", response_model=ShoppingListOut)
|
||||
def add_recipe_ingredients_to_list(
|
||||
self, item_id: UUID4, recipe_id: UUID4, data: ShoppingListAddRecipeParams | None = None
|
||||
):
|
||||
shopping_list, items = self.service.add_recipe_ingredients_to_list(
|
||||
item_id, recipe_id, data.recipe_increment_quantity if data else 1, data.recipe_ingredients if data else None
|
||||
)
|
||||
|
||||
publish_list_item_events(self.publish_event, items)
|
||||
return shopping_list
|
||||
|
||||
@router.post("/{item_id}/recipe/{recipe_id}/delete", response_model=ShoppingListOut)
|
||||
def remove_recipe_ingredients_from_list(
|
||||
self, item_id: UUID4, recipe_id: UUID4, data: ShoppingListRemoveRecipeParams | None = None
|
||||
):
|
||||
shopping_list, items = self.service.remove_recipe_ingredients_from_list(
|
||||
item_id, recipe_id, data.recipe_decrement_quantity if data else 1
|
||||
)
|
||||
|
||||
publish_list_item_events(self.publish_event, items)
|
||||
return shopping_list
|
||||
|
||||
@router.put("/{item_id}/label-settings", response_model=ShoppingListOut)
|
||||
def update_label_settings(self, item_id: UUID4, data: list[ShoppingListMultiPurposeLabelUpdate]):
|
||||
for setting in data:
|
||||
@@ -273,3 +251,25 @@ class ShoppingListController(BaseCrudController):
|
||||
)
|
||||
|
||||
return updated_list
|
||||
|
||||
@router.post("/{item_id}/recipe/{recipe_id}", response_model=ShoppingListOut)
|
||||
def add_recipe_ingredients_to_list(
|
||||
self, item_id: UUID4, recipe_id: UUID4, data: ShoppingListAddRecipeParams | None = None
|
||||
):
|
||||
shopping_list, items = self.service.add_recipe_ingredients_to_list(
|
||||
item_id, recipe_id, data.recipe_increment_quantity if data else 1, data.recipe_ingredients if data else None
|
||||
)
|
||||
|
||||
publish_list_item_events(self.publish_event, items)
|
||||
return shopping_list
|
||||
|
||||
@router.post("/{item_id}/recipe/{recipe_id}/delete", response_model=ShoppingListOut)
|
||||
def remove_recipe_ingredients_from_list(
|
||||
self, item_id: UUID4, recipe_id: UUID4, data: ShoppingListRemoveRecipeParams | None = None
|
||||
):
|
||||
shopping_list, items = self.service.remove_recipe_ingredients_from_list(
|
||||
item_id, recipe_id, data.recipe_decrement_quantity if data else 1
|
||||
)
|
||||
|
||||
publish_list_item_events(self.publish_event, items)
|
||||
return shopping_list
|
||||
|
||||
@@ -7,12 +7,7 @@ from starlette.responses import FileResponse
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.recipe.recipe_timeline_events import RecipeTimelineEventOut
|
||||
|
||||
"""
|
||||
These routes are for development only! These assets are served by Caddy when not
|
||||
in development mode. If you make changes, be sure to test the production container.
|
||||
"""
|
||||
|
||||
router = APIRouter(prefix="/recipes", include_in_schema=False)
|
||||
router = APIRouter(prefix="/recipes")
|
||||
|
||||
|
||||
class ImageType(str, Enum):
|
||||
@@ -30,7 +25,7 @@ async def get_recipe_img(recipe_id: str, file_name: ImageType = ImageType.origin
|
||||
recipe_image = Recipe.directory_from_id(recipe_id).joinpath("images", file_name.value)
|
||||
|
||||
if recipe_image.exists():
|
||||
return FileResponse(recipe_image)
|
||||
return FileResponse(recipe_image, media_type="image/webp")
|
||||
else:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
|
||||
@@ -48,7 +43,7 @@ async def get_recipe_timeline_event_img(
|
||||
)
|
||||
|
||||
if timeline_event_image.exists():
|
||||
return FileResponse(timeline_event_image)
|
||||
return FileResponse(timeline_event_image, media_type="image/webp")
|
||||
else:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
@@ -4,12 +4,7 @@ from starlette.responses import FileResponse
|
||||
|
||||
from mealie.schema.user import PrivateUser
|
||||
|
||||
"""
|
||||
These routes are for development only! These assets are served by Caddy when not
|
||||
in development mode. If you make changes, be sure to test the production container.
|
||||
"""
|
||||
|
||||
router = APIRouter(prefix="/users", include_in_schema=False)
|
||||
router = APIRouter(prefix="/users")
|
||||
|
||||
|
||||
@router.get("/{user_id}/{file_name}", response_class=FileResponse)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user