mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-02-24 08:43:11 -05:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
418a8ec72b | ||
|
|
770630bf73 | ||
|
|
89ee7475a6 | ||
|
|
bca5dd8282 | ||
|
|
dabd93c919 | ||
|
|
6991dff3e6 | ||
|
|
b0eece789d | ||
|
|
9fad4a9dce | ||
|
|
22d8c4d5dc | ||
|
|
7be24d3479 | ||
|
|
fbceb61b9a | ||
|
|
1be5bfaef1 | ||
|
|
fac1df31d3 | ||
|
|
6957e2fa74 | ||
|
|
4f02fae284 | ||
|
|
f2615c97e9 | ||
|
|
6b4c9a400d | ||
|
|
cca11b5a12 | ||
|
|
f697a7ee34 | ||
|
|
0d73338e12 | ||
|
|
2f4c6bd500 | ||
|
|
3807778e2f | ||
|
|
ee87a14401 | ||
|
|
ec458a0a08 | ||
|
|
2ff37c86d6 | ||
|
|
b7da3c0f73 | ||
|
|
d799136f0d | ||
|
|
d1d5754c6d | ||
|
|
52662fdce2 | ||
|
|
8df6033c19 | ||
|
|
c23660007e | ||
|
|
786aa2279c | ||
|
|
ab8c3be367 | ||
|
|
8bf8dfd3ed | ||
|
|
b3aa7aeb1a | ||
|
|
0f2b1d8d3a | ||
|
|
4de6391684 | ||
|
|
c3e68b7d8a | ||
|
|
7557d2e818 | ||
|
|
c22a2fc4a8 | ||
|
|
ad94a4f42f | ||
|
|
e6bf3b3acd | ||
|
|
711dd93851 | ||
|
|
2b6d7811ca | ||
|
|
3373abf787 | ||
|
|
741d37f59e | ||
|
|
b38c19ce71 | ||
|
|
1a385e941c | ||
|
|
c6f5b62ad0 | ||
|
|
84dad84326 | ||
|
|
f369c8fd6e | ||
|
|
467cf46c6d | ||
|
|
360b8e21d9 | ||
|
|
0b851e79ec | ||
|
|
faf716cb7e | ||
|
|
46f1ad7941 | ||
|
|
6e1112c73e |
@@ -12,6 +12,6 @@ repos:
|
||||
exclude: ^tests/data/
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.3.5
|
||||
rev: v0.4.3
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"""add group recipe actions
|
||||
|
||||
Revision ID: 7788478a0338
|
||||
Revises: d7c6efd2de42
|
||||
Create Date: 2024-04-07 01:05:20.816270
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
import mealie.db.migration_types
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "7788478a0338"
|
||||
down_revision = "d7c6efd2de42"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"recipe_actions",
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("action_type", sa.String(), nullable=False),
|
||||
sa.Column("title", sa.String(), nullable=False),
|
||||
sa.Column("url", sa.String(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_recipe_actions_action_type"), "recipe_actions", ["action_type"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_actions_created_at"), "recipe_actions", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_actions_group_id"), "recipe_actions", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_actions_title"), "recipe_actions", ["title"], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f("ix_recipe_actions_title"), table_name="recipe_actions")
|
||||
op.drop_index(op.f("ix_recipe_actions_group_id"), table_name="recipe_actions")
|
||||
op.drop_index(op.f("ix_recipe_actions_created_at"), table_name="recipe_actions")
|
||||
op.drop_index(op.f("ix_recipe_actions_action_type"), table_name="recipe_actions")
|
||||
op.drop_table("recipe_actions")
|
||||
# ### end Alembic commands ###
|
||||
@@ -35,18 +35,24 @@ LOCALE_DATA: dict[str, LocaleData] = {
|
||||
"es-ES": LocaleData(name="Español (Spanish)"),
|
||||
"fi-FI": LocaleData(name="Suomi (Finnish)"),
|
||||
"fr-FR": LocaleData(name="Français (French)"),
|
||||
"gl-ES": LocaleData(name="Galego (Galician)"),
|
||||
"he-IL": LocaleData(name="עברית (Hebrew)", dir="rtl"),
|
||||
"hr-HR": LocaleData(name="Hrvatski (Croatian)"),
|
||||
"hu-HU": LocaleData(name="Magyar (Hungarian)"),
|
||||
"is-IS": LocaleData(name="Íslenska (Icelandic)"),
|
||||
"it-IT": LocaleData(name="Italiano (Italian)"),
|
||||
"ja-JP": LocaleData(name="日本語 (Japanese)"),
|
||||
"ko-KR": LocaleData(name="한국어 (Korean)"),
|
||||
"no-NO": LocaleData(name="Norsk (Norwegian)"),
|
||||
"lt-LT": LocaleData(name="Lietuvių (Lithuanian)"),
|
||||
"lv-LV": LocaleData(name="Latviešu (Latvian)"),
|
||||
"nl-NL": LocaleData(name="Nederlands (Dutch)"),
|
||||
"no-NO": LocaleData(name="Norsk (Norwegian)"),
|
||||
"pl-PL": LocaleData(name="Polski (Polish)"),
|
||||
"pt-BR": LocaleData(name="Português do Brasil (Brazilian Portuguese)"),
|
||||
"pt-PT": LocaleData(name="Português (Portuguese)"),
|
||||
"ro-RO": LocaleData(name="Română (Romanian)"),
|
||||
"ru-RU": LocaleData(name="Pусский (Russian)"),
|
||||
"sl-SI": LocaleData(name="Slovenščina (Slovenian)"),
|
||||
"sr-SP": LocaleData(name="српски (Serbian)"),
|
||||
"sv-SE": LocaleData(name="Svenska (Swedish)"),
|
||||
"tr-TR": LocaleData(name="Türkçe (Turkish)"),
|
||||
|
||||
@@ -20,7 +20,7 @@ Before you can start using OIDC Authentication, you must first configure a new c
|
||||
1. Create a new client application
|
||||
- The Provider type should be OIDC or OAuth2
|
||||
- The Grant type should be `Authorization Code`
|
||||
- The Application type should be `Web`
|
||||
- The Application type should be `Web` or `SPA`
|
||||
- The Client type should be `public`
|
||||
|
||||
2. Configure redirect URI
|
||||
@@ -42,7 +42,9 @@ Before you can start using OIDC Authentication, you must first configure a new c
|
||||
|
||||
4. Configure allowed scopes
|
||||
|
||||
The scopes required are `openid profile email groups`
|
||||
The scopes required are `openid profile email`
|
||||
|
||||
If you plan to use the [groups](#groups) to configure access within Mealie, you will need to also add the scope defined by the `OIDC_GROUPS_CLAIM` environment variable. The default claim is `groups`
|
||||
|
||||
## Mealie Setup
|
||||
|
||||
@@ -50,7 +52,7 @@ Take the client id and your discovery URL and update your environment variables
|
||||
|
||||
### Groups
|
||||
|
||||
There are two (optional) [environment variables](../installation/backend-config.md#openid-connect-oidc) that can control which of the users in your IdP can log in to Mealie and what permissions they will have. The groups should be **defined in your IdP** and be returned in the `groups` claim.
|
||||
There are two (optional) [environment variables](../installation/backend-config.md#openid-connect-oidc) that can control which of the users in your IdP can log in to Mealie and what permissions they will have. Keep in mind that these groups **do not necessarily correspond to groups in Mealie**. The groups claim is configurable via the `OIDC_GROUPS_CLAIM` environment variable. The groups should be **defined in your IdP** and be returned in the configured claim value.
|
||||
|
||||
`OIDC_USER_GROUP`: Users must be a part of this group (within your IdP) to be able to log in.
|
||||
|
||||
|
||||
@@ -81,12 +81,63 @@ The meal planner has the concept of plan rules. These offer a flexible way to us
|
||||
|
||||
The shopping lists feature is a great way to keep track of what you need to buy for your next meal. You can add items directly to the shopping list or link a recipe and all of it's ingredients to track meals during the week.
|
||||
|
||||
!!! warning
|
||||
At this time there isn't a tight integration between meal-plans and shopping lists; however, it's something we have planned for the future.
|
||||
|
||||
|
||||
[Shopping List Demo](https://demo.mealie.io/shopping-lists){ .md-button .md-button--primary }
|
||||
|
||||
## Integrations
|
||||
|
||||
Mealie is designed to integrate with many different external services. There are several ways you can integrate with Mealie to achieve custom IoT automations, data synchronization, and anything else you can think of. [You can work directly with Mealie through the API](./api-usage.md), or leverage other services to make seamless integrations.
|
||||
|
||||
### Notifiers
|
||||
|
||||
Notifiers are event-driven notifications sent when specific actions are performed within Mealie. Some actions include:
|
||||
- creating a recipe
|
||||
- adding items to a shopping list
|
||||
- creating a new mealplan
|
||||
|
||||
Notifiers use the [Apprise library](https://github.com/caronc/apprise/wiki), which integrates with a large number of notification services. In addition, certain custom notifiers send basic event data to the consumer (e.g. the `id` of the resource). These include:
|
||||
|
||||
- `form` and `forms`
|
||||
- `json` and `jsons`
|
||||
- `xml` and `xmls`
|
||||
|
||||
[Notifiers Demo](https://demo.mealie.io/group/notifiers){ .md-button .md-button--primary }
|
||||
|
||||
### Webhooks
|
||||
|
||||
Unlike notifiers, which are event-driven notifications, Webhooks allow you to send scheduled notifications to your desired endpoint. Webhooks are sent on the day of a scheduled mealplan, at the specified time, and contain the mealplan data in the request.
|
||||
|
||||
[Webhooks Demo](https://demo.mealie.io/group/webhooks){ .md-button .md-button--primary }
|
||||
|
||||
### Recipe Actions
|
||||
|
||||
Recipe Actions are custom actions you can add to all recipes in Mealie. This is a great way to add custom integrations that are fired manually. There are two types of recipe actions:
|
||||
|
||||
1. link - these actions will take you directly to an external page
|
||||
2. post - these actions will send a `POST` request to the specified URL, with the recipe JSON in the request body. These can be used, for instance, to manually trigger a webhook in Home Assistant
|
||||
|
||||
Recipe Action URLs can include merge fields to inject the current recipe's data. For instance, you can use the following URL to include a Google search with the recipe's slug:
|
||||
```
|
||||
https://www.google.com/search?q=${slug}
|
||||
```
|
||||
|
||||
When the action is clicked on, the `${slug}` field is replaced with the recipe's slug value. So, for example, it might take you to this URL on one of your recipes:
|
||||
```
|
||||
https://www.google.com/search?q=pasta-fagioli
|
||||
```
|
||||
|
||||
A common use case for "link" recipe actions is to integrate with the Bring! shopping list. Simply add a Recipe Action with the following URL:
|
||||
```
|
||||
https://api.getbring.com/rest/bringrecipes/deeplink?url=${url}&source=web
|
||||
```
|
||||
|
||||
Below is a list of all valid merge fields:
|
||||
|
||||
- ${id}
|
||||
- ${slug}
|
||||
- ${url}
|
||||
|
||||
To add, modify, or delete Recipe Actions, visit the Data Management page (more on that below).
|
||||
|
||||
## Data Management
|
||||
|
||||
|
||||
@@ -98,7 +98,9 @@ For usage, see [Usage - OpenID Connect](../authentication/oidc.md)
|
||||
| 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_SIGNING_ALGORITHM | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
|
||||
| OIDC_USER_CLAIM | email | Optional: 'email', 'preferred_username' |
|
||||
| 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`) |
|
||||
|
||||
### Themeing
|
||||
|
||||
|
||||
@@ -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.4.0 # (3)
|
||||
image: ghcr.io/mealie-recipes/mealie:v1.5.1 # (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.4.0 # (3)
|
||||
image: ghcr.io/mealie-recipes/mealie:v1.5.1 # (3)
|
||||
container_name: mealie
|
||||
restart: always
|
||||
ports:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -70,6 +70,7 @@
|
||||
print: true,
|
||||
printPreferences: true,
|
||||
share: loggedIn,
|
||||
recipeActions: true,
|
||||
}"
|
||||
@print="$emit('print')"
|
||||
/>
|
||||
|
||||
@@ -105,6 +105,26 @@
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
<div v-if="useItems.recipeActions && recipeActions && recipeActions.length">
|
||||
<v-divider />
|
||||
<v-list-group @click.stop>
|
||||
<template #activator>
|
||||
<v-list-item-title>{{ $tc("recipe.recipe-actions") }}</v-list-item-title>
|
||||
</template>
|
||||
<v-list dense class="ma-0 pa-0">
|
||||
<v-list-item
|
||||
v-for="(action, index) in recipeActions"
|
||||
:key="index"
|
||||
class="pl-6"
|
||||
@click="executeRecipeAction(action)"
|
||||
>
|
||||
<v-list-item-title>
|
||||
{{ action.title }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-list-group>
|
||||
</div>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</div>
|
||||
@@ -117,11 +137,12 @@ import RecipeDialogPrintPreferences from "./RecipeDialogPrintPreferences.vue";
|
||||
import RecipeDialogShare from "./RecipeDialogShare.vue";
|
||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { useGroupRecipeActions } from "~/composables/use-group-recipe-actions";
|
||||
import { useGroupSelf } from "~/composables/use-groups";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
import { usePlanTypeOptions } from "~/composables/use-group-mealplan";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import { ShoppingListSummary } from "~/lib/api/types/group";
|
||||
import { GroupRecipeActionOut, ShoppingListSummary } from "~/lib/api/types/group";
|
||||
import { PlanEntryType } from "~/lib/api/types/meal-plan";
|
||||
import { useAxiosDownloader } from "~/composables/api/use-axios-download";
|
||||
|
||||
@@ -134,6 +155,7 @@ export interface ContextMenuIncludes {
|
||||
print: boolean;
|
||||
printPreferences: boolean;
|
||||
share: boolean;
|
||||
recipeActions: boolean;
|
||||
}
|
||||
|
||||
export interface ContextMenuItem {
|
||||
@@ -163,6 +185,7 @@ export default defineComponent({
|
||||
print: true,
|
||||
printPreferences: true,
|
||||
share: true,
|
||||
recipeActions: true,
|
||||
}),
|
||||
},
|
||||
// Append items are added at the end of the useItems list
|
||||
@@ -347,6 +370,19 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
const groupRecipeActionsStore = useGroupRecipeActions();
|
||||
|
||||
async function executeRecipeAction(action: GroupRecipeActionOut) {
|
||||
const response = await groupRecipeActionsStore.execute(action, props.recipe);
|
||||
|
||||
if (action.actionType === "post") {
|
||||
if (!response || (response.status >= 200 && response.status < 300)) {
|
||||
alert.success(i18n.tc("events.message-sent"));
|
||||
} else {
|
||||
alert.error(i18n.tc("events.something-went-wrong"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteRecipe() {
|
||||
await api.recipes.deleteOne(props.slug);
|
||||
@@ -437,6 +473,8 @@ export default defineComponent({
|
||||
...toRefs(state),
|
||||
recipeRef,
|
||||
recipeRefWithScale,
|
||||
executeRecipeAction,
|
||||
recipeActions: groupRecipeActionsStore.recipeActions,
|
||||
shoppingLists,
|
||||
duplicateRecipe,
|
||||
contextMenuEventHandler,
|
||||
|
||||
@@ -143,7 +143,7 @@ import { watchDebounced } from "@vueuse/shared";
|
||||
import SearchFilter from "~/components/Domain/SearchFilter.vue";
|
||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||
import { useCategoryStore, useFoodStore, useTagStore, useToolStore } from "~/composables/store";
|
||||
import { useUserSortPreferences } from "~/composables/use-users/preferences";
|
||||
import { useUserSearchQuerySession } from "~/composables/use-users/preferences";
|
||||
import RecipeCardSection from "~/components/Domain/Recipe/RecipeCardSection.vue";
|
||||
import { IngredientFood, RecipeCategory, RecipeTag, RecipeTool } from "~/lib/api/types/recipe";
|
||||
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||
@@ -177,7 +177,7 @@ export default defineComponent({
|
||||
|
||||
const route = useRoute();
|
||||
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
|
||||
const preferences = useUserSortPreferences();
|
||||
const searchQuerySession = useUserSearchQuerySession();
|
||||
|
||||
const { recipes, appendRecipes, assignSorted, removeRecipe, replaceRecipes } = useLazyRecipes(isOwnGroup.value ? null : groupSlug.value);
|
||||
const categories = isOwnGroup.value ? useCategoryStore() : usePublicCategoryStore(groupSlug.value);
|
||||
@@ -194,7 +194,9 @@ export default defineComponent({
|
||||
|
||||
function calcPassedQuery(): RecipeSearchQuery {
|
||||
return {
|
||||
search: state.value.search,
|
||||
// the search clear button sets search to null, which still renders the query param for a moment,
|
||||
// whereas an empty string is not rendered
|
||||
search: state.value.search ? state.value.search : "",
|
||||
categories: toIDArray(selectedCategories.value),
|
||||
foods: toIDArray(selectedFoods.value),
|
||||
tags: toIDArray(selectedTags.value),
|
||||
@@ -217,14 +219,24 @@ export default defineComponent({
|
||||
};
|
||||
})
|
||||
|
||||
const queryDefaults = {
|
||||
search: "",
|
||||
orderBy: "created_at",
|
||||
orderDirection: "desc" as "asc" | "desc",
|
||||
requireAllCategories: false,
|
||||
requireAllTags: false,
|
||||
requireAllTools: false,
|
||||
requireAllFoods: false,
|
||||
}
|
||||
|
||||
function reset() {
|
||||
state.value.search = "";
|
||||
state.value.orderBy = "created_at";
|
||||
state.value.orderDirection = "desc";
|
||||
state.value.requireAllCategories = false;
|
||||
state.value.requireAllTags = false;
|
||||
state.value.requireAllTools = false;
|
||||
state.value.requireAllFoods = false;
|
||||
state.value.search = queryDefaults.search;
|
||||
state.value.orderBy = queryDefaults.orderBy;
|
||||
state.value.orderDirection = queryDefaults.orderDirection;
|
||||
state.value.requireAllCategories = queryDefaults.requireAllCategories;
|
||||
state.value.requireAllTags = queryDefaults.requireAllTags;
|
||||
state.value.requireAllTools = queryDefaults.requireAllTools;
|
||||
state.value.requireAllFoods = queryDefaults.requireAllFoods;
|
||||
selectedCategories.value = [];
|
||||
selectedFoods.value = [];
|
||||
selectedTags.value = [];
|
||||
@@ -262,12 +274,12 @@ export default defineComponent({
|
||||
foods: passedQuery.value.foods,
|
||||
tags: passedQuery.value.tags,
|
||||
tools: passedQuery.value.tools,
|
||||
// Only add the query param if it's or not default
|
||||
// Only add the query param if it's not the default value
|
||||
...{
|
||||
auto: state.value.auto ? undefined : "false",
|
||||
search: passedQuery.value.search === "" ? undefined : passedQuery.value.search,
|
||||
orderBy: passedQuery.value.orderBy === "created_at" ? undefined : passedQuery.value.orderBy,
|
||||
orderDirection: passedQuery.value.orderDirection === "desc" ? undefined : passedQuery.value.orderDirection,
|
||||
search: passedQuery.value.search === queryDefaults.search ? undefined : passedQuery.value.search,
|
||||
orderBy: passedQuery.value.orderBy === queryDefaults.orderBy ? undefined : passedQuery.value.orderBy,
|
||||
orderDirection: passedQuery.value.orderDirection === queryDefaults.orderDirection ? undefined : passedQuery.value.orderDirection,
|
||||
requireAllCategories: passedQuery.value.requireAllCategories ? "true" : undefined,
|
||||
requireAllTags: passedQuery.value.requireAllTags ? "true" : undefined,
|
||||
requireAllTools: passedQuery.value.requireAllTools ? "true" : undefined,
|
||||
@@ -275,7 +287,7 @@ export default defineComponent({
|
||||
},
|
||||
}
|
||||
await router.push({ query });
|
||||
preferences.value.searchQuery = JSON.stringify(query);
|
||||
searchQuerySession.value.recipe = JSON.stringify(query);
|
||||
}
|
||||
|
||||
function waitUntilAndExecute(
|
||||
@@ -360,25 +372,55 @@ export default defineComponent({
|
||||
|
||||
async function hydrateSearch() {
|
||||
const query = router.currentRoute.query;
|
||||
if (query.auto) {
|
||||
if (query.auto?.length) {
|
||||
state.value.auto = query.auto === "true";
|
||||
}
|
||||
|
||||
if (query.search) {
|
||||
if (query.search?.length) {
|
||||
state.value.search = query.search as string;
|
||||
} else {
|
||||
state.value.search = queryDefaults.search;
|
||||
}
|
||||
|
||||
if (query.orderBy) {
|
||||
if (query.orderBy?.length) {
|
||||
state.value.orderBy = query.orderBy as string;
|
||||
} else {
|
||||
state.value.orderBy = queryDefaults.orderBy;
|
||||
}
|
||||
|
||||
if (query.orderDirection) {
|
||||
if (query.orderDirection?.length) {
|
||||
state.value.orderDirection = query.orderDirection as "asc" | "desc";
|
||||
} else {
|
||||
state.value.orderDirection = queryDefaults.orderDirection;
|
||||
}
|
||||
|
||||
if (query.requireAllCategories?.length) {
|
||||
state.value.requireAllCategories = query.requireAllCategories === "true";
|
||||
} else {
|
||||
state.value.requireAllCategories = queryDefaults.requireAllCategories;
|
||||
}
|
||||
|
||||
if (query.requireAllTags?.length) {
|
||||
state.value.requireAllTags = query.requireAllTags === "true";
|
||||
} else {
|
||||
state.value.requireAllTags = queryDefaults.requireAllTags;
|
||||
}
|
||||
|
||||
if (query.requireAllTools?.length) {
|
||||
state.value.requireAllTools = query.requireAllTools === "true";
|
||||
} else {
|
||||
state.value.requireAllTools = queryDefaults.requireAllTools;
|
||||
}
|
||||
|
||||
if (query.requireAllFoods?.length) {
|
||||
state.value.requireAllFoods = query.requireAllFoods === "true";
|
||||
} else {
|
||||
state.value.requireAllFoods = queryDefaults.requireAllFoods;
|
||||
}
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
if (query.categories) {
|
||||
if (query.categories?.length) {
|
||||
promises.push(
|
||||
waitUntilAndExecute(
|
||||
() => categories.items.value.length > 0,
|
||||
@@ -395,7 +437,35 @@ export default defineComponent({
|
||||
selectedCategories.value = [];
|
||||
}
|
||||
|
||||
if (query.foods) {
|
||||
if (query.tags?.length) {
|
||||
promises.push(
|
||||
waitUntilAndExecute(
|
||||
() => tags.items.value.length > 0,
|
||||
() => {
|
||||
const result = tags.items.value.filter((item) => (query.tags as string[]).includes(item.id as string));
|
||||
selectedTags.value = result as NoUndefinedField<RecipeTag>[];
|
||||
}
|
||||
)
|
||||
);
|
||||
} else {
|
||||
selectedTags.value = [];
|
||||
}
|
||||
|
||||
if (query.tools?.length) {
|
||||
promises.push(
|
||||
waitUntilAndExecute(
|
||||
() => tools.items.value.length > 0,
|
||||
() => {
|
||||
const result = tools.items.value.filter((item) => (query.tools as string[]).includes(item.id));
|
||||
selectedTools.value = result as NoUndefinedField<RecipeTool>[];
|
||||
}
|
||||
)
|
||||
);
|
||||
} else {
|
||||
selectedTools.value = [];
|
||||
}
|
||||
|
||||
if (query.foods?.length) {
|
||||
promises.push(
|
||||
waitUntilAndExecute(
|
||||
() => {
|
||||
@@ -414,45 +484,17 @@ export default defineComponent({
|
||||
selectedFoods.value = [];
|
||||
}
|
||||
|
||||
if (query.tags) {
|
||||
promises.push(
|
||||
waitUntilAndExecute(
|
||||
() => tags.items.value.length > 0,
|
||||
() => {
|
||||
const result = tags.items.value.filter((item) => (query.tags as string[]).includes(item.id as string));
|
||||
selectedTags.value = result as NoUndefinedField<RecipeTag>[];
|
||||
}
|
||||
)
|
||||
);
|
||||
} else {
|
||||
selectedTags.value = [];
|
||||
}
|
||||
|
||||
if (query.tools) {
|
||||
promises.push(
|
||||
waitUntilAndExecute(
|
||||
() => tools.items.value.length > 0,
|
||||
() => {
|
||||
const result = tools.items.value.filter((item) => (query.tools as string[]).includes(item.id));
|
||||
selectedTools.value = result as NoUndefinedField<RecipeTool>[];
|
||||
}
|
||||
)
|
||||
);
|
||||
} else {
|
||||
selectedTools.value = [];
|
||||
}
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
// restore the user's last search query
|
||||
if (preferences.value.searchQuery && !(Object.keys(route.value.query).length > 0)) {
|
||||
if (searchQuerySession.value.recipe && !(Object.keys(route.value.query).length > 0)) {
|
||||
try {
|
||||
const query = JSON.parse(preferences.value.searchQuery);
|
||||
const query = JSON.parse(searchQuerySession.value.recipe);
|
||||
await router.replace({ query });
|
||||
} catch (error) {
|
||||
preferences.value.searchQuery = "";
|
||||
searchQuerySession.value.recipe = "";
|
||||
router.replace({ query: {} });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div @click.prevent>
|
||||
<!-- User Rating -->
|
||||
<v-hover v-slot="{ hover }">
|
||||
<v-rating
|
||||
:value="rating.ratingValue"
|
||||
:half-increments="(!hover) || (!isOwnGroup)"
|
||||
:readonly="!isOwnGroup"
|
||||
:color="hover ? attrs.hoverColor : attrs.color"
|
||||
:background-color="attrs.backgroundColor"
|
||||
v-if="isOwnGroup && (userRating || hover || !ratingsLoaded)"
|
||||
:value="userRating"
|
||||
color="secondary"
|
||||
background-color="secondary lighten-3"
|
||||
length="5"
|
||||
:dense="small ? true : undefined"
|
||||
:size="small ? 15 : undefined"
|
||||
@@ -15,12 +15,25 @@
|
||||
@input="updateRating"
|
||||
@click="updateRating"
|
||||
/>
|
||||
<!-- Group Rating -->
|
||||
<v-rating
|
||||
v-else
|
||||
:value="groupRating"
|
||||
:half-increments="true"
|
||||
:readonly="true"
|
||||
color="grey darken-1"
|
||||
background-color="secondary lighten-3"
|
||||
length="5"
|
||||
:dense="small ? true : undefined"
|
||||
:size="small ? 15 : undefined"
|
||||
hover
|
||||
/>
|
||||
</v-hover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref, useContext, watch } from "@nuxtjs/composition-api";
|
||||
import { computed, defineComponent, ref, watch } from "@nuxtjs/composition-api";
|
||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||
import { useUserSelfRatings } from "~/composables/use-users";
|
||||
export default defineComponent({
|
||||
@@ -45,61 +58,29 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
preferGroupRating: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
const { $auth } = useContext();
|
||||
const { isOwnGroup } = useLoggedInState();
|
||||
const { userRatings, setRating, ready: ratingsLoaded } = useUserSelfRatings();
|
||||
const hideGroupRating = ref(false);
|
||||
|
||||
type Rating = {
|
||||
ratingValue: number | undefined;
|
||||
hasUserRating: boolean | undefined
|
||||
};
|
||||
|
||||
// prefer user rating over group rating
|
||||
const rating = computed<Rating>(() => {
|
||||
if (!ratingsLoaded.value) {
|
||||
return { ratingValue: undefined, hasUserRating: undefined };
|
||||
}
|
||||
if (!($auth.user?.id) || props.preferGroupRating) {
|
||||
return { ratingValue: props.value, hasUserRating: false };
|
||||
}
|
||||
|
||||
const userRating = userRatings.value.find((r) => r.recipeId === props.recipeId);
|
||||
return {
|
||||
ratingValue: userRating?.rating || (hideGroupRating.value ? 0 : props.value),
|
||||
hasUserRating: !!userRating?.rating
|
||||
};
|
||||
const userRating = computed(() => {
|
||||
return userRatings.value.find((r) => r.recipeId === props.recipeId)?.rating;
|
||||
});
|
||||
|
||||
// if a user unsets their rating, we don't want to fall back to the group rating since it's out of sync
|
||||
const hideGroupRating = ref(!!userRating.value);
|
||||
watch(
|
||||
() => rating.value.hasUserRating,
|
||||
() => userRating.value,
|
||||
() => {
|
||||
if (rating.value.hasUserRating && !props.preferGroupRating) {
|
||||
if (userRating.value) {
|
||||
hideGroupRating.value = true;
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const attrs = computed(() => {
|
||||
return isOwnGroup.value ? {
|
||||
// Logged-in user
|
||||
color: rating.value.hasUserRating ? "secondary" : "grey darken-1",
|
||||
hoverColor: "secondary",
|
||||
backgroundColor: "secondary lighten-3",
|
||||
} : {
|
||||
// Anonymous user
|
||||
color: "secondary",
|
||||
hoverColor: "secondary",
|
||||
backgroundColor: "secondary lighten-3",
|
||||
};
|
||||
})
|
||||
const groupRating = computed(() => {
|
||||
return hideGroupRating.value ? 0 : props.value;
|
||||
});
|
||||
|
||||
function updateRating(val: number | null) {
|
||||
if (!isOwnGroup.value) {
|
||||
@@ -113,9 +94,10 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs,
|
||||
isOwnGroup,
|
||||
rating,
|
||||
ratingsLoaded,
|
||||
groupRating,
|
||||
userRating,
|
||||
updateRating,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
||||
// @ts-ignore missing color types
|
||||
import Color from "@sphinxxxx/color-conversion";
|
||||
import { MultiPurposeLabelSummary } from "~/lib/api/types/recipe";
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
label: {
|
||||
@@ -34,11 +37,15 @@ export default defineComponent({
|
||||
const ACCESSIBILITY_THRESHOLD = 0.179;
|
||||
|
||||
function pickTextColorBasedOnBgColorAdvanced(bgColor: string, lightColor: string, darkColor: string) {
|
||||
const color = bgColor.charAt(0) === "#" ? bgColor.substring(1, 7) : bgColor;
|
||||
const r = parseInt(color.substring(0, 2), 16); // hexToR
|
||||
const g = parseInt(color.substring(2, 4), 16); // hexToG
|
||||
const b = parseInt(color.substring(4, 6), 16); // hexToB
|
||||
const uicolors = [r / 255, g / 255, b / 255];
|
||||
try {
|
||||
const color = new Color(bgColor);
|
||||
|
||||
// if opacity is less than 0.3 always return dark color
|
||||
if (color._rgba[3] < 0.3) {
|
||||
return darkColor;
|
||||
}
|
||||
|
||||
const uicolors = [color._rgba[0] / 255, color._rgba[1] / 255, color._rgba[2] / 255];
|
||||
const c = uicolors.map((col) => {
|
||||
if (col <= 0.03928) {
|
||||
return col / 12.92;
|
||||
@@ -47,6 +54,10 @@ export default defineComponent({
|
||||
});
|
||||
const L = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
|
||||
return L > ACCESSIBILITY_THRESHOLD ? darkColor : lightColor;
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
return "black";
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
98
frontend/composables/use-group-recipe-actions.ts
Normal file
98
frontend/composables/use-group-recipe-actions.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { computed, reactive, ref } from "@nuxtjs/composition-api";
|
||||
import { useStoreActions } from "./partials/use-actions-factory";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { GroupRecipeActionOut, RecipeActionType } from "~/lib/api/types/group";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
|
||||
const groupRecipeActions = ref<GroupRecipeActionOut[] | null>(null);
|
||||
const loading = ref(false);
|
||||
|
||||
export function useGroupRecipeActionData() {
|
||||
const data = reactive({
|
||||
id: "",
|
||||
actionType: "link" as RecipeActionType,
|
||||
title: "",
|
||||
url: "",
|
||||
});
|
||||
|
||||
function reset() {
|
||||
data.id = "";
|
||||
data.actionType = "link";
|
||||
data.title = "";
|
||||
data.url = "";
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
reset,
|
||||
};
|
||||
}
|
||||
|
||||
export const useGroupRecipeActions = function (
|
||||
orderBy: string | null = "title",
|
||||
orderDirection: string | null = "asc",
|
||||
) {
|
||||
const api = useUserApi();
|
||||
|
||||
async function refreshGroupRecipeActions() {
|
||||
loading.value = true;
|
||||
const { data } = await api.groupRecipeActions.getAll(1, -1, { orderBy, orderDirection });
|
||||
groupRecipeActions.value = data?.items || null;
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
const recipeActions = computed<GroupRecipeActionOut[] | null>(() => {
|
||||
return groupRecipeActions.value;
|
||||
});
|
||||
|
||||
function parseRecipeActionUrl(url: string, recipe: Recipe): string {
|
||||
/* eslint-disable no-template-curly-in-string */
|
||||
return url
|
||||
.replace("${url}", window.location.href)
|
||||
.replace("${id}", recipe.id || "")
|
||||
.replace("${slug}", recipe.slug || "")
|
||||
/* eslint-enable no-template-curly-in-string */
|
||||
};
|
||||
|
||||
async function execute(action: GroupRecipeActionOut, recipe: Recipe): Promise<void | Response> {
|
||||
const url = parseRecipeActionUrl(action.url, recipe);
|
||||
|
||||
switch (action.actionType) {
|
||||
case "link":
|
||||
window.open(url, "_blank")?.focus();
|
||||
break;
|
||||
case "post":
|
||||
return await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
// The "text/plain" content type header is used here to skip the CORS preflight request,
|
||||
// since it may fail. This is fine, since we don't care about the response, we just want
|
||||
// the request to get sent.
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
body: JSON.stringify(recipe),
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (!groupRecipeActions.value && !loading.value) {
|
||||
refreshGroupRecipeActions();
|
||||
};
|
||||
|
||||
const actions = {
|
||||
...useStoreActions<GroupRecipeActionOut>(api.groupRecipeActions, groupRecipeActions, loading),
|
||||
flushStore() {
|
||||
groupRecipeActions.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
actions,
|
||||
execute,
|
||||
recipeActions,
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Ref, useContext } from "@nuxtjs/composition-api";
|
||||
import { useLocalStorage } from "@vueuse/core";
|
||||
import { useLocalStorage, useSessionStorage } from "@vueuse/core";
|
||||
import { TimelineEventType } from "~/lib/api/types/recipe";
|
||||
|
||||
export interface UserPrintPreferences {
|
||||
@@ -8,6 +8,10 @@ export interface UserPrintPreferences {
|
||||
showNotes: boolean;
|
||||
}
|
||||
|
||||
export interface UserSearchQuery {
|
||||
recipe: string;
|
||||
}
|
||||
|
||||
export enum ImagePosition {
|
||||
hidden = "hidden",
|
||||
left = "left",
|
||||
@@ -20,7 +24,6 @@ export interface UserRecipePreferences {
|
||||
filterNull: boolean;
|
||||
sortIcon: string;
|
||||
useMobileCards: boolean;
|
||||
searchQuery: string;
|
||||
}
|
||||
|
||||
export interface UserShoppingListPreferences {
|
||||
@@ -60,7 +63,6 @@ export function useUserSortPreferences(): Ref<UserRecipePreferences> {
|
||||
filterNull: false,
|
||||
sortIcon: $globals.icons.sortAlphabeticalAscending,
|
||||
useMobileCards: false,
|
||||
searchQuery: "",
|
||||
},
|
||||
{ mergeDefaults: true }
|
||||
// we cast to a Ref because by default it will return an optional type ref
|
||||
@@ -70,6 +72,20 @@ export function useUserSortPreferences(): Ref<UserRecipePreferences> {
|
||||
return fromStorage;
|
||||
}
|
||||
|
||||
export function useUserSearchQuerySession(): Ref<UserSearchQuery> {
|
||||
const fromStorage = useSessionStorage(
|
||||
"search-query",
|
||||
{
|
||||
recipe: "",
|
||||
},
|
||||
{ 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<UserSearchQuery>;
|
||||
|
||||
return fromStorage;
|
||||
}
|
||||
|
||||
|
||||
export function useShoppingListPreferences(): Ref<UserShoppingListPreferences> {
|
||||
const fromStorage = useLocalStorage(
|
||||
|
||||
21
frontend/lang/dateTimeFormats/gl-ES.json
Normal file
21
frontend/lang/dateTimeFormats/gl-ES.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"short": {
|
||||
"month": "short",
|
||||
"day": "numeric",
|
||||
"weekday": "long"
|
||||
},
|
||||
"medium": {
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"year": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"year": "numeric",
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"hour": "numeric",
|
||||
"minute": "numeric"
|
||||
}
|
||||
}
|
||||
21
frontend/lang/dateTimeFormats/hr-HR.json
Normal file
21
frontend/lang/dateTimeFormats/hr-HR.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"short": {
|
||||
"month": "short",
|
||||
"day": "numeric",
|
||||
"weekday": "long"
|
||||
},
|
||||
"medium": {
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"year": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"year": "numeric",
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"hour": "numeric",
|
||||
"minute": "numeric"
|
||||
}
|
||||
}
|
||||
21
frontend/lang/dateTimeFormats/is-IS.json
Normal file
21
frontend/lang/dateTimeFormats/is-IS.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"short": {
|
||||
"month": "short",
|
||||
"day": "numeric",
|
||||
"weekday": "long"
|
||||
},
|
||||
"medium": {
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"year": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"year": "numeric",
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"hour": "numeric",
|
||||
"minute": "numeric"
|
||||
}
|
||||
}
|
||||
21
frontend/lang/dateTimeFormats/lt-LT.json
Normal file
21
frontend/lang/dateTimeFormats/lt-LT.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"short": {
|
||||
"month": "short",
|
||||
"day": "numeric",
|
||||
"weekday": "long"
|
||||
},
|
||||
"medium": {
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"year": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"year": "numeric",
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"hour": "numeric",
|
||||
"minute": "numeric"
|
||||
}
|
||||
}
|
||||
21
frontend/lang/dateTimeFormats/lv-LV.json
Normal file
21
frontend/lang/dateTimeFormats/lv-LV.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"short": {
|
||||
"month": "short",
|
||||
"day": "numeric",
|
||||
"weekday": "long"
|
||||
},
|
||||
"medium": {
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"year": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"year": "numeric",
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"hour": "numeric",
|
||||
"minute": "numeric"
|
||||
}
|
||||
}
|
||||
21
frontend/lang/dateTimeFormats/sl-SI.json
Normal file
21
frontend/lang/dateTimeFormats/sl-SI.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"short": {
|
||||
"month": "short",
|
||||
"day": "numeric",
|
||||
"weekday": "long"
|
||||
},
|
||||
"medium": {
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"year": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"year": "numeric",
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"hour": "numeric",
|
||||
"minute": "numeric"
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Iets het verkeerd geloop!",
|
||||
"subscribed-events": "Ingetekende Gebeure",
|
||||
"test-message-sent": "Toets Boodskap Gestuur",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Nuwe kennisgewing",
|
||||
"event-notifiers": "Gebeurteniskennisgewers",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (oorgeslaan indien leeg)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Toets",
|
||||
"themes": "Temas",
|
||||
"thursday": "Donderdag",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Dinsdag",
|
||||
"type": "Tipe",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Laai prent",
|
||||
"screen-awake": "Hou die skerm aan",
|
||||
"remove-image": "Verwyder prent",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Gevorderde soek",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Verwyder resepte",
|
||||
"source-unit-will-be-deleted": "Bron-eenheid sal verwyder word"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Voorbeeld data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "حدث خطأ ما!",
|
||||
"subscribed-events": "الأحداث التي تم الاشتراك فيها",
|
||||
"test-message-sent": "تم إرسال رسالة تجريبية",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "إشعار جديد",
|
||||
"event-notifiers": "إشعار الحدث",
|
||||
"apprise-url-skipped-if-blank": "الرابط Apprise (يتم تجاهله إذا ما كان فارغً)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "تجربة",
|
||||
"themes": "السمات",
|
||||
"thursday": "الخميس",
|
||||
"title": "Title",
|
||||
"token": "الرمز التعريفي",
|
||||
"tuesday": "الثلاثاء",
|
||||
"type": "النوع",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload image",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Advanced Search",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Нещо се обърка!",
|
||||
"subscribed-events": "Планирани събития",
|
||||
"test-message-sent": "Тестово съобщение е изпратено",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Ново известие",
|
||||
"event-notifiers": "Известия за събитие",
|
||||
"apprise-url-skipped-if-blank": "URL за известяване (пропуска се ако е празно)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Тест",
|
||||
"themes": "Теми",
|
||||
"thursday": "четвъртък",
|
||||
"title": "Title",
|
||||
"token": "Токън",
|
||||
"tuesday": "Вторник",
|
||||
"type": "Тип",
|
||||
@@ -208,7 +210,7 @@
|
||||
"unsaved-changes": "Имате незапазени промени. Желаете ли да ги запазите преди да излезете? Натиснете Ок за запазване и Отказ за отхвърляне на промените.",
|
||||
"clipboard-copy-failure": "Линкът към рецептата е копиран в клипборда.",
|
||||
"confirm-delete-generic-items": "Сигурни ли сте, че желаете да изтриете следните елементи?",
|
||||
"organizers": "Organizers"
|
||||
"organizers": "Органайзер"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Сигурни ли сте, че искате да изтриете <b>{groupName}<b/>?",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Качване на изображение",
|
||||
"screen-awake": "Запази екрана активен",
|
||||
"remove-image": "Премахване на изображение",
|
||||
"nextStep": "Следваща стъпка"
|
||||
"nextStep": "Следваща стъпка",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Разширено търсене",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Изтрий рецепти",
|
||||
"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"
|
||||
},
|
||||
"create-alias": "Създаване на псевдоним",
|
||||
"manage-aliases": "Управление на псевдоними",
|
||||
"seed-data": "Зареждане на данни",
|
||||
@@ -1168,21 +1191,21 @@
|
||||
}
|
||||
},
|
||||
"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": "Вашата статистика на групата дава известна представа как използвате 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": "Общи групови настройки като седмично меню и настройки за поверителност.",
|
||||
@@ -1193,9 +1216,9 @@
|
||||
"notifiers": "Уведомители",
|
||||
"notifiers-description": "Настройте имейл и push известия, които се задействат при конкретни събития.",
|
||||
"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": "Лична информация",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Alguna cosa ha anat malament!",
|
||||
"subscribed-events": "Esdeveniments subscrits",
|
||||
"test-message-sent": "S'ha enviat el missatge",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Nova notificació",
|
||||
"event-notifiers": "Notificacions d'esdeveniments",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (si es deixa buit, s'ignorarà)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Prova",
|
||||
"themes": "Temes",
|
||||
"thursday": "Dijous",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Dimarts",
|
||||
"type": "Tipus",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Puja una imatge",
|
||||
"screen-awake": "Mantenir la pantalla encesa",
|
||||
"remove-image": "Esborrar la imatge",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Cerca avançada",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Dades d'exemple",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Něco se nepovedlo!",
|
||||
"subscribed-events": "Odebírané události",
|
||||
"test-message-sent": "Testovací zpráva odeslána",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Nové oznámení",
|
||||
"event-notifiers": "Notifikace událostí",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (přeskočeno pokud je prázdné)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Motivy",
|
||||
"thursday": "Čtvrtek",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Úterý",
|
||||
"type": "Typ",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Nahrát obrázek",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Další krok"
|
||||
"nextStep": "Další krok",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Pokročilé vyhledávání",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Smazat recepty",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Noget gik galt!",
|
||||
"subscribed-events": "Abonnerede begivenheder",
|
||||
"test-message-sent": "Testbesked sendt",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Ny notifikation",
|
||||
"event-notifiers": "Notifikation om begivenheder",
|
||||
"apprise-url-skipped-if-blank": "Informations link (sprunget over hvis ladet være tomt)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Afprøv",
|
||||
"themes": "Temaer",
|
||||
"thursday": "Torsdag",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Tirsdag",
|
||||
"type": "Type",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload billede",
|
||||
"screen-awake": "Hold skærmen tændt",
|
||||
"remove-image": "Fjern billede",
|
||||
"nextStep": "Næste trin"
|
||||
"nextStep": "Næste trin",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Avanceret søgning",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Slet Opskrifter",
|
||||
"source-unit-will-be-deleted": "Kildeenhed vil blive slettet"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Opret alias",
|
||||
"manage-aliases": "Administrer Aliaser",
|
||||
"seed-data": "Opret standard data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Etwas ist schief gelaufen!",
|
||||
"subscribed-events": "Abonnierte Ereignisse",
|
||||
"test-message-sent": "Testnachricht gesendet",
|
||||
"message-sent": "Daten gesendet",
|
||||
"new-notification": "Neue Benachrichtigung",
|
||||
"event-notifiers": "Ereignis-Benachrichtigungen",
|
||||
"apprise-url-skipped-if-blank": "Apprise-URL (wird übersprungen, wenn leer)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Testen",
|
||||
"themes": "Themen",
|
||||
"thursday": "Donnerstag",
|
||||
"title": "Titel",
|
||||
"token": "Token",
|
||||
"tuesday": "Dienstag",
|
||||
"type": "Typ",
|
||||
@@ -185,7 +187,7 @@
|
||||
"menu": "Menü",
|
||||
"a-name-is-required": "Ein Name wird benötigt",
|
||||
"delete-with-name": "{name} löschen",
|
||||
"confirm-delete-generic-with-name": "Bist du dir sicher, dass du \"{name}\" löschen möchtest?",
|
||||
"confirm-delete-generic-with-name": "Bist du dir sicher, dass du dies löschen möchtest?",
|
||||
"confirm-delete-own-admin-account": "Bitte beachte, dass du versuchst, dein eigenes Administrator-Konto zu löschen! Diese Aktion kann nicht rückgängig gemacht werden und wird dein Konto dauerhaft löschen!",
|
||||
"organizer": "Organisator",
|
||||
"transfer": "Übertragen",
|
||||
@@ -535,7 +537,7 @@
|
||||
"last-made-date": "Zuletzt gemacht {date}",
|
||||
"api-extras-description": "Rezepte-Extras sind ein Hauptmerkmal der Mealie API. Sie ermöglichen es dir, benutzerdefinierte JSON Key-Value-Paare zu einem Rezept zu erstellen, um Drittanbieter-Anwendungen zu steuern. Du kannst diese dazu verwenden, um Automatisierungen auszulösen oder benutzerdefinierte Nachrichten an bestimmte Geräte zu senden.",
|
||||
"message-key": "Nachrichten-Schlüssel",
|
||||
"parse": "Parse",
|
||||
"parse": "Parsen",
|
||||
"attach-images-hint": "Bilder durch Ziehen & Ablegen in den Editor hinzufügen",
|
||||
"drop-image": "Bild hier ablegen",
|
||||
"enable-ingredient-amounts-to-use-this-feature": "Aktiviere Zutatenmengen, um diese Funktion zu nutzen",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Bild hochladen",
|
||||
"screen-awake": "Bildschirm nicht abschalten",
|
||||
"remove-image": "Bild entfernen",
|
||||
"nextStep": "Nächster Schritt"
|
||||
"nextStep": "Nächster Schritt",
|
||||
"recipe-actions": "Rezept-Aktionen",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie verwendet natürliche Sprachverarbeitung (NLP), um Einheiten und Lebensmittel für deine Zutatenliste zu parsen. Diese Funktion ist experimentell und funktioniert möglicherweise nicht immer wie sie sollte. Wenn du die Parser-Ergebnisse nicht verwenden möchtest, wähle 'Abbrechen' und deine Änderungen werden nicht gespeichert.",
|
||||
"ingredient-parser": "Zutaten-Parser",
|
||||
"explanation": "Um den Zutaten-Parser zu verwenden, klicke auf den Button 'Alles parsen', um den Vorgang zu starten. Nachdem die Zutaten analysiert worden sind, kannst du überprüfen, ob die Einträge korrekt erkannt wurden. Der vom Modell errechnete Zuverlässigkeitswert wird rechts neben der Zutat angezeigt. Diese Angabe ist ein Durchschnitt der Einzelwerte und möglicherweise nicht immer ganz korrekt.",
|
||||
"alerts-explainer": "Es werden Warnungen angezeigt, wenn ein passendes Lebensmittel oder eine Einheit gefunden wurde, aber in der Datenbank nicht vorhanden ist.",
|
||||
"select-parser": "Parser auswählen",
|
||||
"natural-language-processor": "Natürliche Sprachverarbeitung (NLP)",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Alles parsen",
|
||||
"no-unit": "Keine Einheit",
|
||||
"missing-unit": "Fehlende Einheit erstellen: {unit}",
|
||||
"missing-food": "Fehlendes Lebensmittel erstellen: {food}",
|
||||
"no-food": "Kein Lebensmittel"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Erweiterte Suche",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Rezepte löschen",
|
||||
"source-unit-will-be-deleted": "Quell-Einheit wird gelöscht"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Rezept-Aktionen Daten",
|
||||
"new-recipe-action": "Neue Rezept-Aktion",
|
||||
"edit-recipe-action": "Rezept-Aktion bearbeiten",
|
||||
"action-type": "Aktionstyp"
|
||||
},
|
||||
"create-alias": "Alias erstellen",
|
||||
"manage-aliases": "Aliasse verwalten",
|
||||
"seed-data": "Musterdaten",
|
||||
@@ -1119,10 +1142,10 @@
|
||||
"info-description-cleanable-images": "Löschbare Bilder",
|
||||
"storage": {
|
||||
"title-temporary-directory": "Temporäres Verzeichnis (.temp)",
|
||||
"title-backups-directory": "Sicherungsverzeichnis (backups)",
|
||||
"title-backups-directory": "Sicherungen-Verzeichnis (backups)",
|
||||
"title-groups-directory": "Gruppen-Verzeichnis (groups)",
|
||||
"title-recipes-directory": "Rezept-Verzeichnis (recipes)",
|
||||
"title-user-directory": "Benutzerverzeichnis (user)"
|
||||
"title-user-directory": "Benutzer-Verzeichnis (user)"
|
||||
},
|
||||
"action-delete-log-files-name": "Logs löschen",
|
||||
"action-delete-log-files-description": "Löscht alle Logdateien",
|
||||
@@ -1147,9 +1170,9 @@
|
||||
"ingredients-natural-language-processor-explanation-2": "Es ist nicht perfekt, aber es erzeugt meist sehr gute Ergebnisse und ist ein guter Anfang, um Zutaten manuell den einzelnen Feldern zuzuordnen. Alternativ kannst du auch den \"Brute\" Prozessor benutzen, der eine Musterabgleich-Technik verwendet, um Zutaten zu identifizieren.",
|
||||
"nlp": "NLP",
|
||||
"brute": "Brute",
|
||||
"show-individual-confidence": "Zeige individuelle Überzeugungswerte an",
|
||||
"show-individual-confidence": "Zeige individuelle Zuverlässigkeitswerte an",
|
||||
"ingredient-text": "Zutaten-Angabe",
|
||||
"average-confident": "{0} überzeugt",
|
||||
"average-confident": "{0} zuverlässig",
|
||||
"try-an-example": "Probier ein Beispiel aus",
|
||||
"parser": "Parser",
|
||||
"background-tasks": "Hintergrundaufgaben",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Κάτι δεν πήγε καλά!",
|
||||
"subscribed-events": "Εγγεγραμμένα Γεγονότα",
|
||||
"test-message-sent": "Το δοκιμαστικό μήνυμα εστάλη",
|
||||
"message-sent": "Το μήνυμα εστάλη",
|
||||
"new-notification": "Νέα ειδοποίηση",
|
||||
"event-notifiers": "Ειδοποιητές Συμβάντος",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Δοκιμή",
|
||||
"themes": "Θέματα",
|
||||
"thursday": "Τρίτη",
|
||||
"title": "Τίτλος",
|
||||
"token": "Token",
|
||||
"tuesday": "Τρίτη",
|
||||
"type": "Τύπος",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload image",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Ενέργειες Συνταγής",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Το Mealie χρησιμοποιεί επεξεργασία φυσικής γλώσσας για να αναλύσει και να δημιουργήσει μονάδες και είδη διατροφής για τα συστατικά της συνταγής σας. Αυτή η λειτουργία είναι πειραματική και μπορεί να μην λειτουργεί πάντα όπως πρέπει. Αν προτιμάτε να μην χρησιμοποιείτε τα αναλυμένα αποτελέσματα, μπορείτε να επιλέξετε 'Ακύρωση' και οι αλλαγές σας δεν θα αποθηκευτούν.",
|
||||
"ingredient-parser": "Αναλυτής Συστατικών",
|
||||
"explanation": "Για να χρησιμοποιήσετε τον αναλυτή συστατικών, κάντε κλικ στο πλήκτρο 'Ανάλυση Ολων' για να ξεκινήσετε τη διαδικασία. Μόλις τα αναλυμένα συστατικά είναι διαθέσιμα, μπορείτε να τα επανεξετάσετε και να βεβαιωθείτε ότι έχουν αναλυθεί σωστά. Η βαθμολογία εμπιστοσύνης του μοντέλου εμφανίζεται στα δεξιά του τίτλου αντικειμένου. Αυτό το σκορ είναι ένας μέσος όρος όλων των επιμέρους βαθμολογιών και μπορεί να μην είναι πάντα εντελώς ακριβής.",
|
||||
"alerts-explainer": "Θα εμφανίζονται ειδοποιήσεις αν βρεθεί ένα αντίστοιχο φαγητό ή μονάδα αλλά δεν υπάρχει στη βάση δεδομένων.",
|
||||
"select-parser": "Επιλέξτε Αναλυτή",
|
||||
"natural-language-processor": "Επεξεργαστής Φυσικής Γλώσσας",
|
||||
"brute-parser": "Αναλυτής Ωμής Βίας",
|
||||
"parse-all": "Ανάλυση Ολων",
|
||||
"no-unit": "Καμία μονάδα",
|
||||
"missing-unit": "Δημιουργία μονάδας που λείπει: {unit}",
|
||||
"missing-food": "Δημιουργία φαγητού που λείπει: {food}",
|
||||
"no-food": "Χωρίς Φαγητό"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Σύνθετη Αναζήτηση",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "Source Unit will be deleted"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Δεδομένα Ενεργειών Συνταγής",
|
||||
"new-recipe-action": "Νέα Ενέργεια Συνταγής",
|
||||
"edit-recipe-action": "Επεξεργασία Ενέργειας Συνταγής",
|
||||
"action-type": "Τύπος Ενέργειας"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Something Went Wrong!",
|
||||
"subscribed-events": "Subscribed Events",
|
||||
"test-message-sent": "Test Message Sent",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "New Notification",
|
||||
"event-notifiers": "Event Notifiers",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Themes",
|
||||
"thursday": "Thursday",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Tuesday",
|
||||
"type": "Type",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload image",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Advanced Search",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Something Went Wrong!",
|
||||
"subscribed-events": "Subscribed Events",
|
||||
"test-message-sent": "Test Message Sent",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "New Notification",
|
||||
"event-notifiers": "Event Notifiers",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Themes",
|
||||
"thursday": "Thursday",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Tuesday",
|
||||
"type": "Type",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload image",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Advanced Search",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "¡Algo ha salido mal!",
|
||||
"subscribed-events": "Eventos suscritos",
|
||||
"test-message-sent": "Mensaje Enviado",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Nueva notificación",
|
||||
"event-notifiers": "Notificaciones de eventos",
|
||||
"apprise-url-skipped-if-blank": "URL de Apprise (omitida si está en blanco)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Prueba",
|
||||
"themes": "Temas",
|
||||
"thursday": "Jueves",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Martes",
|
||||
"type": "Tipo",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Subir imagen",
|
||||
"screen-awake": "Mantener la pantalla encendida",
|
||||
"remove-image": "Eliminar imagen",
|
||||
"nextStep": "Siguiente paso"
|
||||
"nextStep": "Siguiente paso",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Búsqueda avanzada",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Borrar Recetas",
|
||||
"source-unit-will-be-deleted": "Se eliminará la unidad de origen"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Crear un Alias",
|
||||
"manage-aliases": "Administrar Alias",
|
||||
"seed-data": "Datos de ejemplo",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Jotain meni pieleen!",
|
||||
"subscribed-events": "Tilatut tapahtumat",
|
||||
"test-message-sent": "Viesti lähetetty",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Uusi ilmoitus",
|
||||
"event-notifiers": "Tapahtumien ilmoitukset",
|
||||
"apprise-url-skipped-if-blank": "Ilmoitusverkko-osoite (voi jättää tyhjäksi)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Testi",
|
||||
"themes": "Teemat",
|
||||
"thursday": "Torstai",
|
||||
"title": "Title",
|
||||
"token": "Tunniste",
|
||||
"tuesday": "Tiistai",
|
||||
"type": "Tyyppi",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Lataa kuva",
|
||||
"screen-awake": "Pidä näyttö aina päällä",
|
||||
"remove-image": "Poista kuva",
|
||||
"nextStep": "Seuraava askel"
|
||||
"nextStep": "Seuraava askel",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Tarkennettu haku",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Poista Reseptit",
|
||||
"source-unit-will-be-deleted": "Lähdeyksikkö poistetaan"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Tietokannan pohjadata",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"download-log": "Télécharger les logs",
|
||||
"download-recipe-json": "Dernier JSON récupéré",
|
||||
"github": "GitHub",
|
||||
"log-lines": "Lignes de log",
|
||||
"log-lines": "Lignes du journal",
|
||||
"not-demo": "Non démo",
|
||||
"portfolio": "Portfolio",
|
||||
"production": "Production",
|
||||
@@ -50,7 +50,7 @@
|
||||
"category": "Catégorie"
|
||||
},
|
||||
"events": {
|
||||
"apprise-url": "URL apprise",
|
||||
"apprise-url": "URL Apprise",
|
||||
"database": "Base de données",
|
||||
"delete-event": "Supprimer l'événement",
|
||||
"event-delete-confirmation": "Voulez-vous vraiment supprimer cet évènement ?",
|
||||
@@ -64,15 +64,16 @@
|
||||
"something-went-wrong": "Une erreur s'est produite!",
|
||||
"subscribed-events": "Évènements suivis",
|
||||
"test-message-sent": "Message de test envoyé",
|
||||
"message-sent": "Message envoyé",
|
||||
"new-notification": "Nouvelle notification",
|
||||
"event-notifiers": "Notifications d'événements",
|
||||
"apprise-url-skipped-if-blank": "URL Apprise (ignoré si vide)",
|
||||
"enable-notifier": "Activer la notification",
|
||||
"what-events": "À quels événements cette notification doit-elle s'abonner ?",
|
||||
"user-events": "Evénements utilisateur",
|
||||
"mealplan-events": "Évènements du menu",
|
||||
"user-events": "Événements de l'utilisateur",
|
||||
"mealplan-events": "Événements du menu",
|
||||
"when-a-user-in-your-group-creates-a-new-mealplan": "Lorsqu'un utilisateur de votre groupe crée un nouveau menu",
|
||||
"shopping-list-events": "Événements de la liste de courses",
|
||||
"shopping-list-events": "Événements de la liste d'épicerie",
|
||||
"cookbook-events": "Événements du livre de recettes",
|
||||
"tag-events": "Événements des mots-clés",
|
||||
"category-events": "Événements de catégories",
|
||||
@@ -147,8 +148,8 @@
|
||||
"show-all": "Tout afficher",
|
||||
"shuffle": "Mélanger",
|
||||
"sort": "Trier",
|
||||
"sort-ascending": "Tri croissant",
|
||||
"sort-descending": "Tri décroissant",
|
||||
"sort-ascending": "Trier par ordre croissant",
|
||||
"sort-descending": "Trier par ordre décroissant",
|
||||
"sort-alphabetically": "Alphabétique",
|
||||
"status": "Statut",
|
||||
"subject": "Sujet",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Tester",
|
||||
"themes": "Thèmes",
|
||||
"thursday": "Jeudi",
|
||||
"title": "Titre",
|
||||
"token": "Jeton",
|
||||
"tuesday": "Mardi",
|
||||
"type": "Type",
|
||||
@@ -203,12 +205,12 @@
|
||||
"selected-count": "Sélectionné : {count}",
|
||||
"export-all": "Exporter tout",
|
||||
"refresh": "Actualiser",
|
||||
"upload-file": "Transférer un fichier",
|
||||
"upload-file": "Téléverser un fichier",
|
||||
"created-on-date": "Créé le {0}",
|
||||
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous les enregistrer ? Ok pour enregistrer, annuler pour ignorer les modifications.",
|
||||
"clipboard-copy-failure": "Échec de la copie vers le presse-papiers.",
|
||||
"confirm-delete-generic-items": "Êtes-vous sûr de vouloir supprimer les éléments suivants ?",
|
||||
"organizers": "Organizers"
|
||||
"organizers": "Classification"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Êtes-vous certain de vouloir supprimer <b>{groupName}<b/>?",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Ajouter une image",
|
||||
"screen-awake": "Garder l’écran allumé",
|
||||
"remove-image": "Supprimer l’image",
|
||||
"nextStep": "Étape suivante"
|
||||
"nextStep": "Étape suivante",
|
||||
"recipe-actions": "Actions de recette",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie utilise le traitement du langage naturel pour analyser et créer des unités et des aliments pour vos ingrédients de recettes. Cette fonctionnalité est expérimentale et peut ne pas toujours fonctionner comme prévu. Si vous préférez ne pas utiliser les résultats analysés, vous pouvez sélectionner « Annuler » et vos modifications ne seront pas enregistrées.",
|
||||
"ingredient-parser": "Analyseur d'ingrédients",
|
||||
"explanation": "Pour utiliser l'analyseur d'ingrédients, cliquez sur le bouton « Tout analyser » pour démarrer le processus. Une fois les ingrédients disponibles, vous pouvez vérifier qu'ils ont été analysés correctement. Le score de confiance du modèle est affiché à droite du titre de l'article. Ce score est une moyenne de tous les scores individuels et peut ne pas toujours être complètement exact.",
|
||||
"alerts-explainer": "Les alertes seront affichées si un produit ou unité correspondant est trouvé mais n'existe pas dans la base de données.",
|
||||
"select-parser": "Sélectionner l'analyseur",
|
||||
"natural-language-processor": "Traitement du Langage Naturel",
|
||||
"brute-parser": "Analyseur brut",
|
||||
"parse-all": "Tout analyser",
|
||||
"no-unit": "Pas d'unité",
|
||||
"missing-unit": "Créer une unité manquante : {unit}",
|
||||
"missing-food": "Créer un aliment manquant : {food}",
|
||||
"no-food": "Aucun aliment"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Recherche avancée",
|
||||
@@ -723,8 +740,8 @@
|
||||
"volumes-are-configured-correctly": "Les volumes sont configurés correctement.",
|
||||
"status-unknown-try-running-a-validation": "Statut inconnu. Essayez de lancer une validation.",
|
||||
"validate": "Valider",
|
||||
"email-configuration-status": "État de la configuration e-mail",
|
||||
"email-configured": "E-mail configuré",
|
||||
"email-configuration-status": "État de la configuration courriel",
|
||||
"email-configured": "Courriel configuré",
|
||||
"email-test-results": "Résultats des tests e-mail",
|
||||
"ready": "Prêt",
|
||||
"not-ready": "Pas prêt - Vérifier les variables d'environnement",
|
||||
@@ -897,9 +914,9 @@
|
||||
"enable-advanced-content": "Activer le contenu avancé",
|
||||
"enable-advanced-content-description": "Active les fonctionnalités avancées comme la mise à l'échelle des recettes, les clés API, les Webhooks, et la gestion des données. Ne vous inquiétez pas, vous pouvez toujours modifier cela plus tard",
|
||||
"favorite-recipes": "Recettes préférées",
|
||||
"email-or-username": "E-mail ou nom d'utilisateur",
|
||||
"email-or-username": "Courriel ou nom d'utilisateur",
|
||||
"remember-me": "Se souvenir de moi",
|
||||
"please-enter-your-email-and-password": "Veuillez saisir votre e-mail et votre mot de passe",
|
||||
"please-enter-your-email-and-password": "Veuillez entrer votre adresse courriel et votre mot de passe",
|
||||
"invalid-credentials": "Identifiants invalides",
|
||||
"account-locked-please-try-again-later": "Compte verrouillé. Veuillez réessayer plus tard",
|
||||
"user-favorites": "Favoris utilisateur",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Supprimer les recettes",
|
||||
"source-unit-will-be-deleted": "L'unité source sera supprimée"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Données des actions de recette",
|
||||
"new-recipe-action": "Nouvelle action de recette",
|
||||
"edit-recipe-action": "Modifier l'action de recette",
|
||||
"action-type": "Type d'action"
|
||||
},
|
||||
"create-alias": "Créer un alias",
|
||||
"manage-aliases": "Gérer les alias",
|
||||
"seed-data": "Initialiser les données",
|
||||
@@ -1042,7 +1065,7 @@
|
||||
"validation": {
|
||||
"group-name-is-taken": "Le nom du groupe est déjà pris",
|
||||
"username-is-taken": "Nom d’utilisateur déjà utilisé",
|
||||
"email-is-taken": "Cet e-mail est déjà pris",
|
||||
"email-is-taken": "Cette adresse courriel est déjà prise",
|
||||
"this-field-is-required": "Ce champ est obligatoire"
|
||||
},
|
||||
"export": {
|
||||
@@ -1168,21 +1191,21 @@
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
"welcome-user": "👋 Bienvenue, {0}!",
|
||||
"description": "Gérez votre profil, les recettes et les paramètres de groupe.",
|
||||
"get-invite-link": "Obtenir un lien d'invitation",
|
||||
"get-public-link": "Voir le lien public",
|
||||
"account-summary": "Aperçu du compte",
|
||||
"account-summary-description": "Here's a summary of your group's information.",
|
||||
"account-summary-description": "Voici un résumé des informations de votre groupe.",
|
||||
"group-statistics": "Statistiques du groupe",
|
||||
"group-statistics-description": "Les statistiques de votre groupe vous donnent un aperçu de la façon dont vous utilisez Mealie.",
|
||||
"storage-capacity": "Capacité de stockage",
|
||||
"storage-capacity-description": "Votre capacité de stockage est un calcul des images et des ressources que vous avez téléchargées.",
|
||||
"personal": "Personnel",
|
||||
"personal-description": "These are settings that are personal to you. Changes here won't affect other users.",
|
||||
"personal-description": "Il s'agit de paramètres qui vous sont personnels. Les modifications ici n'affecteront pas les autres utilisateurs.",
|
||||
"user-settings": "Paramètres utilisateur",
|
||||
"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": "Gérez vos préférences, changez votre mot de passe et mettez à jour votre adresse courriel.",
|
||||
"api-tokens-description": "Gérez vos jetons d'API pour accéder à partir d'applications externes.",
|
||||
"group-description": "Ces éléments sont partagés au sein de votre groupe. Un changement impactera l'ensemble du groupe !",
|
||||
"group-settings": "Paramètres de groupe",
|
||||
"group-settings-description": "Gérez vos paramètres de groupe habituels tels que le menu de la semaine et les paramètres de confidentialité.",
|
||||
@@ -1191,13 +1214,13 @@
|
||||
"members-description": "Voyez qui est dans votre groupe et gérez leurs autorisations.",
|
||||
"webhooks-description": "Configurez les webhooks qui se déclenchent les jours où il y a un plan au menu.",
|
||||
"notifiers": "Notifications",
|
||||
"notifiers-description": "Configurer les e-mails et les notifications push qui se déclenchent sur des événements spécifiques.",
|
||||
"notifiers-description": "Configurer les courriels et les notifications push qui se déclenchent sur des événements spécifiques.",
|
||||
"manage-data": "Gérer les données",
|
||||
"manage-data-description": "Manage your Mealie data; Foods, Units, Categories, Tags and more.",
|
||||
"manage-data-description": "Gérez vos données Mealie, Aliments, Unités, Catégories, Tags et plus.",
|
||||
"data-migrations": "Migration des données",
|
||||
"data-migrations-description": "Migrate your existing data from other applications like Nextcloud Recipes and Chowdown.",
|
||||
"email-sent": "E-mail envoyé",
|
||||
"error-sending-email": "Erreur lors de l'envoi de l'email",
|
||||
"data-migrations-description": "Migrez vos données existantes à partir d'autres applications comme Nextcloud Cookbook et Chowdown.",
|
||||
"email-sent": "Courriel envoyé",
|
||||
"error-sending-email": "Erreur lors de l'envoi du courriel",
|
||||
"personal-information": "Informations personnelles",
|
||||
"preferences": "Préférences",
|
||||
"show-advanced-description": "Afficher les fonctionnalités avancées (clés API, Webhooks, et gestion des données)",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Une erreur s’est produite !",
|
||||
"subscribed-events": "Évènements suivis",
|
||||
"test-message-sent": "Message de test envoyé",
|
||||
"message-sent": "Message envoyé",
|
||||
"new-notification": "Nouvelle notification",
|
||||
"event-notifiers": "Notifications d'événements",
|
||||
"apprise-url-skipped-if-blank": "URL Apprise (ignoré si vide)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Tester",
|
||||
"themes": "Thèmes",
|
||||
"thursday": "Jeudi",
|
||||
"title": "Titre",
|
||||
"token": "Jeton",
|
||||
"tuesday": "Mardi",
|
||||
"type": "Type",
|
||||
@@ -208,7 +210,7 @@
|
||||
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer avant de partir ? OK pour enregistrer, Annuler pour ignorer les modifications.",
|
||||
"clipboard-copy-failure": "Échec de la copie dans le presse-papiers.",
|
||||
"confirm-delete-generic-items": "Êtes-vous sûr de vouloir supprimer les éléments suivants ?",
|
||||
"organizers": "Organizers"
|
||||
"organizers": "Classification"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Voulez-vous vraiment supprimer <b>{groupName}<b/> ?",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Envoyer une image",
|
||||
"screen-awake": "Garder l’écran allumé",
|
||||
"remove-image": "Supprimer l’image",
|
||||
"nextStep": "Étape suivante"
|
||||
"nextStep": "Étape suivante",
|
||||
"recipe-actions": "Actions de recette",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie utilise le traitement du langage naturel pour analyser et créer des unités et des aliments pour vos ingrédients de recettes. Cette fonctionnalité est expérimentale et peut ne pas toujours fonctionner comme prévu. Si vous préférez ne pas utiliser les résultats analysés, vous pouvez sélectionner « Annuler » et vos modifications ne seront pas enregistrées.",
|
||||
"ingredient-parser": "Analyseur d'ingrédients",
|
||||
"explanation": "Pour utiliser l'analyseur d'ingrédients, cliquez sur le bouton « Tout analyser » pour démarrer le processus. Une fois les ingrédients disponibles, vous pouvez vérifier qu'ils ont été analysés correctement. Le score de confiance du modèle est affiché à droite du titre de l'article. Ce score est une moyenne de tous les scores individuels et peut ne pas toujours être complètement exact.",
|
||||
"alerts-explainer": "Les alertes seront affichées si un produit ou unité correspondant est trouvé mais n'existe pas dans la base de données.",
|
||||
"select-parser": "Sélectionner l'analyseur",
|
||||
"natural-language-processor": "Traitement du Langage Naturel",
|
||||
"brute-parser": "Analyseur brut",
|
||||
"parse-all": "Tout analyser",
|
||||
"no-unit": "Pas d'unité",
|
||||
"missing-unit": "Créer une unité manquante : {unit}",
|
||||
"missing-food": "Créer un aliment manquant : {food}",
|
||||
"no-food": "Aucun aliment"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Recherche avancée",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Supprimer les recettes",
|
||||
"source-unit-will-be-deleted": "L'unité source sera supprimée"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Données des actions de recette",
|
||||
"new-recipe-action": "Nouvelle action de recette",
|
||||
"edit-recipe-action": "Modifier l'action de recette",
|
||||
"action-type": "Type d'action"
|
||||
},
|
||||
"create-alias": "Créer un alias",
|
||||
"manage-aliases": "Gérer les alias",
|
||||
"seed-data": "Initialiser les données",
|
||||
@@ -1168,21 +1191,21 @@
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
"welcome-user": "👋 Bienvenue, {0} !",
|
||||
"description": "Gérez votre profil, les recettes et les paramètres de groupe.",
|
||||
"get-invite-link": "Obtenir un lien d'invitation",
|
||||
"get-public-link": "Voir le lien public",
|
||||
"account-summary": "Aperçu du compte",
|
||||
"account-summary-description": "Here's a summary of your group's information.",
|
||||
"account-summary-description": "Voici un résumé des informations de votre groupe.",
|
||||
"group-statistics": "Statistiques du groupe",
|
||||
"group-statistics-description": "Les statistiques de votre groupe vous donnent un aperçu de la façon dont vous utilisez Mealie.",
|
||||
"storage-capacity": "Capacité de stockage",
|
||||
"storage-capacity-description": "Votre capacité de stockage est un calcul des images et des ressources que vous avez téléchargées.",
|
||||
"personal": "Personnel",
|
||||
"personal-description": "These are settings that are personal to you. Changes here won't affect other users.",
|
||||
"personal-description": "Ici se trouvent des paramètres qui vous sont propres personnellement. Toute modification n'affectera pas les autres utilisateurs.",
|
||||
"user-settings": "Paramètres utilisateur",
|
||||
"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": "Gérez vos préférences, changez votre mot de passe et mettez à jour votre adresse e-mail.",
|
||||
"api-tokens-description": "Gérez vos jetons API pour un accès à partir d'applications externes.",
|
||||
"group-description": "Ces éléments sont partagés au sein de votre groupe. Un changement impactera l'ensemble du groupe !",
|
||||
"group-settings": "Paramètres de groupe",
|
||||
"group-settings-description": "Gérez vos paramètres de groupe habituels tels que le menu de la semaine et les paramètres de confidentialité.",
|
||||
@@ -1193,9 +1216,9 @@
|
||||
"notifiers": "Notifications",
|
||||
"notifiers-description": "Configurer les e-mails et les notifications push qui se déclenchent sur des événements spécifiques.",
|
||||
"manage-data": "Gérer les données",
|
||||
"manage-data-description": "Manage your Mealie data; Foods, Units, Categories, Tags and more.",
|
||||
"manage-data-description": "Gérez vos données Mealie, Aliments, Unités, Catégories, Tags et plus.",
|
||||
"data-migrations": "Migration des données",
|
||||
"data-migrations-description": "Migrate your existing data from other applications like Nextcloud Recipes and Chowdown.",
|
||||
"data-migrations-description": "Migrez vos données existantes à partir d'autres applications comme Nextcloud Cookbook et Chowdown.",
|
||||
"email-sent": "E-mail envoyé",
|
||||
"error-sending-email": "Erreur lors de l'envoi de l'email",
|
||||
"personal-information": "Informations personnelles",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Something Went Wrong!",
|
||||
"subscribed-events": "Subscribed Events",
|
||||
"test-message-sent": "Test Message Sent",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "New Notification",
|
||||
"event-notifiers": "Event Notifiers",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Probar",
|
||||
"themes": "Temas",
|
||||
"thursday": "Xoves",
|
||||
"title": "Title",
|
||||
"token": "Identificador",
|
||||
"tuesday": "Martes",
|
||||
"type": "Tipo",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload image",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Advanced Search",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "משהו השתבש!",
|
||||
"subscribed-events": "אירועים שנרשמת אליהם",
|
||||
"test-message-sent": "הודעת בדיקה נשלחה",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "התראה חדשה",
|
||||
"event-notifiers": "אירועי נוטיפיקציות",
|
||||
"apprise-url-skipped-if-blank": "כתובת Apprise (דלג אם ריק)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "ניסיון",
|
||||
"themes": "ערכות נושא",
|
||||
"thursday": "חמישי",
|
||||
"title": "Title",
|
||||
"token": "טוקן",
|
||||
"tuesday": "שלישי",
|
||||
"type": "סוג",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "העלה תמונה",
|
||||
"screen-awake": "השאר את המסך פעיל",
|
||||
"remove-image": "האם למחוק את התמונה?",
|
||||
"nextStep": "השלב הבא"
|
||||
"nextStep": "השלב הבא",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "חיפוש מתקדם",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "מחיקת מתכונים",
|
||||
"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"
|
||||
},
|
||||
"create-alias": "יצירת שם נרדף",
|
||||
"manage-aliases": "נהל שמות נרדפים",
|
||||
"seed-data": "אכלס נתונים",
|
||||
|
||||
@@ -64,6 +64,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",
|
||||
"new-notification": "Nova Obavijest",
|
||||
"event-notifiers": "Obavještavatelji Događaja",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (preskočeno ako je prazno)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Teme",
|
||||
"thursday": "Četvrtak",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Utorak",
|
||||
"type": "Tip",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Učitavanje Slike",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Napredno Pretraživanje",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Obriši Recepte",
|
||||
"source-unit-will-be-deleted": "Izvorna jedinica će biti izbrisana"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Unesi Podatke",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Hiba történt!",
|
||||
"subscribed-events": "Feliratkozott események",
|
||||
"test-message-sent": "Teszt üzenet elküldve",
|
||||
"message-sent": "Üzenet elküldve",
|
||||
"new-notification": "Új értesítés",
|
||||
"event-notifiers": "Esemény értesítők",
|
||||
"apprise-url-skipped-if-blank": "Értesítendő URL (kihagy, ha üres)",
|
||||
@@ -85,7 +86,7 @@
|
||||
"clear": "Törlés",
|
||||
"close": "Bezár",
|
||||
"confirm": "Megerősítés",
|
||||
"confirm-how-does-everything-look": "How does everything look?",
|
||||
"confirm-how-does-everything-look": "Hogy néz ki?",
|
||||
"confirm-delete-generic": "Biztosan törölni szeretnéd ezt?",
|
||||
"copied_message": "Másolva!",
|
||||
"create": "Létrehozás",
|
||||
@@ -144,11 +145,11 @@
|
||||
"save": "Mentés",
|
||||
"settings": "Beállítások",
|
||||
"share": "Megosztás",
|
||||
"show-all": "Show All",
|
||||
"show-all": "Mutasd az összeset",
|
||||
"shuffle": "Véletlenszerű",
|
||||
"sort": "Rendezés",
|
||||
"sort-ascending": "Sort Ascending",
|
||||
"sort-descending": "Sort Descending",
|
||||
"sort-ascending": "Rendezés növekvő sorrendben",
|
||||
"sort-descending": "Rendezés csökkenő sorrendben",
|
||||
"sort-alphabetically": "Betűrendben",
|
||||
"status": "Állapot",
|
||||
"subject": "Tárgy",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Teszt",
|
||||
"themes": "Témák",
|
||||
"thursday": "Csütörtök",
|
||||
"title": "Cím",
|
||||
"token": "Token",
|
||||
"tuesday": "Kedd",
|
||||
"type": "Típus",
|
||||
@@ -245,7 +247,7 @@
|
||||
"private-group": "Privát csoport",
|
||||
"private-group-description": "Ha a csoportot privátra állítja, az összes nyilvános nézeti beállítás alapértelmezettre lesz állítva. Ez felülírja az egyes receptek nyilvános nézet beállításait.",
|
||||
"enable-public-access": "Nyilvános hozzáférés engedélyezése",
|
||||
"enable-public-access-description": "Make group recipes public by default, and allow visitors to view recipes without logging-in",
|
||||
"enable-public-access-description": "Legyenek a csoport receptek alapértelmezetten publikusak és engedélyezze a látogatóknak a megtekintést belépés nélkül",
|
||||
"allow-users-outside-of-your-group-to-see-your-recipes": "Engedélyezze a csoporton kívüli felhasználók számára a receptek megtekintését",
|
||||
"allow-users-outside-of-your-group-to-see-your-recipes-description": "Ha engedélyezve van, nyilvános megosztási hivatkozással megoszthat adott recepteket a felhasználó felhatalmazása nélkül. Ha le van tiltva, csak olyan felhasználókkal oszthat meg recepteket, akik a csoportjába tartoznak, vagy egy előre generált privát linkkel",
|
||||
"show-nutrition-information": "Táplálkozási információk megjelenítése",
|
||||
@@ -359,11 +361,11 @@
|
||||
},
|
||||
"recipe-data-migrations": "Receptadatok migrációja",
|
||||
"recipe-data-migrations-explanation": "A receptek átemelhetők más támogatott alkalmazásból Mealie-be. Ez egy remek módja a Mealie használatának elkezdésére.",
|
||||
"coming-from-another-application-or-an-even-older-version-of-mealie": "Coming from another application or an even older version of Mealie? Check out migrations and see if your data can be imported.",
|
||||
"coming-from-another-application-or-an-even-older-version-of-mealie": "A Mealie egy másik verzióját használtad vagy valami teljesen más applikációt? Nézd meg az adat importálási lehetőségeinket.",
|
||||
"choose-migration-type": "Válassza ki a migrációs típusát",
|
||||
"tag-all-recipes": "Az összes recept címkézése a {tag-name} címkével",
|
||||
"nextcloud-text": "A Nextcloud-receptek importálhatók a Nextcloudban tárolt adatokat tartalmazó zip-fájlból. Tekintse meg az alábbi példamappaszerkezetet, hogy receptjei biztosan importálhatók legyenek.",
|
||||
"chowdown-text": "Mealie natively supports the chowdown repository format. Download the code repository as a .zip file and upload it below.",
|
||||
"chowdown-text": "Mealie natívan támogatja a chowdown repository formátumot. Töltse le a kódtárat .zip fájlként, és töltse fel alább.",
|
||||
"recipe-1": "Recept 1",
|
||||
"recipe-2": "Recept 2",
|
||||
"paprika-text": "Mealie képes recepteket importálni a Paprika alkalmazásból. Exportálja a receptjeit a Paprikából, nevezze át az export kiterjesztést .zip-re, és töltse fel alább.",
|
||||
@@ -373,8 +375,8 @@
|
||||
"description-long": "Mealie képes recepteket importálni a Plan to Eat alkalmazásból."
|
||||
},
|
||||
"myrecipebox": {
|
||||
"title": "My Recipe Box",
|
||||
"description-long": "Mealie can import recipes from My Recipe Box. Export your recipes in CSV format, then upload the .csv file below."
|
||||
"title": "Az én receptes dobozom",
|
||||
"description-long": "A Mealie képest recepteket importálni az Én Receptes Dobozomból. Exportáld a receptjeidet CSV formátúmba, aztán töltsd fel a .csv fájlt lentebb."
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
@@ -509,8 +511,8 @@
|
||||
"cook-mode": "Főzési mód",
|
||||
"link-ingredients": "Hozzávalók összekapcsolása",
|
||||
"merge-above": "Összevonás a fentivel",
|
||||
"move-to-bottom": "Move To Bottom",
|
||||
"move-to-top": "Move To Top",
|
||||
"move-to-bottom": "Ugrás az aljára",
|
||||
"move-to-top": "Ugrás a tetejére",
|
||||
"reset-scale": "Skála alaphelyzetbe állítása",
|
||||
"decrease-scale-label": "Skála csökkentése 1-gyel",
|
||||
"increase-scale-label": "Skála növelése 1-gyel",
|
||||
@@ -526,7 +528,7 @@
|
||||
"edit-timeline-event": "Idővonal-esemény szerkesztése",
|
||||
"timeline": "Idővonal",
|
||||
"timeline-is-empty": "Az idővonalon még semmi sincs. Próbálja meg elkészíteni ezt a receptet!",
|
||||
"timeline-no-events-found-try-adjusting-filters": "No events found. Try adjusting your search filters.",
|
||||
"timeline-no-events-found-try-adjusting-filters": "Nem találtunk eseményeket. Próbáld meg átállítani a keresési szűrőket.",
|
||||
"group-global-timeline": "{groupName} Globális idővonal",
|
||||
"open-timeline": "Idővonal megnyitása",
|
||||
"made-this": "Elkészítettem ezt",
|
||||
@@ -547,7 +549,7 @@
|
||||
"looking-for-migrations": "Migrációt keres?",
|
||||
"import-with-url": "Importálás URL-címről",
|
||||
"create-recipe": "Recept létrehozása",
|
||||
"create-recipe-description": "Create a new recipe from scratch.",
|
||||
"create-recipe-description": "Adj hozzá egy új receptet a nulláról kezdve.",
|
||||
"create-recipes": "Receptek létrehozása",
|
||||
"import-with-zip": "Importálás .zip formátummal",
|
||||
"create-recipe-from-an-image": "Recept létrehozása fotóról",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Kép feltöltése",
|
||||
"screen-awake": "Képernyő ébren tartása",
|
||||
"remove-image": "Kép etávolítása",
|
||||
"nextStep": "Következő lépés"
|
||||
"nextStep": "Következő lépés",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "A Mealie természetes nyelvi feldolgozást használ a recept összetevőinek elemzésére, az egységek és az élelmiszerelemek létrehozására. Ez a funkció kísérleti jellegű, és előfordulhat, hogy nem mindig működik az elvárt módon. Ha nem szeretné használni az elemzett eredményeket, válassza a 'Mégse' lehetőséget, és a módosítások nem kerülnek mentésre.",
|
||||
"ingredient-parser": "Hozzávaló elemző",
|
||||
"explanation": "A hozzávalók elemzőjének használatához kattintson a 'Parse All' gombra a folyamat elindításához. Amint a feldolgozott hozzávalók elérhetővé válnak, áttekintheti az elemeket, és ellenőrizheti, hogy azok helyesen lettek-e elemezve. A modell megbízhatósági pontszáma az elem címének jobb oldalán jelenik meg. Ez a pontszám az összes egyéni pontszám átlaga, és nem biztos, hogy mindig teljesen pontos.",
|
||||
"alerts-explainer": "Figyelmeztetések jelennek meg, ha talál egy megfelelő élelmiszert vagy egységet, de az nem létezik az adatbázisban.",
|
||||
"select-parser": "Válasszon elemzőt",
|
||||
"natural-language-processor": "Természetes nyelvi feldolgozó",
|
||||
"brute-parser": "Brute elemző",
|
||||
"parse-all": "Összes elemzése",
|
||||
"no-unit": "Mértékegység nélkül",
|
||||
"missing-unit": "Hiányzó mértékegység létrehozása: {unit}",
|
||||
"missing-food": "Hiányzó élelmiszer létrehozása: {food}",
|
||||
"no-food": "Élelmiszer nélküli"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Részletes keresés",
|
||||
@@ -619,7 +636,7 @@
|
||||
"import-summary": "Import összefoglaló",
|
||||
"partial-backup": "Részleges biztonsági mentés",
|
||||
"unable-to-delete-backup": "Nem lehetett létrehozni a biztonsági mentést.",
|
||||
"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": "A biztonsági mentések az oldal adatbázisának és adatkönyvtárának teljes pillanatfelvételei. Ez az összes adatot tartalmazza, és nem lehet beállítani, hogy az adatok részhalmazait kizárja. Ezt úgy is elképzelheti, mint a Mealie egy adott időpontban készült pillanatfelvételét. Ezek adatbázis-független módon szolgálnak az adatok exportálására és importálására, vagy a webhely külső helyre történő mentésére.",
|
||||
"backup-restore": "Biztonsági Mentés/Visszaállítás",
|
||||
"back-restore-description": "A biztonsági mentés visszaállítása felülírja az adatbázisban és az adatkönyvtárban lévő összes aktuális adatot, és a biztonsági mentés tartalmával helyettesíti azokat. {cannot-be-undone} Ha a visszaállítás sikeres, akkor a rendszer kilépteti Önt.",
|
||||
"cannot-be-undone": "Ezt a műveletet visszavonható - óvatosan használja.",
|
||||
@@ -745,9 +762,9 @@
|
||||
"ldap-ready-success-text": "A szükséges LDAP-változók mind beállítva.",
|
||||
"build": "Build",
|
||||
"recipe-scraper-version": "Receptkinyerő verziója",
|
||||
"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."
|
||||
"oidc-ready": "OIDC készen áll",
|
||||
"oidc-ready-error-text": "Nem minden OIDC érték van beállítva. Ezt figyelmen kívül lehet hagyni ha nem használ OIDC hitelesítést.",
|
||||
"oidc-ready-success-text": "A szükséges OIDC változók mind beállítva."
|
||||
},
|
||||
"shopping-list": {
|
||||
"all-lists": "Összes lista",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Receptek törlése",
|
||||
"source-unit-will-be-deleted": "A forrás mennyiségi egység törlésre kerül"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Alias készítése",
|
||||
"manage-aliases": "Alias kezelése",
|
||||
"seed-data": "Kezdőérték adat",
|
||||
@@ -1159,12 +1182,12 @@
|
||||
"setup": {
|
||||
"first-time-setup": "Első beállítás",
|
||||
"welcome-to-mealie-get-started": "Üdvözöl a Mealie! Vágjunk bele",
|
||||
"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",
|
||||
"already-set-up-bring-to-homepage": "Már kész is vagyok, vigyél az új kezdőoldalamra",
|
||||
"common-settings-for-new-sites": "Itt van egy pár alap beállítás az új oldaladhoz",
|
||||
"setup-complete": "Beállítás kész!",
|
||||
"here-are-a-few-things-to-help-you-get-started": "Here are a few things to help you get started with Mealie",
|
||||
"here-are-a-few-things-to-help-you-get-started": "Itt van egy pár dolog ami segíthet a kezdésben a Mealie-vel",
|
||||
"restore-from-v1-backup": "Van egy korábbi Mealie v1 biztonsági másolatod? Itt visszaállíthatod.",
|
||||
"manage-profile-or-get-invite-link": "Manage your own profile, or grab an invite link to share with others."
|
||||
"manage-profile-or-get-invite-link": "Alakítsa a profilját vagy szerezze meg a meghívó link-jét hogy megoszthassa másokkal."
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
@@ -1179,7 +1202,7 @@
|
||||
"storage-capacity": "Tárhely mérete",
|
||||
"storage-capacity-description": "A tárhely kapacitása az Ön által feltöltött képek és eszközök számításából adódik.",
|
||||
"personal": "Személyes",
|
||||
"personal-description": "These are settings that are personal to you. Changes here won't affect other users.",
|
||||
"personal-description": "Ezek a beállítások az Ön személyes beállításai. Az itt végzett változtatások nincsenek hatással más felhasználókra.",
|
||||
"user-settings": "Felhasználói beállítások",
|
||||
"user-settings-description": "Itt kezelheti beállításait, módosíthatja jelszavát és frissítheti e-mail címét.",
|
||||
"api-tokens-description": "API kulcsok kezelése külső alkalmazásokból.",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Something Went Wrong!",
|
||||
"subscribed-events": "Subscribed Events",
|
||||
"test-message-sent": "Test Message Sent",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "New Notification",
|
||||
"event-notifiers": "Event Notifiers",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Themes",
|
||||
"thursday": "Thursday",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Tuesday",
|
||||
"type": "Type",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload image",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Advanced Search",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Si è verificato un errore!",
|
||||
"subscribed-events": "Eventi Sottoscritti",
|
||||
"test-message-sent": "Messaggio di prova inviato",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Nuova Notifica",
|
||||
"event-notifiers": "Notifiche Evento",
|
||||
"apprise-url-skipped-if-blank": "Url di Apprise (ignorato se vuoto)",
|
||||
@@ -85,7 +86,7 @@
|
||||
"clear": "Resetta",
|
||||
"close": "Chiudi",
|
||||
"confirm": "Conferma",
|
||||
"confirm-how-does-everything-look": "How does everything look?",
|
||||
"confirm-how-does-everything-look": "Come ti sembra tutto questo?",
|
||||
"confirm-delete-generic": "Sei sicuro di volere eliminare?",
|
||||
"copied_message": "Copiato!",
|
||||
"create": "Crea",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Temi",
|
||||
"thursday": "Giovedì",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Martedì",
|
||||
"type": "Tipo",
|
||||
@@ -174,7 +176,7 @@
|
||||
"units": "Unità",
|
||||
"back": "Indietro",
|
||||
"next": "Avanti",
|
||||
"start": "Start",
|
||||
"start": "Inizia",
|
||||
"toggle-view": "Cambia Vista",
|
||||
"date": "Data",
|
||||
"id": "Id",
|
||||
@@ -208,7 +210,7 @@
|
||||
"unsaved-changes": "Sono state apportate modifiche non salvate. Salvare prima di uscire? Premi Ok per salvare, Annulla per scartare le modifiche.",
|
||||
"clipboard-copy-failure": "Impossibile copiare negli appunti.",
|
||||
"confirm-delete-generic-items": "Sei sicuro di voler eliminare i seguenti elementi?",
|
||||
"organizers": "Organizers"
|
||||
"organizers": "Organizzatori"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Sei sicuro di volerlo eliminare <b>{groupName}<b/>'?",
|
||||
@@ -244,8 +246,8 @@
|
||||
"group-preferences": "Preferenze Gruppo",
|
||||
"private-group": "Gruppo Privato",
|
||||
"private-group-description": "Impostando il tuo gruppo su privato verranno ripristinate tutte le opzioni di visualizzazione pubblica. Questa opzione sovrascrive le impostazioni di visualizzazione pubblica della singola ricetta.",
|
||||
"enable-public-access": "Enable Public Access",
|
||||
"enable-public-access-description": "Make group recipes public by default, and allow visitors to view recipes without logging-in",
|
||||
"enable-public-access": "Abilita accesso pubblico",
|
||||
"enable-public-access-description": "Rendi pubbliche le ricette di gruppo per impostazione predefinita, e consenti ai visitatori di visualizzare le ricette senza effettuare l'accesso",
|
||||
"allow-users-outside-of-your-group-to-see-your-recipes": "Consenti agli utenti al di fuori del tuo gruppo di vedere le tue ricette",
|
||||
"allow-users-outside-of-your-group-to-see-your-recipes-description": "Se abilitato, è possibile utilizzare un link pubblico per condividere ricette specifiche senza autorizzare l'utente. Se disabilitato, puoi condividere ricette solo con gli utenti che sono nel tuo gruppo o con un link privato pre-generato",
|
||||
"show-nutrition-information": "Mostra informazioni nutrizionali",
|
||||
@@ -359,11 +361,11 @@
|
||||
},
|
||||
"recipe-data-migrations": "Migrazione Dati Ricetta",
|
||||
"recipe-data-migrations-explanation": "Le ricette possono essere migrate da un'altra applicazione supportata da Mealie. Questo è un ottimo modo per iniziare con Mealie.",
|
||||
"coming-from-another-application-or-an-even-older-version-of-mealie": "Coming from another application or an even older version of Mealie? Check out migrations and see if your data can be imported.",
|
||||
"coming-from-another-application-or-an-even-older-version-of-mealie": "Arrivi da un'altra applicazione o da una versione più vecchia di Mealie? Scopri le migrazioni e scopri se i tuoi dati possono essere importati.",
|
||||
"choose-migration-type": "Scegli Il Tipo Di Migrazione",
|
||||
"tag-all-recipes": "Etichetta tutte le ricette con {tag-name} etichetta",
|
||||
"nextcloud-text": "Le ricette di Nextcloud possono essere importate da un file zip che contiene i dati memorizzati in Nextcloud. Osserva la struttura della cartella di esempio qui sotto per assicurarti che le tue ricette siano in grado di essere importate.",
|
||||
"chowdown-text": "Mealie natively supports the chowdown repository format. Download the code repository as a .zip file and upload it below.",
|
||||
"chowdown-text": "Mealie supporta nativamente il formato del repository chowdown. Scarica il repository del codice come file .zip e caricalo qui sotto.",
|
||||
"recipe-1": "Ricetta 1",
|
||||
"recipe-2": "Ricetta 2",
|
||||
"paprika-text": "Mealie può importare ricette dall'applicazione Paprika. Esporta le tue ricette da paprika, rinomina l'estensione di esportazione in .zip e caricala qui sotto.",
|
||||
@@ -373,8 +375,8 @@
|
||||
"description-long": "Mealie può importare le ricette da Plan to Eat."
|
||||
},
|
||||
"myrecipebox": {
|
||||
"title": "My Recipe Box",
|
||||
"description-long": "Mealie can import recipes from My Recipe Box. Export your recipes in CSV format, then upload the .csv file below."
|
||||
"title": "Il mio ricettario",
|
||||
"description-long": "Mealie può importare ricette da My Recipe Box. Esporta le tue ricette in formato HTML, quindi carica il .zip qui sotto."
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
@@ -547,8 +549,8 @@
|
||||
"looking-for-migrations": "Stai Cercando Le Migrazioni?",
|
||||
"import-with-url": "Importa da URL",
|
||||
"create-recipe": "Crea Ricetta",
|
||||
"create-recipe-description": "Create a new recipe from scratch.",
|
||||
"create-recipes": "Create Recipes",
|
||||
"create-recipe-description": "Crea una nuova ricetta da zero.",
|
||||
"create-recipes": "Crea Ricette",
|
||||
"import-with-zip": "Importa da .zip",
|
||||
"create-recipe-from-an-image": "Crea ricetta da un'immagine",
|
||||
"bulk-url-import": "Importazione multipla URL",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Carica immagine",
|
||||
"screen-awake": "Mantieni lo schermo acceso",
|
||||
"remove-image": "Rimuovi immagine",
|
||||
"nextStep": "Passo successivo"
|
||||
"nextStep": "Passo successivo",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Ricerca Avanzata",
|
||||
@@ -745,9 +762,9 @@
|
||||
"ldap-ready-success-text": "Le variabili LDAP richieste sono tutte configurate.",
|
||||
"build": "Versione",
|
||||
"recipe-scraper-version": "Versione Recipe Scraper",
|
||||
"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."
|
||||
"oidc-ready": "Pronto per OIDC",
|
||||
"oidc-ready-error-text": "I valori OIDC non sono configurati. Questo può essere ignorato se non si utilizza Autenticazione OIDC.",
|
||||
"oidc-ready-success-text": "Le variabili OIDC richieste sono tutte impostate."
|
||||
},
|
||||
"shopping-list": {
|
||||
"all-lists": "Tutte le Liste",
|
||||
@@ -855,11 +872,11 @@
|
||||
"link-id": "Link ID",
|
||||
"link-name": "Link Nome",
|
||||
"login": "Login",
|
||||
"login-oidc": "Login with",
|
||||
"or": "or",
|
||||
"login-oidc": "Accedi con",
|
||||
"or": "oppure",
|
||||
"logout": "Logout",
|
||||
"manage-users": "Gestisci Utenti",
|
||||
"manage-users-description": "Create and manage users.",
|
||||
"manage-users-description": "Crea e gestisci gli utenti.",
|
||||
"new-password": "Nuova Password",
|
||||
"new-user": "Nuovo Utente",
|
||||
"password-has-been-reset-to-the-default-password": "La password è stata reimpostata a quella di default",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Elimina Ricette",
|
||||
"source-unit-will-be-deleted": "L'unità di origine verrà eliminata"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Crea Alias",
|
||||
"manage-aliases": "Gestisci Alias",
|
||||
"seed-data": "Dati Predefiniti",
|
||||
@@ -1157,32 +1180,32 @@
|
||||
"no-logs-found": "Nessun Log Trovato",
|
||||
"tasks": "Compiti",
|
||||
"setup": {
|
||||
"first-time-setup": "First Time Setup",
|
||||
"welcome-to-mealie-get-started": "Welcome to Mealie! Let's get started",
|
||||
"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."
|
||||
"first-time-setup": "Configurazione iniziale",
|
||||
"welcome-to-mealie-get-started": "Un benvenuto su Mealie! Iniziamo",
|
||||
"already-set-up-bring-to-homepage": "Ho già configurato tutto, portami alla pagina iniziale",
|
||||
"common-settings-for-new-sites": "Ecco alcune impostazioni comuni per i nuovi siti",
|
||||
"setup-complete": "Configurazione completata!",
|
||||
"here-are-a-few-things-to-help-you-get-started": "Qui ci sono alcune cose per aiutarvi a iniziare con Mealie",
|
||||
"restore-from-v1-backup": "Hai un backup da un'istanza precedente di Mealie v1? Puoi ripristinarlo qui.",
|
||||
"manage-profile-or-get-invite-link": "Gestisci il tuo profilo, o parti da un link di invito per condividere con gli altri."
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
"welcome-user": "👋 Benvenutǝ, {0}!",
|
||||
"description": "Gestisci il tuo profilo, le ricette e le impostazioni di gruppo.",
|
||||
"get-invite-link": "Ottieni Link Di Invito",
|
||||
"get-public-link": "Ottieni link pubblico",
|
||||
"account-summary": "Riepilogo Account",
|
||||
"account-summary-description": "Here's a summary of your group's information.",
|
||||
"account-summary-description": "Ecco un riepilogo delle informazioni del tuo gruppo.",
|
||||
"group-statistics": "Statistiche Gruppo",
|
||||
"group-statistics-description": "Le statistiche di gruppo forniscono alcune informazioni su come stai usando Mealie.",
|
||||
"storage-capacity": "Capacità di Archiviazione",
|
||||
"storage-capacity-description": "La capacità di archiviazione è data dall'insieme delle immagini e delle risorse caricate.",
|
||||
"personal": "Personale",
|
||||
"personal-description": "These are settings that are personal to you. Changes here won't affect other users.",
|
||||
"personal-description": "Queste sono le tue impostazioni personali. Le modifiche non influenzeranno gli altri utenti.",
|
||||
"user-settings": "Impostazioni Utente",
|
||||
"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": "Gestisci le tue preferenze, modifica la tua password e aggiorna la tua email.",
|
||||
"api-tokens-description": "Gestisci i tuoi Token API per l'accesso da applicazioni esterne.",
|
||||
"group-description": "Questi elementi sono condivisi all'interno del tuo gruppo. Modificando uno di essi cambierà per l'intero gruppo!",
|
||||
"group-settings": "Impostazioni Gruppo",
|
||||
"group-settings-description": "Gestisci le impostazioni comuni del gruppo, come il piano alimentare e impostazioni di privacy.",
|
||||
@@ -1193,9 +1216,9 @@
|
||||
"notifiers": "Notifiche",
|
||||
"notifiers-description": "Imposta email e notifiche push che si attivano per eventi specifici.",
|
||||
"manage-data": "Gestisci Dati",
|
||||
"manage-data-description": "Manage your Mealie data; Foods, Units, Categories, Tags and more.",
|
||||
"manage-data-description": "Gestisci i tuoi dati di Mealie; Alimenti, Unità, Categorie, Tag e altro ancora.",
|
||||
"data-migrations": "Migrazioni Dati",
|
||||
"data-migrations-description": "Migrate your existing data from other applications like Nextcloud Recipes and Chowdown.",
|
||||
"data-migrations-description": "Migra i dati esistenti da altre applicazioni come Nextcloud Recipes e Chowdown.",
|
||||
"email-sent": "Email Inviata",
|
||||
"error-sending-email": "Errore Nell'Invio Email",
|
||||
"personal-information": "Informazioni Personali",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "問題が発生しました",
|
||||
"subscribed-events": "購読中のイベント",
|
||||
"test-message-sent": "テストメッセージを送信しました",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "新着通知",
|
||||
"event-notifiers": "イベント通知",
|
||||
"apprise-url-skipped-if-blank": "通知用URL (空欄の場合はスキップ)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "テスト",
|
||||
"themes": "テーマ",
|
||||
"thursday": "木曜日",
|
||||
"title": "Title",
|
||||
"token": "トークン",
|
||||
"tuesday": "火曜日",
|
||||
"type": "タイプ",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "画像をアップロード",
|
||||
"screen-awake": "画面をスリープ状態にしない",
|
||||
"remove-image": "画像を削除",
|
||||
"nextStep": "次のステップ"
|
||||
"nextStep": "次のステップ",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "詳細検索",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "レシピを削除",
|
||||
"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"
|
||||
},
|
||||
"create-alias": "エイリアスを作成",
|
||||
"manage-aliases": "エイリアスの管理",
|
||||
"seed-data": "シードデータ",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Something Went Wrong!",
|
||||
"subscribed-events": "구독한 이벤트",
|
||||
"test-message-sent": "Test Message Sent",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "새 알림",
|
||||
"event-notifiers": "Event Notifiers",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Themes",
|
||||
"thursday": "목요일",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "화요일",
|
||||
"type": "Type",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "이미지 업로드",
|
||||
"screen-awake": "화면을 항상 켠 상태로 유지",
|
||||
"remove-image": "이미지 제거",
|
||||
"nextStep": "다음 단계"
|
||||
"nextStep": "다음 단계",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "고급 검색",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Įvyko klaida!",
|
||||
"subscribed-events": "Prenumeruojami įvykiai",
|
||||
"test-message-sent": "Testinė žinutė išsiųsta",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Naujas pranešimas",
|
||||
"event-notifiers": "Įvykių pranešimai",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (praleidžiama, jei tuščia)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Tikrinti",
|
||||
"themes": "Temos",
|
||||
"thursday": "Ketvirtadienis",
|
||||
"title": "Title",
|
||||
"token": "Prieeigos raktas",
|
||||
"tuesday": "Antradienis",
|
||||
"type": "Tipas",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Įkelti nuotrauką",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Išplėstinė paieška",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Ištrinti receptus",
|
||||
"source-unit-will-be-deleted": "Pirminis vienetas bus ištrintas"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Pradiniai duomenys",
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"about": {
|
||||
"about": "About",
|
||||
"about-mealie": "About Mealie",
|
||||
"api-docs": "API Docs",
|
||||
"api-port": "API Port",
|
||||
"application-mode": "Application Mode",
|
||||
"database-type": "Database Type",
|
||||
"database-url": "Database URL",
|
||||
"default-group": "Default Group",
|
||||
"about": "Par",
|
||||
"about-mealie": "Par Mealie",
|
||||
"api-docs": "API Dokumentācija",
|
||||
"api-port": "API ports",
|
||||
"application-mode": "Lietojumprogrammas režīms",
|
||||
"database-type": "Datubāzes tips",
|
||||
"database-url": "Datubāzes URL",
|
||||
"default-group": "Noklusētā grupa",
|
||||
"demo": "Demo",
|
||||
"demo-status": "Demo Status",
|
||||
"development": "Development",
|
||||
"docs": "Docs",
|
||||
"download-log": "Download Log",
|
||||
"demo-status": "Demonstrācijas statuss",
|
||||
"development": "Izstrāde",
|
||||
"docs": "Dokumentācija",
|
||||
"download-log": "Lejupielādēt žurnālu",
|
||||
"download-recipe-json": "Last Scraped JSON",
|
||||
"github": "Github",
|
||||
"log-lines": "Log Lines",
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Something Went Wrong!",
|
||||
"subscribed-events": "Subscribed Events",
|
||||
"test-message-sent": "Test Message Sent",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "New Notification",
|
||||
"event-notifiers": "Event Notifiers",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Themes",
|
||||
"thursday": "Thursday",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Tuesday",
|
||||
"type": "Type",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload image",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Advanced Search",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Er is iets fout gegaan!",
|
||||
"subscribed-events": "Geabonneerde gebeurtenissen",
|
||||
"test-message-sent": "Testbericht verzonden",
|
||||
"message-sent": "Bericht verstuurd",
|
||||
"new-notification": "Nieuwe melding",
|
||||
"event-notifiers": "Meldingen van gebeurtenissen",
|
||||
"apprise-url-skipped-if-blank": "URL van Apprise (overgeslagen als veld leeg is)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Thema's",
|
||||
"thursday": "donderdag",
|
||||
"title": "Titel",
|
||||
"token": "Token",
|
||||
"tuesday": "dinsdag",
|
||||
"type": "Soort",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Afbeelding uploaden",
|
||||
"screen-awake": "Scherm aan laten staan",
|
||||
"remove-image": "Afbeelding verwijderen",
|
||||
"nextStep": "Volgende stap"
|
||||
"nextStep": "Volgende stap",
|
||||
"recipe-actions": "Acties met recepten ",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie gebruikt natuurlijke taalverwerking om te ontleden en maakt eenheden en levensmiddelen voor de ingrediënten van je recept. Deze functie is experimenteel en werkt misschien niet altijd zoals verwacht. Als u liever niet de bewerkte resultaten gebruikt, kunt u 'Annuleren' selecteren en de wijzigingen worden niet opgeslagen.",
|
||||
"ingredient-parser": "",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Geavanceerd zoeken",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Verwijder recepten",
|
||||
"source-unit-will-be-deleted": "Broneenheid zal worden verwijderd"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Gegevens voor acties met recepten",
|
||||
"new-recipe-action": "Nieuwe actie met recept",
|
||||
"edit-recipe-action": "Pas actie met recept aan",
|
||||
"action-type": "Soort actie"
|
||||
},
|
||||
"create-alias": "Maak alias",
|
||||
"manage-aliases": "Beheer aliassen ",
|
||||
"seed-data": "Voorbeeldgegevens",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Noe gikk galt!",
|
||||
"subscribed-events": "Abonnerte hendelser",
|
||||
"test-message-sent": "Testmelding sendt",
|
||||
"message-sent": "Melding sendt",
|
||||
"new-notification": "Ny varsel",
|
||||
"event-notifiers": "Hendelsesvarsler",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (hoppes over hvis tom)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Temaer",
|
||||
"thursday": "Torsdag",
|
||||
"title": "Tittel",
|
||||
"token": "Token",
|
||||
"tuesday": "Tirsdag",
|
||||
"type": "Type",
|
||||
@@ -208,7 +210,7 @@
|
||||
"unsaved-changes": "Du har ulagrede endringer. Ønsker du å lagre før du forlater? Trykk 'OK' for å lagre, 'Avbryt' for å forkaste endringene.",
|
||||
"clipboard-copy-failure": "Kunne ikke kopiere til utklippstavlen.",
|
||||
"confirm-delete-generic-items": "Er du sikker på at du vil følgende elementer?",
|
||||
"organizers": "Organizers"
|
||||
"organizers": "Organisatorer"
|
||||
},
|
||||
"group": {
|
||||
"are-you-sure-you-want-to-delete-the-group": "Er du sikker på at du vil slette <b>{groupName}<b/>?",
|
||||
@@ -363,7 +365,7 @@
|
||||
"choose-migration-type": "Velg type overføring",
|
||||
"tag-all-recipes": "Merk alle oppskrifter med emneordet {tag-name}",
|
||||
"nextcloud-text": "Oppskrifter fra Nextcloud kan importeres fra en zip-fil som inneholder dataene lagret i Nextcloud. Se eksempelet på mappestrukture nedenfor for å sikre at oppskriftene kan importeres.",
|
||||
"chowdown-text": "Mealie natively supports the chowdown repository format. Download the code repository as a .zip file and upload it below.",
|
||||
"chowdown-text": "Mealie støtter Chowdown-arkivformatet. Last ned kodearkivet som en .zip-fil og last den opp nedenfor.",
|
||||
"recipe-1": "Oppskrift 1",
|
||||
"recipe-2": "Oppskrift 2",
|
||||
"paprika-text": "Mealie kan importere oppskrifter fra Paprika. Eksporter oppskriftene fra Paprika, endre filnavnutvidelsen til .zip og last den opp nedenfor.",
|
||||
@@ -374,7 +376,7 @@
|
||||
},
|
||||
"myrecipebox": {
|
||||
"title": "My Recipe Box",
|
||||
"description-long": "Mealie can import recipes from My Recipe Box. Export your recipes in CSV format, then upload the .csv file below."
|
||||
"description-long": "Mealie kan importere oppskrifter fra My Recipe Box. Eksporter oppskrifter i CSV-format, og last deretter opp .csv-filen nedenfor."
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Last opp bilde",
|
||||
"screen-awake": "Hold skjermen på",
|
||||
"remove-image": "Slett bilde",
|
||||
"nextStep": "Neste trinn"
|
||||
"nextStep": "Neste trinn",
|
||||
"recipe-actions": "Oppskriftshandlinger",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingrediens-parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Avansert søk",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Slett oppskrifter",
|
||||
"source-unit-will-be-deleted": "Kildeenheten vil bli slettet"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Opppgavehandlings-data",
|
||||
"new-recipe-action": "Ny oppskriftshandling",
|
||||
"edit-recipe-action": "Rediger oppskriftshandling",
|
||||
"action-type": "Handlingstype"
|
||||
},
|
||||
"create-alias": "Opprett alias",
|
||||
"manage-aliases": "Administrer aliaser",
|
||||
"seed-data": "Tilføringsdata",
|
||||
@@ -1168,21 +1191,21 @@
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
"welcome-user": "👋 Welcome, {0}!",
|
||||
"welcome-user": "Velkommen, {0}!",
|
||||
"description": "Administrer din profil, oppskrifter og gruppeinnstillinger.",
|
||||
"get-invite-link": "Få invitasjonslenke",
|
||||
"get-public-link": "Få offentlig lenke",
|
||||
"account-summary": "Kontosammendrag",
|
||||
"account-summary-description": "Here's a summary of your group's information.",
|
||||
"account-summary-description": "Her er en oppsummering av informasjonen til gruppen din.",
|
||||
"group-statistics": "Gruppestatistikk",
|
||||
"group-statistics-description": "Gruppestatistikken din gir deg et innblikk i hvordan du bruker Mealie.",
|
||||
"storage-capacity": "Lagringskapasitet",
|
||||
"storage-capacity-description": "Lagringskapasiteten er en beregning av bildene og ressursene du har lastet opp.",
|
||||
"personal": "Personlig",
|
||||
"personal-description": "These are settings that are personal to you. Changes here won't affect other users.",
|
||||
"personal-description": "Dette er personlige innstillinger. Endringer gjort her påvirker ikke andre brukere.",
|
||||
"user-settings": "Brukerinnstillinger",
|
||||
"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": "Administrer innstillingene, endre passordet og oppdater e-postadressen din.",
|
||||
"api-tokens-description": "Administrer dine API-tokens for tilgang fra eksterne programmer.",
|
||||
"group-description": "Disse elementene deles innad i gruppen din. Redigering av elementenee vil føre til endringer for hele gruppen!",
|
||||
"group-settings": "Gruppeinnstillinger",
|
||||
"group-settings-description": "Administrer felles gruppeinnstillinger som innstillinger for måltidsplaner og personvern.",
|
||||
@@ -1193,9 +1216,9 @@
|
||||
"notifiers": "Varslingsagenter",
|
||||
"notifiers-description": "Sett opp e-post- og pushvarsler som utløses av spesifikke hendelser.",
|
||||
"manage-data": "Administrer data",
|
||||
"manage-data-description": "Manage your Mealie data; Foods, Units, Categories, Tags and more.",
|
||||
"manage-data-description": "Administrer dine Mealie-data; Ingredienser, enheter, kategorier, emneknagger og mer.",
|
||||
"data-migrations": "Dataoverføringer",
|
||||
"data-migrations-description": "Migrate your existing data from other applications like Nextcloud Recipes and Chowdown.",
|
||||
"data-migrations-description": "Overfør eksisterende data fra andre programmer som Nextcloud Recipes og Chowdown.",
|
||||
"email-sent": "E-post sendt",
|
||||
"error-sending-email": "Feil ved sending av e-post",
|
||||
"personal-information": "Personlig informasjon",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Coś poszło nie tak!",
|
||||
"subscribed-events": "Zasubskrybowane wydarzenia",
|
||||
"test-message-sent": "Wiadomość została wysłana",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Nowe powiadomienie",
|
||||
"event-notifiers": "Powiadomienia o zdarzeniach",
|
||||
"apprise-url-skipped-if-blank": "URL Apprise (pominięty, jeśli puste)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Testuj",
|
||||
"themes": "Motywy",
|
||||
"thursday": "Czwartek",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Wtorek",
|
||||
"type": "Typ",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Prześlij obraz",
|
||||
"screen-awake": "Pozostaw ekran włączony",
|
||||
"remove-image": "Usuń obraz",
|
||||
"nextStep": "Następny krok"
|
||||
"nextStep": "Następny krok",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Wyszukiwanie zaawansowane",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Usuń Przepisy",
|
||||
"source-unit-will-be-deleted": "Jednostka źródłowa zostanie usunięta"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Utwórz alias",
|
||||
"manage-aliases": "Zarządzaj aliasami",
|
||||
"seed-data": "Dane przykładowe",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Algo deu errado!",
|
||||
"subscribed-events": "Eventos Inscritos",
|
||||
"test-message-sent": "Mensagem de teste enviada",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Nova Notificação",
|
||||
"event-notifiers": "Notificações de Eventos",
|
||||
"apprise-url-skipped-if-blank": "URL Apprise (ignorado se estiver em branco)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Teste",
|
||||
"themes": "Temas",
|
||||
"thursday": "Quinta-feira",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Terça-feira",
|
||||
"type": "Tipo",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Enviar imagem",
|
||||
"screen-awake": "Manter a tela ligada",
|
||||
"remove-image": "Remover imagem",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Pesquisa avançada",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Excluir Receitas",
|
||||
"source-unit-will-be-deleted": "Unidade de origem será excluída"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Criar Apelido",
|
||||
"manage-aliases": "Gerenciar apelidos",
|
||||
"seed-data": "Semear dados",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Algo correu mal!",
|
||||
"subscribed-events": "Eventos Subscritos",
|
||||
"test-message-sent": "Mensagem de teste enviada",
|
||||
"message-sent": "Mensagem Enviada",
|
||||
"new-notification": "Nova Notificação",
|
||||
"event-notifiers": "Notificadores de eventos",
|
||||
"apprise-url-skipped-if-blank": "URL da Apprise (ignorado se vazio)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Teste",
|
||||
"themes": "Temas",
|
||||
"thursday": "Quinta-feira",
|
||||
"title": "Título",
|
||||
"token": "Token",
|
||||
"tuesday": "Terça-feira",
|
||||
"type": "Tipo",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Carregar imagem",
|
||||
"screen-awake": "Manter ecrã ligado",
|
||||
"remove-image": "Remover imagem",
|
||||
"nextStep": "Próximo passo"
|
||||
"nextStep": "Próximo passo",
|
||||
"recipe-actions": "Ações da Receita",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Pesquisa Avançada",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Eliminar Receitas",
|
||||
"source-unit-will-be-deleted": "Unidade de origem será eliminada"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Dados das Ações da Receita",
|
||||
"new-recipe-action": "Nova Ação da Receita",
|
||||
"edit-recipe-action": "Editar Ação da Receita",
|
||||
"action-type": "Tipo de Ação"
|
||||
},
|
||||
"create-alias": "Criar Pseudónimo",
|
||||
"manage-aliases": "Gerir Pseudónimos",
|
||||
"seed-data": "Gerar dados",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Ceva nu a funcţionat corect!",
|
||||
"subscribed-events": "Evenimente la care ești Abonat",
|
||||
"test-message-sent": "Mesaj de test trimis",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Notificare nouă",
|
||||
"event-notifiers": "Notificatori de evenimente",
|
||||
"apprise-url-skipped-if-blank": "URL Apprise (ignorat daca e gol)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Testează",
|
||||
"themes": "Teme",
|
||||
"thursday": "Joi",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Marţi",
|
||||
"type": "Tip",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Încărcare imagine",
|
||||
"screen-awake": "Păstrare ecran aprins",
|
||||
"remove-image": "Șterge Imaginea",
|
||||
"nextStep": "Pasul următor"
|
||||
"nextStep": "Pasul următor",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Căutare avansată",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Что-то пошло не так!",
|
||||
"subscribed-events": "События с подпиской",
|
||||
"test-message-sent": "Тестовое сообщение отправлено",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Новое уведомление",
|
||||
"event-notifiers": "Уведомления о событии",
|
||||
"apprise-url-skipped-if-blank": "URL-адрес (пропущен, если пусто)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Тест",
|
||||
"themes": "Темы",
|
||||
"thursday": "Четверг",
|
||||
"title": "Title",
|
||||
"token": "Токен",
|
||||
"tuesday": "Вторник",
|
||||
"type": "Тип",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Загрузить изображение",
|
||||
"screen-awake": "Держать экран включенным",
|
||||
"remove-image": "Удалить изображение",
|
||||
"nextStep": "След. шаг"
|
||||
"nextStep": "След. шаг",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Расширенный поиск",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Удалить рецепты",
|
||||
"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"
|
||||
},
|
||||
"create-alias": "Создать псевдоним",
|
||||
"manage-aliases": "Управление псевдонимами",
|
||||
"seed-data": "Заполнить данные",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Vyskytla sa chyba",
|
||||
"subscribed-events": "Prihlásené akcie",
|
||||
"test-message-sent": "Testovacia správa bola odoslaná",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Nové upozornenie",
|
||||
"event-notifiers": "Upozornenia udalostí",
|
||||
"apprise-url-skipped-if-blank": "Informačná URL (preskočená, ak je prázdna)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Otestovať",
|
||||
"themes": "Motívy",
|
||||
"thursday": "Štvrtok",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Utorok",
|
||||
"type": "Typ",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Nahrať obrázok",
|
||||
"screen-awake": "Ponechať obrazovku stále zapnutú",
|
||||
"remove-image": "Odstrániť obrázok",
|
||||
"nextStep": "Ďalší krok"
|
||||
"nextStep": "Ďalší krok",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Rozšírené vyhľadávanie",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Odstrániť recepty",
|
||||
"source-unit-will-be-deleted": "Zdroj bude vymazaný"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Vytvoriť alias",
|
||||
"manage-aliases": "Spravovať aliasy",
|
||||
"seed-data": "Naplniť dáta",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Nekaj je šlo narobe!",
|
||||
"subscribed-events": "Naročeni dogodki",
|
||||
"test-message-sent": "Testno sporočilo je bilo poslano",
|
||||
"message-sent": "Sporočilo poslano",
|
||||
"new-notification": "Novo obvestilo",
|
||||
"event-notifiers": "Obvestila o dogodkih",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (preskočeno, če je prazno)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Teme",
|
||||
"thursday": "Četrtek",
|
||||
"title": "Naslov",
|
||||
"token": "Žeton",
|
||||
"tuesday": "Torek",
|
||||
"type": "Tip",
|
||||
@@ -460,7 +462,7 @@
|
||||
"recipe-deleted": "Recept je izbrisan",
|
||||
"recipe-image": "Slika recepta",
|
||||
"recipe-image-updated": "Slika recepta je posodobljena",
|
||||
"recipe-name": "Ime recepta",
|
||||
"recipe-name": "Naslov recepta",
|
||||
"recipe-settings": "Nastavitve recepta",
|
||||
"recipe-update-failed": "Napaka pri posodobitvi recepta",
|
||||
"recipe-updated": "Recept je posodobljen",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Naloži sliko",
|
||||
"screen-awake": "Ohranjanje budnega zaslona",
|
||||
"remove-image": "Odstrani sliko",
|
||||
"nextStep": "Naslednji korak"
|
||||
"nextStep": "Naslednji korak",
|
||||
"recipe-actions": "Opravila na receptu",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uporablja procesiranje naravnega jezika za razčlenjevanje in ustvarjanje živil in enot v seznamu sestavin. Ta storitev je eksperimentalna in včasih ne deluje kot pričakovano. Če dobljenega rezultata ne želiš uporabiti, izberi 'Prekliči' in tvoje spremembe ne bodo shranjene.",
|
||||
"ingredient-parser": "Razčlenjevalnik sestavin",
|
||||
"explanation": "Če želiš uporabiti razčlenjevalnik sestavin, izberi 'Razčleni vse', da pričneš s postopkom. Ko bodo sestavine na voljo, lahko pregledaš podatke in preveriš, če so bili pravilno razčlenjeni. Stopnja zaupanja je prikazana desno od podatka. Ta vrednost je povprečje vseh posameznih vrednosti in ni nujno popolnoma natančna.",
|
||||
"alerts-explainer": "Opozorila bodo prikazana v primeru, da obstaja ujemajoče živilo ali enota, ampak še ne obstaja v podatkovni bazi.",
|
||||
"select-parser": "Izberi razčlenjevalnik",
|
||||
"natural-language-processor": "Procesor naravnega jezika",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Razčleni vse",
|
||||
"no-unit": "Ni enote",
|
||||
"missing-unit": "Ustvari manjkajočo enoto: {unit}",
|
||||
"missing-food": "Ustvari manjkajoče živilo: {food}",
|
||||
"no-food": "Ni živila"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Napredno iskanje",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Izbriši recepte",
|
||||
"source-unit-will-be-deleted": "Izvorna enota bo izbrisana"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Podatki o opravilu na receptu",
|
||||
"new-recipe-action": "Novo opravilo na receptu",
|
||||
"edit-recipe-action": "Urejaj opravilo na receptu",
|
||||
"action-type": "Tip opravila"
|
||||
},
|
||||
"create-alias": "Ustvari alias",
|
||||
"manage-aliases": "Upravljanje z aliasi",
|
||||
"seed-data": "Napolni podatke",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Нешто је кренуло погрешно!",
|
||||
"subscribed-events": "Догађаји на које сте претплаћени",
|
||||
"test-message-sent": "Тест порука је послата",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Ново обавештење",
|
||||
"event-notifiers": "Обавештавач о догађају",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (прескочено ако је празно)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Тест",
|
||||
"themes": "Теме",
|
||||
"thursday": "четвртак",
|
||||
"title": "Title",
|
||||
"token": "Токен",
|
||||
"tuesday": "уторак",
|
||||
"type": "Тип",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload image",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Напредна претрага",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Någonting gick fel",
|
||||
"subscribed-events": "Prenumererade händelser",
|
||||
"test-message-sent": "Testmeddelande Skickat",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Ny avisering",
|
||||
"event-notifiers": "Händelseavisering",
|
||||
"apprise-url-skipped-if-blank": "Apprise-URL (hoppa över om tom)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Tema",
|
||||
"thursday": "Torsdag",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Tisdag",
|
||||
"type": "Typ",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Ladda upp bild",
|
||||
"screen-awake": "Håll skärmen vaken",
|
||||
"remove-image": "Ta bort bild",
|
||||
"nextStep": "Nästa steg"
|
||||
"nextStep": "Nästa steg",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Avancerad sökning",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Radera recept",
|
||||
"source-unit-will-be-deleted": "Källenheten kommer att raderas"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New Recipe Action",
|
||||
"edit-recipe-action": "Edit Recipe Action",
|
||||
"action-type": "Action Type"
|
||||
},
|
||||
"create-alias": "Skapa alias",
|
||||
"manage-aliases": "Hantera alias",
|
||||
"seed-data": "Exempeldata",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Bir sorun oluştu!",
|
||||
"subscribed-events": "Abone Olunan Etkinlikler",
|
||||
"test-message-sent": "Test Mesajı Gönderildi",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "Yeni bildirim",
|
||||
"event-notifiers": "Etkinlik Bildirimleri",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL'si (boşsa geçilir)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Dene",
|
||||
"themes": "Temalar",
|
||||
"thursday": "Perşembe",
|
||||
"title": "Title",
|
||||
"token": "Anahtar",
|
||||
"tuesday": "Salı",
|
||||
"type": "Tür",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Resim yükleyin",
|
||||
"screen-awake": "Ekranı Açık Tut",
|
||||
"remove-image": "Resmi kaldır",
|
||||
"nextStep": "Sonraki adım"
|
||||
"nextStep": "Sonraki adım",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Advanced Search",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Takma Ad Oluştur",
|
||||
"manage-aliases": "Takma Adları Yönet",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Щось пішло не так!",
|
||||
"subscribed-events": "Події, на які підписано",
|
||||
"test-message-sent": "Тестове повідомлення надіслано",
|
||||
"message-sent": "Повідомлення надіслано",
|
||||
"new-notification": "Нове сповіщення",
|
||||
"event-notifiers": "Сповіщувачі",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (пропущено якщо порожній)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Тест",
|
||||
"themes": "Теми",
|
||||
"thursday": "Четвер",
|
||||
"title": "Назва",
|
||||
"token": "Токен",
|
||||
"tuesday": "Вівторок",
|
||||
"type": "Тип",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Вивантажити зображення",
|
||||
"screen-awake": "Тримати екран активним",
|
||||
"remove-image": "Видалити зображення",
|
||||
"nextStep": "Наступний крок"
|
||||
"nextStep": "Наступний крок",
|
||||
"recipe-actions": "Дії рецепту",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie використовує аналіз природної мови для аналізу та створення інгредієнтів та одиниць виміру. Це експериментальна функція і може не завжди працювати належним чином. Якщо ви не хочете використовувати результати аналізу виберіть \"Скасувати\", і зміни не будуть збережені.",
|
||||
"ingredient-parser": "Аналізатор інгредієнтів",
|
||||
"explanation": "Щоб використати аналізатор інгредієнтів, натисніть кнопку 'Аналізувати все', щоб запустити процес. Після того, як інгредієнти проаналізовані, ви можете їх переглянути та переконатися, що вони були проаналізовані правильно. Оцінка надійності аналізу відображена праворуч від назви елемента. Ця оцінка розраховується як середнє значення усіх індивідуальних оцінок і не завжди може бути абсолютно точним.",
|
||||
"alerts-explainer": "Оповіщення будуть відображатися, якщо знайдені продукти або одиниці знайдені яких не існує в базі даних.",
|
||||
"select-parser": "Вибрати аналізатор",
|
||||
"natural-language-processor": "Аналізатор природної мови",
|
||||
"brute-parser": "Простий аналізатор",
|
||||
"parse-all": "Аналізувати все",
|
||||
"no-unit": "Без одиниці",
|
||||
"missing-unit": "Створити відсутню одиниці: {unit}",
|
||||
"missing-food": "Створити відсутню їжу: {food}",
|
||||
"no-food": "Немає їжі"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Розширений пошук",
|
||||
@@ -794,7 +811,7 @@
|
||||
"language": "Мова",
|
||||
"maintenance": "Обслуговування",
|
||||
"background-tasks": "Фонові завдання",
|
||||
"parser": "Парсер",
|
||||
"parser": "Синтаксичний аналізатор (парсер)",
|
||||
"developer": "Розробник",
|
||||
"cookbook": "Кулінарна книга",
|
||||
"create-cookbook": "Створити нову кулінарну книгу"
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Видалити рецепти",
|
||||
"source-unit-will-be-deleted": "Початкову одиницю вимірювання буде видалено"
|
||||
},
|
||||
"recipe-actions": {
|
||||
"recipe-actions-data": "Дані дій рецепта",
|
||||
"new-recipe-action": "Нова дія рецепту",
|
||||
"edit-recipe-action": "Редагувати дії рецепта",
|
||||
"action-type": "Тип Дії"
|
||||
},
|
||||
"create-alias": "Створити псевдонім",
|
||||
"manage-aliases": "Керувати псевдонімами",
|
||||
"seed-data": "Підготовлені дані",
|
||||
@@ -1151,7 +1174,7 @@
|
||||
"ingredient-text": "Текст інгредієнта",
|
||||
"average-confident": "Впевненість {0}",
|
||||
"try-an-example": "Спробувати приклад",
|
||||
"parser": "Парсер",
|
||||
"parser": "Синтаксичний аналізатор (парсер)",
|
||||
"background-tasks": "Фонові задачі",
|
||||
"background-tasks-description": "Тут ви можете переглянути всі запущені фонові задачі та їх статус",
|
||||
"no-logs-found": "Журналів не знайдено",
|
||||
@@ -1168,21 +1191,21 @@
|
||||
}
|
||||
},
|
||||
"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": "Статистика вашої групи дає можливість зрозуміти, як ви користуєтеся 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": "Керуйте спільними налаштуванням груп, такими як плани харчування і налаштування конфіденційності.",
|
||||
@@ -1193,9 +1216,9 @@
|
||||
"notifiers": "Сповіщувачі",
|
||||
"notifiers-description": "Налаштуйте email та push сповіщення, що спрацьовують для певних подій.",
|
||||
"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": "Персональні данні",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "Something Went Wrong!",
|
||||
"subscribed-events": "Subscribed Events",
|
||||
"test-message-sent": "Test Message Sent",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "New Notification",
|
||||
"event-notifiers": "Event Notifiers",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "Test",
|
||||
"themes": "Themes",
|
||||
"thursday": "Thursday",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"tuesday": "Tuesday",
|
||||
"type": "Type",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload image",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "Advanced Search",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "出错了\t#",
|
||||
"subscribed-events": "订阅事件",
|
||||
"test-message-sent": "测试消息已发送",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "新通知",
|
||||
"event-notifiers": "事件通知器",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (如果为空则跳过)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "测试",
|
||||
"themes": "布景主题",
|
||||
"thursday": "周四",
|
||||
"title": "Title",
|
||||
"token": "密钥",
|
||||
"tuesday": "周二",
|
||||
"type": "类型",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "上传图片",
|
||||
"screen-awake": "保持屏幕唤醒",
|
||||
"remove-image": "删除图片",
|
||||
"nextStep": "下一步"
|
||||
"nextStep": "下一步",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "高级搜索",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "删除食谱",
|
||||
"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"
|
||||
},
|
||||
"create-alias": "创建别名",
|
||||
"manage-aliases": "管理别名",
|
||||
"seed-data": "初始数据",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"something-went-wrong": "出了點問題...",
|
||||
"subscribed-events": "關注的事件",
|
||||
"test-message-sent": "測試訊息已發送",
|
||||
"message-sent": "Message Sent",
|
||||
"new-notification": "新通知",
|
||||
"event-notifiers": "Event Notifiers",
|
||||
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
|
||||
@@ -160,6 +161,7 @@
|
||||
"test": "測試",
|
||||
"themes": "佈景主題",
|
||||
"thursday": "星期四",
|
||||
"title": "Title",
|
||||
"token": "密鑰",
|
||||
"tuesday": "星期二",
|
||||
"type": "類型",
|
||||
@@ -582,7 +584,22 @@
|
||||
"upload-image": "Upload image",
|
||||
"screen-awake": "Keep Screen Awake",
|
||||
"remove-image": "Remove image",
|
||||
"nextStep": "Next step"
|
||||
"nextStep": "Next step",
|
||||
"recipe-actions": "Recipe Actions",
|
||||
"parser": {
|
||||
"experimental-alert-text": "Mealie uses natural language processing to parse and create units and food items for your recipe ingredients. This feature is experimental and may not always work as expected. If you prefer not to use the parsed results, you can select 'Cancel' and your changes will not be saved.",
|
||||
"ingredient-parser": "Ingredient Parser",
|
||||
"explanation": "To use the ingredient parser, click the 'Parse All' button to start the process. Once the processed ingredients are available, you can review the items and verify that they were parsed correctly. The model's confidence score is displayed on the right of the item title. This score is an average of all the individual scores and may not always be completely accurate.",
|
||||
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.",
|
||||
"select-parser": "Select Parser",
|
||||
"natural-language-processor": "Natural Language Processor",
|
||||
"brute-parser": "Brute Parser",
|
||||
"parse-all": "Parse All",
|
||||
"no-unit": "No unit",
|
||||
"missing-unit": "Create missing unit: {unit}",
|
||||
"missing-food": "Create missing food: {food}",
|
||||
"no-food": "No Food"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"advanced-search": "進階搜尋",
|
||||
@@ -1001,6 +1018,12 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "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"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
|
||||
@@ -9,6 +9,7 @@ import { UtilsAPI } from "./user/utils";
|
||||
import { FoodAPI } from "./user/recipe-foods";
|
||||
import { UnitAPI } from "./user/recipe-units";
|
||||
import { CookbookAPI } from "./user/group-cookbooks";
|
||||
import { GroupRecipeActionsAPI } from "./user/group-recipe-actions";
|
||||
import { WebhooksAPI } from "./user/group-webhooks";
|
||||
import { RegisterAPI } from "./user/user-registration";
|
||||
import { MealPlanAPI } from "./user/group-mealplan";
|
||||
@@ -36,6 +37,7 @@ export class UserApiClient {
|
||||
public foods: FoodAPI;
|
||||
public units: UnitAPI;
|
||||
public cookbooks: CookbookAPI;
|
||||
public groupRecipeActions: GroupRecipeActionsAPI;
|
||||
public groupWebhooks: WebhooksAPI;
|
||||
public register: RegisterAPI;
|
||||
public mealplans: MealPlanAPI;
|
||||
@@ -65,6 +67,7 @@ export class UserApiClient {
|
||||
this.users = new UserApi(requests);
|
||||
this.groups = new GroupAPI(requests);
|
||||
this.cookbooks = new CookbookAPI(requests);
|
||||
this.groupRecipeActions = new GroupRecipeActionsAPI(requests);
|
||||
this.groupWebhooks = new WebhooksAPI(requests);
|
||||
this.register = new RegisterAPI(requests);
|
||||
this.mealplans = new MealPlanAPI(requests);
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
*/
|
||||
|
||||
export type RecipeActionType =
|
||||
| "link"
|
||||
| "post";
|
||||
export type WebhookType = "mealplan";
|
||||
export type SupportedMigrations =
|
||||
| "nextcloud"
|
||||
@@ -26,6 +29,11 @@ export interface CreateGroupPreferences {
|
||||
recipeDisableAmount?: boolean;
|
||||
groupId: string;
|
||||
}
|
||||
export interface CreateGroupRecipeAction {
|
||||
actionType: RecipeActionType;
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
export interface CreateInviteToken {
|
||||
uses: number;
|
||||
}
|
||||
@@ -191,6 +199,13 @@ export interface GroupEventNotifierUpdate {
|
||||
options?: GroupEventNotifierOptions;
|
||||
id: string;
|
||||
}
|
||||
export interface GroupRecipeActionOut {
|
||||
actionType: RecipeActionType;
|
||||
title: string;
|
||||
url: string;
|
||||
groupId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface GroupStatistics {
|
||||
totalRecipes: number;
|
||||
totalUsers: number;
|
||||
@@ -230,6 +245,12 @@ export interface ReadWebhook {
|
||||
groupId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface SaveGroupRecipeAction {
|
||||
actionType: RecipeActionType;
|
||||
title: string;
|
||||
url: string;
|
||||
groupId: string;
|
||||
}
|
||||
export interface SaveInviteToken {
|
||||
usesLeft: number;
|
||||
groupId: string;
|
||||
|
||||
14
frontend/lib/api/user/group-recipe-actions.ts
Normal file
14
frontend/lib/api/user/group-recipe-actions.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { BaseCRUDAPI } from "../base/base-clients";
|
||||
import { CreateGroupRecipeAction, GroupRecipeActionOut } from "~/lib/api/types/group";
|
||||
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
groupRecipeActions: `${prefix}/groups/recipe-actions`,
|
||||
groupRecipeActionsId: (id: string | number) => `${prefix}/groups/recipe-actions/${id}`,
|
||||
};
|
||||
|
||||
export class GroupRecipeActionsAPI extends BaseCRUDAPI<CreateGroupRecipeAction, GroupRecipeActionOut> {
|
||||
baseRoute = routes.groupRecipeActions;
|
||||
itemRoute = routes.groupRecipeActionsId;
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import { BaseCRUDAPI } from "../base/base-clients";
|
||||
import { QueryValue, route } from "~/lib/api/base/route";
|
||||
import { PaginationData, RequestResponse } from "~/lib/api/types/non-generated";
|
||||
import { PaginationData } from "~/lib/api/types/non-generated";
|
||||
import {
|
||||
ChangePassword,
|
||||
DeleteTokenResponse,
|
||||
GroupInDB,
|
||||
LongLiveTokenIn,
|
||||
LongLiveTokenOut,
|
||||
ResetPassword,
|
||||
@@ -30,7 +29,6 @@ const routes = {
|
||||
groupUsers: `${prefix}/users/group-users`,
|
||||
usersSelf: `${prefix}/users/self`,
|
||||
ratingsSelf: `${prefix}/users/self/ratings`,
|
||||
groupsSelf: `${prefix}/users/self/group`,
|
||||
passwordReset: `${prefix}/users/reset-password`,
|
||||
passwordChange: `${prefix}/users/password`,
|
||||
users: `${prefix}/users`,
|
||||
@@ -57,10 +55,6 @@ export class UserApi extends BaseCRUDAPI<UserIn, UserOut, UserBase> {
|
||||
return await this.requests.get<PaginationData<UserSummary>>(route(routes.groupUsers, { page, perPage, ...params }));
|
||||
}
|
||||
|
||||
async getSelfGroup(): Promise<RequestResponse<GroupInDB>> {
|
||||
return await this.requests.get(routes.groupsSelf, {});
|
||||
}
|
||||
|
||||
async addFavorite(id: string, slug: string) {
|
||||
return await this.requests.post(routes.usersIdFavoritesSlug(id, slug), {});
|
||||
}
|
||||
|
||||
@@ -156,6 +156,7 @@ export default {
|
||||
propertyName: "access_token",
|
||||
},
|
||||
refresh: { url: "api/auth/refresh", method: "post" },
|
||||
logout: { url: "api/auth/logout", method: "post" },
|
||||
user: { url: "api/users/self", method: "get" },
|
||||
},
|
||||
},
|
||||
@@ -260,6 +261,12 @@ export default {
|
||||
"en-GB": require("./lang/dateTimeFormats/en-GB.json"),
|
||||
"fi-FI": require("./lang/dateTimeFormats/fi-FI.json"),
|
||||
"vi-VN": require("./lang/dateTimeFormats/vi-VN.json"),
|
||||
"sl-SI": require("./lang/dateTimeFormats/sl-SI.json"),
|
||||
"lv-LV": require("./lang/dateTimeFormats/lv-LV.json"),
|
||||
"is-IS": require("./lang/dateTimeFormats/is-IS.json"),
|
||||
"gl-ES": require("./lang/dateTimeFormats/gl-ES.json"),
|
||||
"lt-LT": require("./lang/dateTimeFormats/lt-LT.json"),
|
||||
"hr-HR": require("./lang/dateTimeFormats/hr-HR.json"),
|
||||
// END: DATE_LOCALES
|
||||
},
|
||||
fallbackLocale: "en-US",
|
||||
|
||||
@@ -2,35 +2,30 @@
|
||||
<v-container v-if="recipe">
|
||||
<v-container>
|
||||
<v-alert dismissible border="left" colored-border type="warning" elevation="2" :icon="$globals.icons.alert">
|
||||
<b>Experimental Feature</b>
|
||||
<b>{{ $tc("banner-experimental.title") }}</b>
|
||||
<div>
|
||||
Mealie can use natural language processing to attempt to parse and create units, and foods for your Recipe
|
||||
ingredients. This is experimental and may not work as expected. If you choose to not use the parsed results
|
||||
you can select cancel and your changes will not be saved.
|
||||
{{ $tc("recipe.parser.experimental-alert-text") }}
|
||||
</div>
|
||||
</v-alert>
|
||||
|
||||
<BaseCardSectionTitle title="Ingredients Processor">
|
||||
To use the ingredient parser, click the "Parse All" button and the process will start. When the processed
|
||||
ingredients are available, you can look through the items and verify that they were parsed correctly. The models
|
||||
confidence score is displayed on the right of the title item. This is an average of all scores and may not be
|
||||
wholely accurate.
|
||||
<BaseCardSectionTitle :title="$tc('recipe.parser.ingredient-parser')">
|
||||
<div class="mt-4">{{ $tc("recipe.parser.explanation") }}</div>
|
||||
|
||||
<div class="my-4">
|
||||
Alerts will be displayed if a matching foods or unit is found but does not exists in the database.
|
||||
{{ $tc("recipe.parser.alerts-explainer") }}
|
||||
</div>
|
||||
<div class="d-flex align-center mb-n4">
|
||||
<div class="mb-4">Select Parser</div>
|
||||
<div class="mb-4">{{ $tc("recipe.parser.select-parser") }}</div>
|
||||
<BaseOverflowButton
|
||||
v-model="parser"
|
||||
btn-class="mx-2 mb-4"
|
||||
:items="[
|
||||
{
|
||||
text: 'Natural Language Processor ',
|
||||
text: $tc('recipe.parser.natural-language-processor'),
|
||||
value: 'nlp',
|
||||
},
|
||||
{
|
||||
text: 'Brute Parser',
|
||||
text: $tc('recipe.parser.brute-parser'),
|
||||
value: 'brute',
|
||||
},
|
||||
]"
|
||||
@@ -42,9 +37,9 @@
|
||||
<BaseButton cancel class="mr-auto" @click="$router.go(-1)"></BaseButton>
|
||||
<BaseButton color="info" @click="fetchParsed">
|
||||
<template #icon> {{ $globals.icons.foods }}</template>
|
||||
Parse All
|
||||
{{ $tc("recipe.parser.parse-all") }}
|
||||
</BaseButton>
|
||||
<BaseButton save @click="saveAll"> Save All </BaseButton>
|
||||
<BaseButton save @click="saveAll" />
|
||||
</div>
|
||||
|
||||
<v-expansion-panels v-model="panels" multiple>
|
||||
@@ -145,6 +140,8 @@ export default defineComponent({
|
||||
const slug = route.value.params.slug;
|
||||
const api = useUserApi();
|
||||
|
||||
const { i18n } = useContext();
|
||||
|
||||
const { recipe, loading } = useRecipe(slug);
|
||||
|
||||
invoke(async () => {
|
||||
@@ -170,13 +167,15 @@ export default defineComponent({
|
||||
if (unitError || foodError) {
|
||||
if (unitError) {
|
||||
if (ing?.ingredient?.unit?.name) {
|
||||
unitErrorMessage = `Create missing unit '${ing?.ingredient?.unit?.name || "No unit"}'`;
|
||||
const unit = ing.ingredient.unit.name || i18n.tc("recipe.parser.no-unit");
|
||||
unitErrorMessage = i18n.t("recipe.parser.missing-unit", { unit }).toString();
|
||||
}
|
||||
}
|
||||
|
||||
if (foodError) {
|
||||
if (ing?.ingredient?.food?.name) {
|
||||
foodErrorMessage = `Create missing food '${ing.ingredient.food.name || "No food"}'?`;
|
||||
const food = ing.ingredient.food.name || i18n.tc("recipe.parser.no-food");
|
||||
foodErrorMessage = i18n.t("recipe.parser.missing-food", { food }).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,7 +363,7 @@ export default defineComponent({
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: "Parser",
|
||||
title: this.$tc("recipe.parser.ingredient-parser"),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -41,6 +41,7 @@ export default defineComponent({
|
||||
const { i18n } = useContext();
|
||||
const buttonLookup: { [key: string]: string } = {
|
||||
recipes: i18n.tc("general.recipes"),
|
||||
recipeActions: i18n.tc("recipe.recipe-actions"),
|
||||
foods: i18n.tc("general.foods"),
|
||||
units: i18n.tc("general.units"),
|
||||
labels: i18n.tc("data-pages.labels.labels"),
|
||||
@@ -56,6 +57,11 @@ export default defineComponent({
|
||||
text: i18n.t("general.recipes"),
|
||||
value: "new",
|
||||
to: "/group/data/recipes",
|
||||
},
|
||||
{
|
||||
text: i18n.t("recipe.recipe-actions"),
|
||||
value: "new",
|
||||
to: "/group/data/recipe-actions",
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
@@ -92,7 +98,13 @@ export default defineComponent({
|
||||
]);
|
||||
|
||||
const buttonText = computed(() => {
|
||||
const last = route.value.path.split("/").pop();
|
||||
const last = route.value.path
|
||||
.split("/")
|
||||
.pop()
|
||||
// convert hypenated-values to camelCase
|
||||
?.replace(/-([a-z])/g, function (g) {
|
||||
return g[1].toUpperCase();
|
||||
})
|
||||
|
||||
if (last) {
|
||||
return buttonLookup[last];
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
v-model="state.editDialog"
|
||||
:icon="$globals.icons.tags"
|
||||
:title="$t('data-pages.labels.edit-label')"
|
||||
:submit-icon="$globals.icons.save"
|
||||
:submit-text="$tc('general.save')"
|
||||
@submit="editSaveLabel"
|
||||
>
|
||||
|
||||
265
frontend/pages/group/data/recipe-actions.vue
Normal file
265
frontend/pages/group/data/recipe-actions.vue
Normal file
@@ -0,0 +1,265 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Create Dialog -->
|
||||
<BaseDialog
|
||||
v-model="state.createDialog"
|
||||
:title="$t('data-pages.recipe-actions.new-recipe-action')"
|
||||
:icon="$globals.icons.primary"
|
||||
@submit="createAction"
|
||||
>
|
||||
<v-card-text>
|
||||
<v-form ref="domNewActionForm">
|
||||
<v-text-field
|
||||
v-model="createTarget.title"
|
||||
autofocus
|
||||
:label="$t('general.title')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="createTarget.url"
|
||||
:label="$t('general.url')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
<v-select
|
||||
v-model="createTarget.actionType"
|
||||
:items="actionTypeOptions"
|
||||
:label="$t('data-pages.recipe-actions.action-type')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
|
||||
<!-- Edit Dialog -->
|
||||
<BaseDialog
|
||||
v-model="state.editDialog"
|
||||
:icon="$globals.icons.primary"
|
||||
:title="$t('data-pages.recipe-actions.edit-recipe-action')"
|
||||
:submit-text="$tc('general.save')"
|
||||
@submit="editSaveAction"
|
||||
>
|
||||
<v-card-text v-if="editTarget">
|
||||
<div class="mt-4">
|
||||
<v-text-field v-model="editTarget.title" :label="$t('general.title')"/>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<v-text-field v-model="editTarget.url" :label="$t('general.url')"/>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<v-select
|
||||
v-model="editTarget.actionType"
|
||||
:items="actionTypeOptions"
|
||||
:label="$t('data-pages.recipe-actions.action-type')"
|
||||
/>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
|
||||
<!-- Delete Dialog -->
|
||||
<BaseDialog
|
||||
v-model="state.deleteDialog"
|
||||
:title="$tc('general.confirm')"
|
||||
:icon="$globals.icons.alertCircle"
|
||||
color="error"
|
||||
@confirm="deleteAction"
|
||||
>
|
||||
<v-card-text>
|
||||
{{ $t("general.confirm-delete-generic") }}
|
||||
<p v-if="deleteTarget" class="mt-4 ml-4">{{ deleteTarget.title }}</p>
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
|
||||
<!-- Bulk Delete Dialog -->
|
||||
<BaseDialog
|
||||
v-model="state.bulkDeleteDialog"
|
||||
width="650px"
|
||||
:title="$tc('general.confirm')"
|
||||
:icon="$globals.icons.alertCircle"
|
||||
color="error"
|
||||
@confirm="deleteSelected"
|
||||
>
|
||||
<v-card-text>
|
||||
<p class="h4">{{ $t('general.confirm-delete-generic-items') }}</p>
|
||||
<v-card outlined>
|
||||
<v-virtual-scroll height="400" item-height="25" :items="bulkDeleteTarget">
|
||||
<template #default="{ item }">
|
||||
<v-list-item class="pb-2">
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ item.name }}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</v-card>
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
|
||||
<!-- Data Table -->
|
||||
<BaseCardSectionTitle :icon="$globals.icons.primary" section :title="$tc('data-pages.recipe-actions.recipe-actions-data')"> </BaseCardSectionTitle>
|
||||
<CrudTable
|
||||
:table-config="tableConfig"
|
||||
:headers.sync="tableHeaders"
|
||||
:data="actions || []"
|
||||
:bulk-actions="[{icon: $globals.icons.delete, text: $tc('general.delete'), event: 'delete-selected'}]"
|
||||
@delete-one="deleteEventHandler"
|
||||
@edit-one="editEventHandler"
|
||||
@delete-selected="bulkDeleteEventHandler"
|
||||
>
|
||||
<template #button-row>
|
||||
<BaseButton create @click="state.createDialog = true">{{ $t("general.create") }}</BaseButton>
|
||||
</template>
|
||||
<template #item.onHand="{ item }">
|
||||
<v-icon :color="item.onHand ? 'success' : undefined">
|
||||
{{ item.onHand ? $globals.icons.check : $globals.icons.close }}
|
||||
</v-icon>
|
||||
</template>
|
||||
</CrudTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
import { useGroupRecipeActions, useGroupRecipeActionData } from "~/composables/use-group-recipe-actions";
|
||||
import { GroupRecipeActionOut } from "~/lib/api/types/group";
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const { i18n } = useContext();
|
||||
const tableConfig = {
|
||||
hideColumns: true,
|
||||
canExport: true,
|
||||
};
|
||||
const tableHeaders = [
|
||||
{
|
||||
text: i18n.t("general.id"),
|
||||
value: "id",
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
text: i18n.t("general.title"),
|
||||
value: "title",
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
text: i18n.t("general.url"),
|
||||
value: "url",
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
text: i18n.t("data-pages.recipe-actions.action-type"),
|
||||
value: "actionType",
|
||||
show: true,
|
||||
},
|
||||
];
|
||||
|
||||
const state = reactive({
|
||||
createDialog: false,
|
||||
editDialog: false,
|
||||
deleteDialog: false,
|
||||
bulkDeleteDialog: false,
|
||||
});
|
||||
|
||||
const actionData = useGroupRecipeActionData();
|
||||
const actionStore = useGroupRecipeActions(null, null);
|
||||
const actionTypeOptions = ["link", "post"]
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Create Action
|
||||
|
||||
async function createAction() {
|
||||
// @ts-ignore groupId isn't required
|
||||
await actionStore.actions.createOne({
|
||||
actionType: actionData.data.actionType,
|
||||
title: actionData.data.title,
|
||||
url: actionData.data.url,
|
||||
});
|
||||
actionData.reset();
|
||||
state.createDialog = false;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Edit Action
|
||||
|
||||
const editTarget = ref<GroupRecipeActionOut | null>(null);
|
||||
|
||||
function editEventHandler(item: GroupRecipeActionOut) {
|
||||
state.editDialog = true;
|
||||
editTarget.value = item;
|
||||
}
|
||||
|
||||
async function editSaveAction() {
|
||||
if (!editTarget.value) {
|
||||
return;
|
||||
}
|
||||
await actionStore.actions.updateOne(editTarget.value);
|
||||
state.editDialog = false;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Delete Action
|
||||
|
||||
const deleteTarget = ref<GroupRecipeActionOut | null>(null);
|
||||
|
||||
function deleteEventHandler(item: GroupRecipeActionOut) {
|
||||
state.deleteDialog = true;
|
||||
deleteTarget.value = item;
|
||||
}
|
||||
|
||||
async function deleteAction() {
|
||||
if (!deleteTarget.value || deleteTarget.value.id === undefined) {
|
||||
return;
|
||||
}
|
||||
await actionStore.actions.deleteOne(deleteTarget.value.id);
|
||||
state.deleteDialog = false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Bulk Delete Action
|
||||
|
||||
const bulkDeleteTarget = ref<GroupRecipeActionOut[]>([]);
|
||||
function bulkDeleteEventHandler(selection: GroupRecipeActionOut[]) {
|
||||
bulkDeleteTarget.value = selection;
|
||||
state.bulkDeleteDialog = true;
|
||||
}
|
||||
|
||||
async function deleteSelected() {
|
||||
for (const item of bulkDeleteTarget.value) {
|
||||
await actionStore.actions.deleteOne(item.id);
|
||||
}
|
||||
bulkDeleteTarget.value = [];
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
tableConfig,
|
||||
tableHeaders,
|
||||
actionTypeOptions,
|
||||
actions: actionStore.recipeActions,
|
||||
validators,
|
||||
|
||||
// create
|
||||
createTarget: actionData.data,
|
||||
createAction,
|
||||
|
||||
// edit
|
||||
editTarget,
|
||||
editEventHandler,
|
||||
editSaveAction,
|
||||
|
||||
// delete
|
||||
deleteTarget,
|
||||
deleteEventHandler,
|
||||
deleteAction,
|
||||
|
||||
// bulk delete
|
||||
bulkDeleteTarget,
|
||||
bulkDeleteEventHandler,
|
||||
deleteSelected,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -201,7 +201,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
function isDirectLogin() {
|
||||
return router.currentRoute.query.direct
|
||||
return Object.keys(router.currentRoute.query).includes("direct")
|
||||
}
|
||||
|
||||
async function oidcAuthenticate() {
|
||||
|
||||
@@ -58,11 +58,18 @@
|
||||
</div>
|
||||
|
||||
<!-- Reorder Labels -->
|
||||
<BaseDialog v-model="reorderLabelsDialog" :icon="$globals.icons.tagArrowUp" :title="$t('shopping-list.reorder-labels')">
|
||||
<BaseDialog
|
||||
v-model="reorderLabelsDialog"
|
||||
:icon="$globals.icons.tagArrowUp"
|
||||
:title="$t('shopping-list.reorder-labels')"
|
||||
:submit-icon="$globals.icons.save"
|
||||
:submit-text="$tc('general.save')"
|
||||
@submit="saveLabelOrder"
|
||||
@close="cancelLabelOrder">
|
||||
<v-card height="fit-content" max-height="70vh" style="overflow-y: auto;">
|
||||
<draggable :value="shoppingList.labelSettings" handle=".handle" class="my-2" @start="loadingCounter += 1" @end="loadingCounter -= 1" @input="updateLabelOrder">
|
||||
<div v-for="(labelSetting, index) in shoppingList.labelSettings" :key="labelSetting.id">
|
||||
<MultiPurposeLabelSection v-model="shoppingList.labelSettings[index]" use-color />
|
||||
<draggable v-if="localLabels" :value="localLabels" handle=".handle" class="my-2" @input="updateLabelOrder">
|
||||
<div v-for="(labelSetting, index) in localLabels" :key="labelSetting.id">
|
||||
<MultiPurposeLabelSection v-model="localLabels[index]" use-color />
|
||||
</div>
|
||||
</draggable>
|
||||
</v-card>
|
||||
@@ -103,7 +110,9 @@
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="mt-4 d-flex justify-end">
|
||||
<BaseButton v-if="preferences.viewByLabel" edit class="mr-2" @click="reorderLabelsDialog = true">
|
||||
<BaseButton
|
||||
v-if="preferences.viewByLabel" edit class="mr-2"
|
||||
@click="toggleReorderLabelsDialog">
|
||||
<template #icon> {{ $globals.icons.tags }} </template>
|
||||
{{ $t('shopping-list.reorder-labels') }}
|
||||
</BaseButton>
|
||||
@@ -279,6 +288,7 @@ export default defineComponent({
|
||||
const edit = ref(false);
|
||||
const reorderLabelsDialog = ref(false);
|
||||
const settingsDialog = ref(false);
|
||||
const preserveItemOrder = ref(false);
|
||||
|
||||
const route = useRoute();
|
||||
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
|
||||
@@ -299,8 +309,19 @@ export default defineComponent({
|
||||
loadingCounter.value -= 1;
|
||||
|
||||
// only update the list with the new value if we're not loading, to prevent UI jitter
|
||||
if (!loadingCounter.value) {
|
||||
if (loadingCounter.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
shoppingList.value = newListValue;
|
||||
updateListItemOrder();
|
||||
}
|
||||
|
||||
function updateListItemOrder() {
|
||||
if (!preserveItemOrder.value) {
|
||||
groupAndSortListItemsByFood();
|
||||
updateItemsByLabel();
|
||||
} else {
|
||||
sortListItems();
|
||||
updateItemsByLabel();
|
||||
}
|
||||
@@ -480,6 +501,8 @@ export default defineComponent({
|
||||
// Labels, Units, Foods
|
||||
// TODO: Extract to Composable
|
||||
|
||||
const localLabels = ref<ShoppingListMultiPurposeLabelOut[]>()
|
||||
|
||||
const { labels: allLabels } = useLabelStore();
|
||||
const { units: allUnits } = useUnitStore();
|
||||
const { foods: allFoods } = useFoodStore();
|
||||
@@ -493,7 +516,10 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
function toggleReorderLabelsDialog() {
|
||||
// stop polling and populate localLabels
|
||||
loadingCounter.value += 1
|
||||
reorderLabelsDialog.value = !reorderLabelsDialog.value
|
||||
localLabels.value = shoppingList.value?.labelSettings
|
||||
}
|
||||
|
||||
async function toggleSettingsDialog() {
|
||||
@@ -503,7 +529,7 @@ export default defineComponent({
|
||||
settingsDialog.value = !settingsDialog.value;
|
||||
}
|
||||
|
||||
async function updateLabelOrder(labelSettings: ShoppingListMultiPurposeLabelOut[]) {
|
||||
function updateLabelOrder(labelSettings: ShoppingListMultiPurposeLabelOut[]) {
|
||||
if (!shoppingList.value) {
|
||||
return;
|
||||
}
|
||||
@@ -513,16 +539,31 @@ export default defineComponent({
|
||||
return labelSetting;
|
||||
});
|
||||
|
||||
// setting this doesn't have any effect on the data since it's refreshed automatically, but it makes the ux feel smoother
|
||||
shoppingList.value.labelSettings = labelSettings;
|
||||
updateItemsByLabel();
|
||||
localLabels.value = labelSettings
|
||||
}
|
||||
|
||||
function cancelLabelOrder() {
|
||||
loadingCounter.value -= 1
|
||||
if (!shoppingList.value) {
|
||||
return;
|
||||
}
|
||||
// restore original state
|
||||
localLabels.value = shoppingList.value.labelSettings
|
||||
}
|
||||
|
||||
async function saveLabelOrder() {
|
||||
if (!shoppingList.value || !localLabels.value || (localLabels.value === shoppingList.value.labelSettings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadingCounter.value += 1;
|
||||
const { data } = await userApi.shopping.lists.updateLabelSettings(shoppingList.value.id, labelSettings);
|
||||
const { data } = await userApi.shopping.lists.updateLabelSettings(shoppingList.value.id, localLabels.value);
|
||||
loadingCounter.value -= 1;
|
||||
|
||||
if (data) {
|
||||
refresh();
|
||||
// update shoppingList labels using the API response
|
||||
shoppingList.value.labelSettings = (data as ShoppingListOut).labelSettings;
|
||||
updateItemsByLabel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,12 +584,62 @@ export default defineComponent({
|
||||
|
||||
const itemsByLabel = ref<{ [key: string]: ShoppingListItemOut[] }>({});
|
||||
|
||||
interface ListItemGroup {
|
||||
position: number;
|
||||
createdAt: string;
|
||||
items: ShoppingListItemOut[];
|
||||
}
|
||||
|
||||
function groupAndSortListItemsByFood() {
|
||||
if (!shoppingList.value?.listItems?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const checkedItemKey = "__checkedItem"
|
||||
const listItemGroupsMap = new Map<string, ListItemGroup>();
|
||||
listItemGroupsMap.set(checkedItemKey, {position: Number.MAX_SAFE_INTEGER, createdAt: "", items: []});
|
||||
|
||||
// group items by checked status, food, or note
|
||||
shoppingList.value.listItems.forEach((item) => {
|
||||
const key = item.checked ? checkedItemKey : item.isFood && item.food?.name
|
||||
? item.food.name
|
||||
: item.note || ""
|
||||
|
||||
const group = listItemGroupsMap.get(key);
|
||||
if (!group) {
|
||||
listItemGroupsMap.set(key, {position: item.position || 0, createdAt: item.createdAt || "", items: [item]});
|
||||
} else {
|
||||
group.items.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
// sort group items by position ascending, then createdAt descending
|
||||
const listItemGroups = Array.from(listItemGroupsMap.values());
|
||||
listItemGroups.sort((a, b) => (a.position > b.position || a.createdAt < b.createdAt ? 1 : -1));
|
||||
|
||||
// sort group items by position ascending, then createdAt descending, and aggregate them
|
||||
const sortedItems: ShoppingListItemOut[] = [];
|
||||
let nextPosition = 0;
|
||||
listItemGroups.forEach((listItemGroup) => {
|
||||
// @ts-ignore none of these fields are undefined
|
||||
listItemGroup.items.sort((a, b) => (a.position > b.position || a.createdAt < b.createdAt ? 1 : -1));
|
||||
listItemGroup.items.forEach((item) => {
|
||||
item.position = nextPosition;
|
||||
nextPosition += 1;
|
||||
sortedItems.push(item);
|
||||
})
|
||||
});
|
||||
|
||||
shoppingList.value.listItems = sortedItems;
|
||||
}
|
||||
|
||||
function sortListItems() {
|
||||
if (!shoppingList.value?.listItems?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// sort by position ascending, then createdAt descending
|
||||
// @ts-ignore none of these fields are undefined
|
||||
shoppingList.value.listItems.sort((a, b) => (a.position > b.position || a.createdAt < b.createdAt ? 1 : -1))
|
||||
}
|
||||
|
||||
@@ -682,8 +773,7 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
|
||||
sortListItems();
|
||||
updateItemsByLabel();
|
||||
updateListItemOrder();
|
||||
|
||||
loadingCounter.value += 1;
|
||||
const { data } = await userApi.shopping.items.updateOne(item.id, item);
|
||||
@@ -759,6 +849,9 @@ export default defineComponent({
|
||||
shoppingList.value.listItems = uncheckedItems.concat(listItems.value.checked);
|
||||
}
|
||||
|
||||
// since the user has manually reordered the list, we should preserve this order
|
||||
preserveItemOrder.value = true;
|
||||
|
||||
updateListItems();
|
||||
}
|
||||
|
||||
@@ -776,6 +869,9 @@ export default defineComponent({
|
||||
allUncheckedItems.push(...itemsByLabel.value[labelName]);
|
||||
}
|
||||
|
||||
// since the user has manually reordered the list, we should preserve this order
|
||||
preserveItemOrder.value = true;
|
||||
|
||||
// save changes
|
||||
return updateIndexUnchecked(allUncheckedItems);
|
||||
}
|
||||
@@ -873,7 +969,10 @@ export default defineComponent({
|
||||
toggleReorderLabelsDialog,
|
||||
settingsDialog,
|
||||
toggleSettingsDialog,
|
||||
localLabels,
|
||||
updateLabelOrder,
|
||||
cancelLabelOrder,
|
||||
saveLabelOrder,
|
||||
saveListItem,
|
||||
shoppingList,
|
||||
showChecked,
|
||||
|
||||
@@ -190,7 +190,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, useContext, ref, toRefs, reactive, useAsync, useRoute } from "@nuxtjs/composition-api";
|
||||
import { invoke, until } from "@vueuse/core";
|
||||
import UserProfileLinkCard from "@/components/Domain/User/UserProfileLinkCard.vue";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
@@ -198,7 +197,7 @@ import { alert } from "~/composables/use-toast";
|
||||
import UserAvatar from "@/components/Domain/User/UserAvatar.vue";
|
||||
import { useAsyncKey } from "~/composables/use-utils";
|
||||
import StatsCards from "~/components/global/StatsCards.vue";
|
||||
import { GroupInDB, UserOut } from "~/lib/api/types/user";
|
||||
import { UserOut } from "~/lib/api/types/user";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UserProfile",
|
||||
@@ -216,7 +215,6 @@ export default defineComponent({
|
||||
|
||||
// @ts-ignore $auth.user is typed as unknown, but it's a user
|
||||
const user = computed<UserOut | null>(() => $auth.user);
|
||||
const group = ref<GroupInDB | null>(null);
|
||||
|
||||
const showPublicLink = ref(false);
|
||||
const publicLink = ref("");
|
||||
@@ -225,16 +223,6 @@ export default defineComponent({
|
||||
const token = ref("");
|
||||
const api = useUserApi();
|
||||
|
||||
invoke(async () => {
|
||||
await until(user.value).not.toBeNull();
|
||||
if (!user.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = await api.users.getSelfGroup();
|
||||
group.value = data;
|
||||
});
|
||||
|
||||
async function getSignupLink() {
|
||||
const { data } = await api.groups.createInvitation({ uses: 1 });
|
||||
if (data) {
|
||||
@@ -333,7 +321,6 @@ export default defineComponent({
|
||||
getStatsTitle,
|
||||
getStatsIcon,
|
||||
getStatsTo,
|
||||
group,
|
||||
stats,
|
||||
user,
|
||||
constructLink,
|
||||
|
||||
@@ -9,14 +9,12 @@ export default class DynamicOpenIDConnectScheme extends OpenIDConnectScheme {
|
||||
|
||||
async mounted() {
|
||||
await this.getConfiguration();
|
||||
this.options.scope = ["openid", "profile", "email", "groups"]
|
||||
|
||||
this.configurationDocument = new ConfigurationDocument(
|
||||
this,
|
||||
this.$auth.$storage
|
||||
)
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return await super.mounted()
|
||||
}
|
||||
@@ -78,7 +76,10 @@ export default class DynamicOpenIDConnectScheme extends OpenIDConnectScheme {
|
||||
})
|
||||
// Update tokens with mealie token
|
||||
this.updateTokens(response)
|
||||
} catch {
|
||||
} catch (e) {
|
||||
if (e.response?.status === 401 || e.response?.status === 500) {
|
||||
this.$auth.reset()
|
||||
}
|
||||
const currentUrl = new URL(window.location.href)
|
||||
if (currentUrl.pathname === "/login" && currentUrl.searchParams.has("direct")) {
|
||||
return
|
||||
@@ -109,6 +110,11 @@ export default class DynamicOpenIDConnectScheme extends OpenIDConnectScheme {
|
||||
const data = await response.json();
|
||||
this.options.endpoints.configuration = data.configurationUrl;
|
||||
this.options.clientId = data.clientId;
|
||||
this.options.scope = ["openid", "profile", "email"]
|
||||
if (data.groupsClaim !== null) {
|
||||
this.options.scope.push(data.groupsClaim)
|
||||
}
|
||||
console.log(this.options.scope)
|
||||
} catch (error) {
|
||||
// pass
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
import fastapi
|
||||
import jwt
|
||||
from fastapi import BackgroundTasks, Depends, HTTPException, Request, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import JWTError, jwt
|
||||
from jwt.exceptions import PyJWTError
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core import root_logger
|
||||
@@ -96,8 +97,8 @@ async def get_current_user(
|
||||
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET, algorithms=[ALGORITHM])
|
||||
user_id: str = payload.get("sub")
|
||||
long_token: str = payload.get("long_token")
|
||||
user_id: str | None = payload.get("sub")
|
||||
long_token: str | None = payload.get("long_token")
|
||||
|
||||
if long_token is not None:
|
||||
return validate_long_live_token(session, token, payload.get("id"))
|
||||
@@ -106,7 +107,7 @@ async def get_current_user(
|
||||
raise credentials_exception
|
||||
|
||||
token_data = TokenData(user_id=user_id)
|
||||
except JWTError as e:
|
||||
except PyJWTError as e:
|
||||
raise credentials_exception from e
|
||||
|
||||
repos = get_repositories(session)
|
||||
@@ -126,7 +127,7 @@ async def get_integration_id(token: str = Depends(oauth2_scheme)) -> str:
|
||||
decoded_token = jwt.decode(token, settings.SECRET, algorithms=[ALGORITHM])
|
||||
return decoded_token.get("integration_id", DEFAULT_INTEGRATION_ID)
|
||||
|
||||
except JWTError as e:
|
||||
except PyJWTError as e:
|
||||
raise credentials_exception from e
|
||||
|
||||
|
||||
@@ -162,7 +163,7 @@ def validate_file_token(token: str | None = None) -> Path:
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET, algorithms=[ALGORITHM])
|
||||
file_path = Path(payload.get("file"))
|
||||
except JWTError as e:
|
||||
except PyJWTError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="could not validate file token",
|
||||
@@ -181,7 +182,7 @@ def validate_recipe_token(token: str | None = None) -> str:
|
||||
|
||||
Raises:
|
||||
HTTPException: 400 Bad Request when no token or the recipe doesn't exist
|
||||
HTTPException: 401 JWTError when token is invalid
|
||||
HTTPException: 401 PyJWTError when token is invalid
|
||||
|
||||
Returns:
|
||||
str: token data
|
||||
@@ -192,7 +193,7 @@ def validate_recipe_token(token: str | None = None) -> str:
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET, algorithms=[ALGORITHM])
|
||||
slug: str | None = payload.get("slug")
|
||||
except JWTError as e:
|
||||
except PyJWTError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="could not validate file token",
|
||||
|
||||
@@ -2,7 +2,7 @@ import abc
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
from jose import jwt
|
||||
import jwt
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.config import get_app_settings
|
||||
|
||||
@@ -35,7 +35,9 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
|
||||
repos = get_repositories(self.session)
|
||||
|
||||
user = self.try_get_user(claims.get(settings.OIDC_USER_CLAIM))
|
||||
group_claim = claims.get("groups", [])
|
||||
is_admin = False
|
||||
if settings.OIDC_USER_GROUP or settings.OIDC_ADMIN_GROUP:
|
||||
group_claim = claims.get(settings.OIDC_GROUPS_CLAIM, [])
|
||||
is_admin = settings.OIDC_ADMIN_GROUP in group_claim if settings.OIDC_ADMIN_GROUP else False
|
||||
is_valid_user = settings.OIDC_USER_GROUP in group_claim if settings.OIDC_USER_GROUP else True
|
||||
|
||||
@@ -68,18 +70,18 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
|
||||
return self.get_access_token(user, settings.OIDC_REMEMBER_ME) # type: ignore
|
||||
|
||||
if user:
|
||||
if user.admin != is_admin:
|
||||
if settings.OIDC_ADMIN_GROUP and user.admin != is_admin:
|
||||
self._logger.debug(f"[OIDC] {'Setting' if is_admin else 'Removing'} user as admin")
|
||||
user.admin = is_admin
|
||||
repos.users.update(user.id, user)
|
||||
return self.get_access_token(user, settings.OIDC_REMEMBER_ME)
|
||||
|
||||
self._logger.info("[OIDC] Found user but their AuthMethod does not match OIDC")
|
||||
self._logger.warning("[OIDC] Found user but their AuthMethod does not match OIDC")
|
||||
return None
|
||||
|
||||
def get_claims(self, settings: AppSettings) -> JWTClaims | None:
|
||||
"""Get the claims from the ID token and check if the required claims are present"""
|
||||
required_claims = {"preferred_username", "name", "email"}
|
||||
required_claims = {"preferred_username", "name", "email", settings.OIDC_USER_CLAIM}
|
||||
jwks = OpenIDProvider.get_jwks()
|
||||
if not jwks:
|
||||
return None
|
||||
@@ -91,15 +93,18 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
|
||||
self._logger.error(
|
||||
f"[OIDC] Unsupported algorithm '{algorithm}'. Unable to decode id token due to mismatched algorithm."
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
claims.validate()
|
||||
except ExpiredTokenError as e:
|
||||
self._logger.debug(f"[OIDC] {e.error}: {e.description}")
|
||||
self._logger.error(f"[OIDC] {e.error}: {e.description}")
|
||||
return None
|
||||
except Exception as e:
|
||||
self._logger.error("[OIDC] Exception while validating id_token claims", e)
|
||||
|
||||
if not claims:
|
||||
self._logger.warning("[OIDC] Claims not found")
|
||||
self._logger.error("[OIDC] Claims not found")
|
||||
return None
|
||||
if not required_claims.issubset(claims.keys()):
|
||||
self._logger.error(
|
||||
@@ -116,20 +121,27 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
|
||||
|
||||
if not (settings.OIDC_READY and settings.OIDC_CONFIGURATION_URL):
|
||||
return None
|
||||
configuration = None
|
||||
with requests.get(settings.OIDC_CONFIGURATION_URL, timeout=5) as config_response:
|
||||
|
||||
session = requests.Session()
|
||||
if settings.OIDC_TLS_CACERTFILE:
|
||||
session.verify = settings.OIDC_TLS_CACERTFILE
|
||||
|
||||
config_response = session.get(settings.OIDC_CONFIGURATION_URL, timeout=5)
|
||||
config_response.raise_for_status()
|
||||
configuration = config_response.json()
|
||||
|
||||
if not configuration:
|
||||
OpenIDProvider._logger.warning("[OIDC] Unable to fetch configuration from the OIDC_CONFIGURATION_URL")
|
||||
session.close()
|
||||
return None
|
||||
|
||||
jwks_uri = configuration.get("jwks_uri", None)
|
||||
if not jwks_uri:
|
||||
OpenIDProvider._logger.warning("[OIDC] Unable to find the jwks_uri from the OIDC_CONFIGURATION_URL")
|
||||
session.close()
|
||||
return None
|
||||
|
||||
with requests.get(jwks_uri, timeout=5) as response:
|
||||
response = session.get(jwks_uri, timeout=5)
|
||||
response.raise_for_status()
|
||||
session.close()
|
||||
return JsonWebKey.import_key_set(response.json())
|
||||
|
||||
@@ -2,8 +2,8 @@ import secrets
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
|
||||
import jwt
|
||||
from fastapi import Request
|
||||
from jose import jwt
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core import root_logger
|
||||
|
||||
@@ -192,16 +192,20 @@ class AppSettings(BaseSettings):
|
||||
OIDC_REMEMBER_ME: bool = False
|
||||
OIDC_SIGNING_ALGORITHM: str = "RS256"
|
||||
OIDC_USER_CLAIM: str = "email"
|
||||
OIDC_GROUPS_CLAIM: str | None = "groups"
|
||||
OIDC_TLS_CACERTFILE: str | None = None
|
||||
|
||||
@property
|
||||
def OIDC_READY(self) -> bool:
|
||||
"""Validates OIDC settings are all set"""
|
||||
|
||||
required = {self.OIDC_CLIENT_ID, self.OIDC_CONFIGURATION_URL}
|
||||
required = {self.OIDC_CLIENT_ID, self.OIDC_CONFIGURATION_URL, self.OIDC_USER_CLAIM}
|
||||
not_none = None not in required
|
||||
valid_user_claim = self.OIDC_USER_CLAIM in ["email", "preferred_username"]
|
||||
valid_group_claim = True
|
||||
if (not self.OIDC_USER_GROUP or not self.OIDC_ADMIN_GROUP) and not self.OIDC_GROUPS_CLAIM:
|
||||
valid_group_claim = False
|
||||
|
||||
return self.OIDC_AUTH_ENABLED and not_none and valid_user_claim
|
||||
return self.OIDC_AUTH_ENABLED and not_none and valid_group_claim
|
||||
|
||||
# ===============================================
|
||||
# Testing Config
|
||||
|
||||
@@ -27,10 +27,10 @@ class GUID(TypeDecorator):
|
||||
return str(value)
|
||||
else:
|
||||
if not isinstance(value, uuid.UUID):
|
||||
return "%.32x" % uuid.UUID(value).int
|
||||
return f"{uuid.UUID(value).int:032x}"
|
||||
else:
|
||||
# hexstring
|
||||
return "%.32x" % value.int
|
||||
return f"{value.int:032x}"
|
||||
|
||||
def load_dialect_impl(self, dialect):
|
||||
if dialect.name == "postgresql":
|
||||
|
||||
@@ -5,6 +5,7 @@ from .group import *
|
||||
from .invite_tokens import *
|
||||
from .mealplan import *
|
||||
from .preferences import *
|
||||
from .recipe_action import *
|
||||
from .report import *
|
||||
from .shopping_list import *
|
||||
from .webhooks import *
|
||||
|
||||
@@ -25,6 +25,7 @@ if TYPE_CHECKING:
|
||||
from ..users import User
|
||||
from .events import GroupEventNotifierModel
|
||||
from .exports import GroupDataExportsModel
|
||||
from .recipe_action import GroupRecipeAction
|
||||
from .report import ReportModel
|
||||
from .shopping_list import ShoppingList
|
||||
|
||||
@@ -64,6 +65,7 @@ class Group(SqlAlchemyBase, BaseMixins):
|
||||
GroupMealPlan, order_by="GroupMealPlan.date", **common_args
|
||||
)
|
||||
webhooks: Mapped[list[GroupWebhooksModel]] = orm.relationship(GroupWebhooksModel, **common_args)
|
||||
recipe_actions: Mapped[list["GroupRecipeAction"]] = orm.relationship("GroupRecipeAction", **common_args)
|
||||
cookbooks: Mapped[list[CookBook]] = orm.relationship(CookBook, **common_args)
|
||||
server_tasks: Mapped[list[ServerTaskModel]] = orm.relationship(ServerTaskModel, **common_args)
|
||||
data_exports: Mapped[list["GroupDataExportsModel"]] = orm.relationship("GroupDataExportsModel", **common_args)
|
||||
@@ -82,6 +84,7 @@ class Group(SqlAlchemyBase, BaseMixins):
|
||||
exclude={
|
||||
"users",
|
||||
"webhooks",
|
||||
"recipe_actions",
|
||||
"shopping_lists",
|
||||
"cookbooks",
|
||||
"preferences",
|
||||
|
||||
25
mealie/db/models/group/recipe_action.py
Normal file
25
mealie/db/models/group/recipe_action.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import ForeignKey, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from .._model_base import BaseMixins, SqlAlchemyBase
|
||||
from .._model_utils import GUID, auto_init
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from group import Group
|
||||
|
||||
|
||||
class GroupRecipeAction(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "recipe_actions"
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
group_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("groups.id"), index=True)
|
||||
group: Mapped["Group"] = relationship("Group", back_populates="recipe_actions", single_parent=True)
|
||||
|
||||
action_type: Mapped[str] = mapped_column(String, index=True)
|
||||
title: Mapped[str] = mapped_column(String, index=True)
|
||||
url: Mapped[str] = mapped_column(String)
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
@@ -260,10 +260,9 @@ def receive_description(target: RecipeModel, value: str, oldvalue, initiator):
|
||||
@event.listens_for(RecipeModel, "before_update")
|
||||
def calculate_rating(mapper, connection, target: RecipeModel):
|
||||
session = object_session(target)
|
||||
if not session:
|
||||
if not (session and session.is_modified(target, "rating")):
|
||||
return
|
||||
|
||||
if session.is_modified(target, "rating"):
|
||||
history = get_history(target, "rating")
|
||||
old_value = history.deleted[0] if history.deleted else None
|
||||
new_value = history.added[0] if history.added else None
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "Iets het skeefgeloop"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Nuwe resepname moet uniek wees"
|
||||
"unique-name-error": "Nuwe resepname moet uniek wees",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Cup Flour",
|
||||
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "Geen resepte voldoen aan jou reëls nie"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "حدث خطأ غير متوقع"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "يجب أن تكون أسماء الوصفات فريدة"
|
||||
"unique-name-error": "يجب أن تكون أسماء الوصفات فريدة",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Cup Flour",
|
||||
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "لا توجد وصفات تتطابق مع المرشحات الخاصة بك"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "Възникна неочаквана грешка"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Името на рецептата трябва да е уникално"
|
||||
"unique-name-error": "Името на рецептата трябва да е уникално",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 чаша брашно",
|
||||
"step-text": "Стъпките на рецептата, както и други полета в страницата с рецепти поддържат синтаксис за маркиране.\n\n **Добавяне на връзка**\n\n [Моята връзка](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "Няма рецепти отговарящи на Вашите условия"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "S'ha produït un error inesperat"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "El nom de la recepta ha de ser únic"
|
||||
"unique-name-error": "El nom de la recepta ha de ser únic",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Cup Flour",
|
||||
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "Cap recepta coincideix amb les teues regles"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "Došlo k nečekané chybě"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Názvy receptů musí být jedinečné"
|
||||
"unique-name-error": "Názvy receptů musí být jedinečné",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Cup Flour",
|
||||
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "Žádné recepty neodpovídají vašim pravidlům"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "Der opstod en uventet fejl"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Opskriftsnavnet er allerede i brug"
|
||||
"unique-name-error": "Opskriftsnavnet er allerede i brug",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Cup Flour",
|
||||
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "Ingen opskrifter matcher dine regler"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "Ein unerwarteter Fehler ist aufgetreten"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Rezeptnamen müssen einzigartig sein"
|
||||
"unique-name-error": "Rezeptnamen müssen einzigartig sein",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "250 g Mehl",
|
||||
"step-text": "Zubereitungs-Schritte und andere Felder der Rezeptseite unterstützen Markdown Syntax.\n\n**Füge einen Link hinzu**\n\n[Mein Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "Keine Rezepte entsprechen deinen Regeln"
|
||||
@@ -37,7 +41,7 @@
|
||||
"day": "Tag|Tage",
|
||||
"hour": "Stunde|Stunden",
|
||||
"minute": "Minute|Minuten",
|
||||
"second": "sekunde|sekunden",
|
||||
"second": "Sekunde|Sekunden",
|
||||
"millisecond": "Millisekunde|Millisekunden",
|
||||
"microsecond": "Mikrosekunde|Mikrosekunden"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "An unexpected error occurred"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Recipe names must be unique"
|
||||
"unique-name-error": "Recipe names must be unique",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Κύπελλο Αλεύρι",
|
||||
"step-text": "Βήματα συνταγής, καθώς και άλλα πεδία στη σύνταξη σήμανσης της σελίδας συνταγής.\n\n**Προσθήκη συνδέσμου**\n\n[Ο σύνδεσμος μου](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "No recipes match your rules"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "An unexpected error occurred"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Recipe names must be unique"
|
||||
"unique-name-error": "Recipe names must be unique",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Cup Flour",
|
||||
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "No recipes match your rules"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "An unexpected error occurred"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Recipe names must be unique"
|
||||
"unique-name-error": "Recipe names must be unique",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Cup Flour",
|
||||
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "No recipes match your rules"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "Se ha producido un error inesperado"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "El nombre de la receta debe ser único"
|
||||
"unique-name-error": "El nombre de la receta debe ser único",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Cup Flour",
|
||||
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "No hay recetas que coincidan con tus reglas"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "Tapahtui odottamaton virhe"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Reseptien nimien täytyy olla yksilöllisiä"
|
||||
"unique-name-error": "Reseptien nimien täytyy olla yksilöllisiä",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Cup Flour",
|
||||
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "Määritysten mukaisia reseptejä ei löytynyt"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "Une erreur inattendue s'est produite"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Les noms de recette doivent être uniques"
|
||||
"unique-name-error": "Les noms de recette doivent être uniques",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 tasse de Farine",
|
||||
"step-text": "Les étapes de la recette ainsi que les autres champs de la page de recette supportent la syntaxe markdown.\n\n**Ajouter un lien**\n\n[Mon lien](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "Aucune recette ne correspond à vos règles"
|
||||
@@ -33,12 +37,12 @@
|
||||
"generic-deleted": "{name} a été supprimé"
|
||||
},
|
||||
"datetime": {
|
||||
"year": "year|years",
|
||||
"day": "day|days",
|
||||
"hour": "hour|hours",
|
||||
"year": "année|années",
|
||||
"day": "jour|jours",
|
||||
"hour": "heure|heures",
|
||||
"minute": "minute|minutes",
|
||||
"second": "second|seconds",
|
||||
"millisecond": "millisecond|milliseconds",
|
||||
"microsecond": "microsecond|microseconds"
|
||||
"second": "seconde|secondes",
|
||||
"millisecond": "milliseconde|millisecondes",
|
||||
"microsecond": "microseconde|microsecondes"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "Une erreur inattendue s’est produite"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Les noms de recette doivent être uniques"
|
||||
"unique-name-error": "Les noms de recette doivent être uniques",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "100 g de farine",
|
||||
"step-text": "Les étapes de la recette ainsi que les autres champs de la page de recette supportent la syntaxe markdown.\n\n**Ajouter un lien**\n\n[Mon lien](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "Aucune recette ne correspond à vos règles"
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"server-error": "An unexpected error occurred"
|
||||
},
|
||||
"recipe": {
|
||||
"unique-name-error": "Recipe names must be unique"
|
||||
"unique-name-error": "Recipe names must be unique",
|
||||
"recipe-defaults": {
|
||||
"ingredient-note": "1 Cup Flour",
|
||||
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
|
||||
}
|
||||
},
|
||||
"mealplan": {
|
||||
"no-recipes-match-your-rules": "No recipes match your rules"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user