Compare commits

...

36 Commits

Author SHA1 Message Date
Hayden
d1c5a6ed8c New Crowdin updates (#3635) 2024-05-23 07:48:06 +02:00
Michael Genson
ca26639525 feat: Data Management from Shopping List (#3603)
Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
2024-05-22 21:58:16 +00:00
renovate[bot]
89982f3e5f fix(deps): update dependency requests to v2.32.2 (#3632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-22 20:32:00 +10:00
renovate[bot]
8485b17490 fix(deps): update dependency openai to v1.30.1 (#3633)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-22 09:57:12 +00:00
Michael Genson
5c57b3dd1a feat: OpenAI Ingredient Parsing (#3581) 2024-05-22 09:45:07 +00:00
renovate[bot]
4c8bbdcde2 chore(deps): update dependency mkdocs-material to v9.5.24 (#3629)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-21 21:59:17 +10:00
renovate[bot]
2607066570 fix(deps): update dependency apprise to v1.8.0 (#3588)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-20 23:19:03 +00:00
renovate[bot]
8b7c8be51d fix(deps): update dependency requests to v2.32.1 (#3631)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-20 18:08:33 -05:00
nephlm
c70a5cb72c fix: Fix file not found error with individual recipe export/download. (#3579) 2024-05-20 17:53:14 -05:00
Michael Genson
c610ec1344 fix: Broken Import (#3630) 2024-05-20 07:55:01 -08:00
Michael Genson
61becdbec7 chore: Remove Server Tasks (#3592) 2024-05-20 14:51:37 +00:00
renovate[bot]
78d2a3b8aa chore(deps): update dependency pylint to v3.2.2 (#3625)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-20 20:37:03 +10:00
Michael Genson
a4c3b0da71 fix: NLP Ingredient Parser Misses Some Fractions (#3618) 2024-05-20 10:18:11 +00:00
Hayden
3d3279738b New Crowdin updates (#3622) 2024-05-20 00:36:33 +00:00
renovate[bot]
495d643ed9 fix(deps): update dependency rapidfuzz to v3.9.1 (#3623)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-19 19:28:23 -05:00
renovate[bot]
9094d24e50 chore(deps): update dependency pytest to v8.2.1 (#3621)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-19 15:53:19 -05:00
renovate[bot]
aa4d0f9958 chore(deps): update dependency pytest-asyncio to v0.23.7 (#3620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-19 10:59:31 -05:00
renovate[bot]
cb821994ae chore(deps): update dependency pylint to v3.2.1 (#3616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-18 12:53:27 -05:00
Hayden
b07a3a31f7 New Crowdin updates (#3614) 2024-05-17 17:48:33 -05:00
Zac Warham
68ff5f4b1c Fixed comment describing method (#3611) 2024-05-17 00:30:01 +00:00
renovate[bot]
cde9d166a4 chore(deps): update dependency mkdocs-material to v9.5.23 (#3605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2024-05-16 22:29:23 +00:00
Hayden
041145423f New Crowdin updates (#3604) 2024-05-16 17:11:54 -05:00
renovate[bot]
c227519fb7 chore(deps): update dependency pylint to v3.2.0 (#3598)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-14 09:04:29 -05:00
Hayden
94223d2903 New Crowdin updates (#3596) 2024-05-13 12:24:07 -05:00
renovate[bot]
e015c65d92 chore(deps): update dependency pylint to v3.1.1 (#3595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-13 15:30:03 +00:00
renovate[bot]
53916badf3 chore(deps): update dependency mkdocs-material to v9.5.22 (#3589)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-12 20:00:30 -05:00
Michael Genson
c82549ccb4 feat: Default To Fractions When Unit Is Empty (#3587)
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2024-05-12 14:15:26 -05:00
Hayden
554b3fa749 New Crowdin updates (#3590) 2024-05-12 17:59:58 +01:00
Carter
3f263281e7 Add time-based caching for JWKS fetching (#3586) 2024-05-11 21:21:55 -05:00
renovate[bot]
dc47145af6 chore(deps): update dependency pre-commit to v3.7.1 (#3583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-11 17:24:29 +00:00
Hayden
0ccee3584c New Crowdin updates (#3585) 2024-05-11 12:15:17 -05:00
Hayden
dc8aadc327 New Crowdin updates (#3580) 2024-05-10 15:28:14 +00:00
renovate[bot]
efbb571bc2 chore(deps): update dependency ruff to v0.4.4 (#3577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-09 18:39:31 -05:00
Hayden
1df75328d7 New Crowdin updates (#3576) 2024-05-09 14:45:26 +02:00
Hayden
85e402ccc3 New Crowdin updates (#3575) 2024-05-08 14:18:40 +02:00
github-actions[bot]
53a1f04562 docs(auto): Update image tag, for release v1.6.0 (#3571)
Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
2024-05-07 18:04:46 +02:00
113 changed files with 1746 additions and 1163 deletions

View File

@@ -26,7 +26,7 @@ Do the following for each recipe you want to intelligently handle ingredients.
6. Click the Edit button/icon again 6. Click the Edit button/icon again
7. Scroll to the ingredients and you should see new fields for Amount, Unit, Food, and Note. The Note in particular will contain the original text of the Recipe. 7. Scroll to the ingredients and you should see new fields for Amount, Unit, Food, and Note. The Note in particular will contain the original text of the Recipe.
8. Click `Parse` and you will be taken to the ingredient parsing page. 8. Click `Parse` and you will be taken to the ingredient parsing page.
9. Choose your parser. The `Natural Language Parser` works very well, but you can also use the `Brute Parser`. 9. Choose your parser. The `Natural Language Parser` works very well, but you can also use the `Brute Parser`, or the `OpenAI Parser` if you've [enabled OpenAI support](./installation/backend-config.md#openai).
10. Click `Parse All`, and your ingredients should be separated out into Units and Foods based on your seeding in Step 1 above. 10. Click `Parse All`, and your ingredients should be separated out into Units and Foods based on your seeding in Step 1 above.
11. For ingredients where the Unit or Food was not found, you can click a button to accept an automatically suggested Food to add to the database. Or, manually enter the Unit/Food and hit `Enter` (or click `Create`) to add it to the database 11. For ingredients where the Unit or Food was not found, you can click a button to accept an automatically suggested Food to add to the database. Or, manually enter the Unit/Food and hit `Enter` (or click `Create`) to add it to the database
12. When done, click `Save All` and you will be taken back to the recipe. Now the Unit and Food fields of the recipe should be filled out. 12. When done, click `Save All` and you will be taken back to the recipe. Now the Unit and Food fields of the recipe should be filled out.

View File

@@ -102,6 +102,20 @@ For usage, see [Usage - OpenID Connect](../authentication/oidc.md)
| 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_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`) | | OIDC_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) |
### OpenAI
:octicons-tag-24: v1.7.0
Mealie supports various integrations using OpenAI. To enable OpenAI, [you must provide your OpenAI API key](https://platform.openai.com/api-keys). You can tweak how OpenAI is used using these backend settings. Please note that while OpenAI usage is optimized to reduce API costs, you're unlikely to be able to use OpenAI features with the free tier limits.
| Variables | Default | Description |
| ------------------------- | :------: | ------------------------------------------------------------------------------------------------------------------------------ |
| OPENAI_BASE_URL | None | The base URL for the OpenAI API. If you're not sure, leave this empty to use the standard OpenAI platform |
| OPENAI_API_KEY | None | Your OpenAI API Key. Enables OpenAI-related features |
| OPENAI_MODEL | gpt-4o | Which OpenAI model to use. If you're not sure, leave this empty |
| OPENAI_WORKERS | 2 | Number of OpenAI workers per request. Higher values may increase processing speed, but will incur additional API costs |
| OPENAI_SEND_DATABASE_DATA | True | Whether to send Mealie data to OpenAI to improve request accuracy. This will incur additional API costs |
### Themeing ### Themeing
Setting the following environmental variables will change the theme of the frontend. Note that the themes are the same for all users. This is a break-change when migration from v0.x.x -> 1.x.x. Setting the following environmental variables will change the theme of the frontend. Note that the themes are the same for all users. This is a break-change when migration from v0.x.x -> 1.x.x.

View File

@@ -7,7 +7,7 @@ PostgreSQL might be considered if you need to support many concurrent users. In
```yaml ```yaml
services: services:
mealie: mealie:
image: ghcr.io/mealie-recipes/mealie:v1.5.1 # (3) image: ghcr.io/mealie-recipes/mealie:v1.6.0 # (3)
container_name: mealie container_name: mealie
restart: always restart: always
ports: ports:

View File

@@ -11,7 +11,7 @@ SQLite is a popular, open source, self-contained, zero-configuration database th
```yaml ```yaml
services: services:
mealie: mealie:
image: ghcr.io/mealie-recipes/mealie:v1.5.1 # (3) image: ghcr.io/mealie-recipes/mealie:v1.6.0 # (3)
container_name: mealie container_name: mealie
restart: always restart: always
ports: ports:

File diff suppressed because one or more lines are too long

View File

@@ -29,6 +29,7 @@
</v-col> </v-col>
<v-col v-if="!disableAmount" sm="12" md="3" cols="12"> <v-col v-if="!disableAmount" sm="12" md="3" cols="12">
<v-autocomplete <v-autocomplete
ref="unitAutocomplete"
v-model="value.unit" v-model="value.unit"
:search-input.sync="unitSearch" :search-input.sync="unitSearch"
auto-select-first auto-select-first
@@ -57,6 +58,7 @@
<!-- Foods Input --> <!-- Foods Input -->
<v-col v-if="!disableAmount" m="12" md="3" cols="12" class=""> <v-col v-if="!disableAmount" m="12" md="3" cols="12" class="">
<v-autocomplete <v-autocomplete
ref="foodAutocomplete"
v-model="value.food" v-model="value.food"
:search-input.sync="foodSearch" :search-input.sync="foodSearch"
auto-select-first auto-select-first
@@ -200,11 +202,13 @@ export default defineComponent({
const foodStore = useFoodStore(); const foodStore = useFoodStore();
const foodData = useFoodData(); const foodData = useFoodData();
const foodSearch = ref(""); const foodSearch = ref("");
const foodAutocomplete = ref<HTMLInputElement>();
async function createAssignFood() { async function createAssignFood() {
foodData.data.name = foodSearch.value; foodData.data.name = foodSearch.value;
props.value.food = await foodStore.actions.createOne(foodData.data) || undefined; props.value.food = await foodStore.actions.createOne(foodData.data) || undefined;
foodData.reset(); foodData.reset();
foodAutocomplete.value?.blur();
} }
// ================================================== // ==================================================
@@ -212,11 +216,13 @@ export default defineComponent({
const unitStore = useUnitStore(); const unitStore = useUnitStore();
const unitsData = useUnitData(); const unitsData = useUnitData();
const unitSearch = ref(""); const unitSearch = ref("");
const unitAutocomplete = ref<HTMLInputElement>();
async function createAssignUnit() { async function createAssignUnit() {
unitsData.data.name = unitSearch.value; unitsData.data.name = unitSearch.value;
props.value.unit = await unitStore.actions.createOne(unitsData.data) || undefined; props.value.unit = await unitStore.actions.createOne(unitsData.data) || undefined;
unitsData.reset(); unitsData.reset();
unitAutocomplete.value?.blur();
} }
const state = reactive({ const state = reactive({
@@ -269,7 +275,9 @@ export default defineComponent({
contextMenuOptions, contextMenuOptions,
handleUnitEnter, handleUnitEnter,
handleFoodEnter, handleFoodEnter,
foodAutocomplete,
createAssignFood, createAssignFood,
unitAutocomplete,
createAssignUnit, createAssignUnit,
foods: foodStore.foods, foods: foodStore.foods,
foodSearch, foodSearch,

View File

@@ -9,6 +9,7 @@
:item-id.sync="listItem.foodId" :item-id.sync="listItem.foodId"
:label="$t('shopping-list.food')" :label="$t('shopping-list.food')"
:icon="$globals.icons.foods" :icon="$globals.icons.foods"
@create="createAssignFood"
/> />
<InputLabelType <InputLabelType
v-model="listItem.unit" v-model="listItem.unit"
@@ -16,6 +17,7 @@
:item-id.sync="listItem.unitId" :item-id.sync="listItem.unitId"
:label="$t('general.units')" :label="$t('general.units')"
:icon="$globals.icons.units" :icon="$globals.icons.units"
@create="createAssignUnit"
/> />
</div> </div>
<div class="d-md-flex align-center" style="gap: 20px"> <div class="d-md-flex align-center" style="gap: 20px">
@@ -28,7 +30,8 @@
@keypress="handleNoteKeyPress" @keypress="handleNoteKeyPress"
></v-textarea> ></v-textarea>
</div> </div>
<div class="d-flex align-end" style="gap: 20px"> <div class="d-flex flex-wrap align-end" style="gap: 20px">
<div class="d-flex align-end">
<div> <div>
<InputQuantity v-model="listItem.quantity" /> <InputQuantity v-model="listItem.quantity" />
</div> </div>
@@ -60,6 +63,17 @@
</v-card> </v-card>
</v-menu> </v-menu>
</div> </div>
<BaseButton
v-if="listItem.labelId && listItem.food && listItem.labelId !== listItem.food.labelId"
small
color="info"
:icon="$globals.icons.tagArrowRight"
:text="$tc('shopping-list.save-label')"
class="mt-2 align-items-flex-start"
@click="assignLabelToFood"
/>
<v-spacer />
</div>
</v-card-text> </v-card-text>
</v-card> </v-card>
<v-card-actions class="ma-0 pt-0 pb-1 justify-end"> <v-card-actions class="ma-0 pt-0 pb-1 justify-end">
@@ -100,6 +114,7 @@ import { defineComponent, computed, watch } from "@nuxtjs/composition-api";
import { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/group"; import { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/group";
import { MultiPurposeLabelOut } from "~/lib/api/types/labels"; import { MultiPurposeLabelOut } from "~/lib/api/types/labels";
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe"; import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
import { useFoodStore, useFoodData, useUnitStore, useUnitData } from "~/composables/store";
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -121,6 +136,12 @@ export default defineComponent({
}, },
}, },
setup(props, context) { setup(props, context) {
const foodStore = useFoodStore();
const foodData = useFoodData();
const unitStore = useUnitStore();
const unitData = useUnitData();
const listItem = computed({ const listItem = computed({
get: () => { get: () => {
return props.value; return props.value;
@@ -139,8 +160,47 @@ export default defineComponent({
} }
); );
async function createAssignFood(val: string) {
// keep UI reactive
listItem.value.food ? listItem.value.food.name = val : listItem.value.food = { name: val };
foodData.data.name = val;
const newFood = await foodStore.actions.createOne(foodData.data);
if (newFood) {
listItem.value.food = newFood;
listItem.value.foodId = newFood.id;
}
foodData.reset();
}
async function createAssignUnit(val: string) {
// keep UI reactive
listItem.value.unit ? listItem.value.unit.name = val : listItem.value.unit = { name: val };
unitData.data.name = val;
const newUnit = await unitStore.actions.createOne(unitData.data);
if (newUnit) {
listItem.value.unit = newUnit;
listItem.value.unitId = newUnit.id;
}
unitData.reset();
}
async function assignLabelToFood() {
if (!(listItem.value.food && listItem.value.foodId && listItem.value.labelId)) {
return;
}
listItem.value.food.labelId = listItem.value.labelId;
// @ts-ignore the food will have an id, even though TS says it might not
await foodStore.actions.updateOne(listItem.value.food);
}
return { return {
listItem, listItem,
createAssignFood,
createAssignUnit,
assignLabelToFood,
}; };
}, },
methods: { methods: {

View File

@@ -14,15 +14,15 @@
> >
<v-icon v-if="!iconRight" left> <v-icon v-if="!iconRight" left>
<slot name="icon"> <slot name="icon">
{{ btnAttrs.icon }} {{ icon || btnAttrs.icon }}
</slot> </slot>
</v-icon> </v-icon>
<slot name="default"> <slot name="default">
{{ btnAttrs.text }} {{ text || btnAttrs.text }}
</slot> </slot>
<v-icon v-if="iconRight" right> <v-icon v-if="iconRight" right>
<slot name="icon"> <slot name="icon">
{{ btnAttrs.icon }} {{ icon || btnAttrs.icon }}
</slot> </slot>
</v-icon> </v-icon>
</v-btn> </v-btn>
@@ -103,6 +103,14 @@ export default defineComponent({
type: String, type: String,
default: null, default: null,
}, },
text: {
type: String,
default: null,
},
icon: {
type: String,
default: null,
},
iconRight: { iconRight: {
type: Boolean, type: Boolean,
default: false, default: false,

View File

@@ -15,13 +15,15 @@
<v-list v-if="mode === MODES.model" dense> <v-list v-if="mode === MODES.model" dense>
<v-list-item-group v-model="itemGroup"> <v-list-item-group v-model="itemGroup">
<template v-for="(item, index) in items"> <template v-for="(item, index) in items">
<v-list-item :key="index" @click="setValue(item)"> <div v-if="!item.hide" :key="index">
<v-list-item @click="setValue(item)">
<v-list-item-icon v-if="item.icon"> <v-list-item-icon v-if="item.icon">
<v-icon>{{ item.icon }}</v-icon> <v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon> </v-list-item-icon>
<v-list-item-title>{{ item.text }}</v-list-item-title> <v-list-item-title>{{ item.text }}</v-list-item-title>
</v-list-item> </v-list-item>
<v-divider v-if="item.divider" :key="`divider-${index}`" class="my-1" ></v-divider> <v-divider v-if="item.divider" :key="`divider-${index}`" class="my-1" ></v-divider>
</div>
</template> </template>
</v-list-item-group> </v-list-item-group>
</v-list> </v-list>
@@ -29,26 +31,30 @@
<v-list v-else-if="mode === MODES.link" dense> <v-list v-else-if="mode === MODES.link" dense>
<v-list-item-group v-model="itemGroup"> <v-list-item-group v-model="itemGroup">
<template v-for="(item, index) in items"> <template v-for="(item, index) in items">
<v-list-item :key="index" :to="item.to"> <div v-if="!item.hide" :key="index">
<v-list-item :to="item.to">
<v-list-item-icon v-if="item.icon"> <v-list-item-icon v-if="item.icon">
<v-icon>{{ item.icon }}</v-icon> <v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon> </v-list-item-icon>
<v-list-item-title>{{ item.text }}</v-list-item-title> <v-list-item-title>{{ item.text }}</v-list-item-title>
</v-list-item> </v-list-item>
<v-divider v-if="item.divider" :key="`divider-${index}`" class="my-1" ></v-divider> <v-divider v-if="item.divider" :key="`divider-${index}`" class="my-1" ></v-divider>\
</div>
</template> </template>
</v-list-item-group> </v-list-item-group>
</v-list> </v-list>
<!-- Event --> <!-- Event -->
<v-list v-else-if="mode === MODES.event" dense> <v-list v-else-if="mode === MODES.event" dense>
<template v-for="(item, index) in items"> <template v-for="(item, index) in items">
<v-list-item :key="index" @click="$emit(item.event)"> <div v-if="!item.hide" :key="index">
<v-list-item @click="$emit(item.event)">
<v-list-item-icon v-if="item.icon"> <v-list-item-icon v-if="item.icon">
<v-icon>{{ item.icon }}</v-icon> <v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon> </v-list-item-icon>
<v-list-item-title>{{ item.text }}</v-list-item-title> <v-list-item-title>{{ item.text }}</v-list-item-title>
</v-list-item> </v-list-item>
<v-divider v-if="item.divider" :key="`divider-${index}`" class="my-1" ></v-divider> <v-divider v-if="item.divider" :key="`divider-${index}`" class="my-1" ></v-divider>
</div>
</template> </template>
</v-list> </v-list>
</v-menu> </v-menu>
@@ -74,6 +80,7 @@ export interface MenuItem {
value?: string; value?: string;
event?: string; event?: string;
divider?: boolean; divider?: boolean;
hide?:boolean;
} }
export default defineComponent({ export default defineComponent({

View File

@@ -1,14 +1,27 @@
<template> <template>
<v-autocomplete <v-autocomplete
ref="autocompleteRef"
v-model="itemVal" v-model="itemVal"
v-bind="$attrs" v-bind="$attrs"
:search-input.sync="searchInput"
item-text="name" item-text="name"
return-object return-object
:items="items" :items="items"
:prepend-icon="icon || $globals.icons.tags" :prepend-icon="icon || $globals.icons.tags"
auto-select-first
clearable clearable
hide-details hide-details
/> @keyup.enter="emitCreate"
>
<template v-if="$listeners.create" #no-data>
<div class="caption text-center pb-2">{{ $t("recipe.press-enter-to-create") }}</div>
</template>
<template v-if="$listeners.create" #append-item>
<div class="px-2">
<BaseButton block small @click="emitCreate"></BaseButton>
</div>
</template>
</v-autocomplete>
</template> </template>
<script lang="ts"> <script lang="ts">
@@ -31,7 +44,7 @@
* Both the ID and Item can be synced. The item can be synced using the v-model syntax and the itemId can be synced * Both the ID and Item can be synced. The item can be synced using the v-model syntax and the itemId can be synced
* using the .sync syntax `item-id.sync="item.labelId"` * using the .sync syntax `item-id.sync="item.labelId"`
*/ */
import { defineComponent, computed } from "@nuxtjs/composition-api"; import { computed, defineComponent, ref } from "@nuxtjs/composition-api";
import { MultiPurposeLabelSummary } from "~/lib/api/types/labels"; import { MultiPurposeLabelSummary } from "~/lib/api/types/labels";
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe"; import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
@@ -59,6 +72,8 @@ export default defineComponent({
}, },
}, },
setup(props, context) { setup(props, context) {
const autocompleteRef = ref<HTMLInputElement>();
const searchInput = ref("");
const itemIdVal = computed({ const itemIdVal = computed({
get: () => { get: () => {
return props.itemId || undefined; return props.itemId || undefined;
@@ -78,9 +93,20 @@ export default defineComponent({
}, },
}); });
function emitCreate() {
if (props.items.some(item => item.name === searchInput.value)) {
return;
}
context.emit("create", searchInput.value);
autocompleteRef.value?.blur();
}
return { return {
autocompleteRef,
itemVal, itemVal,
itemIdVal, itemIdVal,
searchInput,
emitCreate,
}; };
}, },
}); });

View File

@@ -34,6 +34,12 @@ describe(parseIngredientText.name, () => {
expect(parseIngredientText(ingredient, false, 1, true)).contain("1 <sup>1</sup>").and.to.contain("<sub>2</sub>"); expect(parseIngredientText(ingredient, false, 1, true)).contain("1 <sup>1</sup>").and.to.contain("<sub>2</sub>");
}); });
test("ingredient text with fraction when unit is null", () => {
const ingredient = createRecipeIngredient({ quantity: 1.5, unit: undefined });
expect(parseIngredientText(ingredient, false, 1, true)).contain("1 <sup>1</sup>").and.to.contain("<sub>2</sub>");
});
test("ingredient text with fraction no formatting", () => { test("ingredient text with fraction no formatting", () => {
const ingredient = createRecipeIngredient({ quantity: 1.5, unit: { fraction: true, id: "1", name: "cup" } }); const ingredient = createRecipeIngredient({ quantity: 1.5, unit: { fraction: true, id: "1", name: "cup" } });
const result = parseIngredientText(ingredient, false, 1, false); const result = parseIngredientText(ingredient, false, 1, false);

View File

@@ -53,7 +53,9 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
// casting to number is required as sometimes quantity is a string // casting to number is required as sometimes quantity is a string
if (quantity && Number(quantity) !== 0) { if (quantity && Number(quantity) !== 0) {
if (unit?.fraction) { if (unit && !unit.fraction) {
returnQty = (quantity * scale).toString();
} else {
const fraction = frac(quantity * scale, 10, true); const fraction = frac(quantity * scale, 10, true);
if (fraction[0] !== undefined && fraction[0] > 0) { if (fraction[0] !== undefined && fraction[0] > 0) {
returnQty += fraction[0]; returnQty += fraction[0];
@@ -64,8 +66,6 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
` <sup>${fraction[1]}</sup>&frasl;<sub>${fraction[2]}</sub>` : ` <sup>${fraction[1]}</sup>&frasl;<sub>${fraction[2]}</sub>` :
` ${fraction[1]}/${fraction[2]}`; ` ${fraction[1]}/${fraction[2]}`;
} }
} else {
returnQty = (quantity * scale).toString();
} }
} }

View File

@@ -10,7 +10,7 @@ const storeLoading = ref(false);
/** /**
* useFoodData returns a template reactive object * useFoodData returns a template reactive object
* for managing the creation of units. It also provides a * for managing the creation of foods. It also provides a
* function to reset the data back to the initial state. * function to reset the data back to the initial state.
*/ */
export const useFoodData = function () { export const useFoodData = function () {

View File

@@ -1,6 +1,6 @@
import { Ref, useContext } from "@nuxtjs/composition-api"; import { Ref, useContext } from "@nuxtjs/composition-api";
import { useLocalStorage, useSessionStorage } from "@vueuse/core"; import { useLocalStorage, useSessionStorage } from "@vueuse/core";
import { TimelineEventType } from "~/lib/api/types/recipe"; import { RegisteredParser, TimelineEventType } from "~/lib/api/types/recipe";
export interface UserPrintPreferences { export interface UserPrintPreferences {
imagePosition: string; imagePosition: string;
@@ -36,6 +36,10 @@ export interface UserTimelinePreferences {
types: TimelineEventType[]; types: TimelineEventType[];
} }
export interface UserParsingPreferences {
parser: RegisteredParser;
}
export function useUserPrintPreferences(): Ref<UserPrintPreferences> { export function useUserPrintPreferences(): Ref<UserPrintPreferences> {
const fromStorage = useLocalStorage( const fromStorage = useLocalStorage(
"recipe-print-preferences", "recipe-print-preferences",
@@ -116,3 +120,17 @@ export function useTimelinePreferences(): Ref<UserTimelinePreferences> {
return fromStorage; return fromStorage;
} }
export function useParsingPreferences(): Ref<UserParsingPreferences> {
const fromStorage = useLocalStorage(
"parsing-preferences",
{
parser: "nlp",
},
{ 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<UserParsingPreferences>;
return fromStorage;
}

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Resep skraper weergawe", "recipe-scraper-version": "Resep skraper weergawe",
"oidc-ready": "OIDC Klar", "oidc-ready": "OIDC Klar",
"oidc-ready-error-text": "Ikke alle OIDC værdier er konfigureret. Dette kan ignoreres hvis du ikke bruger OIDC godkendelse.", "oidc-ready-error-text": "Ikke alle OIDC værdier er konfigureret. Dette kan ignoreres hvis du ikke bruger OIDC godkendelse.",
"oidc-ready-success-text": "Krævede OIDC variabler er udfyldt." "oidc-ready-success-text": "Krævede OIDC variabler er udfyldt.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Alle lyste", "all-lists": "Alle lyste",
@@ -778,6 +782,7 @@
"food": "Voedsel", "food": "Voedsel",
"note": "Nota", "note": "Nota",
"label": "Etiket", "label": "Etiket",
"save-label": "Save Label",
"linked-item-warning": "Hierdie item is gekoppel aan een of meer resepte. Die aanpassing van die eenhede of bestanddele sal onverwagte resultate lewer wanneer die resep van hierdie lys bygevoeg of verwyder word.", "linked-item-warning": "Hierdie item is gekoppel aan een of meer resepte. Die aanpassing van die eenhede of bestanddele sal onverwagte resultate lewer wanneer die resep van hierdie lys bygevoeg of verwyder word.",
"toggle-food": "Voedsel skakelling", "toggle-food": "Voedsel skakelling",
"manage-labels": "Bestuur etikette", "manage-labels": "Bestuur etikette",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Dit is nie perfek nie, maar dit lewer oor die algemeen uitstekende resultate en is 'n goeie beginpunt vir die handverwerking van bestanddele in individuele velde. Jy kan ook die \"Brute\" verwerker gebruik wat 'n patroonpastegniek gebruik om bestanddele te identifiseer.", "ingredients-natural-language-processor-explanation-2": "Dit is nie perfek nie, maar dit lewer oor die algemeen uitstekende resultate en is 'n goeie beginpunt vir die handverwerking van bestanddele in individuele velde. Jy kan ook die \"Brute\" verwerker gebruik wat 'n patroonpastegniek gebruik om bestanddele te identifiseer.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Wys individuele oortuiging", "show-individual-confidence": "Wys individuele oortuiging",
"ingredient-text": "Bestanddeel teks", "ingredient-text": "Bestanddeel teks",
"average-confident": "{0} oortuig", "average-confident": "{0} oortuig",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "All Lists", "all-lists": "All Lists",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Версия на скрепер на рецепти", "recipe-scraper-version": "Версия на скрепер на рецепти",
"oidc-ready": "Готов за OIDC", "oidc-ready": "Готов за OIDC",
"oidc-ready-error-text": "Не всички OIDC стойности са конфигурирани. Това може да бъде игнорирано, ако не използвате OIDC удостоверяване.", "oidc-ready-error-text": "Не всички OIDC стойности са конфигурирани. Това може да бъде игнорирано, ако не използвате OIDC удостоверяване.",
"oidc-ready-success-text": "Задължителните OIDC променливи са зададени." "oidc-ready-success-text": "Задължителните OIDC променливи са зададени.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Всички списъци", "all-lists": "Всички списъци",
@@ -778,6 +782,7 @@
"food": "Продукт", "food": "Продукт",
"note": "Бележка", "note": "Бележка",
"label": "Етикет", "label": "Етикет",
"save-label": "Save Label",
"linked-item-warning": "Елементът е добавен към една или повече рецепти. Редактиране на единиците или храните ще се отрази с непредвидими резултати когато добавяте или премахвате рецепта от списъка.", "linked-item-warning": "Елементът е добавен към една или повече рецепти. Редактиране на единиците или храните ще се отрази с непредвидими резултати когато добавяте или премахвате рецепта от списъка.",
"toggle-food": "Превключване на храна", "toggle-food": "Превключване на храна",
"manage-labels": "Управление на етикети", "manage-labels": "Управление на етикети",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Не е перфектно, но като цяло дава страхотни резултати и е добра отправна точка за ръчно анализиране на съставки в отделни полета. Като алтернатива можете също да използвате процесора \"Груб\", който използва техника за съвпадение на шаблони, за да идентифицира съставките.", "ingredients-natural-language-processor-explanation-2": "Не е перфектно, но като цяло дава страхотни резултати и е добра отправна точка за ръчно анализиране на съставки в отделни полета. Като алтернатива можете също да използвате процесора \"Груб\", който използва техника за съвпадение на шаблони, за да идентифицира съставките.",
"nlp": "ПЕЕ", "nlp": "ПЕЕ",
"brute": "Груб", "brute": "Груб",
"openai": "OpenAI",
"show-individual-confidence": "Покажи индивидуална увереност", "show-individual-confidence": "Покажи индивидуална увереност",
"ingredient-text": "Текст на съставката", "ingredient-text": "Текст на съставката",
"average-confident": "{0} Уверен", "average-confident": "{0} Уверен",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "Analitzador d'OpenAI",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "Llest per OpenAI",
"openai-ready-error-text": "No tots els valors d'OpenAI estan configurats. Es pot ignorar si no s'estan utilitzant les funcionalitats d'OpenAI.",
"openai-ready-success-text": "Les variables requerides per OpenAI estan establertes."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Totes les llistes", "all-lists": "Totes les llistes",
@@ -778,6 +782,7 @@
"food": "Aliments", "food": "Aliments",
"note": "Nota", "note": "Nota",
"label": "Etiqueta", "label": "Etiqueta",
"save-label": "Save Label",
"linked-item-warning": "Aquest element està enllaçat amb una o més receptes. Modificar les unitats o els aliments pot provocar resultats inesperats en afegir o elimina la recepta del llistat.", "linked-item-warning": "Aquest element està enllaçat amb una o més receptes. Modificar les unitats o els aliments pot provocar resultats inesperats en afegir o elimina la recepta del llistat.",
"toggle-food": "Mostra el nom de l'aliment", "toggle-food": "Mostra el nom de l'aliment",
"manage-labels": "Gestiona etiquetes", "manage-labels": "Gestiona etiquetes",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -340,7 +340,7 @@
}, },
"nextcloud": { "nextcloud": {
"description": "Migrovat data z instance Nextcloud Cookbook", "description": "Migrovat data z instance Nextcloud Cookbook",
"description-long": "Nextcloud recipes can be imported from a zip file that contains the data stored in Nextcloud. See the example folder structure below to ensure your recipes are able to be imported.", "description-long": "Nextcloud recepty lze importovat ze souboru zip, který obsahuje data uložená v Nextcloudu. Podívejte se na příklad struktury složek níže, abyste se ujistili, že vaše recepty lze importovat.",
"title": "Nextcloud Cookbook" "title": "Nextcloud Cookbook"
}, },
"copymethat": { "copymethat": {
@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Všechny seznamy", "all-lists": "Všechny seznamy",
@@ -778,6 +782,7 @@
"food": "Jídlo", "food": "Jídlo",
"note": "Poznámka", "note": "Poznámka",
"label": "Popisek", "label": "Popisek",
"save-label": "Save Label",
"linked-item-warning": "Tato položka je propojena s jedním nebo více recepty. Úprava jednotky nebo jídla bude mít neočekávané důsledky při přidání nebo odebrání receptu z tohoto seznamu.", "linked-item-warning": "Tato položka je propojena s jedním nebo více recepty. Úprava jednotky nebo jídla bude mít neočekávané důsledky při přidání nebo odebrání receptu z tohoto seznamu.",
"toggle-food": "Přepnout typ položky", "toggle-food": "Přepnout typ položky",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Není to dokonalé, ale obecně to přináší skvělé výsledky a je dobrým výchozím bodem pro ruční zpracování ingrediencí do jednotlivých polí. Alternativně můžete také použít procesor \"Brute\", který používá metodu porovnávání vzorců pro idenfikaci ingrediencí.", "ingredients-natural-language-processor-explanation-2": "Není to dokonalé, ale obecně to přináší skvělé výsledky a je dobrým výchozím bodem pro ruční zpracování ingrediencí do jednotlivých polí. Alternativně můžete také použít procesor \"Brute\", který používá metodu porovnávání vzorců pro idenfikaci ingrediencí.",
"nlp": "ZPJ", "nlp": "ZPJ",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Zobrazit individuální důvěru", "show-individual-confidence": "Zobrazit individuální důvěru",
"ingredient-text": "Text přísady", "ingredient-text": "Text přísady",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Version på opskrift-indsamler", "recipe-scraper-version": "Version på opskrift-indsamler",
"oidc-ready": "OIDC er Klar", "oidc-ready": "OIDC er Klar",
"oidc-ready-error-text": "Ikke alle OIDC værdier er konfigureret. Dette kan ignoreres, hvis du ikke bruger OIDC godkendelse.", "oidc-ready-error-text": "Ikke alle OIDC værdier er konfigureret. Dette kan ignoreres, hvis du ikke bruger OIDC godkendelse.",
"oidc-ready-success-text": "Alle påkrævede OIDC værdier er angivet." "oidc-ready-success-text": "Alle påkrævede OIDC værdier er angivet.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Alle lister", "all-lists": "Alle lister",
@@ -778,6 +782,7 @@
"food": "Fødevarer", "food": "Fødevarer",
"note": "Note", "note": "Note",
"label": "Etiket", "label": "Etiket",
"save-label": "Save Label",
"linked-item-warning": "Dette element er tilknyttet en eller flere opskrifter. Justering af enheder eller fødevarer vil give uventede resultater, når du tilføjer eller fjerner opskriften fra denne liste.", "linked-item-warning": "Dette element er tilknyttet en eller flere opskrifter. Justering af enheder eller fødevarer vil give uventede resultater, når du tilføjer eller fjerner opskriften fra denne liste.",
"toggle-food": "Slå fødevarer til/fra", "toggle-food": "Slå fødevarer til/fra",
"manage-labels": "Håndter etiketter", "manage-labels": "Håndter etiketter",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Det er ikke perfekt, men giver generelt gode resultater og er et godt udgangspunkt for manuel redigering af ingredienser i individuelle felter. Alternativt kan du også bruge \"Brute\" metoden, der bruger en mønstermatchende teknik til at identificere ingredienser.", "ingredients-natural-language-processor-explanation-2": "Det er ikke perfekt, men giver generelt gode resultater og er et godt udgangspunkt for manuel redigering af ingredienser i individuelle felter. Alternativt kan du også bruge \"Brute\" metoden, der bruger en mønstermatchende teknik til at identificere ingredienser.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Vis individual konfidensscore", "show-individual-confidence": "Vis individual konfidensscore",
"ingredient-text": "Ingredienstekst", "ingredient-text": "Ingredienstekst",
"average-confident": "{0} konfidens", "average-confident": "{0} konfidens",

View File

@@ -594,6 +594,7 @@
"select-parser": "Parser auswählen", "select-parser": "Parser auswählen",
"natural-language-processor": "Natürliche Sprachverarbeitung (NLP)", "natural-language-processor": "Natürliche Sprachverarbeitung (NLP)",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Alles parsen", "parse-all": "Alles parsen",
"no-unit": "Keine Einheit", "no-unit": "Keine Einheit",
"missing-unit": "Fehlende Einheit erstellen: {unit}", "missing-unit": "Fehlende Einheit erstellen: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Rezept Scraper Version", "recipe-scraper-version": "Rezept Scraper Version",
"oidc-ready": "OIDC bereit", "oidc-ready": "OIDC bereit",
"oidc-ready-error-text": "Es sind nicht alle OIDC-Werte konfiguriert. Wenn du keine OIDC-Authentifizierung benutzt, kannst du das ignorieren.", "oidc-ready-error-text": "Es sind nicht alle OIDC-Werte konfiguriert. Wenn du keine OIDC-Authentifizierung benutzt, kannst du das ignorieren.",
"oidc-ready-success-text": "Alle erforderlichen OIDC-Variablen sind gesetzt." "oidc-ready-success-text": "Alle erforderlichen OIDC-Variablen sind gesetzt.",
"openai-ready": "OpenAI bereit",
"openai-ready-error-text": "Es sind nicht alle OpenAI-Werte konfiguriert. Wenn du die OpenAI Funktionen nicht benutzt, kannst du das ignorieren.",
"openai-ready-success-text": "Alle erforderlichen OpenAI-Variablen sind hinterlegt."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Alle Listen", "all-lists": "Alle Listen",
@@ -778,6 +782,7 @@
"food": "Lebensmittel", "food": "Lebensmittel",
"note": "Notiz", "note": "Notiz",
"label": "Etikett", "label": "Etikett",
"save-label": "Save Label",
"linked-item-warning": "Dieser Eintrag ist mit einem oder mehreren Rezepten verknüpft. Das Ändern der Einheiten oder Lebensmittel führt zu unerwarteten Ergebnissen, wenn das Rezept von dieser Einkaufsliste entfernt oder hinzugefügt wird.", "linked-item-warning": "Dieser Eintrag ist mit einem oder mehreren Rezepten verknüpft. Das Ändern der Einheiten oder Lebensmittel führt zu unerwarteten Ergebnissen, wenn das Rezept von dieser Einkaufsliste entfernt oder hinzugefügt wird.",
"toggle-food": "Lebensmittel-Eingabe umschalten", "toggle-food": "Lebensmittel-Eingabe umschalten",
"manage-labels": "Etiketten verwalten", "manage-labels": "Etiketten verwalten",
@@ -1170,6 +1175,7 @@
"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.", "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", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Zeige individuelle Zuverlässigkeitswerte an", "show-individual-confidence": "Zeige individuelle Zuverlässigkeitswerte an",
"ingredient-text": "Zutaten-Angabe", "ingredient-text": "Zutaten-Angabe",
"average-confident": "{0} zuverlässig", "average-confident": "{0} zuverlässig",

View File

@@ -73,7 +73,7 @@
"user-events": "Συμβάντα Χρήστη", "user-events": "Συμβάντα Χρήστη",
"mealplan-events": "Mealplan Events", "mealplan-events": "Mealplan Events",
"when-a-user-in-your-group-creates-a-new-mealplan": "When a user in your group creates a new mealplan", "when-a-user-in-your-group-creates-a-new-mealplan": "When a user in your group creates a new mealplan",
"shopping-list-events": "Shopping List Events", "shopping-list-events": "Συμβάντα Λιστών Αγορών",
"cookbook-events": "Cookbook Events", "cookbook-events": "Cookbook Events",
"tag-events": "Tag Events", "tag-events": "Tag Events",
"category-events": "Category Events", "category-events": "Category Events",
@@ -314,7 +314,7 @@
"random-side": "Τυχαίο Συνοδευτικό", "random-side": "Τυχαίο Συνοδευτικό",
"this-rule-will-apply": "This rule will apply {dayCriteria} {mealTypeCriteria}.", "this-rule-will-apply": "This rule will apply {dayCriteria} {mealTypeCriteria}.",
"to-all-days": "σε όλες τις ημέρες", "to-all-days": "σε όλες τις ημέρες",
"on-days": "on {0}s", "on-days": "κάθε {0}",
"for-all-meal-types": "για όλα τα είδη γεύματος", "for-all-meal-types": "για όλα τα είδη γεύματος",
"for-type-meal-types": "for {0} meal types", "for-type-meal-types": "for {0} meal types",
"meal-plan-rules": "Κανόνες Προγράμματος Γευμάτων", "meal-plan-rules": "Κανόνες Προγράμματος Γευμάτων",
@@ -594,6 +594,7 @@
"select-parser": "Επιλέξτε Αναλυτή", "select-parser": "Επιλέξτε Αναλυτή",
"natural-language-processor": "Επεξεργαστής Φυσικής Γλώσσας", "natural-language-processor": "Επεξεργαστής Φυσικής Γλώσσας",
"brute-parser": "Αναλυτής Ωμής Βίας", "brute-parser": "Αναλυτής Ωμής Βίας",
"openai-parser": "Αναλυτής OpenAI",
"parse-all": "Ανάλυση Ολων", "parse-all": "Ανάλυση Ολων",
"no-unit": "Καμία μονάδα", "no-unit": "Καμία μονάδα",
"missing-unit": "Δημιουργία μονάδας που λείπει: {unit}", "missing-unit": "Δημιουργία μονάδας που λείπει: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Δεν έχουν ρυθμιστεί όλες οι τιμές OpenAI. Αυτό μπορεί να αγνοηθεί αν δεν χρησιμοποιείτε τα χαρακτηριστικά του OpenAI.",
"openai-ready-success-text": "Ολες οι απαιτούμενες μεταβλητές OpenAI έχουν οριστεί."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Όλες οι λίστες", "all-lists": "Όλες οι λίστες",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Αποθήκεύση ετικέτας",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "All Lists", "all-lists": "All Lists",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "All Lists", "all-lists": "All Lists",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -64,7 +64,7 @@
"something-went-wrong": "¡Algo ha salido mal!", "something-went-wrong": "¡Algo ha salido mal!",
"subscribed-events": "Eventos suscritos", "subscribed-events": "Eventos suscritos",
"test-message-sent": "Mensaje Enviado", "test-message-sent": "Mensaje Enviado",
"message-sent": "Message Sent", "message-sent": "Mensaje Enviado",
"new-notification": "Nueva notificación", "new-notification": "Nueva notificación",
"event-notifiers": "Notificaciones de eventos", "event-notifiers": "Notificaciones de eventos",
"apprise-url-skipped-if-blank": "URL de Apprise (omitida si está en blanco)", "apprise-url-skipped-if-blank": "URL de Apprise (omitida si está en blanco)",
@@ -81,12 +81,12 @@
"recipe-events": "Eventos de receta" "recipe-events": "Eventos de receta"
}, },
"general": { "general": {
"add": "Add", "add": "Agregar",
"cancel": "Cancelar", "cancel": "Cancelar",
"clear": "Eliminar", "clear": "Eliminar",
"close": "Cerrar", "close": "Cerrar",
"confirm": "Confirmar", "confirm": "Confirmar",
"confirm-how-does-everything-look": "How does everything look?", "confirm-how-does-everything-look": "¿Cómo se ve todo?",
"confirm-delete-generic": "¿Estás seguro de que quieres eliminarlo?", "confirm-delete-generic": "¿Estás seguro de que quieres eliminarlo?",
"copied_message": "¡Copiado!", "copied_message": "¡Copiado!",
"create": "Crear", "create": "Crear",
@@ -145,23 +145,23 @@
"save": "Guardar", "save": "Guardar",
"settings": "Ajustes", "settings": "Ajustes",
"share": "Compartir", "share": "Compartir",
"show-all": "Show All", "show-all": "Mostrar Todo",
"shuffle": "Aleatorio", "shuffle": "Aleatorio",
"sort": "Ordenar", "sort": "Ordenar",
"sort-ascending": "Sort Ascending", "sort-ascending": "Orden Ascendente",
"sort-descending": "Sort Descending", "sort-descending": "Orden Descendente",
"sort-alphabetically": "Alfabéticamente", "sort-alphabetically": "Alfabéticamente",
"status": "Estado", "status": "Estado",
"subject": "Asunto", "subject": "Asunto",
"submit": "Enviar", "submit": "Enviar",
"success-count": "Éxito: {count}", "success-count": "Éxito: {count}",
"sunday": "Domingo", "sunday": "Domingo",
"system": "System", "system": "Sistema",
"templates": "Plantillas:", "templates": "Plantillas:",
"test": "Prueba", "test": "Prueba",
"themes": "Temas", "themes": "Temas",
"thursday": "Jueves", "thursday": "Jueves",
"title": "Title", "title": "Título",
"token": "Token", "token": "Token",
"tuesday": "Martes", "tuesday": "Martes",
"type": "Tipo", "type": "Tipo",
@@ -176,7 +176,7 @@
"units": "Unidades", "units": "Unidades",
"back": "Volver", "back": "Volver",
"next": "Siguiente", "next": "Siguiente",
"start": "Start", "start": "Comenzar",
"toggle-view": "Cambiar vista", "toggle-view": "Cambiar vista",
"date": "Fecha", "date": "Fecha",
"id": "Id", "id": "Id",
@@ -210,7 +210,7 @@
"unsaved-changes": "Tienes cambios sin guardar. ¿Quieres guardar antes de salir? Aceptar para guardar, Cancelar para descartar cambios.", "unsaved-changes": "Tienes cambios sin guardar. ¿Quieres guardar antes de salir? Aceptar para guardar, Cancelar para descartar cambios.",
"clipboard-copy-failure": "No se pudo copiar al portapapeles.", "clipboard-copy-failure": "No se pudo copiar al portapapeles.",
"confirm-delete-generic-items": "¿Estás seguro que quieres eliminar los siguientes elementos?", "confirm-delete-generic-items": "¿Estás seguro que quieres eliminar los siguientes elementos?",
"organizers": "Organizers" "organizers": "Organizadores"
}, },
"group": { "group": {
"are-you-sure-you-want-to-delete-the-group": "Por favor, confirma que deseas eliminar <b>{groupName}<b/>", "are-you-sure-you-want-to-delete-the-group": "Por favor, confirma que deseas eliminar <b>{groupName}<b/>",
@@ -246,8 +246,8 @@
"group-preferences": "Preferencias de grupo", "group-preferences": "Preferencias de grupo",
"private-group": "Grupo privado", "private-group": "Grupo privado",
"private-group-description": "Establecer el grupo como privado, cambiará todas las opciones de visualización a las opciones predeterminadas. Puede cambiarse individualmente en la configuración de cada receta.", "private-group-description": "Establecer el grupo como privado, cambiará todas las opciones de visualización a las opciones predeterminadas. Puede cambiarse individualmente en la configuración de cada receta.",
"enable-public-access": "Enable Public Access", "enable-public-access": "Habilitar acceso público",
"enable-public-access-description": "Make group recipes public by default, and allow visitors to view recipes without logging-in", "enable-public-access-description": "Habilitar recetas públicas por defecto y permitir que usuarios anónimos vean recetas",
"allow-users-outside-of-your-group-to-see-your-recipes": "Permite a los usuarios fuera de tu grupo ver tus recetas", "allow-users-outside-of-your-group-to-see-your-recipes": "Permite a los usuarios fuera de tu grupo ver tus recetas",
"allow-users-outside-of-your-group-to-see-your-recipes-description": "Cuando esté habilitado, puede utilizar un enlace público para compartir recetas específicas sin autorizar al usuario. Cuando está desactivado, sólo puedes compartir recetas con usuarios que estén en tu grupo o generando un enlace privado de forma previa", "allow-users-outside-of-your-group-to-see-your-recipes-description": "Cuando esté habilitado, puede utilizar un enlace público para compartir recetas específicas sin autorizar al usuario. Cuando está desactivado, sólo puedes compartir recetas con usuarios que estén en tu grupo o generando un enlace privado de forma previa",
"show-nutrition-information": "Mostrar la información nutricional", "show-nutrition-information": "Mostrar la información nutricional",
@@ -361,11 +361,11 @@
}, },
"recipe-data-migrations": "Migración de recetas", "recipe-data-migrations": "Migración de recetas",
"recipe-data-migrations-explanation": "Las recetas pueden migrarse desde otra aplicación soportada a Mealie. Esta es una excelente manera de empezar con Mealie.", "recipe-data-migrations-explanation": "Las recetas pueden migrarse desde otra aplicación soportada a Mealie. Esta es una excelente manera de empezar 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": "¿Vienes de otra app o una versión previa de Mealie? Revisa migraciones para ver si tus datos pueden ser importados.",
"choose-migration-type": "Elegir tipo de migración", "choose-migration-type": "Elegir tipo de migración",
"tag-all-recipes": "Etiqueta todas las recetas con la etiqueta {tag-name}", "tag-all-recipes": "Etiqueta todas las recetas con la etiqueta {tag-name}",
"nextcloud-text": "Las recetas Nextcloud se pueden importar desde un archivo zip que contiene los datos almacenados en Nextcloud. Consulte la estructura de carpetas de ejemplo a continuación para asegurarse de que sus recetas pueden ser importadas.", "nextcloud-text": "Las recetas Nextcloud se pueden importar desde un archivo zip que contiene los datos almacenados en Nextcloud. Consulte la estructura de carpetas de ejemplo a continuación para asegurarse de que sus recetas pueden ser importadas.",
"chowdown-text": "Mealie natively supports the chowdown repository format. Download the code repository as a .zip file and upload it below.", "chowdown-text": "Mealie soporta el formato de repositorio chowndown de manera nativa. Descarga el código fuente como archivo .zip y súbelo a continuación.",
"recipe-1": "Receta 1", "recipe-1": "Receta 1",
"recipe-2": "Receta 2", "recipe-2": "Receta 2",
"paprika-text": "Mealie puede importar recetas de la aplicación Paprika. Exporta tus recetas de paprika, renombra la extensión del fichero a .zip y súbelo a continuación.", "paprika-text": "Mealie puede importar recetas de la aplicación Paprika. Exporta tus recetas de paprika, renombra la extensión del fichero a .zip y súbelo a continuación.",
@@ -376,7 +376,7 @@
}, },
"myrecipebox": { "myrecipebox": {
"title": "My Recipe Box", "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 puede importar recetas de My Recipe Box. Exporta tus recetas en formato CSV y sube el archivo a continuación."
} }
}, },
"new-recipe": { "new-recipe": {
@@ -512,7 +512,7 @@
"link-ingredients": "Vincular ingredientes", "link-ingredients": "Vincular ingredientes",
"merge-above": "Combinar por encima", "merge-above": "Combinar por encima",
"move-to-bottom": "Mover al fondo", "move-to-bottom": "Mover al fondo",
"move-to-top": "Move To Top", "move-to-top": "Mover al Inicio",
"reset-scale": "Reiniciar", "reset-scale": "Reiniciar",
"decrease-scale-label": "Disminuir escala en 1", "decrease-scale-label": "Disminuir escala en 1",
"increase-scale-label": "Aumentar escala en 1", "increase-scale-label": "Aumentar escala en 1",
@@ -528,7 +528,7 @@
"edit-timeline-event": "Editar evento en la cronología", "edit-timeline-event": "Editar evento en la cronología",
"timeline": "Cronología", "timeline": "Cronología",
"timeline-is-empty": "Aún no hay nada en la línea de tiempo. ¡Intenta hacer esta receta!", "timeline-is-empty": "Aún no hay nada en la línea de tiempo. ¡Intenta hacer esta receta!",
"timeline-no-events-found-try-adjusting-filters": "No events found. Try adjusting your search filters.", "timeline-no-events-found-try-adjusting-filters": "No se encontraron eventos. Intenta ajustar los filtros de búsqueda.",
"group-global-timeline": "Línea de tiempo global de {groupName}", "group-global-timeline": "Línea de tiempo global de {groupName}",
"open-timeline": "Abrir línea de tiempo", "open-timeline": "Abrir línea de tiempo",
"made-this": "Lo hice", "made-this": "Lo hice",
@@ -549,8 +549,8 @@
"looking-for-migrations": "¿Buscas las Migraciones?", "looking-for-migrations": "¿Buscas las Migraciones?",
"import-with-url": "Importar por url", "import-with-url": "Importar por url",
"create-recipe": "Crear receta", "create-recipe": "Crear receta",
"create-recipe-description": "Create a new recipe from scratch.", "create-recipe-description": "Crear nueva receta desde cero.",
"create-recipes": "Create Recipes", "create-recipes": "Crear Recetas",
"import-with-zip": "Importar desde .zip", "import-with-zip": "Importar desde .zip",
"create-recipe-from-an-image": "Crear receta a partir de una imagen", "create-recipe-from-an-image": "Crear receta a partir de una imagen",
"bulk-url-import": "Importación masiva desde URL", "bulk-url-import": "Importación masiva desde URL",
@@ -585,20 +585,21 @@
"screen-awake": "Mantener la pantalla encendida", "screen-awake": "Mantener la pantalla encendida",
"remove-image": "Eliminar imagen", "remove-image": "Eliminar imagen",
"nextStep": "Siguiente paso", "nextStep": "Siguiente paso",
"recipe-actions": "Recipe Actions", "recipe-actions": "Acciones de Receta",
"parser": { "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.", "experimental-alert-text": "Mealie usa procesamiento de lenguaje natural para analizar y crear unidades y productos para los ingredientes de tu receta. Esta característica es experimental y puede no ser confiable. Si prefieres no usar los resultados analizados, elige 'Cancelar' y los cambios serán descartados.",
"ingredient-parser": "Ingredient Parser", "ingredient-parser": "Analizador de Ingredientes",
"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.", "explanation": "Para usar el analizador de ingredientes, haz clic en 'Analizar Todo'. Una vez que los ingredientes procesados estén disponibles, puedes revisar los productos y verificar que fueron analizados correctamente. El puntaje de confianza del modelo se muestra a la derecha del título del producto. Este puntaje es un promedio de todos los puntajes individuales y puede no ser del todo preciso.",
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.", "alerts-explainer": "Las alertas se mostrarán si se encuentra un alimento o unidad que coincida pero que no exista en la base de datos.",
"select-parser": "Select Parser", "select-parser": "Seleccionar Analizador",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Procesador de Lenguaje Natural",
"brute-parser": "Brute Parser", "brute-parser": "Analizador Bruto",
"parse-all": "Parse All", "openai-parser": "OpenAI Parser",
"no-unit": "No unit", "parse-all": "Analizar Todo",
"missing-unit": "Create missing unit: {unit}", "no-unit": "Sin unidad",
"missing-food": "Create missing food: {food}", "missing-unit": "Crear unidad faltante: {unit}",
"no-food": "No Food" "missing-food": "Crear comida faltante: {food}",
"no-food": "Sin Comida"
} }
}, },
"search": { "search": {
@@ -636,7 +637,7 @@
"import-summary": "Importar resumen", "import-summary": "Importar resumen",
"partial-backup": "Copia de seguridad parcial", "partial-backup": "Copia de seguridad parcial",
"unable-to-delete-backup": "No se puede eliminar la copia de seguridad.", "unable-to-delete-backup": "No se puede eliminar la copia de seguridad.",
"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": "Las copias de seguridad son guardados completos de las carpetas database y data del sitio. Esto incluye todo y no puede ser configurado para excluir subconjuntos de datos. Puedes pensar en ello como una 'instantánea' de Mealie en un momento en específico. Estas sirven para exportar e importar datos, o clonar el sitio a otra ubicación de manera intercompatible.",
"backup-restore": "Restaurar Copia de Seguridad", "backup-restore": "Restaurar Copia de Seguridad",
"back-restore-description": "Restaurar esta copia de seguridad sobrescribirá todos los datos actuales de su base de datos y del directorio de datos y los sustituirá por el contenido de esta copia. {cannot-be-undone} Si la restauración se realiza correctamente, se cerrará su sesión.", "back-restore-description": "Restaurar esta copia de seguridad sobrescribirá todos los datos actuales de su base de datos y del directorio de datos y los sustituirá por el contenido de esta copia. {cannot-be-undone} Si la restauración se realiza correctamente, se cerrará su sesión.",
"cannot-be-undone": "Esta acción no se puede deshacer, use con precaución.", "cannot-be-undone": "Esta acción no se puede deshacer, use con precaución.",
@@ -762,9 +763,12 @@
"ldap-ready-success-text": "Las variables LDAP requeridas están todas definidas.", "ldap-ready-success-text": "Las variables LDAP requeridas están todas definidas.",
"build": "Compilación", "build": "Compilación",
"recipe-scraper-version": "Versión de Analizador de Recetas", "recipe-scraper-version": "Versión de Analizador de Recetas",
"oidc-ready": "OIDC Ready", "oidc-ready": "OIDC Listo",
"oidc-ready-error-text": "Not all OIDC Values are configured. This can be ignored if you are not using OIDC Authentication.", "oidc-ready-error-text": "No todos los valores OIDC están configurados. Puedes ignorar esto si no estás usando autenticación OIDC.",
"oidc-ready-success-text": "Required OIDC variables are all set." "oidc-ready-success-text": "Todas las variables OIDC requeridas están configuradas.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Todas las listas", "all-lists": "Todas las listas",
@@ -778,6 +782,7 @@
"food": "Alimentos", "food": "Alimentos",
"note": "Nota", "note": "Nota",
"label": "Etiqueta", "label": "Etiqueta",
"save-label": "Save Label",
"linked-item-warning": "Este elemento está vinculado a una o más recetas. Ajustar las unidades o los alimentos producirá resultados inesperados al añadir o quitar la receta de esta lista.", "linked-item-warning": "Este elemento está vinculado a una o más recetas. Ajustar las unidades o los alimentos producirá resultados inesperados al añadir o quitar la receta de esta lista.",
"toggle-food": "Mostrar nombre del alimento", "toggle-food": "Mostrar nombre del alimento",
"manage-labels": "Administrar etiquetas", "manage-labels": "Administrar etiquetas",
@@ -872,11 +877,11 @@
"link-id": "ID del enlace", "link-id": "ID del enlace",
"link-name": "Nombre del enlace", "link-name": "Nombre del enlace",
"login": "Iniciar sesión", "login": "Iniciar sesión",
"login-oidc": "Login with", "login-oidc": "Acceder con",
"or": "or", "or": "o",
"logout": "Cerrar Sesión", "logout": "Cerrar Sesión",
"manage-users": "Administrar usuarios", "manage-users": "Administrar usuarios",
"manage-users-description": "Create and manage users.", "manage-users-description": "Crear y gestionar usuarios.",
"new-password": "Nueva contraseña", "new-password": "Nueva contraseña",
"new-user": "Nuevo usuario", "new-user": "Nuevo usuario",
"password-has-been-reset-to-the-default-password": "La contraseña se ha restablecido a su valor por defecto", "password-has-been-reset-to-the-default-password": "La contraseña se ha restablecido a su valor por defecto",
@@ -1019,10 +1024,10 @@
"source-unit-will-be-deleted": "Se eliminará la unidad de origen" "source-unit-will-be-deleted": "Se eliminará la unidad de origen"
}, },
"recipe-actions": { "recipe-actions": {
"recipe-actions-data": "Recipe Actions Data", "recipe-actions-data": "Datos de Acciones de Receta",
"new-recipe-action": "New Recipe Action", "new-recipe-action": "Nueva Acción de Receta",
"edit-recipe-action": "Edit Recipe Action", "edit-recipe-action": "Editar Acción de Receta",
"action-type": "Action Type" "action-type": "Tipo de Acción"
}, },
"create-alias": "Crear un Alias", "create-alias": "Crear un Alias",
"manage-aliases": "Administrar Alias", "manage-aliases": "Administrar Alias",
@@ -1113,15 +1118,15 @@
"selection-mode-desc": "El modo de selección es el modo principal que puede utilizarse para introducir datos:", "selection-mode-desc": "El modo de selección es el modo principal que puede utilizarse para introducir datos:",
"selection-mode-steps": { "selection-mode-steps": {
"draw": "Dibuja un rectángulo sobre el texto que deseas seleccionar.", "draw": "Dibuja un rectángulo sobre el texto que deseas seleccionar.",
"click": "Click on any field on the right and then click back on the rectangle above the image.", "click": "Haz clic en cualquier campo a la derecha y luego haz clic en el rectángulo sobre la imagen.",
"result": "The selected text will appear inside the previously selected field." "result": "El texto seleccionado aparecerá dentro del campo elegido."
}, },
"pan-and-zoom-mode": "Modo Panorámico y Zoom", "pan-and-zoom-mode": "Modo Panorámico y Zoom",
"pan-and-zoom-desc": "Select pan and zoom by clicking the icon. This mode allows to zoom inside the image and move around to make using big images easier.", "pan-and-zoom-desc": "Selecciona, desplaza y haz zoom haciendo clic en el icono. Este modo te permite hacer zoom dentro de la imagen y desplazarte para facilitar el uso de imágenes grandes.",
"split-text-mode": "Modos de división de texto", "split-text-mode": "Modos de división de texto",
"split-modes": { "split-modes": {
"line-mode": "Modo de línea (por defecto)", "line-mode": "Modo de línea (por defecto)",
"line-mode-desc": "In line mode, the text will be propagated by keeping the original line breaks. This mode is useful when using bulk add on a list of ingredients where one ingredient is one line.", "line-mode-desc": "En el modo línea, el texto será propagado manteniendo los saltos de línea originales. Este modo es útil cuando se usa 'agregar varios' y cada ingrediente corresponde a una línea de texto.",
"block-mode": "Modo en bloque", "block-mode": "Modo en bloque",
"block-mode-desc": "En el modo de bloque, el texto se dividirá en bloques. Este modo es útil cuando se agregan instrucciones que están escritas en párrafos.", "block-mode-desc": "En el modo de bloque, el texto se dividirá en bloques. Este modo es útil cuando se agregan instrucciones que están escritas en párrafos.",
"flat-mode": "Modo texto sin formato", "flat-mode": "Modo texto sin formato",
@@ -1150,7 +1155,7 @@
"action-delete-log-files-name": "Borrar archivos de registro", "action-delete-log-files-name": "Borrar archivos de registro",
"action-delete-log-files-description": "Elimina todos los archivos de registro", "action-delete-log-files-description": "Elimina todos los archivos de registro",
"action-clean-directories-name": "Limpiar directorios", "action-clean-directories-name": "Limpiar directorios",
"action-clean-directories-description": "Removes all the recipe folders that are not valid UUIDs", "action-clean-directories-description": "Remueve todas las carpetas de receta sin UUID válido",
"action-clean-temporary-files-name": "Eliminar archivos temporales", "action-clean-temporary-files-name": "Eliminar archivos temporales",
"action-clean-temporary-files-description": "Eliminar todos los archivos y carpetas del directorio .temp", "action-clean-temporary-files-description": "Eliminar todos los archivos y carpetas del directorio .temp",
"action-clean-images-name": "Limpiar imágenes", "action-clean-images-name": "Limpiar imágenes",
@@ -1165,11 +1170,12 @@
"mainentance": { "mainentance": {
"actions-title": "Acciones" "actions-title": "Acciones"
}, },
"ingredients-natural-language-processor": "Ingredients Natural Language Processor", "ingredients-natural-language-processor": "Procesador de Lenguaje Natural de Ingredientes",
"ingredients-natural-language-processor-explanation": "Mealie uses Conditional Random Fields (CRFs) for parsing and processing ingredients. The model used for ingredients is based off a data set of over 100,000 ingredients from a dataset compiled by the New York Times. Note that as the model is trained in English only, you may have varied results when using the model in other languages. This page is a playground for testing the model.", "ingredients-natural-language-processor-explanation": "Mealie usa CRFs (Campos Condicionales Aleatorios) para analizar y procesar ingredientes. El modelo para ingredientes está basado en un conjunto de más de 100.000 ingredientes de una base de datos perteneciente a New York Times. Ya que el modelo sólo está entrenado en inglés, puede que obtengas resultados variados al utilizar otros lenguajes. Esta página es un sitio de prueba para probar el modelo.",
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "Si bien no es perfecto, entrega buenos resultados en general y es un buen punto de partida para empezar a analizar ingredientes a campos individuales. Alternativamente puedes usar el procesador \"bruto\", que utiliza una técnica de reconocimiento de patrones para identificar ingredientes.",
"nlp": "PLN", "nlp": "PLN",
"brute": "En bruto", "brute": "En bruto",
"openai": "OpenAI",
"show-individual-confidence": "Mostrar confianza individual", "show-individual-confidence": "Mostrar confianza individual",
"ingredient-text": "Texto del ingrediente", "ingredient-text": "Texto del ingrediente",
"average-confident": "{0} Confianza", "average-confident": "{0} Confianza",
@@ -1180,45 +1186,45 @@
"no-logs-found": "No se encontraron registros", "no-logs-found": "No se encontraron registros",
"tasks": "Tareas", "tasks": "Tareas",
"setup": { "setup": {
"first-time-setup": "First Time Setup", "first-time-setup": "Configuración Inicial",
"welcome-to-mealie-get-started": "Welcome to Mealie! Let's get started", "welcome-to-mealie-get-started": "¡Bienvenido a Mealie! Empecemos",
"already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage", "already-set-up-bring-to-homepage": "Estoy bien, solo llévame al Inicio",
"common-settings-for-new-sites": "Here are some common settings for new sites", "common-settings-for-new-sites": "Aquí hay algunos ajustes comunes para sitios nuevos",
"setup-complete": "Setup Complete!", "setup-complete": "¡Configuración completada!",
"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": "Aquí hay algunas cosas para ayudarte a empezar con Mealie",
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.", "restore-from-v1-backup": "¿Tienes una copia de seguridad de Mealie v1? Puedes restaurarla aquí.",
"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": "Gestiona tu perfil, o usa un enlace de invitación para compartir con otros."
} }
}, },
"profile": { "profile": {
"welcome-user": "👋 Welcome, {0}!", "welcome-user": "👋 ¡Bienvenido, {0}!",
"description": "Administra tu perfil, recetas y ajustes de grupo.", "description": "Administra tu perfil, recetas y ajustes de grupo.",
"get-invite-link": "Obtener enlace de invitación", "get-invite-link": "Obtener enlace de invitación",
"get-public-link": "Obtener enlace público", "get-public-link": "Obtener enlace público",
"account-summary": "Información de la cuenta", "account-summary": "Información de la cuenta",
"account-summary-description": "Here's a summary of your group's information.", "account-summary-description": "Aquí hay un resumen de la información del grupo.",
"group-statistics": "Estadísticas del grupo", "group-statistics": "Estadísticas del grupo",
"group-statistics-description": "Your Group Statistics provide some insight how you're using Mealie.", "group-statistics-description": "Tus estadísticas de grupo proporcionan información sobre cómo utilizas Mealie.",
"storage-capacity": "Capacidad de almacenamiento", "storage-capacity": "Capacidad de almacenamiento",
"storage-capacity-description": "Your storage capacity is a calculation of the images and assets you have uploaded.", "storage-capacity-description": "Tu capacidad de almacenamiento es el cálculo de las imágenes y recursos que has subido.",
"personal": "Personal", "personal": "Personal",
"personal-description": "These are settings that are personal to you. Changes here won't affect other users.", "personal-description": "Estos ajustes son para ti. Cualquier cambio no afectará otros usuarios.",
"user-settings": "Ajustes de usuario", "user-settings": "Ajustes de usuario",
"user-settings-description": "Manage your preferences, change your password, and update your email.", "user-settings-description": "Gestiona tus ajustes, cambia tu contraseña y actualiza tu correo electrónico.",
"api-tokens-description": "Manage your API Tokens for access from external applications.", "api-tokens-description": "Administra tus API tokens para el acceso desde apps externas.",
"group-description": "These items are shared within your group. Editing one of them will change it for the whole group!", "group-description": "Estos elementos se comparten dentro del grupo. ¡Editar cualquiera de ellos lo modificará para todo el grupo!",
"group-settings": "Ajustes de grupo", "group-settings": "Ajustes de grupo",
"group-settings-description": "Manage your common group settings like mealplan and privacy settings.", "group-settings-description": "Gestiona tus ajustes comunes de grupo, como la configuración de plan de comidas y privacidad.",
"cookbooks-description": "Manage a collection of recipe categories and generate pages for them.", "cookbooks-description": "Gestiona un a colección de categorías de receta y genera páginas para estas.",
"members": "Miembros", "members": "Miembros",
"members-description": "Ver quién está en tu grupo y administrar sus permisos.", "members-description": "Ver quién está en tu grupo y administrar sus permisos.",
"webhooks-description": "Setup webhooks that trigger on days that you have have mealplan scheduled.", "webhooks-description": "Setup webhooks that trigger on days that you have have mealplan scheduled.",
"notifiers": "Notificaciones", "notifiers": "Notificaciones",
"notifiers-description": "Setup email and push notifications that trigger on specific events.", "notifiers-description": "Setup email and push notifications that trigger on specific events.",
"manage-data": "Administrar datos", "manage-data": "Administrar datos",
"manage-data-description": "Manage your Mealie data; Foods, Units, Categories, Tags and more.", "manage-data-description": "Gestiona tus datos de Mealie; Comidas, unidades, categorías, etiquetas y más.",
"data-migrations": "Migración de datos", "data-migrations": "Migración de datos",
"data-migrations-description": "Migrate your existing data from other applications like Nextcloud Recipes and Chowdown.", "data-migrations-description": "Migra tus datos existentes desde otras aplicaciones como Nextcloud Recipes o Chowdown.",
"email-sent": "Email enviado", "email-sent": "Email enviado",
"error-sending-email": "Error enviando email", "error-sending-email": "Error enviando email",
"personal-information": "Datos Personales", "personal-information": "Datos Personales",

View File

@@ -64,7 +64,7 @@
"something-went-wrong": "Jotain meni pieleen!", "something-went-wrong": "Jotain meni pieleen!",
"subscribed-events": "Tilatut tapahtumat", "subscribed-events": "Tilatut tapahtumat",
"test-message-sent": "Viesti lähetetty", "test-message-sent": "Viesti lähetetty",
"message-sent": "Message Sent", "message-sent": "Viesti lähetetty",
"new-notification": "Uusi ilmoitus", "new-notification": "Uusi ilmoitus",
"event-notifiers": "Tapahtumien ilmoitukset", "event-notifiers": "Tapahtumien ilmoitukset",
"apprise-url-skipped-if-blank": "Ilmoitusverkko-osoite (voi jättää tyhjäksi)", "apprise-url-skipped-if-blank": "Ilmoitusverkko-osoite (voi jättää tyhjäksi)",
@@ -161,7 +161,7 @@
"test": "Testi", "test": "Testi",
"themes": "Teemat", "themes": "Teemat",
"thursday": "Torstai", "thursday": "Torstai",
"title": "Title", "title": "Otsikko",
"token": "Tunniste", "token": "Tunniste",
"tuesday": "Tiistai", "tuesday": "Tiistai",
"type": "Tyyppi", "type": "Tyyppi",
@@ -559,8 +559,8 @@
"new-recipe-names-must-be-unique": "Reseptin nimen on oltava yksilöllinen", "new-recipe-names-must-be-unique": "Reseptin nimen on oltava yksilöllinen",
"scrape-recipe": "Reseptin kaappain", "scrape-recipe": "Reseptin kaappain",
"scrape-recipe-description": "Kaappaa resepti urlin avulla. Anna sen reseptin url-osoite, jonka haluat kaapata, ja Mealie yrittää kaapata reseptin kyseiseltä sivustolta ja lisätä sen kokoelmaasi.", "scrape-recipe-description": "Kaappaa resepti urlin avulla. Anna sen reseptin url-osoite, jonka haluat kaapata, ja Mealie yrittää kaapata reseptin kyseiseltä sivustolta ja lisätä sen kokoelmaasi.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?", "scrape-recipe-have-a-lot-of-recipes": "Haluatko kerätä useamman reseptin kerralla?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer", "scrape-recipe-suggest-bulk-importer": "Kokeile massasiirtotyökalua",
"import-original-keywords-as-tags": "Tuo alkuperäiset avainsanat tunnisteiksi", "import-original-keywords-as-tags": "Tuo alkuperäiset avainsanat tunnisteiksi",
"stay-in-edit-mode": "Pysy muokkaustilassa", "stay-in-edit-mode": "Pysy muokkaustilassa",
"import-from-zip": "Tuo zip-arkistosta", "import-from-zip": "Tuo zip-arkistosta",
@@ -585,20 +585,21 @@
"screen-awake": "Pidä näyttö aina päällä", "screen-awake": "Pidä näyttö aina päällä",
"remove-image": "Poista kuva", "remove-image": "Poista kuva",
"nextStep": "Seuraava askel", "nextStep": "Seuraava askel",
"recipe-actions": "Recipe Actions", "recipe-actions": "Reseptin toiminnot",
"parser": { "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.", "experimental-alert-text": "Mealie käyttää luonnollisen kielen prosessointia jäsentääkseen ja luodakseen yksiköitä ja tarvikkeita reseptiesi ainesosille. Ominaisuus on kokeellinen, eikä välttämättä toimi aina odotetulla tavalla. Jos et halua näitä jäsennettyjä tuloksia, voit painaa \"Peruuta\", ja muutoksiasi ei tallenneta.",
"ingredient-parser": "Ingredient Parser", "ingredient-parser": "Ainesosan jäsentäjä",
"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.", "explanation": "Paina \"Jäsennä kaikki\" -painiketta käyttääksesi ainesosien jäsennystyökalua. Kun ainesosat ovat saatavilla, voit tarkastella kohteita ja varmistaa, että jäsennys onnistui. Mallin luottamusarvo mainitaan kohteen nimen oikealla puolella. Tämä arvo on yksittäisten arvojen keskiarvo, eikä se ole aina kovin tarkka.",
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.", "alerts-explainer": "Ohjelmisto hälyttää, mikäli jokin sopiva ruoka tai yksikkö löytyy, mutta sitä ei ole olemassa tietokannassa.",
"select-parser": "Select Parser", "select-parser": "Valitse jäsentäjätyökalu",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Luonnollisen kielen prosessointityökalu",
"brute-parser": "Brute Parser", "brute-parser": "Voimakas jäsentäjätyökalu",
"parse-all": "Parse All", "openai-parser": "OpenAI Parser",
"no-unit": "No unit", "parse-all": "Jäsennä kaikki",
"missing-unit": "Create missing unit: {unit}", "no-unit": "Ei yksikköä",
"missing-food": "Create missing food: {food}", "missing-unit": "Luo puuttuva yksikkö: {unit}",
"no-food": "No Food" "missing-food": "Luo puuttuva ruoka: {food}",
"no-food": "Ei ruokaa"
} }
}, },
"search": { "search": {
@@ -717,7 +718,7 @@
"no-unused-items": "Ei käyttämättömiä kohteita", "no-unused-items": "Ei käyttämättömiä kohteita",
"recipes-affected": "Ei vaikuttanut resepteihin|Vaikutti yhteen reseptiin|Vaikutti {count} reseptiin", "recipes-affected": "Ei vaikuttanut resepteihin|Vaikutti yhteen reseptiin|Vaikutti {count} reseptiin",
"remove-unused": "Poista käyttämättömät", "remove-unused": "Poista käyttämättömät",
"title-case-all": "Title Case All", "title-case-all": "Isot alkukirjaimet kaikille sanoille",
"toolbox": "Työkalut", "toolbox": "Työkalut",
"unorganized": "Järjestämätön" "unorganized": "Järjestämätön"
}, },
@@ -763,8 +764,11 @@
"build": "Koonti", "build": "Koonti",
"recipe-scraper-version": "Reseptikaappaimen versio", "recipe-scraper-version": "Reseptikaappaimen versio",
"oidc-ready": "OIDC valmis", "oidc-ready": "OIDC valmis",
"oidc-ready-error-text": "Not all OIDC Values are configured. This can be ignored if you are not using OIDC Authentication.", "oidc-ready-error-text": "Kaikkia OIDC-arvoja ei ole määritelty. Jos et käytä OIDC-todennusta, voidaan asia jättää huomiotta.",
"oidc-ready-success-text": "Required OIDC variables are all set." "oidc-ready-success-text": "Kaikki vaaditut OIDC-muuttujat asetettu.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Kaikki ostoslistat", "all-lists": "Kaikki ostoslistat",
@@ -778,6 +782,7 @@
"food": "Elintarvikkeet", "food": "Elintarvikkeet",
"note": "Muistiinpano", "note": "Muistiinpano",
"label": "Tunnus", "label": "Tunnus",
"save-label": "Save Label",
"linked-item-warning": "Tämä kohde on linkitetty yhteen tai useampaan reseptiin. Yksikköjen tai elintarvikkeiden muuttaminen tuottaa odottamattomia tuloksia, kun reseptiä lisätään tai poistetaan tästä luettelosta.", "linked-item-warning": "Tämä kohde on linkitetty yhteen tai useampaan reseptiin. Yksikköjen tai elintarvikkeiden muuttaminen tuottaa odottamattomia tuloksia, kun reseptiä lisätään tai poistetaan tästä luettelosta.",
"toggle-food": "Vaihda elintarvike", "toggle-food": "Vaihda elintarvike",
"manage-labels": "Hallinnoi nimikkeitä", "manage-labels": "Hallinnoi nimikkeitä",
@@ -837,7 +842,7 @@
"untagged-count": "Tunnisteettomat {count}", "untagged-count": "Tunnisteettomat {count}",
"create-a-tag": "Luo tunniste", "create-a-tag": "Luo tunniste",
"tag-name": "Tunnisteen nimi", "tag-name": "Tunnisteen nimi",
"tag": "Tag" "tag": "Tunniste"
}, },
"tool": { "tool": {
"tools": "Työkalut", "tools": "Työkalut",
@@ -847,7 +852,7 @@
"create-new-tool": "Luo Uusi Työkalu", "create-new-tool": "Luo Uusi Työkalu",
"on-hand-checkbox-label": "Näytä työkalut, jotka omistan jo (valittu)", "on-hand-checkbox-label": "Näytä työkalut, jotka omistan jo (valittu)",
"required-tools": "Tarvittavat Työkalut", "required-tools": "Tarvittavat Työkalut",
"tool": "Tool" "tool": "Työkalu"
}, },
"user": { "user": {
"admin": "Ylläpitäjä", "admin": "Ylläpitäjä",
@@ -872,11 +877,11 @@
"link-id": "Linkin ID", "link-id": "Linkin ID",
"link-name": "Linkin nimi", "link-name": "Linkin nimi",
"login": "Kirjaudu", "login": "Kirjaudu",
"login-oidc": "Login with", "login-oidc": "Kirjaudu sisään käyttäen",
"or": "or", "or": "tai",
"logout": "Uloskirjaudu", "logout": "Uloskirjaudu",
"manage-users": "Käyttäjien hallinta", "manage-users": "Käyttäjien hallinta",
"manage-users-description": "Create and manage users.", "manage-users-description": "Luo ja hallitse käyttäjiä.",
"new-password": "Uusi salasana", "new-password": "Uusi salasana",
"new-user": "Uusi käyttäjä", "new-user": "Uusi käyttäjä",
"password-has-been-reset-to-the-default-password": "Salasana on palautettu oletussalasanaksi", "password-has-been-reset-to-the-default-password": "Salasana on palautettu oletussalasanaksi",
@@ -885,7 +890,7 @@
"password-updated": "Salasana päivitetty", "password-updated": "Salasana päivitetty",
"password": "Salasana", "password": "Salasana",
"password-strength": "Salasana on {strength}", "password-strength": "Salasana on {strength}",
"please-enter-password": "Please enter your new password.", "please-enter-password": "Syötä uusi salasanasi.",
"register": "Rekisteröidy", "register": "Rekisteröidy",
"reset-password": "Palauta salasana", "reset-password": "Palauta salasana",
"sign-in": "Kirjaudu", "sign-in": "Kirjaudu",
@@ -906,7 +911,7 @@
"username": "Käyttäjänimi", "username": "Käyttäjänimi",
"users-header": "KÄYTTÄJÄT", "users-header": "KÄYTTÄJÄT",
"users": "Käyttäjät", "users": "Käyttäjät",
"user-not-found": "User not found", "user-not-found": "Käyttäjää ei löytynyt",
"webhook-time": "Webhook-aika", "webhook-time": "Webhook-aika",
"webhooks-enabled": "Webhookit käytössä", "webhooks-enabled": "Webhookit käytössä",
"you-are-not-allowed-to-create-a-user": "Sinulla ei ole oikeutta luoda käyttäjää", "you-are-not-allowed-to-create-a-user": "Sinulla ei ole oikeutta luoda käyttäjää",
@@ -929,7 +934,7 @@
"user-management": "Käyttäjien Hallinta", "user-management": "Käyttäjien Hallinta",
"reset-locked-users": "Nollaa Lukitut Käyttäjät", "reset-locked-users": "Nollaa Lukitut Käyttäjät",
"admin-user-creation": "Ylläpitokäyttäjän Luonti", "admin-user-creation": "Ylläpitokäyttäjän Luonti",
"admin-user-management": "Admin User Management", "admin-user-management": "Ylläpidon käyttäjien hallinta",
"user-details": "Käyttäjän tiedot", "user-details": "Käyttäjän tiedot",
"user-name": "Käyttäjänimi", "user-name": "Käyttäjänimi",
"authentication-method": "Todentamistapa", "authentication-method": "Todentamistapa",
@@ -940,11 +945,11 @@
"user-can-manage-group": "Käyttäjä voi hallita ryhmää", "user-can-manage-group": "Käyttäjä voi hallita ryhmää",
"user-can-organize-group-data": "Käyttäjä voi järjestellä ryhmän tietoja", "user-can-organize-group-data": "Käyttäjä voi järjestellä ryhmän tietoja",
"enable-advanced-features": "Salli edistyneemmät ominaisuudet", "enable-advanced-features": "Salli edistyneemmät ominaisuudet",
"it-looks-like-this-is-your-first-time-logging-in": "It looks like this is your first time logging in.", "it-looks-like-this-is-your-first-time-logging-in": "Tämä vaikuttaa olevan ensimmäinen kirjautumisesi.",
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "Don't want to see this anymore? Be sure to change your email in your user settings!", "dont-want-to-see-this-anymore-be-sure-to-change-your-email": "Vaihda sähköpostisi asetuksista, jos et halua nähdä tätä enää.",
"forgot-password": "Forgot Password", "forgot-password": "Unohditko salasanasi",
"forgot-password-text": "Please enter your email address and we will send you a link to reset your password.", "forgot-password-text": "Syötä sähköpostiosoitteesi, niin voit muuttaa salasanaasi linkin kautta.",
"changes-reflected-immediately": "Changes to this user will be reflected immediately." "changes-reflected-immediately": "Muutokset tähän käyttäjään astuvat välittömästi voimaan."
}, },
"language-dialog": { "language-dialog": {
"translated": "käännetty", "translated": "käännetty",
@@ -966,8 +971,8 @@
"food-label": "Elintarvikkeiden nimike", "food-label": "Elintarvikkeiden nimike",
"edit-food": "Muokkaa elintarviketta", "edit-food": "Muokkaa elintarviketta",
"food-data": "Elintarvikkeiden tiedot", "food-data": "Elintarvikkeiden tiedot",
"example-food-singular": "ex: Onion", "example-food-singular": "esim. sipuli",
"example-food-plural": "ex: Onions" "example-food-plural": "esim. sipulit"
}, },
"units": { "units": {
"seed-dialog-text": "Lisää tietokantaan yksiköt paikallisen kielen perusteella.", "seed-dialog-text": "Lisää tietokantaan yksiköt paikallisen kielen perusteella.",
@@ -978,7 +983,7 @@
"merging-unit-into-unit": "Yhdistä {0} ja {1} yhdeksi", "merging-unit-into-unit": "Yhdistä {0} ja {1} yhdeksi",
"create-unit": "Luo yksikkö", "create-unit": "Luo yksikkö",
"abbreviation": "Lyhenne", "abbreviation": "Lyhenne",
"plural-abbreviation": "Plural Abbreviation", "plural-abbreviation": "Monikon lyhenne",
"description": "Kuvaus", "description": "Kuvaus",
"display-as-fraction": "Näytä murtolukuna", "display-as-fraction": "Näytä murtolukuna",
"use-abbreviation": "Käytä Lyhennettä", "use-abbreviation": "Käytä Lyhennettä",
@@ -986,10 +991,10 @@
"unit-data": "Yksikkötiedot", "unit-data": "Yksikkötiedot",
"use-abbv": "Käytä lyhennettä.", "use-abbv": "Käytä lyhennettä.",
"fraction": "Murtoluku", "fraction": "Murtoluku",
"example-unit-singular": "ex: Tablespoon", "example-unit-singular": "esim. ruokalusikka",
"example-unit-plural": "ex: Tablespoons", "example-unit-plural": "esim. ruokalusikat",
"example-unit-abbreviation-singular": "ex: Tbsp", "example-unit-abbreviation-singular": "esim. rkl",
"example-unit-abbreviation-plural": "ex: Tbsps" "example-unit-abbreviation-plural": "esim. rkl"
}, },
"labels": { "labels": {
"seed-dialog-text": "Lisää tietokantaan yleiset tunnisteet paikallisen kielen perusteella.", "seed-dialog-text": "Lisää tietokantaan yleiset tunnisteet paikallisen kielen perusteella.",
@@ -1019,13 +1024,13 @@
"source-unit-will-be-deleted": "Lähdeyksikkö poistetaan" "source-unit-will-be-deleted": "Lähdeyksikkö poistetaan"
}, },
"recipe-actions": { "recipe-actions": {
"recipe-actions-data": "Recipe Actions Data", "recipe-actions-data": "Reseptin toimintojen käyttötiedot",
"new-recipe-action": "New Recipe Action", "new-recipe-action": "Uusi reseptin toiminto",
"edit-recipe-action": "Edit Recipe Action", "edit-recipe-action": "Muuta reseptin toimintoa",
"action-type": "Action Type" "action-type": "Toiminnon tyyppi"
}, },
"create-alias": "Create Alias", "create-alias": "Luo alias",
"manage-aliases": "Manage Aliases", "manage-aliases": "Hallitse aliaksia",
"seed-data": "Tietokannan pohjadata", "seed-data": "Tietokannan pohjadata",
"seed": "Lisää pohjadata", "seed": "Lisää pohjadata",
"data-management": "Tietojen hallinta", "data-management": "Tietojen hallinta",
@@ -1035,9 +1040,9 @@
"columns": "Sarakkeet", "columns": "Sarakkeet",
"combine": "Yhdistä", "combine": "Yhdistä",
"categories": { "categories": {
"edit-category": "Edit Category", "edit-category": "Muuta luokkaa",
"new-category": "New Category", "new-category": "Uusi luokka",
"category-data": "Category Data" "category-data": "Luokan tiedot"
}, },
"tags": { "tags": {
"new-tag": "New Tag", "new-tag": "New Tag",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Se ei ole täydellinen, mutta se tuottaa hyviä tuloksia yleensä ja on hyvä lähtökohta manuaalisesti jäsentää ainesosia yksittäisiin kenttiin. Vaihtoehtoisesti voit myös käyttää Brute-prosessori, joka käyttää kuvion täsmäystekniikkaa tunnistamaan ainesosia.", "ingredients-natural-language-processor-explanation-2": "Se ei ole täydellinen, mutta se tuottaa hyviä tuloksia yleensä ja on hyvä lähtökohta manuaalisesti jäsentää ainesosia yksittäisiin kenttiin. Vaihtoehtoisesti voit myös käyttää Brute-prosessori, joka käyttää kuvion täsmäystekniikkaa tunnistamaan ainesosia.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Näytä yksilöllinen luottamus", "show-individual-confidence": "Näytä yksilöllinen luottamus",
"ingredient-text": "Ainesosan Teksti", "ingredient-text": "Ainesosan Teksti",
"average-confident": "{0} Luottamus", "average-confident": "{0} Luottamus",

View File

@@ -594,6 +594,7 @@
"select-parser": "Sélectionner l'analyseur", "select-parser": "Sélectionner l'analyseur",
"natural-language-processor": "Traitement du Langage Naturel", "natural-language-processor": "Traitement du Langage Naturel",
"brute-parser": "Analyseur brut", "brute-parser": "Analyseur brut",
"openai-parser": "OpenAI Parser",
"parse-all": "Tout analyser", "parse-all": "Tout analyser",
"no-unit": "Pas d'unité", "no-unit": "Pas d'unité",
"missing-unit": "Créer une unité manquante : {unit}", "missing-unit": "Créer une unité manquante : {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Version du Scraper de recette", "recipe-scraper-version": "Version du Scraper de recette",
"oidc-ready": "Prêt pour OIDC", "oidc-ready": "Prêt pour OIDC",
"oidc-ready-error-text": "Toutes les valeurs OIDC ne sont pas configurées. Vous pouvez ignorer cet avertissement si vous nutilisez pas lauthentification OIDC.", "oidc-ready-error-text": "Toutes les valeurs OIDC ne sont pas configurées. Vous pouvez ignorer cet avertissement si vous nutilisez pas lauthentification OIDC.",
"oidc-ready-success-text": "Les variables OIDC obligatoires sont toutes définies." "oidc-ready-success-text": "Les variables OIDC obligatoires sont toutes définies.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Toutes les listes", "all-lists": "Toutes les listes",
@@ -778,6 +782,7 @@
"food": "Aliments", "food": "Aliments",
"note": "Note", "note": "Note",
"label": "Étiquette", "label": "Étiquette",
"save-label": "Save Label",
"linked-item-warning": "Cet article est lié à une ou plusieurs recettes. Ajuster les unités ou les aliments donnera des résultats inattendus lors de lajout ou de la suppression de la recette de cette liste.", "linked-item-warning": "Cet article est lié à une ou plusieurs recettes. Ajuster les unités ou les aliments donnera des résultats inattendus lors de lajout ou de la suppression de la recette de cette liste.",
"toggle-food": "Activer/Désactiver aliment", "toggle-food": "Activer/Désactiver aliment",
"manage-labels": "Gérer les libellés", "manage-labels": "Gérer les libellés",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Ce n'est pas parfait, mais cela donne de bons résultats en général et est un bon point de départ pour décomposer manuellement les ingrédients dans des champs individuels. Alternativement, vous pouvez également utiliser le processeur « Brut » qui utilise une technique de correspondance (patterns) pour identifier les ingrédients.", "ingredients-natural-language-processor-explanation-2": "Ce n'est pas parfait, mais cela donne de bons résultats en général et est un bon point de départ pour décomposer manuellement les ingrédients dans des champs individuels. Alternativement, vous pouvez également utiliser le processeur « Brut » qui utilise une technique de correspondance (patterns) pour identifier les ingrédients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brut", "brute": "Brut",
"openai": "OpenAI",
"show-individual-confidence": "Afficher la confiance individuelle", "show-individual-confidence": "Afficher la confiance individuelle",
"ingredient-text": "Texte de l'ingrédient", "ingredient-text": "Texte de l'ingrédient",
"average-confident": "Confiant à {0}", "average-confident": "Confiant à {0}",

View File

@@ -594,6 +594,7 @@
"select-parser": "Sélectionner l'analyseur", "select-parser": "Sélectionner l'analyseur",
"natural-language-processor": "Traitement du Langage Naturel", "natural-language-processor": "Traitement du Langage Naturel",
"brute-parser": "Analyseur brut", "brute-parser": "Analyseur brut",
"openai-parser": "OpenAI Parser",
"parse-all": "Tout analyser", "parse-all": "Tout analyser",
"no-unit": "Pas d'unité", "no-unit": "Pas d'unité",
"missing-unit": "Créer une unité manquante : {unit}", "missing-unit": "Créer une unité manquante : {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Version du Scraper de recette", "recipe-scraper-version": "Version du Scraper de recette",
"oidc-ready": "Prêt pour OIDC", "oidc-ready": "Prêt pour OIDC",
"oidc-ready-error-text": "Toutes les valeurs OIDC ne sont pas configurées. Vous pouvez ignorer cet avertissement si vous nutilisez pas lauthentification OIDC.", "oidc-ready-error-text": "Toutes les valeurs OIDC ne sont pas configurées. Vous pouvez ignorer cet avertissement si vous nutilisez pas lauthentification OIDC.",
"oidc-ready-success-text": "Les variables OIDC obligatoires sont toutes définies." "oidc-ready-success-text": "Les variables OIDC obligatoires sont toutes définies.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Toutes les listes", "all-lists": "Toutes les listes",
@@ -778,6 +782,7 @@
"food": "Aliment", "food": "Aliment",
"note": "Note", "note": "Note",
"label": "Étiquette", "label": "Étiquette",
"save-label": "Save Label",
"linked-item-warning": "Cet article est lié à une ou plusieurs recettes. Ajuster les unités ou les aliments donnera des résultats inattendus lors de lajout ou de la suppression de la recette de cette liste.", "linked-item-warning": "Cet article est lié à une ou plusieurs recettes. Ajuster les unités ou les aliments donnera des résultats inattendus lors de lajout ou de la suppression de la recette de cette liste.",
"toggle-food": "Activer/Désactiver aliment", "toggle-food": "Activer/Désactiver aliment",
"manage-labels": "Gérer les libellés", "manage-labels": "Gérer les libellés",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Ce n'est pas parfait, mais cela donne de bons résultats en général et est un bon point de départ pour décomposer manuellement les ingrédients dans des champs individuels. Alternativement, vous pouvez également utiliser le processeur « Brut » qui utilise une technique de correspondance (patterns) pour identifier les ingrédients.", "ingredients-natural-language-processor-explanation-2": "Ce n'est pas parfait, mais cela donne de bons résultats en général et est un bon point de départ pour décomposer manuellement les ingrédients dans des champs individuels. Alternativement, vous pouvez également utiliser le processeur « Brut » qui utilise une technique de correspondance (patterns) pour identifier les ingrédients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brut", "brute": "Brut",
"openai": "OpenAI",
"show-individual-confidence": "Afficher la confiance individuelle", "show-individual-confidence": "Afficher la confiance individuelle",
"ingredient-text": "Texte de l'ingrédient", "ingredient-text": "Texte de l'ingrédient",
"average-confident": "Confiant à {0}", "average-confident": "Confiant à {0}",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "All Lists", "all-lists": "All Lists",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "גרסת מפענך המתכונים", "recipe-scraper-version": "גרסת מפענך המתכונים",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "כל הרשימות", "all-lists": "כל הרשימות",
@@ -778,6 +782,7 @@
"food": "אוכל", "food": "אוכל",
"note": "הערה", "note": "הערה",
"label": "תווית", "label": "תווית",
"save-label": "Save Label",
"linked-item-warning": "האובייקט הזה מקושר לאחד או יותר מתכונים. שינוי היחידות או האוכל יוביל לתוצאות בלתי צפויות בהוספה או הסרת מתכונים מהרשימה.", "linked-item-warning": "האובייקט הזה מקושר לאחד או יותר מתכונים. שינוי היחידות או האוכל יוביל לתוצאות בלתי צפויות בהוספה או הסרת מתכונים מהרשימה.",
"toggle-food": "הצג/הסתר אוכל", "toggle-food": "הצג/הסתר אוכל",
"manage-labels": "ניהול תויות", "manage-labels": "ניהול תויות",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "זה לא מושלם, אבל מספק תוצאות טובות באופן כללי ומספק נקודת התחלה טובה עבור עיבוד ידני של מרכיבים לשדות נפרדים. לחילופין ניתן להשתמש בעיבוד \"נוקשה\" לזיהוי המרכיבים.", "ingredients-natural-language-processor-explanation-2": "זה לא מושלם, אבל מספק תוצאות טובות באופן כללי ומספק נקודת התחלה טובה עבור עיבוד ידני של מרכיבים לשדות נפרדים. לחילופין ניתן להשתמש בעיבוד \"נוקשה\" לזיהוי המרכיבים.",
"nlp": "NLP", "nlp": "NLP",
"brute": "נוקשה", "brute": "נוקשה",
"openai": "OpenAI",
"show-individual-confidence": "הראה תאימות בודדת", "show-individual-confidence": "הראה תאימות בודדת",
"ingredient-text": "טקסט מרכיב", "ingredient-text": "טקסט מרכיב",
"average-confident": "{0} דיוק", "average-confident": "{0} דיוק",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Verzija skraper alata za recepte", "recipe-scraper-version": "Verzija skraper alata za recepte",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Svi Popisi", "all-lists": "Svi Popisi",
@@ -778,6 +782,7 @@
"food": "Namirnica", "food": "Namirnica",
"note": "Bilješka", "note": "Bilješka",
"label": "Oznaka", "label": "Oznaka",
"save-label": "Save Label",
"linked-item-warning": "Ova stavka je povezana s jednim ili više recepata. Prilagođavanje jedinica ili namirnica može rezultirati neočekivanim rezultatima prilikom dodavanja ili uklanjanja recepta s ove liste.", "linked-item-warning": "Ova stavka je povezana s jednim ili više recepata. Prilagođavanje jedinica ili namirnica može rezultirati neočekivanim rezultatima prilikom dodavanja ili uklanjanja recepta s ove liste.",
"toggle-food": "Prebaci prekidač Namirnice", "toggle-food": "Prebaci prekidač Namirnice",
"manage-labels": "Upravljanje Oznakama", "manage-labels": "Upravljanje Oznakama",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Nije savršen, ali općenito daje odlične rezultate i dobra je polazna točka za ručno parsiranje sastojaka u pojedinačna polja. Alternativno, možete koristiti \"Brute\" procesor koji koristi tehniku usklađivanja uzoraka za identifikaciju sastojaka.", "ingredients-natural-language-processor-explanation-2": "Nije savršen, ali općenito daje odlične rezultate i dobra je polazna točka za ručno parsiranje sastojaka u pojedinačna polja. Alternativno, možete koristiti \"Brute\" procesor koji koristi tehniku usklađivanja uzoraka za identifikaciju sastojaka.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Prikaži pojedinačno povjerenje", "show-individual-confidence": "Prikaži pojedinačno povjerenje",
"ingredient-text": "Tekst Sastojka", "ingredient-text": "Tekst Sastojka",
"average-confident": "{0} - Siguran", "average-confident": "{0} - Siguran",

View File

@@ -594,6 +594,7 @@
"select-parser": "Válasszon elemzőt", "select-parser": "Válasszon elemzőt",
"natural-language-processor": "Természetes nyelvi feldolgozó", "natural-language-processor": "Természetes nyelvi feldolgozó",
"brute-parser": "Brute elemző", "brute-parser": "Brute elemző",
"openai-parser": "OpenAI elemző",
"parse-all": "Összes elemzése", "parse-all": "Összes elemzése",
"no-unit": "Mértékegység nélkül", "no-unit": "Mértékegység nélkül",
"missing-unit": "Hiányzó mértékegység létrehozása: {unit}", "missing-unit": "Hiányzó mértékegység létrehozása: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Receptkinyerő verziója", "recipe-scraper-version": "Receptkinyerő verziója",
"oidc-ready": "OIDC készen áll", "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-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." "oidc-ready-success-text": "A szükséges OIDC változók mind beállítva.",
"openai-ready": "OpenAI készen áll",
"openai-ready-error-text": "Nem minden OpenAI érték van beállítva. Ez figyelmen kívül hagyható, ha nem használja az OpenAI funkcióit.",
"openai-ready-success-text": "Minden szükséges OpenAI változó beállítva."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Összes lista", "all-lists": "Összes lista",
@@ -778,6 +782,7 @@
"food": "Étel", "food": "Étel",
"note": "Megjegyzés", "note": "Megjegyzés",
"label": "Címke", "label": "Címke",
"save-label": "Save Label",
"linked-item-warning": "Ez a tétel egy vagy több recepthez kapcsolódik. A mennyiségi egységek vagy az alapanyagok átállítása nem várt eredményeket fog eredményezni, amikor hozzáadja vagy eltávolítja a receptet ebből a listából.", "linked-item-warning": "Ez a tétel egy vagy több recepthez kapcsolódik. A mennyiségi egységek vagy az alapanyagok átállítása nem várt eredményeket fog eredményezni, amikor hozzáadja vagy eltávolítja a receptet ebből a listából.",
"toggle-food": "Váltás alapanyagokra", "toggle-food": "Váltás alapanyagokra",
"manage-labels": "Címkék kezelése", "manage-labels": "Címkék kezelése",
@@ -1022,7 +1027,7 @@
"recipe-actions-data": "Recipe Actions Data", "recipe-actions-data": "Recipe Actions Data",
"new-recipe-action": "New Recipe Action", "new-recipe-action": "New Recipe Action",
"edit-recipe-action": "Edit Recipe Action", "edit-recipe-action": "Edit Recipe Action",
"action-type": "Action Type" "action-type": "Művelet típusa"
}, },
"create-alias": "Alias készítése", "create-alias": "Alias készítése",
"manage-aliases": "Alias kezelése", "manage-aliases": "Alias kezelése",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Nem tökéletes, de általában nagyszerű eredményeket ad, és jó kiindulópont a hozzávalók kézi elemzésére az egyedi mezőkbe. Alternatívaként használhatja a \"Brute\" feldolgozót is, amely egy mintaillesztési technikát használ a hozzávalók azonosítására.", "ingredients-natural-language-processor-explanation-2": "Nem tökéletes, de általában nagyszerű eredményeket ad, és jó kiindulópont a hozzávalók kézi elemzésére az egyedi mezőkbe. Alternatívaként használhatja a \"Brute\" feldolgozót is, amely egy mintaillesztési technikát használ a hozzávalók azonosítására.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "", "show-individual-confidence": "",
"ingredient-text": "Hozzávaló szöveg", "ingredient-text": "Hozzávaló szöveg",
"average-confident": "{0}-os bizonyosság", "average-confident": "{0}-os bizonyosság",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "All Lists", "all-lists": "All Lists",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -587,18 +587,19 @@
"nextStep": "Passo successivo", "nextStep": "Passo successivo",
"recipe-actions": "Recipe Actions", "recipe-actions": "Recipe Actions",
"parser": { "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.", "experimental-alert-text": "Mealie utilizza l'elaborazione del linguaggio naturale per analizzare e creare unità e prodotti alimentari per i vostri ingredienti di ricetta. Questa funzione è sperimentale e potrebbe non funzionare sempre come previsto. Se preferisci non usare i risultati analizzati, puoi selezionare 'Annulla' e le tue modifiche non saranno salvate.",
"ingredient-parser": "Ingredient Parser", "ingredient-parser": "Analizzatore ingredienti",
"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.", "explanation": "Per utilizzare l'analizzatore degli ingredienti, fare clic sul pulsante 'Analizza tutto' per avviare il processo. Una volta che gli ingredienti elaborati saranno disponibili, sarà possibile rivedere gli elementi e verificare che siano stati analizzati correttamente. Il punteggio di confidenza del modello viene visualizzato alla destra del titolo dell'elemento. Questo punteggio è una media di tutti i singoli punteggi e potrebbe non essere sempre completamente accurato.",
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.", "alerts-explainer": "Gli avvisi verranno visualizzati se si trova un prodotto o un'unità corrispondente ma non esiste nel database.",
"select-parser": "Select Parser", "select-parser": "Seleziona Analizzatore",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Analizzatore di Linguaggio Naturale",
"brute-parser": "Brute Parser", "brute-parser": "Analizzatore brutale",
"parse-all": "Parse All", "openai-parser": "OpenAI Parser",
"no-unit": "No unit", "parse-all": "Analizza tutto",
"missing-unit": "Create missing unit: {unit}", "no-unit": "Nessuna unità",
"missing-food": "Create missing food: {food}", "missing-unit": "Crea unità mancante: {unit}",
"no-food": "No Food" "missing-food": "Crea cibo mancante: {food}",
"no-food": "Nessun Alimento"
} }
}, },
"search": { "search": {
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Versione Recipe Scraper", "recipe-scraper-version": "Versione Recipe Scraper",
"oidc-ready": "Pronto per OIDC", "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-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." "oidc-ready-success-text": "Le variabili OIDC richieste sono tutte impostate.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Tutte le Liste", "all-lists": "Tutte le Liste",
@@ -778,6 +782,7 @@
"food": "Alimenti", "food": "Alimenti",
"note": "Nota", "note": "Nota",
"label": "Etichetta", "label": "Etichetta",
"save-label": "Save Label",
"linked-item-warning": "Questo elemento è collegato a una o più ricette. La modifica delle unità o degli alimenti potrebbe dare risultati inattesi quando si aggiunge o si rimuove la ricetta da questo elenco.", "linked-item-warning": "Questo elemento è collegato a una o più ricette. La modifica delle unità o degli alimenti potrebbe dare risultati inattesi quando si aggiunge o si rimuove la ricetta da questo elenco.",
"toggle-food": "Attiva/Disattiva Alimento", "toggle-food": "Attiva/Disattiva Alimento",
"manage-labels": "Gestisci Etichette", "manage-labels": "Gestisci Etichette",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Non è perfetto, ma produce ottimi risultati in generale ed è un buon punto di partenza per separare manualmente gli ingredienti in singoli campi. In alternativa, è anche possibile utilizzare il processore \"Bruto\" che utilizza una tecnica di corrispondenza di modello per identificare gli ingredienti.", "ingredients-natural-language-processor-explanation-2": "Non è perfetto, ma produce ottimi risultati in generale ed è un buon punto di partenza per separare manualmente gli ingredienti in singoli campi. In alternativa, è anche possibile utilizzare il processore \"Bruto\" che utilizza una tecnica di corrispondenza di modello per identificare gli ingredienti.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Bruto", "brute": "Bruto",
"openai": "OpenAI",
"show-individual-confidence": "Mostra fiducia individuale", "show-individual-confidence": "Mostra fiducia individuale",
"ingredient-text": "Testo Ingrediente", "ingredient-text": "Testo Ingrediente",
"average-confident": "{0} Fiducia", "average-confident": "{0} Fiducia",

View File

@@ -18,8 +18,8 @@
"log-lines": "ログの行", "log-lines": "ログの行",
"not-demo": "非デモ", "not-demo": "非デモ",
"portfolio": "ポートフォリオ", "portfolio": "ポートフォリオ",
"production": "生産", "production": "Production",
"support": "お問い合わせ", "support": "ヘルプ",
"version": "バージョン", "version": "バージョン",
"unknown-version": "不明", "unknown-version": "不明",
"sponsor": "スポンサー" "sponsor": "スポンサー"
@@ -29,7 +29,7 @@
"code": "コード", "code": "コード",
"file": "ファイル", "file": "ファイル",
"image": "画像", "image": "画像",
"new-asset": "新しい資産", "new-asset": "新しいアセット",
"pdf": "PDF", "pdf": "PDF",
"recipe": "レシピ", "recipe": "レシピ",
"show-assets": "資産を表示", "show-assets": "資産を表示",
@@ -64,7 +64,7 @@
"something-went-wrong": "問題が発生しました", "something-went-wrong": "問題が発生しました",
"subscribed-events": "購読中のイベント", "subscribed-events": "購読中のイベント",
"test-message-sent": "テストメッセージを送信しました", "test-message-sent": "テストメッセージを送信しました",
"message-sent": "Message Sent", "message-sent": "メッセージが送信されました",
"new-notification": "新着通知", "new-notification": "新着通知",
"event-notifiers": "イベント通知", "event-notifiers": "イベント通知",
"apprise-url-skipped-if-blank": "通知用URL (空欄の場合はスキップ)", "apprise-url-skipped-if-blank": "通知用URL (空欄の場合はスキップ)",
@@ -72,12 +72,12 @@
"what-events": "この通知はどのイベントを購読すべきですか?", "what-events": "この通知はどのイベントを購読すべきですか?",
"user-events": "ユーザーイベント", "user-events": "ユーザーイベント",
"mealplan-events": "献立イベント", "mealplan-events": "献立イベント",
"when-a-user-in-your-group-creates-a-new-mealplan": "グループ内のユーザーが新しい食事プランを作成したとき", "when-a-user-in-your-group-creates-a-new-mealplan": "グループ内のユーザーが新しい献立を作成したとき",
"shopping-list-events": "買い物リストイベント", "shopping-list-events": "買い物リストイベント",
"cookbook-events": "料理本イベント", "cookbook-events": "料理本イベント",
"tag-events": "タグイベント", "tag-events": "タグイベント",
"category-events": "カテゴリイベント", "category-events": "カテゴリイベント",
"when-a-new-user-joins-your-group": "新しいユーザーがあなたのグループに参加する際", "when-a-new-user-joins-your-group": "新しいユーザーがグループに参加したとき",
"recipe-events": "レシピイベント" "recipe-events": "レシピイベント"
}, },
"general": { "general": {
@@ -161,7 +161,7 @@
"test": "テスト", "test": "テスト",
"themes": "テーマ", "themes": "テーマ",
"thursday": "木曜日", "thursday": "木曜日",
"title": "Title", "title": "タイトル",
"token": "トークン", "token": "トークン",
"tuesday": "火曜日", "tuesday": "火曜日",
"type": "タイプ", "type": "タイプ",
@@ -585,7 +585,7 @@
"screen-awake": "画面をスリープ状態にしない", "screen-awake": "画面をスリープ状態にしない",
"remove-image": "画像を削除", "remove-image": "画像を削除",
"nextStep": "次のステップ", "nextStep": "次のステップ",
"recipe-actions": "Recipe Actions", "recipe-actions": "レシピ操作",
"parser": { "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.", "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", "ingredient-parser": "Ingredient Parser",
@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper バージョン", "recipe-scraper-version": "Recipe Scraper バージョン",
"oidc-ready": "OIDC対応", "oidc-ready": "OIDC対応",
"oidc-ready-error-text": "すべてのOIDC値が設定されていません。OIDC認証を使用していない場合は無視できます。", "oidc-ready-error-text": "すべてのOIDC値が設定されていません。OIDC認証を使用していない場合は無視できます。",
"oidc-ready-success-text": "必要なOIDC変数はすべて設定されています。" "oidc-ready-success-text": "必要なOIDC変数はすべて設定されています。",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "すべてのリスト", "all-lists": "すべてのリスト",
@@ -778,6 +782,7 @@
"food": "食料", "food": "食料",
"note": "メモ", "note": "メモ",
"label": "ラベル", "label": "ラベル",
"save-label": "Save Label",
"linked-item-warning": "このアイテムは 1 つ以上のレシピにリンクされています。このリストにレシピを追加または削除するときに、単位や食品を調整すると予期しない結果が生じることがあります。", "linked-item-warning": "このアイテムは 1 つ以上のレシピにリンクされています。このリストにレシピを追加または削除するときに、単位や食品を調整すると予期しない結果が生じることがあります。",
"toggle-food": "食料の切り替え", "toggle-food": "食料の切り替え",
"manage-labels": "ラベルの管理", "manage-labels": "ラベルの管理",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "これは完璧ではありませんが、一般的に優れた結果が得られ、成分を手動で個別のフィールドに解析するための良い出発点となります。あるいは、パターン マッチング技術を使用して成分を識別する「Brute」プロセッサを使用することもできます。", "ingredients-natural-language-processor-explanation-2": "これは完璧ではありませんが、一般的に優れた結果が得られ、成分を手動で個別のフィールドに解析するための良い出発点となります。あるいは、パターン マッチング技術を使用して成分を識別する「Brute」プロセッサを使用することもできます。",
"nlp": "NLP", "nlp": "NLP",
"brute": "ブルート", "brute": "ブルート",
"openai": "OpenAI",
"show-individual-confidence": "個々の信頼性を表示", "show-individual-confidence": "個々の信頼性を表示",
"ingredient-text": "材料テキスト", "ingredient-text": "材料テキスト",
"average-confident": "{0} 自信あり", "average-confident": "{0} 自信あり",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "All Lists", "all-lists": "All Lists",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Visi sąrašai", "all-lists": "Visi sąrašai",
@@ -778,6 +782,7 @@
"food": "Maistas", "food": "Maistas",
"note": "Pastaba", "note": "Pastaba",
"label": "Žyma", "label": "Žyma",
"save-label": "Save Label",
"linked-item-warning": "Šis įrašas susietas su vienu ar keliais receptais. Jei pakeisite vienetus ar produktus, galimi nenumatyti rezultatai pridedant ar pašalinant receptą iš šio sąrašo.", "linked-item-warning": "Šis įrašas susietas su vienu ar keliais receptais. Jei pakeisite vienetus ar produktus, galimi nenumatyti rezultatai pridedant ar pašalinant receptą iš šio sąrašo.",
"toggle-food": "Įjungti produktą", "toggle-food": "Įjungti produktą",
"manage-labels": "Tvarkyti žymas", "manage-labels": "Tvarkyti žymas",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Šiuo metu funkcionalumo veikimas tobulinamas, bet jau duoda pakankamai gerus rezultatus ir yra nebloga pradžia rankiniu būdu skirstant ingredientus į atskirus laukelius. Taip pat galima pasinaudoti funkcija \"Netikslus apdorojimas\", kuri atpažįsta ingredientus pagal šabloninį algoritmą.", "ingredients-natural-language-processor-explanation-2": "Šiuo metu funkcionalumo veikimas tobulinamas, bet jau duoda pakankamai gerus rezultatus ir yra nebloga pradžia rankiniu būdu skirstant ingredientus į atskirus laukelius. Taip pat galima pasinaudoti funkcija \"Netikslus apdorojimas\", kuri atpažįsta ingredientus pagal šabloninį algoritmą.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Netikslus apdorojimas", "brute": "Netikslus apdorojimas",
"openai": "OpenAI",
"show-individual-confidence": "Rodyti individualų patikimumą", "show-individual-confidence": "Rodyti individualų patikimumą",
"ingredient-text": "Ingrediento tekstas", "ingredient-text": "Ingrediento tekstas",
"average-confident": "{0} patikimumas", "average-confident": "{0} patikimumas",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "All Lists", "all-lists": "All Lists",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI verwerker",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Versie van de receptenscraper", "recipe-scraper-version": "Versie van de receptenscraper",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI staat klaar",
"openai-ready-error-text": "Niet alle tekstvakken voor OpenAI zijn ingevuld. Als je geen OpenAI gebruikt kun je dit leeg laten.",
"openai-ready-success-text": "Verplichte tekstvakken voor OpenAI zijn ingevuld."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Alle lijsten", "all-lists": "Alle lijsten",
@@ -778,6 +782,7 @@
"food": "Voedsel", "food": "Voedsel",
"note": "Notitie", "note": "Notitie",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "Dit element is gekoppeld aan een of meer recepten. Het aanpassen van de eenheden of ingrediënten zal onverwachte resultaten opleveren bij het toevoegen of verwijderen van het recept uit deze lijst.", "linked-item-warning": "Dit element is gekoppeld aan een of meer recepten. Het aanpassen van de eenheden of ingrediënten zal onverwachte resultaten opleveren bij het toevoegen of verwijderen van het recept uit deze lijst.",
"toggle-food": "Voedsel schakelen", "toggle-food": "Voedsel schakelen",
"manage-labels": "Labels beheren", "manage-labels": "Labels beheren",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Het is niet perfect, maar het levert uitstekende resultaten op in het algemeen en is een goed uitgangspunt voor het handmatig verwerken van ingrediënten in afzonderlijke velden. Je kunt ook de \"Brute\" processor gebruiken die een patroonovereenkomende techniek gebruikt om ingrediënten te identificeren.", "ingredients-natural-language-processor-explanation-2": "Het is niet perfect, maar het levert uitstekende resultaten op in het algemeen en is een goed uitgangspunt voor het handmatig verwerken van ingrediënten in afzonderlijke velden. Je kunt ook de \"Brute\" processor gebruiken die een patroonovereenkomende techniek gebruikt om ingrediënten te identificeren.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI kunstmatige intelligentie",
"show-individual-confidence": "Individuele overtuiging tonen", "show-individual-confidence": "Individuele overtuiging tonen",
"ingredient-text": "Ingrediënt tekst", "ingredient-text": "Ingrediënt tekst",
"average-confident": "{0} overtuigd", "average-confident": "{0} overtuigd",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Versjon på oppskrift-scraper", "recipe-scraper-version": "Versjon på oppskrift-scraper",
"oidc-ready": "OIDC klar", "oidc-ready": "OIDC klar",
"oidc-ready-error-text": "Ikke alle OIDC-verdier er konfigurert. Dette kan ignoreres hvis du ikke bruker OIDC-autentisering.", "oidc-ready-error-text": "Ikke alle OIDC-verdier er konfigurert. Dette kan ignoreres hvis du ikke bruker OIDC-autentisering.",
"oidc-ready-success-text": "Alle obligratoriske OIDC-variabler er satt." "oidc-ready-success-text": "Alle obligratoriske OIDC-variabler er satt.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Alle lister", "all-lists": "Alle lister",
@@ -778,6 +782,7 @@
"food": "Matvare", "food": "Matvare",
"note": "Notat", "note": "Notat",
"label": "Etikett", "label": "Etikett",
"save-label": "Save Label",
"linked-item-warning": "Dette elementet er koblet til én eller flere oppskrifter. Å endre enheter eller matvarer vil føre til uventede resultater når oppskriften legges til eller fjernes fra denne listen.", "linked-item-warning": "Dette elementet er koblet til én eller flere oppskrifter. Å endre enheter eller matvarer vil føre til uventede resultater når oppskriften legges til eller fjernes fra denne listen.",
"toggle-food": "Vis/skjul matvare", "toggle-food": "Vis/skjul matvare",
"manage-labels": "Administrer etiketter", "manage-labels": "Administrer etiketter",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Modellen er ikke perfekt, men det gir generelt sett gode resultater og er et godt utgangspunkt for å manuelt analysere ingredienser i individuelle felt. Alternativt kan du også bruke 'Brute'-prosessoren som bruker mønstergjenkjennelsesteknikker for å identifisere ingredienser.", "ingredients-natural-language-processor-explanation-2": "Modellen er ikke perfekt, men det gir generelt sett gode resultater og er et godt utgangspunkt for å manuelt analysere ingredienser i individuelle felt. Alternativt kan du også bruke 'Brute'-prosessoren som bruker mønstergjenkjennelsesteknikker for å identifisere ingredienser.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Vis individuell konfidens", "show-individual-confidence": "Vis individuell konfidens",
"ingredient-text": "Ingredienstekst", "ingredient-text": "Ingredienstekst",
"average-confident": "{0} Troverdig", "average-confident": "{0} Troverdig",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Wersja Scrapera Przepisów", "recipe-scraper-version": "Wersja Scrapera Przepisów",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Wszystkie listy", "all-lists": "Wszystkie listy",
@@ -778,6 +782,7 @@
"food": "Jedzenie", "food": "Jedzenie",
"note": "Notatka", "note": "Notatka",
"label": "Etykieta", "label": "Etykieta",
"save-label": "Save Label",
"linked-item-warning": "Ten przedmiot jest powiązany z jednym lub kilkoma przepisami. Dostosowanie jednostek lub żywności przyniesie niespodziewane wyniki podczas dodawania lub usuwania przepisu z tej listy.", "linked-item-warning": "Ten przedmiot jest powiązany z jednym lub kilkoma przepisami. Dostosowanie jednostek lub żywności przyniesie niespodziewane wyniki podczas dodawania lub usuwania przepisu z tej listy.",
"toggle-food": "Przełącz Żywność", "toggle-food": "Przełącz Żywność",
"manage-labels": "Zarządzaj Etykietami", "manage-labels": "Zarządzaj Etykietami",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Nie jest on idealny, ale na ogół daje świetne wyniki i jest dobrym punktem wyjścia do ręcznego przydzielania składników w poszczególne pola. Alternatywnie możesz również użyć procesora \"Brut\", który używa dopasowywujący schemat do identyfikacji składników.", "ingredients-natural-language-processor-explanation-2": "Nie jest on idealny, ale na ogół daje świetne wyniki i jest dobrym punktem wyjścia do ręcznego przydzielania składników w poszczególne pola. Alternatywnie możesz również użyć procesora \"Brut\", który używa dopasowywujący schemat do identyfikacji składników.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brut", "brute": "Brut",
"openai": "OpenAI",
"show-individual-confidence": "Pokaż pojedyncze pewności", "show-individual-confidence": "Pokaż pojedyncze pewności",
"ingredient-text": "Tekst składnika", "ingredient-text": "Tekst składnika",
"average-confident": "Pewność {0}", "average-confident": "Pewność {0}",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Versão do receptor de receita", "recipe-scraper-version": "Versão do receptor de receita",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Todas as Listas", "all-lists": "Todas as Listas",
@@ -778,6 +782,7 @@
"food": "Comida", "food": "Comida",
"note": "Nota", "note": "Nota",
"label": "Marcador", "label": "Marcador",
"save-label": "Save Label",
"linked-item-warning": "Este item está vinculado a uma ou mais receitas. Ajustar as unidades ou alimentos produzirá resultados inesperados ao adicionar ou remover a receita desta lista.", "linked-item-warning": "Este item está vinculado a uma ou mais receitas. Ajustar as unidades ou alimentos produzirá resultados inesperados ao adicionar ou remover a receita desta lista.",
"toggle-food": "Mostrar nome da Comida", "toggle-food": "Mostrar nome da Comida",
"manage-labels": "Gerenciar marcadores", "manage-labels": "Gerenciar marcadores",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Não é perfeito, mas produz grandes resultados em geral e é um bom ponto de partida para analisar ingredientes manualmente em campos individuais. Como alternativa, você também pode usar o processador \"Brute\" que usa uma técnica de correspondência de padrões para identificar ingredientes.", "ingredients-natural-language-processor-explanation-2": "Não é perfeito, mas produz grandes resultados em geral e é um bom ponto de partida para analisar ingredientes manualmente em campos individuais. Como alternativa, você também pode usar o processador \"Brute\" que usa uma técnica de correspondência de padrões para identificar ingredientes.",
"nlp": "PLN", "nlp": "PLN",
"brute": "Bruto", "brute": "Bruto",
"openai": "OpenAI",
"show-individual-confidence": "Mostrar confiança individual", "show-individual-confidence": "Mostrar confiança individual",
"ingredient-text": "Texto de Ingrediente", "ingredient-text": "Texto de Ingrediente",
"average-confident": "{0} confiante", "average-confident": "{0} confiante",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Versão do Scraper de receitas", "recipe-scraper-version": "Versão do Scraper de receitas",
"oidc-ready": "Suporta OICD", "oidc-ready": "Suporta OICD",
"oidc-ready-error-text": "Nem todos os valores OICD estão configurados. Pode ignorar isto se não estiver a utilizar autenticação OICD.", "oidc-ready-error-text": "Nem todos os valores OICD estão configurados. Pode ignorar isto se não estiver a utilizar autenticação OICD.",
"oidc-ready-success-text": "As variáveis OICD necessárias estão definidas." "oidc-ready-success-text": "As variáveis OICD necessárias estão definidas.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Todas as Listas", "all-lists": "Todas as Listas",
@@ -778,6 +782,7 @@
"food": "Alimentos", "food": "Alimentos",
"note": "Nota", "note": "Nota",
"label": "Rótulo", "label": "Rótulo",
"save-label": "Save Label",
"linked-item-warning": "Este item tem ligação a uma ou mais receitas. Ajustar as unidades ou alimentos irá produzir resultados inesperados quando adicionar ou remover a receita desta lista.", "linked-item-warning": "Este item tem ligação a uma ou mais receitas. Ajustar as unidades ou alimentos irá produzir resultados inesperados quando adicionar ou remover a receita desta lista.",
"toggle-food": "Alternar Alimento", "toggle-food": "Alternar Alimento",
"manage-labels": "Gerir Rótulos", "manage-labels": "Gerir Rótulos",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Não é perfeito, mas produz bons resultados em geral e é um bom ponto de partida para, manualmente, fazer corresponder ingredientes a campos individuais. Em alternativa, também pode usar o processador \"Brute\" que usa uma técnica de correspondência de padrões para identificar ingredientes.", "ingredients-natural-language-processor-explanation-2": "Não é perfeito, mas produz bons resultados em geral e é um bom ponto de partida para, manualmente, fazer corresponder ingredientes a campos individuais. Em alternativa, também pode usar o processador \"Brute\" que usa uma técnica de correspondência de padrões para identificar ingredientes.",
"nlp": "PLN", "nlp": "PLN",
"brute": "Bruto", "brute": "Bruto",
"openai": "OpenAI",
"show-individual-confidence": "Mostrar confiança individual", "show-individual-confidence": "Mostrar confiança individual",
"ingredient-text": "Texto de Ingrediente", "ingredient-text": "Texto de Ingrediente",
"average-confident": "{0} Confiante", "average-confident": "{0} Confiante",

View File

@@ -64,7 +64,7 @@
"something-went-wrong": "Ceva nu a funcţionat corect!", "something-went-wrong": "Ceva nu a funcţionat corect!",
"subscribed-events": "Evenimente la care ești Abonat", "subscribed-events": "Evenimente la care ești Abonat",
"test-message-sent": "Mesaj de test trimis", "test-message-sent": "Mesaj de test trimis",
"message-sent": "Message Sent", "message-sent": "Mesaj trimis",
"new-notification": "Notificare nouă", "new-notification": "Notificare nouă",
"event-notifiers": "Notificatori de evenimente", "event-notifiers": "Notificatori de evenimente",
"apprise-url-skipped-if-blank": "URL Apprise (ignorat daca e gol)", "apprise-url-skipped-if-blank": "URL Apprise (ignorat daca e gol)",
@@ -161,7 +161,7 @@
"test": "Testează", "test": "Testează",
"themes": "Teme", "themes": "Teme",
"thursday": "Joi", "thursday": "Joi",
"title": "Title", "title": "Titlu",
"token": "Token", "token": "Token",
"tuesday": "Marţi", "tuesday": "Marţi",
"type": "Tip", "type": "Tip",
@@ -210,7 +210,7 @@
"unsaved-changes": "Aveți modificări nesalvate. Doriți să salvați înainte de a închide aplicația? Apăsați \"OK\" pentru a salva sau \"Anulare\" pentru a renunța la modificări.", "unsaved-changes": "Aveți modificări nesalvate. Doriți să salvați înainte de a închide aplicația? Apăsați \"OK\" pentru a salva sau \"Anulare\" pentru a renunța la modificări.",
"clipboard-copy-failure": "Copierea în clipboard a eșuat.", "clipboard-copy-failure": "Copierea în clipboard a eșuat.",
"confirm-delete-generic-items": "Sunteți sigur că doriți să ștergeți următoarele?", "confirm-delete-generic-items": "Sunteți sigur că doriți să ștergeți următoarele?",
"organizers": "Organizers" "organizers": "Organizatori"
}, },
"group": { "group": {
"are-you-sure-you-want-to-delete-the-group": "Sunteți sigur că doriți să ștergeți <b>{groupName}<b/>?", "are-you-sure-you-want-to-delete-the-group": "Sunteți sigur că doriți să ștergeți <b>{groupName}<b/>?",
@@ -365,7 +365,7 @@
"choose-migration-type": "Alegeți tipul de migrare", "choose-migration-type": "Alegeți tipul de migrare",
"tag-all-recipes": "Etichetați toate rețetele cu eticheta {tag-name}", "tag-all-recipes": "Etichetați toate rețetele cu eticheta {tag-name}",
"nextcloud-text": "Rețetele din Nextcloud pot fi importate dintr-un fișier de tip \".ZIP\" ce conține toate datele stocate în Nextcloud. Vedeți structura directorului din exemplul de mai jos pentru a vă asigura că rețetele pot fi importate cu succes.", "nextcloud-text": "Rețetele din Nextcloud pot fi importate dintr-un fișier de tip \".ZIP\" ce conține toate datele stocate în Nextcloud. Vedeți structura directorului din exemplul de mai jos pentru a vă asigura că rețetele pot fi importate cu succes.",
"chowdown-text": "Mealie natively supports the chowdown repository format. Download the code repository as a .zip file and upload it below.", "chowdown-text": "Mealie acceptă nativ formatul repo-ului Chowdown. Descărcați repository-ul de cod ca fișier \".zip\" și încărcați-l mai jos.",
"recipe-1": "Rețeta 1", "recipe-1": "Rețeta 1",
"recipe-2": "Rețeta 2", "recipe-2": "Rețeta 2",
"paprika-text": "Mealie poate importa rețete din aplicația Paprika. Exportă rețetele din Paprika, redenumește extensia de export în \".zip\" și încarcă-o folosind câmpul de mai jos.", "paprika-text": "Mealie poate importa rețete din aplicația Paprika. Exportă rețetele din Paprika, redenumește extensia de export în \".zip\" și încarcă-o folosind câmpul de mai jos.",
@@ -375,7 +375,7 @@
"description-long": "Mealie poate importa rețete din Planul de Masă." "description-long": "Mealie poate importa rețete din Planul de Masă."
}, },
"myrecipebox": { "myrecipebox": {
"title": "My Recipe Box", "title": "Cutia mea de rețete",
"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 can import recipes from My Recipe Box. Export your recipes in CSV format, then upload the .csv file below."
} }
}, },
@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "All Lists", "all-lists": "All Lists",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Версия скрейпера рецептов", "recipe-scraper-version": "Версия скрейпера рецептов",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Все списки", "all-lists": "Все списки",
@@ -778,6 +782,7 @@
"food": "Продукты", "food": "Продукты",
"note": "Заметка", "note": "Заметка",
"label": "Метка", "label": "Метка",
"save-label": "Save Label",
"linked-item-warning": "Этот предмет связан с одним или несколькими рецептами. Обновление единиц измерения или продуктов приведет к неожиданным результатам при добавлении или удалении рецепта из этого списка.", "linked-item-warning": "Этот предмет связан с одним или несколькими рецептами. Обновление единиц измерения или продуктов приведет к неожиданным результатам при добавлении или удалении рецепта из этого списка.",
"toggle-food": "Переключить продукты", "toggle-food": "Переключить продукты",
"manage-labels": "Настройки меток", "manage-labels": "Настройки меток",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Он не совершенен, но, в целом, дает хорошие результаты и является хорошей отправной точкой для ручного разбора ингредиентов в отдельных полях. Иначе, для определения ингредиентов можно использовать процессор \"Brute\", использующий технику сопоставления образцов (pattern matching).", "ingredients-natural-language-processor-explanation-2": "Он не совершенен, но, в целом, дает хорошие результаты и является хорошей отправной точкой для ручного разбора ингредиентов в отдельных полях. Иначе, для определения ингредиентов можно использовать процессор \"Brute\", использующий технику сопоставления образцов (pattern matching).",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Показать уверенность каждого поля", "show-individual-confidence": "Показать уверенность каждого поля",
"ingredient-text": "Описание ингредиента", "ingredient-text": "Описание ингредиента",
"average-confident": "Уверенность {0}", "average-confident": "Уверенность {0}",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Verzia scrapera receptov", "recipe-scraper-version": "Verzia scrapera receptov",
"oidc-ready": "OIDC pripravené", "oidc-ready": "OIDC pripravené",
"oidc-ready-error-text": "Niektoré z OIDC hodnôt nie sú nakonfigurované. Toto varovanie je možné ignorovať, ak nepoužívate OIDC autentifikáciu.", "oidc-ready-error-text": "Niektoré z OIDC hodnôt nie sú nakonfigurované. Toto varovanie je možné ignorovať, ak nepoužívate OIDC autentifikáciu.",
"oidc-ready-success-text": "Všetky potrebné OIDC premenné sú nastavené." "oidc-ready-success-text": "Všetky potrebné OIDC premenné sú nastavené.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Všetky zoznamy", "all-lists": "Všetky zoznamy",
@@ -778,6 +782,7 @@
"food": "Jedlo", "food": "Jedlo",
"note": "Poznámka", "note": "Poznámka",
"label": "Štítok", "label": "Štítok",
"save-label": "Save Label",
"linked-item-warning": "Táto položka je prepojená s jedným alebo viacerými receptami. Zmena jednotiek alebo jedál bude viesť k neželaným zmenám pri pridávaní alebo odoberaní receptov z tohto zoznamu.", "linked-item-warning": "Táto položka je prepojená s jedným alebo viacerými receptami. Zmena jednotiek alebo jedál bude viesť k neželaným zmenám pri pridávaní alebo odoberaní receptov z tohto zoznamu.",
"toggle-food": "Prepnúť jedlo", "toggle-food": "Prepnúť jedlo",
"manage-labels": "Spravovať štítky", "manage-labels": "Spravovať štítky",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Napriek tomu, že nie je bezchybným, vo všeobecnosti poskytuje veľmi dobré výsledky, ktoré môžu slúžiť ako výborný štartovací bod manuálneho parsovania textov prísad do príslušných polí. Alternatívne tiež môžete využiť procesor založený na využití \"hrubej sily\", ktorý využíva na identifikáciu jednotlivých prísad metódu vyhľadávania vzorov v poskytnutom texte.", "ingredients-natural-language-processor-explanation-2": "Napriek tomu, že nie je bezchybným, vo všeobecnosti poskytuje veľmi dobré výsledky, ktoré môžu slúžiť ako výborný štartovací bod manuálneho parsovania textov prísad do príslušných polí. Alternatívne tiež môžete využiť procesor založený na využití \"hrubej sily\", ktorý využíva na identifikáciu jednotlivých prísad metódu vyhľadávania vzorov v poskytnutom texte.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Hrubá sila", "brute": "Hrubá sila",
"openai": "OpenAI",
"show-individual-confidence": "Zobraziť individuálne miery spoľahlivosti", "show-individual-confidence": "Zobraziť individuálne miery spoľahlivosti",
"ingredient-text": "Text prísady", "ingredient-text": "Text prísady",
"average-confident": "{0} Spoľahlivý", "average-confident": "{0} Spoľahlivý",

View File

@@ -594,6 +594,7 @@
"select-parser": "Izberi razčlenjevalnik", "select-parser": "Izberi razčlenjevalnik",
"natural-language-processor": "Procesor naravnega jezika", "natural-language-processor": "Procesor naravnega jezika",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Razčleni vse", "parse-all": "Razčleni vse",
"no-unit": "Ni enote", "no-unit": "Ni enote",
"missing-unit": "Ustvari manjkajočo enoto: {unit}", "missing-unit": "Ustvari manjkajočo enoto: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Verzija strgalnika receptov", "recipe-scraper-version": "Verzija strgalnika receptov",
"oidc-ready": "OIDC Ready", "oidc-ready": "OIDC Ready",
"oidc-ready-error-text": "Nekatere OIDC vrednosti niso nastavljene. To lahko ignoriraš, če ne uporabljaš OIDC avtentikacije.", "oidc-ready-error-text": "Nekatere OIDC vrednosti niso nastavljene. To lahko ignoriraš, če ne uporabljaš OIDC avtentikacije.",
"oidc-ready-success-text": "Vse zahtevane OIDC spremenljivke so nastavljene." "oidc-ready-success-text": "Vse zahtevane OIDC spremenljivke so nastavljene.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Vsi seznami", "all-lists": "Vsi seznami",
@@ -778,6 +782,7 @@
"food": "Živilo", "food": "Živilo",
"note": "Opomba", "note": "Opomba",
"label": "Oznaka", "label": "Oznaka",
"save-label": "Save Label",
"linked-item-warning": "Ta sestavina je povezana na en ali več receptov. Če spremenite enote količin, se lahko kaj podre, ko dodajate ali odstranjujete iz seznama.", "linked-item-warning": "Ta sestavina je povezana na en ali več receptov. Če spremenite enote količin, se lahko kaj podre, ko dodajate ali odstranjujete iz seznama.",
"toggle-food": "Preklopi med hrano", "toggle-food": "Preklopi med hrano",
"manage-labels": "Upravljanje oznak", "manage-labels": "Upravljanje oznak",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Rezultati niso popolni, ampak so v splošnem uporabni in služijo kot dobro izhodišče za ročno urejanje sestavin v posamezna polja. Kot alternativo lahko uporabiš \"Brute\" procesor, ki deluje na osnovi ujemanja vzorcev za identifikacijo sestavin.", "ingredients-natural-language-processor-explanation-2": "Rezultati niso popolni, ampak so v splošnem uporabni in služijo kot dobro izhodišče za ročno urejanje sestavin v posamezna polja. Kot alternativo lahko uporabiš \"Brute\" procesor, ki deluje na osnovi ujemanja vzorcev za identifikacijo sestavin.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Pokaži zaupanje v posamezne postavke", "show-individual-confidence": "Pokaži zaupanje v posamezne postavke",
"ingredient-text": "Besedilo s sestavino", "ingredient-text": "Besedilo s sestavino",
"average-confident": "{0} zanesljivost", "average-confident": "{0} zanesljivost",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Сви спискови", "all-lists": "Сви спискови",
@@ -778,6 +782,7 @@
"food": "Храна", "food": "Храна",
"note": "Note", "note": "Note",
"label": "Natpis", "label": "Natpis",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Управљај натписима", "manage-labels": "Управљај натписима",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Није савршено, али даје одличне резултате и добра је почетна тачка за ручно разлагање саставних делова у појединачна поља. Као алтернативу, такође можете користити \"Брут\" процесор који користи технику усклађивања шаблона за идентификацију састојака.", "ingredients-natural-language-processor-explanation-2": "Није савршено, али даје одличне резултате и добра је почетна тачка за ручно разлагање саставних делова у појединачна поља. Као алтернативу, такође можете користити \"Брут\" процесор који користи технику усклађивања шаблона за идентификацију састојака.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -64,7 +64,7 @@
"something-went-wrong": "Någonting gick fel", "something-went-wrong": "Någonting gick fel",
"subscribed-events": "Prenumererade händelser", "subscribed-events": "Prenumererade händelser",
"test-message-sent": "Testmeddelande Skickat", "test-message-sent": "Testmeddelande Skickat",
"message-sent": "Message Sent", "message-sent": "Meddelandet skickat",
"new-notification": "Ny avisering", "new-notification": "Ny avisering",
"event-notifiers": "Händelseavisering", "event-notifiers": "Händelseavisering",
"apprise-url-skipped-if-blank": "Apprise-URL (hoppa över om tom)", "apprise-url-skipped-if-blank": "Apprise-URL (hoppa över om tom)",
@@ -145,11 +145,11 @@
"save": "Spara", "save": "Spara",
"settings": "Inställningar", "settings": "Inställningar",
"share": "Dela", "share": "Dela",
"show-all": "Show All", "show-all": "Visa allt",
"shuffle": "Blanda", "shuffle": "Blanda",
"sort": "Sortering", "sort": "Sortering",
"sort-ascending": "Sort Ascending", "sort-ascending": "Sortera stigande",
"sort-descending": "Sort Descending", "sort-descending": "Sortera fallande",
"sort-alphabetically": "Alfabetisk", "sort-alphabetically": "Alfabetisk",
"status": "Status", "status": "Status",
"subject": "Ämne", "subject": "Ämne",
@@ -161,7 +161,7 @@
"test": "Test", "test": "Test",
"themes": "Tema", "themes": "Tema",
"thursday": "Torsdag", "thursday": "Torsdag",
"title": "Title", "title": "Rubrik",
"token": "Token", "token": "Token",
"tuesday": "Tisdag", "tuesday": "Tisdag",
"type": "Typ", "type": "Typ",
@@ -210,7 +210,7 @@
"unsaved-changes": "Du har osparade ändringar. Vill du spara innan du lämnar? Tryck Okej att spara, Avbryt för att ignorera ändringar.", "unsaved-changes": "Du har osparade ändringar. Vill du spara innan du lämnar? Tryck Okej att spara, Avbryt för att ignorera ändringar.",
"clipboard-copy-failure": "Det gick inte att kopiera till urklipp.", "clipboard-copy-failure": "Det gick inte att kopiera till urklipp.",
"confirm-delete-generic-items": "Är du säker på att du vill radera följande objekt?", "confirm-delete-generic-items": "Är du säker på att du vill radera följande objekt?",
"organizers": "Organizers" "organizers": "Organisatörer"
}, },
"group": { "group": {
"are-you-sure-you-want-to-delete-the-group": "Är du säker på att du vill radera <b>{groupName}<b/>?", "are-you-sure-you-want-to-delete-the-group": "Är du säker på att du vill radera <b>{groupName}<b/>?",
@@ -246,8 +246,8 @@
"group-preferences": "Gruppinställningar", "group-preferences": "Gruppinställningar",
"private-group": "Privat grupp", "private-group": "Privat grupp",
"private-group-description": "Sätta gruppen till privat kommer att sätta alla publika visningslägen till standard. Det skriver över publika inställningar för individuella recept.", "private-group-description": "Sätta gruppen till privat kommer att sätta alla publika visningslägen till standard. Det skriver över publika inställningar för individuella recept.",
"enable-public-access": "Enable Public Access", "enable-public-access": "Tillåt offentlig åtkomst",
"enable-public-access-description": "Make group recipes public by default, and allow visitors to view recipes without logging-in", "enable-public-access-description": "Gör recept i grupp, offentliga som standard och tillåt besökare att se recept utan att logga in",
"allow-users-outside-of-your-group-to-see-your-recipes": "Tillåt användare utanför din grupp att se dina recept", "allow-users-outside-of-your-group-to-see-your-recipes": "Tillåt användare utanför din grupp att se dina recept",
"allow-users-outside-of-your-group-to-see-your-recipes-description": "Om aktiverad kan du dela en publik länk för specifika recept utan att användaren behöver auktorisera sig. Om avaktiverad kan du bara dela med användare i din grupp eller med en systemskapad länk", "allow-users-outside-of-your-group-to-see-your-recipes-description": "Om aktiverad kan du dela en publik länk för specifika recept utan att användaren behöver auktorisera sig. Om avaktiverad kan du bara dela med användare i din grupp eller med en systemskapad länk",
"show-nutrition-information": "Visa näringsinnehåll", "show-nutrition-information": "Visa näringsinnehåll",
@@ -361,7 +361,7 @@
}, },
"recipe-data-migrations": "Migrering av receptdata", "recipe-data-migrations": "Migrering av receptdata",
"recipe-data-migrations-explanation": "Recept kan migreras från en annan applikation som stöds till Mealie. Detta är ett bra sätt att komma igång med Mealie.", "recipe-data-migrations-explanation": "Recept kan migreras från en annan applikation som stöds till Mealie. Detta är ett bra sätt att komma igång med 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": "Kommer från en annan app, program eller tidigare version av Mealie? Kika på migrationer och se om din data kan bli importerad.",
"choose-migration-type": "Välj migrationstyp", "choose-migration-type": "Välj migrationstyp",
"tag-all-recipes": "Tagga alla recept med {tag-name} tagg", "tag-all-recipes": "Tagga alla recept med {tag-name} tagg",
"nextcloud-text": "Nextcloud-recept kan importeras från en zip-fil som innehåller data som lagras i Nextcloud. Se exempelmappens struktur nedan för att säkerställa att dina recept kan importeras.", "nextcloud-text": "Nextcloud-recept kan importeras från en zip-fil som innehåller data som lagras i Nextcloud. Se exempelmappens struktur nedan för att säkerställa att dina recept kan importeras.",
@@ -375,8 +375,8 @@
"description-long": "Mealie kan importera recept från Plan to Eat." "description-long": "Mealie kan importera recept från Plan to Eat."
}, },
"myrecipebox": { "myrecipebox": {
"title": "My Recipe Box", "title": "Min receptlåda",
"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 importera recept från appen 'My Recipe Box'. Exportera dina recept i CSV format, och sen ladda upp .csv filen nedan."
} }
}, },
"new-recipe": { "new-recipe": {
@@ -528,7 +528,7 @@
"edit-timeline-event": "Redigera tidslinjehändelse", "edit-timeline-event": "Redigera tidslinjehändelse",
"timeline": "Tidslinje", "timeline": "Tidslinje",
"timeline-is-empty": "Inget på tidslinjen än. Försök att göra detta recept!", "timeline-is-empty": "Inget på tidslinjen än. Försök att göra detta recept!",
"timeline-no-events-found-try-adjusting-filters": "No events found. Try adjusting your search filters.", "timeline-no-events-found-try-adjusting-filters": "Inga händelser hittades. Försök justera filtren angivna i sökningen.",
"group-global-timeline": "{groupName} Global tidslinje", "group-global-timeline": "{groupName} Global tidslinje",
"open-timeline": "Öppna tidslinje", "open-timeline": "Öppna tidslinje",
"made-this": "Jag lagade den här", "made-this": "Jag lagade den här",
@@ -549,7 +549,7 @@
"looking-for-migrations": "Letar du efter migreringar?", "looking-for-migrations": "Letar du efter migreringar?",
"import-with-url": "Importera från URL", "import-with-url": "Importera från URL",
"create-recipe": "Skapa recept", "create-recipe": "Skapa recept",
"create-recipe-description": "Create a new recipe from scratch.", "create-recipe-description": "Skapa nytt recept från grunden.",
"create-recipes": "Skapa recept", "create-recipes": "Skapa recept",
"import-with-zip": "Importera från .zip", "import-with-zip": "Importera från .zip",
"create-recipe-from-an-image": "Skapa recept från en bild", "create-recipe-from-an-image": "Skapa recept från en bild",
@@ -587,17 +587,18 @@
"nextStep": "Nästa steg", "nextStep": "Nästa steg",
"recipe-actions": "Recipe Actions", "recipe-actions": "Recipe Actions",
"parser": { "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.", "experimental-alert-text": "Mealie använder naturligt språk för att tolka enheter och livsmedel som behövs för dina recept. Denna funktion är experimentell och kanske inte alltid funkar som förväntat. Om du föredrar att inte använda de tolkade resultatet, kan du välja 'Avbryt' och förändringarna kommer då inte sparas.",
"ingredient-parser": "Ingredient Parser", "ingredient-parser": "Ingrediensanalysator",
"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.", "explanation": "För att använda ingrediensen parser, klicka på \"Parse All\" knappen för att starta processen. När de bearbetade ingredienserna är tillgängliga, kan du granska objekten och kontrollera att de tolkades korrekt. Modellens självförtroende poäng visas till höger om artikelns titel. Denna poäng är ett genomsnitt av alla individuella poäng och kanske inte alltid vara helt korrekt.",
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.", "alerts-explainer": "Varning kommer visas om en matchande ingrediens eller enhet hittas, men inte existerar i databasen.",
"select-parser": "Select Parser", "select-parser": "Välj tolk",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Naturlig språkbehandlare",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"parse-all": "Parse All", "openai-parser": "OpenAI Parser",
"no-unit": "No unit", "parse-all": "Analysera allt",
"missing-unit": "Create missing unit: {unit}", "no-unit": "Ingen enhet",
"missing-food": "Create missing food: {food}", "missing-unit": "Skapa saknad enhet: {unit}",
"missing-food": "Skapa saknad ingrediens: {food}",
"no-food": "No Food" "no-food": "No Food"
} }
}, },
@@ -762,9 +763,12 @@
"ldap-ready-success-text": "Alla obligatoriska LDAP-variabler är satta.", "ldap-ready-success-text": "Alla obligatoriska LDAP-variabler är satta.",
"build": "Bygg", "build": "Bygg",
"recipe-scraper-version": "Version av Recept-scraper", "recipe-scraper-version": "Version av Recept-scraper",
"oidc-ready": "OIDC Ready", "oidc-ready": "OIDC Klar",
"oidc-ready-error-text": "Not all OIDC Values are configured. This can be ignored if you are not using OIDC Authentication.", "oidc-ready-error-text": "Alla OIDC-värden är inte konfigurerade. Detta kan ignoreras om du inte använder OIDC-autentisering.",
"oidc-ready-success-text": "Required OIDC variables are all set." "oidc-ready-success-text": "Alla obligatoriska OIDC-variabler är satta.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Visa alla listor", "all-lists": "Visa alla listor",
@@ -778,6 +782,7 @@
"food": "Mat", "food": "Mat",
"note": "Anteckning", "note": "Anteckning",
"label": "Etikett", "label": "Etikett",
"save-label": "Save Label",
"linked-item-warning": "Denna artikel är länkad till ett eller flera recept. Justering av enheter eller livsmedel ger oväntade resultat när du lägger till eller tar bort receptet från denna lista.", "linked-item-warning": "Denna artikel är länkad till ett eller flera recept. Justering av enheter eller livsmedel ger oväntade resultat när du lägger till eller tar bort receptet från denna lista.",
"toggle-food": "Växla mat", "toggle-food": "Växla mat",
"manage-labels": "Hantera etiketter", "manage-labels": "Hantera etiketter",
@@ -876,7 +881,7 @@
"or": "eller", "or": "eller",
"logout": "Logga ut", "logout": "Logga ut",
"manage-users": "Hantera användare", "manage-users": "Hantera användare",
"manage-users-description": "Create and manage users.", "manage-users-description": "Skapa och hantera användare.",
"new-password": "Nytt lösenord", "new-password": "Nytt lösenord",
"new-user": "Ny användare", "new-user": "Ny användare",
"password-has-been-reset-to-the-default-password": "Lösenordet har återställts till standardlösenordet", "password-has-been-reset-to-the-default-password": "Lösenordet har återställts till standardlösenordet",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Det är inte perfekt, men det ger bra resultat i allmänhet och är en bra utgångspunkt för att manuellt tolka ingredienser i enskilda områden. Alternativt kan du också använda \"Brute\" processor som använder en mönstermatchningsteknik för att identifiera ingredienser.", "ingredients-natural-language-processor-explanation-2": "Det är inte perfekt, men det ger bra resultat i allmänhet och är en bra utgångspunkt för att manuellt tolka ingredienser i enskilda områden. Alternativt kan du också använda \"Brute\" processor som använder en mönstermatchningsteknik för att identifiera ingredienser.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Visa individuella självförtroende", "show-individual-confidence": "Visa individuella självförtroende",
"ingredient-text": "Ingrediens text", "ingredient-text": "Ingrediens text",
"average-confident": "{0} Säker", "average-confident": "{0} Säker",

View File

@@ -64,7 +64,7 @@
"something-went-wrong": "Bir sorun oluştu!", "something-went-wrong": "Bir sorun oluştu!",
"subscribed-events": "Abone Olunan Etkinlikler", "subscribed-events": "Abone Olunan Etkinlikler",
"test-message-sent": "Test Mesajı Gönderildi", "test-message-sent": "Test Mesajı Gönderildi",
"message-sent": "Message Sent", "message-sent": "Mesaj Gönderildi",
"new-notification": "Yeni bildirim", "new-notification": "Yeni bildirim",
"event-notifiers": "Etkinlik Bildirimleri", "event-notifiers": "Etkinlik Bildirimleri",
"apprise-url-skipped-if-blank": "Apprise URL'si (boşsa geçilir)", "apprise-url-skipped-if-blank": "Apprise URL'si (boşsa geçilir)",
@@ -78,7 +78,7 @@
"tag-events": "Etiket Etkinlikleri", "tag-events": "Etiket Etkinlikleri",
"category-events": "Kategori Etkinlikleri", "category-events": "Kategori Etkinlikleri",
"when-a-new-user-joins-your-group": "Grubunuza yeni bir kullanıcı katıldığında", "when-a-new-user-joins-your-group": "Grubunuza yeni bir kullanıcı katıldığında",
"recipe-events": "Recipe Events" "recipe-events": "Tarif Etkinlikleri"
}, },
"general": { "general": {
"add": "Ekle", "add": "Ekle",
@@ -161,7 +161,7 @@
"test": "Dene", "test": "Dene",
"themes": "Temalar", "themes": "Temalar",
"thursday": "Perşembe", "thursday": "Perşembe",
"title": "Title", "title": "Başlık",
"token": "Anahtar", "token": "Anahtar",
"tuesday": "Salı", "tuesday": "Salı",
"type": "Tür", "type": "Tür",
@@ -332,7 +332,7 @@
"no-file-selected": "Dosya seçilmedi", "no-file-selected": "Dosya seçilmedi",
"no-migration-data-available": "No Migration Data Available", "no-migration-data-available": "No Migration Data Available",
"previous-migrations": "Önceki Yer Değiştirme", "previous-migrations": "Önceki Yer Değiştirme",
"recipe-migration": "Recipe Migration", "recipe-migration": "Tarif Taşıma",
"chowdown": { "chowdown": {
"description": "Migrate data from Chowdown", "description": "Migrate data from Chowdown",
"description-long": "Mealie yerel olarak chowdown veri havuzu biçimini destekler. Kod havuzunu bir .zip dosyası olarak indirin ve aşağıya yükleyin.", "description-long": "Mealie yerel olarak chowdown veri havuzu biçimini destekler. Kod havuzunu bir .zip dosyası olarak indirin ve aşağıya yükleyin.",
@@ -362,7 +362,7 @@
"recipe-data-migrations": "Recipe Data Migrations", "recipe-data-migrations": "Recipe Data Migrations",
"recipe-data-migrations-explanation": "Recipes can be migrated from another supported application to Mealie. This is a great way to get started with Mealie.", "recipe-data-migrations-explanation": "Recipes can be migrated from another supported application to Mealie. This is a great way to get started with Mealie.",
"coming-from-another-application-or-an-even-older-version-of-mealie": "Başka bir uygulamadan mı yoksa Mealie'nin daha eski bir sürümünden mi geliyorsunuz? Taşıma işlemlerine göz atın ve verilerinizin içe aktarılıp aktarılamayacağını görün.", "coming-from-another-application-or-an-even-older-version-of-mealie": "Başka bir uygulamadan mı yoksa Mealie'nin daha eski bir sürümünden mi geliyorsunuz? Taşıma işlemlerine göz atın ve verilerinizin içe aktarılıp aktarılamayacağını görün.",
"choose-migration-type": "Choose Migration Type", "choose-migration-type": "Taşıma Türünü Seçin",
"tag-all-recipes": "Tag all recipes with {tag-name} tag", "tag-all-recipes": "Tag all recipes with {tag-name} tag",
"nextcloud-text": "Nextcloud recipes can be imported from a zip file that contains the data stored in Nextcloud. See the example folder structure below to ensure your recipes are able to be imported.", "nextcloud-text": "Nextcloud recipes can be imported from a zip file that contains the data stored in Nextcloud. See the example folder structure below to ensure your recipes are able to be imported.",
"chowdown-text": "Mealie yerel olarak chowdown veri havuzu formatını destekler. Kod deposunu .zip dosyası olarak indirin ve aşağıya yükleyin.", "chowdown-text": "Mealie yerel olarak chowdown veri havuzu formatını destekler. Kod deposunu .zip dosyası olarak indirin ve aşağıya yükleyin.",
@@ -537,7 +537,7 @@
"last-made-date": "En Son {date} Yapıldı", "last-made-date": "En Son {date} Yapıldı",
"api-extras-description": "Tarif ekstraları Mealie API'nin önemli bir özelliğidir. Üçüncü taraf uygulamalardan referans almak üzere bir tarif içinde özel JSON anahtar/değer çiftleri oluşturmanıza olanak tanır. Bu tuşları, örneğin otomasyonları tetiklemek veya istediğiniz cihaza iletilecek özel mesajları bilgi sağlamak için kullanabilirsiniz.", "api-extras-description": "Tarif ekstraları Mealie API'nin önemli bir özelliğidir. Üçüncü taraf uygulamalardan referans almak üzere bir tarif içinde özel JSON anahtar/değer çiftleri oluşturmanıza olanak tanır. Bu tuşları, örneğin otomasyonları tetiklemek veya istediğiniz cihaza iletilecek özel mesajları bilgi sağlamak için kullanabilirsiniz.",
"message-key": "İleti Anahtarı", "message-key": "İleti Anahtarı",
"parse": "Parse", "parse": "Ayrıştırma",
"attach-images-hint": "Düzenleyiciye sürükleyip bırakarak görselleri ekleyin", "attach-images-hint": "Düzenleyiciye sürükleyip bırakarak görselleri ekleyin",
"drop-image": "Yüklenecek resimi sürükleyip bırakın", "drop-image": "Yüklenecek resimi sürükleyip bırakın",
"enable-ingredient-amounts-to-use-this-feature": "Enable ingredient amounts to use this feature", "enable-ingredient-amounts-to-use-this-feature": "Enable ingredient amounts to use this feature",
@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Hazır", "oidc-ready": "OIDC Hazır",
"oidc-ready-error-text": "Tüm OIDC Değerleri yapılandırılmamıştır. OIDC Kimlik Doğrulamasını kullanmıyorsanız bu göz ardı edilebilir.", "oidc-ready-error-text": "Tüm OIDC Değerleri yapılandırılmamıştır. OIDC Kimlik Doğrulamasını kullanmıyorsanız bu göz ardı edilebilir.",
"oidc-ready-success-text": "Gerekli OIDC değişkenlerinin tümü ayarlanmıştır." "oidc-ready-success-text": "Gerekli OIDC değişkenlerinin tümü ayarlanmıştır.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "All Lists", "all-lists": "All Lists",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Not", "note": "Not",
"label": "Etiket", "label": "Etiket",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Mükemmel değil, ancak genel olarak harika sonuçlar veriyor ve malzemeleri manuel olarak ayrı alanlara ayrıştırmak için iyi bir başlangıç noktası. Alternatif olarak, malzemeleri tanımlamak için bir model eşleştirme tekniği kullanan \"Brute\" işlemciyi de kullanabilirsiniz.", "ingredients-natural-language-processor-explanation-2": "Mükemmel değil, ancak genel olarak harika sonuçlar veriyor ve malzemeleri manuel olarak ayrı alanlara ayrıştırmak için iyi bir başlangıç noktası. Alternatif olarak, malzemeleri tanımlamak için bir model eşleştirme tekniği kullanan \"Brute\" işlemciyi de kullanabilirsiniz.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Kaba", "brute": "Kaba",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "İçerik Metni", "ingredient-text": "İçerik Metni",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -594,6 +594,7 @@
"select-parser": "Вибрати аналізатор", "select-parser": "Вибрати аналізатор",
"natural-language-processor": "Аналізатор природної мови", "natural-language-processor": "Аналізатор природної мови",
"brute-parser": "Простий аналізатор", "brute-parser": "Простий аналізатор",
"openai-parser": "OpenAI Parser",
"parse-all": "Аналізувати все", "parse-all": "Аналізувати все",
"no-unit": "Без одиниці", "no-unit": "Без одиниці",
"missing-unit": "Створити відсутню одиниці: {unit}", "missing-unit": "Створити відсутню одиниці: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Версія парсера рецептів", "recipe-scraper-version": "Версія парсера рецептів",
"oidc-ready": "OIDC готово", "oidc-ready": "OIDC готово",
"oidc-ready-error-text": "Не всі значення OIDC налаштовано. Це можна ігнорувати, якщо ви не використовуєте авторизацію OIDC.", "oidc-ready-error-text": "Не всі значення OIDC налаштовано. Це можна ігнорувати, якщо ви не використовуєте авторизацію OIDC.",
"oidc-ready-success-text": "Всі необхідні змінні OIDC встановлені." "oidc-ready-success-text": "Всі необхідні змінні OIDC встановлені.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "Всі списки", "all-lists": "Всі списки",
@@ -778,6 +782,7 @@
"food": "Продукт", "food": "Продукт",
"note": "Нотатка", "note": "Нотатка",
"label": "Етикетка", "label": "Етикетка",
"save-label": "Save Label",
"linked-item-warning": "Цей предмет зв'язано з одним або більше рецептами. Зміна одиниць виміру або продуктів дасть неочікувані результати при додаванні або видаленні рецепту з цього списку.", "linked-item-warning": "Цей предмет зв'язано з одним або більше рецептами. Зміна одиниць виміру або продуктів дасть неочікувані результати при додаванні або видаленні рецепту з цього списку.",
"toggle-food": "Перемкнути продукт", "toggle-food": "Перемкнути продукт",
"manage-labels": "Керування етикетками", "manage-labels": "Керування етикетками",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "Модель не ідеальна, але дає непогані результати в загальному і є хорошим початковим пунктом для ручного парсингу інгредієнтів в окремі поля. Крім того, ви можете використовувати \"Brute\" процесор, який використовує метод підбору для визначення інгредієнтів.", "ingredients-natural-language-processor-explanation-2": "Модель не ідеальна, але дає непогані результати в загальному і є хорошим початковим пунктом для ручного парсингу інгредієнтів в окремі поля. Крім того, ви можете використовувати \"Brute\" процесор, який використовує метод підбору для визначення інгредієнтів.",
"nlp": "ОПМ", "nlp": "ОПМ",
"brute": "Брут", "brute": "Брут",
"openai": "OpenAI",
"show-individual-confidence": "Показати індивідуальну впевненість", "show-individual-confidence": "Показати індивідуальну впевненість",
"ingredient-text": "Текст інгредієнта", "ingredient-text": "Текст інгредієнта",
"average-confident": "Впевненість {0}", "average-confident": "Впевненість {0}",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "All Lists", "all-lists": "All Lists",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -64,7 +64,7 @@
"something-went-wrong": "出错了\t#", "something-went-wrong": "出错了\t#",
"subscribed-events": "订阅事件", "subscribed-events": "订阅事件",
"test-message-sent": "测试消息已发送", "test-message-sent": "测试消息已发送",
"message-sent": "Message Sent", "message-sent": "已发送消息",
"new-notification": "新通知", "new-notification": "新通知",
"event-notifiers": "事件通知器", "event-notifiers": "事件通知器",
"apprise-url-skipped-if-blank": "Apprise URL (如果为空则跳过)", "apprise-url-skipped-if-blank": "Apprise URL (如果为空则跳过)",
@@ -161,7 +161,7 @@
"test": "测试", "test": "测试",
"themes": "布景主题", "themes": "布景主题",
"thursday": "周四", "thursday": "周四",
"title": "Title", "title": "标题",
"token": "密钥", "token": "密钥",
"tuesday": "周二", "tuesday": "周二",
"type": "类型", "type": "类型",
@@ -210,7 +210,7 @@
"unsaved-changes": "你有未保存的更改。你希望现在离开前保存吗?保存选择“是”,不保存选择“取消”。", "unsaved-changes": "你有未保存的更改。你希望现在离开前保存吗?保存选择“是”,不保存选择“取消”。",
"clipboard-copy-failure": "未能复制到剪切板。", "clipboard-copy-failure": "未能复制到剪切板。",
"confirm-delete-generic-items": "你确定删除以下条目吗?", "confirm-delete-generic-items": "你确定删除以下条目吗?",
"organizers": "组织者" "organizers": "管理器"
}, },
"group": { "group": {
"are-you-sure-you-want-to-delete-the-group": "您确定要删除<b>{groupName}<b/>吗?", "are-you-sure-you-want-to-delete-the-group": "您确定要删除<b>{groupName}<b/>吗?",
@@ -585,20 +585,21 @@
"screen-awake": "保持屏幕唤醒", "screen-awake": "保持屏幕唤醒",
"remove-image": "删除图片", "remove-image": "删除图片",
"nextStep": "下一步", "nextStep": "下一步",
"recipe-actions": "Recipe Actions", "recipe-actions": "食谱行为",
"parser": { "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.", "experimental-alert-text": "Mealie使用自然语言处理解析食材,并生成对应的计量单位和食物条目。此项功能尚在试验阶段,并非每次都能达到预期效果。如果你不想使用解析结果,可以选择取消,此时修改将不会被保存。",
"ingredient-parser": "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.", "explanation": "若要使用食材解析器,请单击“全部解析”按钮。当解析结果出现时,你可以检查结果是否正确。解析模型的置信分数显示在条目名称的右侧。该分数是各项分数的平均值,且不总是准确的。",
"alerts-explainer": "Alerts will be displayed if a matching foods or unit is found but does not exists in the database.", "alerts-explainer": "当匹配到食物或计量单位,而该结果不在数据库中时,会显示提醒。",
"select-parser": "Select Parser", "select-parser": "选取解析器",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "自然语言处理器",
"brute-parser": "Brute Parser", "brute-parser": "暴力解析器",
"parse-all": "Parse All", "openai-parser": "OpenAI Parser",
"no-unit": "No unit", "parse-all": "全部解析",
"missing-unit": "Create missing unit: {unit}", "no-unit": "没有计量单位",
"missing-food": "Create missing food: {food}", "missing-unit": "创建缺失的计量单位:{unit}",
"no-food": "No Food" "missing-food": "创建缺失的食物:{food}",
"no-food": "没有食物"
} }
}, },
"search": { "search": {
@@ -764,7 +765,10 @@
"recipe-scraper-version": "食谱刮削器版本", "recipe-scraper-version": "食谱刮削器版本",
"oidc-ready": "OIDC 已就绪", "oidc-ready": "OIDC 已就绪",
"oidc-ready-error-text": "某些OIDC环境变量尚未配置。如果你不使用OIDC验证可以忽略该报错", "oidc-ready-error-text": "某些OIDC环境变量尚未配置。如果你不使用OIDC验证可以忽略该报错",
"oidc-ready-success-text": "OIDC所需的环境变量均已配置。" "oidc-ready-success-text": "OIDC所需的环境变量均已配置。",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "所有购物清单", "all-lists": "所有购物清单",
@@ -778,6 +782,7 @@
"food": "食品", "food": "食品",
"note": "备注", "note": "备注",
"label": "标注", "label": "标注",
"save-label": "Save Label",
"linked-item-warning": "此条目已经与一个或多个食谱有关。若强行变更它的单位或食品,会在增减下方关联食谱份数时产生意外结果。", "linked-item-warning": "此条目已经与一个或多个食谱有关。若强行变更它的单位或食品,会在增减下方关联食谱份数时产生意外结果。",
"toggle-food": "是否为数据库中的食品", "toggle-food": "是否为数据库中的食品",
"manage-labels": "管理标签", "manage-labels": "管理标签",
@@ -876,7 +881,7 @@
"or": "或", "or": "或",
"logout": "登出", "logout": "登出",
"manage-users": "管理用户", "manage-users": "管理用户",
"manage-users-description": "Create and manage users.", "manage-users-description": "创建并管理用户。",
"new-password": "新密码", "new-password": "新密码",
"new-user": "新建用户", "new-user": "新建用户",
"password-has-been-reset-to-the-default-password": "密码已被重置为默认密码", "password-has-been-reset-to-the-default-password": "密码已被重置为默认密码",
@@ -1019,10 +1024,10 @@
"source-unit-will-be-deleted": "“待合并单位”将会被删除" "source-unit-will-be-deleted": "“待合并单位”将会被删除"
}, },
"recipe-actions": { "recipe-actions": {
"recipe-actions-data": "Recipe Actions Data", "recipe-actions-data": "食谱行为数据",
"new-recipe-action": "New Recipe Action", "new-recipe-action": "新建食谱行为",
"edit-recipe-action": "Edit Recipe Action", "edit-recipe-action": "编辑食谱行为",
"action-type": "Action Type" "action-type": "行为种类"
}, },
"create-alias": "创建别名", "create-alias": "创建别名",
"manage-aliases": "管理别名", "manage-aliases": "管理别名",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "它不完美但通常来说结果还不错。推荐用它上手来把食材手动解析成独立字段。或者你也可以选择“Brute解析器“它使用一种模式匹配技术来识别食材。", "ingredients-natural-language-processor-explanation-2": "它不完美但通常来说结果还不错。推荐用它上手来把食材手动解析成独立字段。或者你也可以选择“Brute解析器“它使用一种模式匹配技术来识别食材。",
"nlp": "自然语言处理", "nlp": "自然语言处理",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "显示个体置信度", "show-individual-confidence": "显示个体置信度",
"ingredient-text": "食材文本", "ingredient-text": "食材文本",
"average-confident": "{0}置信度", "average-confident": "{0}置信度",
@@ -1185,9 +1191,9 @@
"already-set-up-bring-to-homepage": "我已经配置好了,直接跳转到主页", "already-set-up-bring-to-homepage": "我已经配置好了,直接跳转到主页",
"common-settings-for-new-sites": "这有一些新站点的常见设置", "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", "here-are-a-few-things-to-help-you-get-started": "以下这些可以帮助你开始使用Mealie",
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.", "restore-from-v1-backup": "有之前Mealie v1实例的备份数据你可以在这里恢复它们。",
"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": "管理你自己的个人资料,或者获取邀请链接分享给其他人。"
} }
}, },
"profile": { "profile": {
@@ -1196,16 +1202,16 @@
"get-invite-link": "生成邀请链接", "get-invite-link": "生成邀请链接",
"get-public-link": "生成公开链接", "get-public-link": "生成公开链接",
"account-summary": "账户概况", "account-summary": "账户概况",
"account-summary-description": "Here's a summary of your group's information.", "account-summary-description": "以下是你群组信息的摘要。",
"group-statistics": "群组统计", "group-statistics": "群组统计",
"group-statistics-description": "群组统计为你如何使用Mealie提供一些深入信息。", "group-statistics-description": "群组统计为你如何使用Mealie提供一些深入信息。",
"storage-capacity": "总储存容量", "storage-capacity": "总储存容量",
"storage-capacity-description": "你的存储容量基于你上传的图片和资源计算得出。", "storage-capacity-description": "你的存储容量基于你上传的图片和资源计算得出。",
"personal": "个人设置", "personal": "个人设置",
"personal-description": "These are settings that are personal to you. Changes here won't affect other users.", "personal-description": "这些是你的个人设置。此处的更改不影响同组其他用户。",
"user-settings": "个人资料", "user-settings": "个人资料",
"user-settings-description": "Manage your preferences, change your password, and update your email.", "user-settings-description": "管理偏好、更换密码或邮箱",
"api-tokens-description": "Manage your API Tokens for access from external applications.", "api-tokens-description": "管理用于外部程序访问的API令牌。",
"group-description": "这些项目已在你的群组中共享,一旦被编辑,更改之处会对所有群组成员生效!", "group-description": "这些项目已在你的群组中共享,一旦被编辑,更改之处会对所有群组成员生效!",
"group-settings": "基础选项", "group-settings": "基础选项",
"group-settings-description": "管理常见的群组设置,如饮食计划和隐私设置。", "group-settings-description": "管理常见的群组设置,如饮食计划和隐私设置。",
@@ -1216,9 +1222,9 @@
"notifiers": "通知方案", "notifiers": "通知方案",
"notifiers-description": "设置基于特定事件触发的邮件提醒和通知推送。", "notifiers-description": "设置基于特定事件触发的邮件提醒和通知推送。",
"manage-data": "数据库", "manage-data": "数据库",
"manage-data-description": "Manage your Mealie data; Foods, Units, Categories, Tags and more.", "manage-data-description": "管理Mealie数据包括食物种类、计量单位、分类、标签等",
"data-migrations": "数据迁移", "data-migrations": "数据迁移",
"data-migrations-description": "Migrate your existing data from other applications like Nextcloud Recipes and Chowdown.", "data-migrations-description": "将其他应用(如Nextcloud RecipesChowdown的现有数据迁移至Mealie。",
"email-sent": "邮件已发送", "email-sent": "邮件已发送",
"error-sending-email": "发送邮件出错", "error-sending-email": "发送邮件出错",
"personal-information": "个人信息", "personal-information": "个人信息",

View File

@@ -594,6 +594,7 @@
"select-parser": "Select Parser", "select-parser": "Select Parser",
"natural-language-processor": "Natural Language Processor", "natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser", "brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"parse-all": "Parse All", "parse-all": "Parse All",
"no-unit": "No unit", "no-unit": "No unit",
"missing-unit": "Create missing unit: {unit}", "missing-unit": "Create missing unit: {unit}",
@@ -764,7 +765,10 @@
"recipe-scraper-version": "Recipe Scraper Version", "recipe-scraper-version": "Recipe Scraper Version",
"oidc-ready": "OIDC Ready", "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-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-success-text": "Required OIDC variables are all set.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
}, },
"shopping-list": { "shopping-list": {
"all-lists": "所有清單", "all-lists": "所有清單",
@@ -778,6 +782,7 @@
"food": "Food", "food": "Food",
"note": "Note", "note": "Note",
"label": "Label", "label": "Label",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.", "linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food", "toggle-food": "Toggle Food",
"manage-labels": "Manage Labels", "manage-labels": "Manage Labels",
@@ -1170,6 +1175,7 @@
"ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.", "ingredients-natural-language-processor-explanation-2": "It's not perfect, but it yields great results in general and is a good starting point for manually parsing ingredients into individual fields. Alternatively, you can also use the \"Brute\" processor that uses a pattern matching technique to identify ingredients.",
"nlp": "NLP", "nlp": "NLP",
"brute": "Brute", "brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "Show individual confidence", "show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text", "ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",

View File

@@ -85,12 +85,6 @@ export default defineComponent({
title: i18n.tc("sidebar.maintenance"), title: i18n.tc("sidebar.maintenance"),
restricted: true, restricted: true,
}, },
{
icon: $globals.icons.check,
to: "/admin/background-tasks",
title: i18n.tc("sidebar.background-tasks"),
restricted: true,
},
{ {
icon: $globals.icons.slotMachine, icon: $globals.icons.slotMachine,
to: "/admin/parser", to: "/admin/parser",

View File

@@ -1,19 +0,0 @@
import { BaseAPI } from "../base/base-clients";
import { ServerTask } from "~/lib/api/types/server";
import { PaginationData } from "~/lib/api/types/non-generated";
const prefix = "/api";
const routes = {
base: `${prefix}/admin/server-tasks`,
};
export class AdminTaskAPI extends BaseAPI {
async testTask() {
return await this.requests.post<ServerTask>(`${routes.base}`, {});
}
async getAll() {
return await this.requests.get<PaginationData<ServerTask>>(routes.base);
}
}

View File

@@ -1,5 +1,4 @@
import { AdminAboutAPI } from "./admin/admin-about"; import { AdminAboutAPI } from "./admin/admin-about";
import { AdminTaskAPI } from "./admin/admin-tasks";
import { AdminUsersApi } from "./admin/admin-users"; import { AdminUsersApi } from "./admin/admin-users";
import { AdminGroupsApi } from "./admin/admin-groups"; import { AdminGroupsApi } from "./admin/admin-groups";
import { AdminBackupsApi } from "./admin/admin-backups"; import { AdminBackupsApi } from "./admin/admin-backups";
@@ -9,7 +8,6 @@ import { ApiRequestInstance } from "~/lib/api/types/non-generated";
export class AdminAPI { export class AdminAPI {
public about: AdminAboutAPI; public about: AdminAboutAPI;
public serverTasks: AdminTaskAPI;
public users: AdminUsersApi; public users: AdminUsersApi;
public groups: AdminGroupsApi; public groups: AdminGroupsApi;
public backups: AdminBackupsApi; public backups: AdminBackupsApi;
@@ -18,7 +16,6 @@ export class AdminAPI {
constructor(requests: ApiRequestInstance) { constructor(requests: ApiRequestInstance) {
this.about = new AdminAboutAPI(requests); this.about = new AdminAboutAPI(requests);
this.serverTasks = new AdminTaskAPI(requests);
this.users = new AdminUsersApi(requests); this.users = new AdminUsersApi(requests);
this.groups = new AdminGroupsApi(requests); this.groups = new AdminGroupsApi(requests);
this.backups = new AdminBackupsApi(requests); this.backups = new AdminBackupsApi(requests);

View File

@@ -15,7 +15,6 @@ import { RegisterAPI } from "./user/user-registration";
import { MealPlanAPI } from "./user/group-mealplan"; import { MealPlanAPI } from "./user/group-mealplan";
import { EmailAPI } from "./user/email"; import { EmailAPI } from "./user/email";
import { BulkActionsAPI } from "./user/recipe-bulk-actions"; import { BulkActionsAPI } from "./user/recipe-bulk-actions";
import { GroupServerTaskAPI } from "./user/group-tasks";
import { ToolsApi } from "./user/organizer-tools"; import { ToolsApi } from "./user/organizer-tools";
import { GroupMigrationApi } from "./user/group-migrations"; import { GroupMigrationApi } from "./user/group-migrations";
import { GroupReportsApi } from "./user/group-reports"; import { GroupReportsApi } from "./user/group-reports";
@@ -46,7 +45,6 @@ export class UserApiClient {
public bulk: BulkActionsAPI; public bulk: BulkActionsAPI;
public groupMigration: GroupMigrationApi; public groupMigration: GroupMigrationApi;
public groupReports: GroupReportsApi; public groupReports: GroupReportsApi;
public grouperServerTasks: GroupServerTaskAPI;
public tools: ToolsApi; public tools: ToolsApi;
public shopping: ShoppingApi; public shopping: ShoppingApi;
public multiPurposeLabels: MultiPurposeLabelsApi; public multiPurposeLabels: MultiPurposeLabelsApi;
@@ -72,7 +70,6 @@ export class UserApiClient {
this.register = new RegisterAPI(requests); this.register = new RegisterAPI(requests);
this.mealplans = new MealPlanAPI(requests); this.mealplans = new MealPlanAPI(requests);
this.mealplanRules = new MealPlanRulesApi(requests); this.mealplanRules = new MealPlanRulesApi(requests);
this.grouperServerTasks = new GroupServerTaskAPI(requests);
// Group // Group
this.groupMigration = new GroupMigrationApi(requests); this.groupMigration = new GroupMigrationApi(requests);

View File

@@ -13,6 +13,7 @@ export interface AdminAboutInfo {
enableOidc: boolean; enableOidc: boolean;
oidcRedirect: boolean; oidcRedirect: boolean;
oidcProviderName: string; oidcProviderName: string;
enableOpenai: boolean;
versionLatest: string; versionLatest: string;
apiPort: number; apiPort: number;
apiDocs: boolean; apiDocs: boolean;
@@ -40,6 +41,7 @@ export interface AppInfo {
enableOidc: boolean; enableOidc: boolean;
oidcRedirect: boolean; oidcRedirect: boolean;
oidcProviderName: string; oidcProviderName: string;
enableOpenai: boolean;
} }
export interface AppStartupInfo { export interface AppStartupInfo {
isFirstLogin: boolean; isFirstLogin: boolean;
@@ -80,6 +82,7 @@ export interface CheckAppConfig {
emailReady: boolean; emailReady: boolean;
ldapReady: boolean; ldapReady: boolean;
oidcReady: boolean; oidcReady: boolean;
enableOpenai: boolean;
baseUrlSet: boolean; baseUrlSet: boolean;
isUpToDate: boolean; isUpToDate: boolean;
} }

View File

@@ -6,7 +6,7 @@
*/ */
export type ExportTypes = "json"; export type ExportTypes = "json";
export type RegisteredParser = "nlp" | "brute"; export type RegisteredParser = "nlp" | "brute" | "openai";
export type TimelineEventType = "system" | "info" | "comment"; export type TimelineEventType = "system" | "info" | "comment";
export type TimelineEventImage = "has image" | "does not have image"; export type TimelineEventImage = "has image" | "does not have image";

View File

@@ -1,25 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
*/
export type ServerTaskNames = "Background Task" | "Database Backup" | "Bulk Recipe Import";
export type ServerTaskStatus = "running" | "finished" | "failed";
export interface ServerTask {
groupId: string;
name?: ServerTaskNames & string;
createdAt?: string;
status?: ServerTaskStatus & string;
log?: string;
id: number;
}
export interface ServerTaskCreate {
groupId: string;
name?: ServerTaskNames & string;
createdAt?: string;
status?: ServerTaskStatus & string;
log?: string;
}

View File

@@ -1,13 +0,0 @@
import { BaseAPI } from "../base/base-clients";
import { ServerTask } from "~/lib/api/types/server";
const prefix = "/api";
const routes = {
base: `${prefix}/groups/server-tasks`,
};
export class GroupServerTaskAPI extends BaseAPI {
async getAll() {
return await this.requests.get<ServerTask[]>(routes.base);
}
}

View File

@@ -17,7 +17,7 @@ import {
} from "~/lib/api/types/recipe"; } from "~/lib/api/types/recipe";
import { ApiRequestInstance, PaginationData } from "~/lib/api/types/non-generated"; import { ApiRequestInstance, PaginationData } from "~/lib/api/types/non-generated";
export type Parser = "nlp" | "brute"; export type Parser = "nlp" | "brute" | "openai";
export interface CreateAsset { export interface CreateAsset {
name: string; name: string;

View File

@@ -9,6 +9,7 @@ import {
mdiSquareEditOutline, mdiSquareEditOutline,
mdiClose, mdiClose,
mdiTagArrowUpOutline, mdiTagArrowUpOutline,
mdiTagArrowRight,
mdiTagMultipleOutline, mdiTagMultipleOutline,
mdiShapeOutline, mdiShapeOutline,
mdiBookOutline, mdiBookOutline,
@@ -293,6 +294,7 @@ export const icons = {
// Organization // Organization
tags: mdiTagMultipleOutline, tags: mdiTagMultipleOutline,
tagArrowUp: mdiTagArrowUpOutline, tagArrowUp: mdiTagArrowUpOutline,
tagArrowRight: mdiTagArrowRight,
categories: mdiShapeOutline, categories: mdiShapeOutline,
pages: mdiBookOutline, pages: mdiBookOutline,
book: mdiBookOpenPageVariant, book: mdiBookOpenPageVariant,

View File

@@ -1,87 +0,0 @@
<template>
<v-container class="narrow-container">
<BasePageTitle divider>
<template #header>
<v-img max-height="125" max-width="125" :src="require('~/static/svgs/manage-tasks.svg')"></v-img>
</template>
<template #title> {{ $t('admin.background-tasks') }} </template>
{{ $t('admin.background-tasks-description') }}
</BasePageTitle>
<v-card-actions>
<BaseButton color="info" :loading="loading" @click="refreshTasks">
<template #icon> {{ $globals.icons.refresh }} </template>
{{ $t('general.refresh') }}
</BaseButton>
<BaseButton color="info" @click="testTask">
<template #icon> {{ $globals.icons.testTube }} </template>
{{ $t('general.test') }}
</BaseButton>
</v-card-actions>
<v-expansion-panels class="mt-2">
<v-expansion-panel v-for="(task, i) in tasks" :key="i">
<v-expansion-panel-header>
<span>
<v-progress-circular v-if="task.status === 'running'" indeterminate color="info"></v-progress-circular>
<v-icon v-else-if="task.status === 'finished'" large color="success"> {{ $globals.icons.check }}</v-icon>
<v-icon v-else-if="task.status === 'failed'" large color="error"> {{ $globals.icons.close }}</v-icon>
<v-icon v-else-if="task.status === 'pending'" large color="gray"> {{ $globals.icons.pending }}</v-icon>
<span class="ml-2">
{{ task.name }}
</span>
</span>
{{ $d(Date.parse(task.createdAt), "short") }}
</v-expansion-panel-header>
<v-expansion-panel-content style="white-space: pre-line">
{{ task.log === "" ? $t('admin.no-logs-found') : task.log }}
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
</v-container>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref } from "@nuxtjs/composition-api";
import { ServerTask } from "~/lib/api/types/server";
import { useAdminApi } from "~/composables/api";
export default defineComponent({
layout: "admin",
setup() {
const api = useAdminApi();
const tasks = ref<ServerTask[]>([]);
const loading = ref(false);
async function refreshTasks() {
loading.value = true;
const { data } = await api.serverTasks.getAll();
if (data) {
tasks.value = data.items;
}
loading.value = false;
}
async function testTask() {
await api.serverTasks.testTask();
refreshTasks();
}
onMounted(async () => {
await refreshTasks();
});
return {
loading,
refreshTasks,
testTask,
tasks,
};
},
head() {
return {
title: this.$t("admin.tasks"),
};
},
});
</script>

View File

@@ -13,6 +13,7 @@
<v-btn-toggle v-model="parser" dense mandatory @change="processIngredient"> <v-btn-toggle v-model="parser" dense mandatory @change="processIngredient">
<v-btn value="nlp"> {{ $t('admin.nlp') }} </v-btn> <v-btn value="nlp"> {{ $t('admin.nlp') }} </v-btn>
<v-btn value="brute"> {{ $t('admin.brute') }} </v-btn> <v-btn value="brute"> {{ $t('admin.brute') }} </v-btn>
<v-btn value="openai"> {{ $t('admin.openai') }} </v-btn>
</v-btn-toggle> </v-btn-toggle>
<v-checkbox v-model="showConfidence" class="ml-5" :label="$t('admin.show-individual-confidence')"></v-checkbox> <v-checkbox v-model="showConfidence" class="ml-5" :label="$t('admin.show-individual-confidence')"></v-checkbox>
@@ -63,8 +64,8 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, reactive, ref, toRefs, useContext } from "@nuxtjs/composition-api"; import { defineComponent, reactive, ref, toRefs, useContext } from "@nuxtjs/composition-api";
import { IngredientConfidence } from "~/lib/api/types/recipe";
import { useUserApi } from "~/composables/api"; import { useUserApi } from "~/composables/api";
import { IngredientConfidence } from "~/lib/api/types/recipe";
import { Parser } from "~/lib/api/user/recipes/recipe"; import { Parser } from "~/lib/api/user/recipes/recipe";
type ConfidenceAttribute = "average" | "comment" | "name" | "unit" | "quantity" | "food"; type ConfidenceAttribute = "average" | "comment" | "name" | "unit" | "quantity" | "food";

View File

@@ -268,6 +268,15 @@ export default defineComponent({
color: appConfig.value.oidcReady ? goodColor : warningColor, color: appConfig.value.oidcReady ? goodColor : warningColor,
icon: appConfig.value.oidcReady ? goodIcon : warningIcon, icon: appConfig.value.oidcReady ? goodIcon : warningIcon,
}, },
{
id: "openai-ready",
text: i18n.t("settings.openai-ready"),
status: appConfig.value.enableOpenai,
errorText: i18n.t("settings.openai-ready-error-text"),
successText: i18n.t("settings.openai-ready-success-text"),
color: appConfig.value.enableOpenai ? goodColor : warningColor,
icon: appConfig.value.enableOpenai ? goodIcon : warningIcon,
},
]; ];
return data; return data;
}); });

View File

@@ -19,29 +19,28 @@
<BaseOverflowButton <BaseOverflowButton
v-model="parser" v-model="parser"
btn-class="mx-2 mb-4" btn-class="mx-2 mb-4"
:items="[ :items="availableParsers"
{
text: $tc('recipe.parser.natural-language-processor'),
value: 'nlp',
},
{
text: $tc('recipe.parser.brute-parser'),
value: 'brute',
},
]"
/> />
</div> </div>
</BaseCardSectionTitle> </BaseCardSectionTitle>
<div class="d-flex mt-n3 mb-4 justify-end" style="gap: 5px"> <div class="d-flex mt-n3 mb-4 justify-end" style="gap: 5px">
<BaseButton cancel class="mr-auto" @click="$router.go(-1)"></BaseButton> <BaseButton cancel class="mr-auto" @click="$router.go(-1)"></BaseButton>
<BaseButton color="info" @click="fetchParsed"> <BaseButton color="info" :disabled="parserLoading" @click="fetchParsed">
<template #icon> {{ $globals.icons.foods }}</template> <template #icon> {{ $globals.icons.foods }}</template>
{{ $tc("recipe.parser.parse-all") }} {{ $tc("recipe.parser.parse-all") }}
</BaseButton> </BaseButton>
<BaseButton save @click="saveAll" /> <BaseButton save :disabled="parserLoading" @click="saveAll" />
</div> </div>
<div v-if="parserLoading">
<AppLoader
v-if="parserLoading"
:loading="parserLoading"
waiting-text=""
/>
</div>
<div v-else>
<v-expansion-panels v-model="panels" multiple> <v-expansion-panels v-model="panels" multiple>
<draggable <draggable
v-if="parsedIng.length > 0" v-if="parsedIng.length > 0"
@@ -92,14 +91,21 @@
</v-expansion-panel> </v-expansion-panel>
</draggable> </draggable>
</v-expansion-panels> </v-expansion-panels>
</div>
</v-container> </v-container>
</v-container> </v-container>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref, useContext, useRoute, useRouter } from "@nuxtjs/composition-api"; import { computed, defineComponent, ref, useContext, useRoute, useRouter, watch } from "@nuxtjs/composition-api";
import { invoke, until } from "@vueuse/core"; import { invoke, until } from "@vueuse/core";
import draggable from "vuedraggable"; import draggable from "vuedraggable";
import RecipeIngredientEditor from "~/components/Domain/Recipe/RecipeIngredientEditor.vue";
import { useAppInfo, useUserApi } from "~/composables/api";
import { useRecipe } from "~/composables/recipes";
import { useFoodData, useFoodStore, useUnitData, useUnitStore } from "~/composables/store";
import { useParsingPreferences } from "~/composables/use-users/preferences";
import { uuid4 } from "~/composables/use-utils";
import { import {
CreateIngredientFood, CreateIngredientFood,
CreateIngredientUnit, CreateIngredientUnit,
@@ -108,12 +114,7 @@ import {
ParsedIngredient, ParsedIngredient,
RecipeIngredient, RecipeIngredient,
} from "~/lib/api/types/recipe"; } from "~/lib/api/types/recipe";
import RecipeIngredientEditor from "~/components/Domain/Recipe/RecipeIngredientEditor.vue";
import { useUserApi } from "~/composables/api";
import { useRecipe } from "~/composables/recipes";
import { useFoodData, useFoodStore, useUnitStore, useUnitData } from "~/composables/store";
import { Parser } from "~/lib/api/user/recipes/recipe"; import { Parser } from "~/lib/api/user/recipes/recipe";
import { uuid4 } from "~/composables/use-utils";
interface Error { interface Error {
ingredientIndex: number; ingredientIndex: number;
@@ -130,7 +131,7 @@ export default defineComponent({
}, },
middleware: ["auth", "group-only"], middleware: ["auth", "group-only"],
setup() { setup() {
const { $auth } = useContext(); const { $auth, i18n } = useContext();
const panels = ref<number[]>([]); const panels = ref<number[]>([]);
const route = useRoute(); const route = useRoute();
@@ -139,10 +140,10 @@ export default defineComponent({
const router = useRouter(); const router = useRouter();
const slug = route.value.params.slug; const slug = route.value.params.slug;
const api = useUserApi(); const api = useUserApi();
const appInfo = useAppInfo();
const { i18n } = useContext();
const { recipe, loading } = useRecipe(slug); const { recipe, loading } = useRecipe(slug);
const parserLoading = ref(false);
invoke(async () => { invoke(async () => {
await until(recipe).not.toBeNull(); await until(recipe).not.toBeNull();
@@ -152,10 +153,32 @@ export default defineComponent({
const ingredients = ref<any[]>([]); const ingredients = ref<any[]>([]);
const availableParsers = computed(() => {
return [
{
"text": i18n.tc("recipe.parser.natural-language-processor"),
"value": "nlp",
},
{
"text": i18n.tc("recipe.parser.brute-parser"),
"value": "brute",
},
{
"text": i18n.tc("recipe.parser.openai-parser"),
"value": "openai",
"hide": !appInfo.value?.enableOpenai,
},
]
});
// ========================================================= // =========================================================
// Parser Logic // Parser Logic
const parser = ref<Parser>("nlp"); const parserPreferences = useParsingPreferences();
const parser = ref<Parser>(parserPreferences.value.parser || "nlp");
const parsedIng = ref<ParsedIngredient[]>([]); const parsedIng = ref<ParsedIngredient[]>([]);
watch(parser, (val) => {
parserPreferences.value.parser = val;
});
function processIngredientError(ing: ParsedIngredient, index: number): Error { function processIngredientError(ing: ParsedIngredient, index: number): Error {
const unitError = !checkForUnit(ing.ingredient.unit); const unitError = !checkForUnit(ing.ingredient.unit);
@@ -195,7 +218,10 @@ export default defineComponent({
return; return;
} }
const raw = recipe.value.recipeIngredient.map((ing) => ing.note ?? ""); const raw = recipe.value.recipeIngredient.map((ing) => ing.note ?? "");
parserLoading.value = true;
const { data } = await api.recipes.parseIngredients(parser.value, raw); const { data } = await api.recipes.parseIngredients(parser.value, raw);
parserLoading.value = false;
if (data) { if (data) {
// When we send the recipe ingredient text to be parsed, we lose the reference to the original unparsed ingredient. // When we send the recipe ingredient text to be parsed, we lose the reference to the original unparsed ingredient.
@@ -343,6 +369,7 @@ export default defineComponent({
return { return {
parser, parser,
availableParsers,
saveAll, saveAll,
createFood, createFood,
createUnit, createUnit,
@@ -358,6 +385,7 @@ export default defineComponent({
parsedIng, parsedIng,
recipe, recipe,
loading, loading,
parserLoading,
ingredients, ingredients,
}; };
}, },

View File

@@ -1,12 +1,13 @@
import shutil
import tempfile import tempfile
from collections.abc import AsyncGenerator, Callable, Generator from collections.abc import Callable, Generator
from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from shutil import rmtree
from uuid import uuid4 from uuid import uuid4
import fastapi import fastapi
import jwt import jwt
from fastapi import BackgroundTasks, Depends, HTTPException, Request, status from fastapi import Depends, HTTPException, Request, status
from fastapi.security import OAuth2PasswordBearer from fastapi.security import OAuth2PasswordBearer
from jwt.exceptions import PyJWTError from jwt.exceptions import PyJWTError
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
@@ -205,24 +206,26 @@ def validate_recipe_token(token: str | None = None) -> str:
return slug return slug
async def temporary_zip_path() -> AsyncGenerator[Path, None]: @contextmanager
def get_temporary_zip_path(auto_unlink=True) -> Generator[Path, None, None]:
app_dirs.TEMP_DIR.mkdir(exist_ok=True, parents=True) app_dirs.TEMP_DIR.mkdir(exist_ok=True, parents=True)
temp_path = app_dirs.TEMP_DIR.joinpath("my_zip_archive.zip") temp_path = app_dirs.TEMP_DIR.joinpath("my_zip_archive.zip")
try: try:
yield temp_path yield temp_path
finally: finally:
if auto_unlink:
temp_path.unlink(missing_ok=True) temp_path.unlink(missing_ok=True)
async def temporary_dir(background_tasks: BackgroundTasks) -> AsyncGenerator[Path, None]: @contextmanager
def get_temporary_path(auto_unlink=True) -> Generator[Path, None, None]:
temp_path = app_dirs.TEMP_DIR.joinpath(uuid4().hex) temp_path = app_dirs.TEMP_DIR.joinpath(uuid4().hex)
temp_path.mkdir(exist_ok=True, parents=True) temp_path.mkdir(exist_ok=True, parents=True)
try: try:
yield temp_path yield temp_path
finally: finally:
background_tasks.add_task(shutil.rmtree, temp_path) if auto_unlink:
rmtree(temp_path)
def temporary_file(ext: str = "") -> Callable[[], Generator[tempfile._TemporaryFileWrapper, None, None]]: def temporary_file(ext: str = "") -> Callable[[], Generator[tempfile._TemporaryFileWrapper, None, None]]:

View File

@@ -1,3 +1,4 @@
import time
from datetime import timedelta from datetime import timedelta
from functools import lru_cache from functools import lru_cache
@@ -82,7 +83,7 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
def get_claims(self, settings: AppSettings) -> JWTClaims | None: def get_claims(self, settings: AppSettings) -> JWTClaims | None:
"""Get the claims from the ID token and check if the required claims are present""" """Get the claims from the ID token and check if the required claims are present"""
required_claims = {"preferred_username", "name", "email", settings.OIDC_USER_CLAIM} required_claims = {"preferred_username", "name", "email", settings.OIDC_USER_CLAIM}
jwks = OpenIDProvider.get_jwks() jwks = OpenIDProvider.get_jwks(self.get_ttl_hash()) # cache the key set for 30 minutes
if not jwks: if not jwks:
return None return None
@@ -115,8 +116,9 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
@lru_cache @lru_cache
@staticmethod @staticmethod
def get_jwks() -> KeySet | None: def get_jwks(ttl_hash=None) -> KeySet | None:
"""Get the key set from the open id configuration""" """Get the key set from the openid configuration"""
del ttl_hash # ttl_hash is used for caching only
settings = get_app_settings() settings = get_app_settings()
if not (settings.OIDC_READY and settings.OIDC_CONFIGURATION_URL): if not (settings.OIDC_READY and settings.OIDC_CONFIGURATION_URL):
@@ -145,3 +147,6 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
response.raise_for_status() response.raise_for_status()
session.close() session.close()
return JsonWebKey.import_key_set(response.json()) return JsonWebKey.import_key_set(response.json())
def get_ttl_hash(self, seconds=1800):
return time.time() // seconds

View File

@@ -207,6 +207,31 @@ class AppSettings(BaseSettings):
return self.OIDC_AUTH_ENABLED and not_none and valid_group_claim return self.OIDC_AUTH_ENABLED and not_none and valid_group_claim
# ===============================================
# OpenAI Configuration
OPENAI_BASE_URL: str | None = None
"""The base URL for the OpenAI API. Leave this unset for most usecases"""
OPENAI_API_KEY: str | None = None
"""Your OpenAI API key. Required to enable OpenAI features"""
OPENAI_MODEL: str = "gpt-4o"
"""Which OpenAI model to send requests to. Leave this unset for most usecases"""
OPENAI_WORKERS: int = 2
"""
Number of OpenAI workers per request. Higher values may increase
processing speed, but will incur additional API costs
"""
OPENAI_SEND_DATABASE_DATA: bool = True
"""
Sending database data may increase accuracy in certain requests,
but will incur additional API costs
"""
@property
def OPENAI_ENABLED(self) -> bool:
"""Validates OpenAI settings are all set"""
return bool(self.OPENAI_API_KEY and self.OPENAI_MODEL)
# =============================================== # ===============================================
# Testing Config # Testing Config

View File

@@ -21,7 +21,13 @@ from .mealplan import GroupMealPlan
from .preferences import GroupPreferencesModel from .preferences import GroupPreferencesModel
if TYPE_CHECKING: if TYPE_CHECKING:
from ..recipe import IngredientFoodModel, IngredientUnitModel, RecipeModel, Tag, Tool from ..recipe import (
IngredientFoodModel,
IngredientUnitModel,
RecipeModel,
Tag,
Tool,
)
from ..users import User from ..users import User
from .events import GroupEventNotifierModel from .events import GroupEventNotifierModel
from .exports import GroupDataExportsModel from .exports import GroupDataExportsModel
@@ -67,7 +73,7 @@ class Group(SqlAlchemyBase, BaseMixins):
webhooks: Mapped[list[GroupWebhooksModel]] = orm.relationship(GroupWebhooksModel, **common_args) webhooks: Mapped[list[GroupWebhooksModel]] = orm.relationship(GroupWebhooksModel, **common_args)
recipe_actions: Mapped[list["GroupRecipeAction"]] = orm.relationship("GroupRecipeAction", **common_args) recipe_actions: Mapped[list["GroupRecipeAction"]] = orm.relationship("GroupRecipeAction", **common_args)
cookbooks: Mapped[list[CookBook]] = orm.relationship(CookBook, **common_args) cookbooks: Mapped[list[CookBook]] = orm.relationship(CookBook, **common_args)
server_tasks: Mapped[list[ServerTaskModel]] = orm.relationship(ServerTaskModel, **common_args) server_tasks: Mapped[list["ServerTaskModel"]] = orm.relationship("ServerTaskModel", **common_args)
data_exports: Mapped[list["GroupDataExportsModel"]] = orm.relationship("GroupDataExportsModel", **common_args) data_exports: Mapped[list["GroupDataExportsModel"]] = orm.relationship("GroupDataExportsModel", **common_args)
shopping_lists: Mapped[list["ShoppingList"]] = orm.relationship("ShoppingList", **common_args) shopping_lists: Mapped[list["ShoppingList"]] = orm.relationship("ShoppingList", **common_args)
group_reports: Mapped[list["ReportModel"]] = orm.relationship("ReportModel", **common_args) group_reports: Mapped[list["ReportModel"]] = orm.relationship("ReportModel", **common_args)

View File

@@ -14,6 +14,8 @@ if TYPE_CHECKING:
class ServerTaskModel(SqlAlchemyBase, BaseMixins): class ServerTaskModel(SqlAlchemyBase, BaseMixins):
# Server Tasks are deprecated, but the table still exists in the database
__tablename__ = "server_tasks" __tablename__ = "server_tasks"
name: Mapped[str] = mapped_column(String, nullable=False) name: Mapped[str] = mapped_column(String, nullable=False)
completed_date: Mapped[datetime] = mapped_column(DateTime, nullable=True) completed_date: Mapped[datetime] = mapped_column(DateTime, nullable=True)

View File

@@ -5,8 +5,8 @@
"recipe": { "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": { "recipe-defaults": {
"ingredient-note": "1 Cup Flour", "ingredient-note": "1 Taza de harina",
"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" "step-text": "Los pasos de receta, así como otros campos en la página de recetas, soportan sintaxis Markdown.\n\n**Agregar un enlace**\n\n[Mi Enlace](https://demo.mealie.io)\n"
} }
}, },
"mealplan": { "mealplan": {

View File

@@ -3,19 +3,19 @@
"server-error": "予期しないエラーが発生しました" "server-error": "予期しないエラーが発生しました"
}, },
"recipe": { "recipe": {
"unique-name-error": "レシピ名は固有のものでなければなりません", "unique-name-error": "レシピ名が重複しています",
"recipe-defaults": { "recipe-defaults": {
"ingredient-note": "1 Cup Flour", "ingredient-note": "小麦粉1カップ",
"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" "step-text": "レシピの手順など、レシピページはmarkdown構文に対応しています\n\n例\n[ミーリー](https://demo.mealie.io)\n"
} }
}, },
"mealplan": { "mealplan": {
"no-recipes-match-your-rules": "ルールに致するレシピはありません" "no-recipes-match-your-rules": "ルールに致するレシピはありません"
}, },
"user": { "user": {
"user-updated": "ユーザを更新しました", "user-updated": "ユーザを更新しました",
"password-updated": "パスワードを更新しました", "password-updated": "パスワードを更新しました",
"invalid-current-password": "現在のパスワードが無効です", "invalid-current-password": "現在のパスワードと一致しません",
"ldap-update-password-unavailable": "パスワードを更新できません。ユーザーはLDAPによって制御されています" "ldap-update-password-unavailable": "パスワードを更新できません。ユーザーはLDAPによって制御されています"
}, },
"group": { "group": {
@@ -26,15 +26,15 @@
"no-entry-found": "リクエストされた情報は見つかりませんでした", "no-entry-found": "リクエストされた情報は見つかりませんでした",
"integrity-error": "データベースの整合性エラー", "integrity-error": "データベースの整合性エラー",
"username-conflict-error": "この ユーザー名はすでに使用されています。", "username-conflict-error": "この ユーザー名はすでに使用されています。",
"email-conflict-error": "この Eメイルはすでに使用されています。" "email-conflict-error": "このメールアドレスはすでに使用されています。"
}, },
"notifications": { "notifications": {
"generic-created": "{name} が作成されました", "generic-created": "{name} が作成されました",
"generic-updated": "{name} が更新されました", "generic-updated": "{name} が更新されました",
"generic-created-with-url": "{name} 作成されています。 {url}", "generic-created-with-url": "{name} 作成されました: {url}",
"generic-updated-with-url": "{name} 更新されています。 {url}", "generic-updated-with-url": "{name} 更新されました: {url}",
"generic-duplicated": "{name} が複製されました", "generic-duplicated": "{name} が複製されました",
"generic-deleted": "{name} 削除されています" "generic-deleted": "{name} 削除されました"
}, },
"datetime": { "datetime": {
"year": "年", "year": "年",

View File

@@ -5,8 +5,8 @@
"recipe": { "recipe": {
"unique-name-error": "Receptnamn måste vara unika", "unique-name-error": "Receptnamn måste vara unika",
"recipe-defaults": { "recipe-defaults": {
"ingredient-note": "1 Cup Flour", "ingredient-note": "1 kopp mjöl",
"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" "step-text": "Receptsteg samt andra fält i receptsidan stöder markdown syntax.\n\n**Lägg till en länk**\n\n[Min länk](https://demo.mealie.io)\n"
} }
}, },
"mealplan": { "mealplan": {

View File

@@ -5,8 +5,8 @@
"recipe": { "recipe": {
"unique-name-error": "食谱名称必须唯一", "unique-name-error": "食谱名称必须唯一",
"recipe-defaults": { "recipe-defaults": {
"ingredient-note": "1 Cup Flour", "ingredient-note": "1杯面粉",
"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" "step-text": "食谱步骤以及食谱页面的其他字段支持markdown语法。\n\n**添加一个链接**\n\n[我的链接](https://demo.mealie.io)\n"
} }
}, },
"mealplan": { "mealplan": {

View File

@@ -29,7 +29,6 @@ from mealie.db.models.recipe.recipe_timeline import RecipeTimelineEvent
from mealie.db.models.recipe.shared import RecipeShareTokenModel from mealie.db.models.recipe.shared import RecipeShareTokenModel
from mealie.db.models.recipe.tag import Tag from mealie.db.models.recipe.tag import Tag
from mealie.db.models.recipe.tool import Tool from mealie.db.models.recipe.tool import Tool
from mealie.db.models.server.task import ServerTaskModel
from mealie.db.models.users import LongLiveToken, User from mealie.db.models.users import LongLiveToken, User
from mealie.db.models.users.password_reset import PasswordResetModel from mealie.db.models.users.password_reset import PasswordResetModel
from mealie.db.models.users.user_to_recipe import UserToRecipe from mealie.db.models.users.user_to_recipe import UserToRecipe
@@ -59,7 +58,6 @@ from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUni
from mealie.schema.recipe.recipe_share_token import RecipeShareToken from mealie.schema.recipe.recipe_share_token import RecipeShareToken
from mealie.schema.recipe.recipe_timeline_events import RecipeTimelineEventOut from mealie.schema.recipe.recipe_timeline_events import RecipeTimelineEventOut
from mealie.schema.reports.reports import ReportEntryOut, ReportOut from mealie.schema.reports.reports import ReportEntryOut, ReportOut
from mealie.schema.server import ServerTask
from mealie.schema.user import GroupInDB, LongLiveTokenInDB, PrivateUser from mealie.schema.user import GroupInDB, LongLiveTokenInDB, PrivateUser
from mealie.schema.user.user import UserRatingOut from mealie.schema.user.user import UserRatingOut
from mealie.schema.user.user_passwords import PrivatePasswordResetToken from mealie.schema.user.user_passwords import PrivatePasswordResetToken
@@ -162,10 +160,6 @@ class AllRepositories:
# ================================================================ # ================================================================
# Group # Group
@cached_property
def server_tasks(self) -> RepositoryGeneric[ServerTask, ServerTaskModel]:
return RepositoryGeneric(self.session, PK_ID, ServerTaskModel, ServerTask)
@cached_property @cached_property
def groups(self) -> RepositoryGroup: def groups(self) -> RepositoryGroup:
return RepositoryGroup(self.session, PK_ID, Group, GroupInDB) return RepositoryGroup(self.session, PK_ID, Group, GroupInDB)

View File

@@ -1,6 +1,6 @@
{ {
"acorn-squash": "橡果南瓜", "acorn-squash": "橡果南瓜",
"alfalfa-sprouts": "alfalfa sprouts", "alfalfa-sprouts": "紫花苜蓿芽",
"anchovies": "凤尾鱼", "anchovies": "凤尾鱼",
"apples": "苹果", "apples": "苹果",
"artichoke": "洋蓟", "artichoke": "洋蓟",
@@ -18,7 +18,7 @@
"blackberries": "黑莓", "blackberries": "黑莓",
"brassicas": "甘蓝", "brassicas": "甘蓝",
"bok-choy": "小白菜", "bok-choy": "小白菜",
"broccoflower": "broccoflower", "broccoflower": "西蓝花",
"broccoli": "西兰花", "broccoli": "西兰花",
"broccolini": "broccolini", "broccolini": "broccolini",
"broccoli-rabe": "broccoli rabe", "broccoli-rabe": "broccoli rabe",

View File

@@ -1,6 +1,6 @@
[ [
{ {
"name": "農産物" "name": "青果物"
}, },
{ {
"name": "穀物" "name": "穀物"
@@ -42,7 +42,7 @@
"name": "健康食品" "name": "健康食品"
}, },
{ {
"name": "世帯" "name": "家庭"
}, },
{ {
"name": "食肉製品" "name": "食肉製品"

View File

@@ -70,12 +70,12 @@
"abbreviation": "mg" "abbreviation": "mg"
}, },
"splash": { "splash": {
"name": "しぶき", "name": "少量",
"description": "", "description": "",
"abbreviation": "" "abbreviation": ""
}, },
"dash": { "dash": {
"name": "ダッシュ", "name": "少量",
"description": "", "description": "",
"abbreviation": "" "abbreviation": ""
}, },
@@ -90,7 +90,7 @@
"abbreviation": "" "abbreviation": ""
}, },
"clove": { "clove": {
"name": "クローブ", "name": "欠片",
"description": "", "description": "",
"abbreviation": "" "abbreviation": ""
}, },

View File

@@ -70,12 +70,12 @@
"abbreviation": "毫克" "abbreviation": "毫克"
}, },
"splash": { "splash": {
"name": "splash", "name": "适量",
"description": "", "description": "",
"abbreviation": "" "abbreviation": ""
}, },
"dash": { "dash": {
"name": "dash", "name": "少许",
"description": "", "description": "",
"abbreviation": "" "abbreviation": ""
}, },

View File

@@ -8,7 +8,6 @@ from . import (
admin_maintenance, admin_maintenance,
admin_management_groups, admin_management_groups,
admin_management_users, admin_management_users,
admin_server_tasks,
) )
router = AdminAPIRouter(prefix="/admin") router = AdminAPIRouter(prefix="/admin")
@@ -17,7 +16,6 @@ router.include_router(admin_about.router, tags=["Admin: About"])
router.include_router(admin_management_users.router, tags=["Admin: Manage Users"]) router.include_router(admin_management_users.router, tags=["Admin: Manage Users"])
router.include_router(admin_management_groups.router, tags=["Admin: Manage Groups"]) router.include_router(admin_management_groups.router, tags=["Admin: Manage Groups"])
router.include_router(admin_email.router, tags=["Admin: Email"]) router.include_router(admin_email.router, tags=["Admin: Email"])
router.include_router(admin_server_tasks.router, tags=["Admin: Server Tasks"])
router.include_router(admin_backups.router, tags=["Admin: Backups"]) router.include_router(admin_backups.router, tags=["Admin: Backups"])
router.include_router(admin_maintenance.router, tags=["Admin: Maintenance"]) router.include_router(admin_maintenance.router, tags=["Admin: Maintenance"])
router.include_router(admin_analytics.router, tags=["Admin: Analytics"]) router.include_router(admin_analytics.router, tags=["Admin: Analytics"])

View File

@@ -33,6 +33,7 @@ class AdminAboutController(BaseAdminController):
enable_oidc=settings.OIDC_AUTH_ENABLED, enable_oidc=settings.OIDC_AUTH_ENABLED,
oidc_redirect=settings.OIDC_AUTO_REDIRECT, oidc_redirect=settings.OIDC_AUTO_REDIRECT,
oidc_provider_name=settings.OIDC_PROVIDER_NAME, oidc_provider_name=settings.OIDC_PROVIDER_NAME,
enable_openai=settings.OPENAI_ENABLED,
) )
@router.get("/statistics", response_model=AppStatistics) @router.get("/statistics", response_model=AppStatistics)
@@ -55,4 +56,5 @@ class AdminAboutController(BaseAdminController):
base_url_set=settings.BASE_URL != "http://localhost:8080", base_url_set=settings.BASE_URL != "http://localhost:8080",
is_up_to_date=APP_VERSION == "develop" or APP_VERSION == "nightly" or get_latest_version() == APP_VERSION, is_up_to_date=APP_VERSION == "develop" or APP_VERSION == "nightly" or get_latest_version() == APP_VERSION,
oidc_ready=settings.OIDC_READY, oidc_ready=settings.OIDC_READY,
enable_openai=settings.OPENAI_ENABLED,
) )

View File

@@ -1,27 +0,0 @@
from fastapi import BackgroundTasks, Depends
from mealie.routes._base import BaseAdminController, controller
from mealie.routes._base.routers import UserAPIRouter
from mealie.schema.response.pagination import PaginationQuery
from mealie.schema.server.tasks import ServerTask, ServerTaskNames, ServerTaskPagination
from mealie.services.server_tasks import BackgroundExecutor, test_executor_func
router = UserAPIRouter()
@controller(router)
class AdminServerTasksController(BaseAdminController):
@router.get("/server-tasks", response_model=ServerTaskPagination)
def get_all(self, q: PaginationQuery = Depends(PaginationQuery)):
response = self.repos.server_tasks.page_all(
pagination=q,
override=ServerTask,
)
response.set_pagination_guides(router.url_path_for("get_all"), q.model_dump())
return response
@router.post("/server-tasks", response_model=ServerTask, status_code=201)
def create_test_tasks(self, bg_tasks: BackgroundTasks):
bg_executor = BackgroundExecutor(self.group.id, self.repos, bg_tasks)
return bg_executor.dispatch(ServerTaskNames.default, test_executor_func)

View File

@@ -32,6 +32,7 @@ def get_app_info(session: Session = Depends(generate_session)):
enable_oidc=settings.OIDC_READY, enable_oidc=settings.OIDC_READY,
oidc_redirect=settings.OIDC_AUTO_REDIRECT, oidc_redirect=settings.OIDC_AUTO_REDIRECT,
oidc_provider_name=settings.OIDC_PROVIDER_NAME, oidc_provider_name=settings.OIDC_PROVIDER_NAME,
enable_openai=settings.OPENAI_ENABLED,
) )

View File

@@ -1,10 +1,9 @@
import shutil import shutil
from pathlib import Path
from fastapi import Depends, File, Form from fastapi import File, Form
from fastapi.datastructures import UploadFile from fastapi.datastructures import UploadFile
from mealie.core.dependencies import temporary_zip_path from mealie.core.dependencies import get_temporary_zip_path
from mealie.routes._base import BaseUserController, controller from mealie.routes._base import BaseUserController, controller
from mealie.routes._base.routers import UserAPIRouter from mealie.routes._base.routers import UserAPIRouter
from mealie.schema.group.group_migration import SupportedMigrations from mealie.schema.group.group_migration import SupportedMigrations
@@ -32,8 +31,8 @@ class GroupMigrationController(BaseUserController):
add_migration_tag: bool = Form(False), add_migration_tag: bool = Form(False),
migration_type: SupportedMigrations = Form(...), migration_type: SupportedMigrations = Form(...),
archive: UploadFile = File(...), archive: UploadFile = File(...),
temp_path: Path = Depends(temporary_zip_path),
): ):
with get_temporary_zip_path() as temp_path:
# Save archive to temp_path # Save archive to temp_path
with temp_path.open("wb") as buffer: with temp_path.open("wb") as buffer:
shutil.copyfileobj(archive.file, buffer) shutil.copyfileobj(archive.file, buffer)
@@ -66,4 +65,5 @@ class GroupMigrationController(BaseUserController):
migrator = constructor(**args) migrator = constructor(**args)
return migrator.migrate(f"{migration_type.value.title()} Migration") migration_result = migrator.migrate(f"{migration_type.value.title()} Migration")
return migration_result

View File

@@ -11,11 +11,12 @@ router = APIRouter(prefix="/parser")
@controller(router) @controller(router)
class IngredientParserController(BaseUserController): class IngredientParserController(BaseUserController):
@router.post("/ingredients", response_model=list[ParsedIngredient]) @router.post("/ingredients", response_model=list[ParsedIngredient])
def parse_ingredients(self, ingredients: IngredientsRequest): async def parse_ingredients(self, ingredients: IngredientsRequest):
parser = get_parser(ingredients.parser, self.group_id, self.session) parser = get_parser(ingredients.parser, self.group_id, self.session)
return parser.parse(ingredients.ingredients) return await parser.parse(ingredients.ingredients)
@router.post("/ingredient", response_model=ParsedIngredient) @router.post("/ingredient", response_model=ParsedIngredient)
def parse_ingredient(self, ingredient: IngredientRequest): async def parse_ingredient(self, ingredient: IngredientRequest):
parser = get_parser(ingredient.parser, self.group_id, self.session) parser = get_parser(ingredient.parser, self.group_id, self.session)
return parser.parse([ingredient.ingredient])[0] response = await parser.parse([ingredient.ingredient])
return response[0]

View File

@@ -1,9 +1,9 @@
from functools import cached_property from functools import cached_property
from pathlib import Path from pathlib import Path
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, HTTPException
from mealie.core.dependencies.dependencies import temporary_zip_path from mealie.core.dependencies.dependencies import get_temporary_zip_path
from mealie.core.security import create_file_token from mealie.core.security import create_file_token
from mealie.routes._base import BaseUserController, controller from mealie.routes._base import BaseUserController, controller
from mealie.schema.group.group_exports import GroupDataExport from mealie.schema.group.group_exports import GroupDataExport
@@ -44,7 +44,8 @@ class RecipeBulkActionsController(BaseUserController):
self.service.delete_recipes(delete_recipes.recipes) self.service.delete_recipes(delete_recipes.recipes)
@router.post("/export", status_code=202) @router.post("/export", status_code=202)
def bulk_export_recipes(self, export_recipes: ExportRecipes, temp_path=Depends(temporary_zip_path)): def bulk_export_recipes(self, export_recipes: ExportRecipes):
with get_temporary_zip_path() as temp_path:
self.service.export_recipes(temp_path, export_recipes.recipes) self.service.export_recipes(temp_path, export_recipes.recipes)
@router.get("/export/download") @router.get("/export/download")

View File

@@ -1,5 +1,5 @@
from functools import cached_property from functools import cached_property
from shutil import copyfileobj from shutil import copyfileobj, rmtree
from uuid import UUID from uuid import UUID
from zipfile import ZipFile from zipfile import ZipFile
@@ -10,11 +10,11 @@ from fastapi.datastructures import UploadFile
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from pydantic import UUID4, BaseModel, Field from pydantic import UUID4, BaseModel, Field
from slugify import slugify from slugify import slugify
from starlette.background import BackgroundTask
from starlette.responses import FileResponse from starlette.responses import FileResponse
from mealie.core import exceptions from mealie.core import exceptions
from mealie.core.dependencies import temporary_zip_path from mealie.core.dependencies import get_temporary_path, get_temporary_zip_path, validate_recipe_token
from mealie.core.dependencies.dependencies import temporary_dir, validate_recipe_token
from mealie.core.security import create_recipe_slug_token from mealie.core.security import create_recipe_slug_token
from mealie.db.models.group.cookbook import CookBook from mealie.db.models.group.cookbook import CookBook
from mealie.pkgs import cache from mealie.pkgs import cache
@@ -103,7 +103,7 @@ class RecipeExportController(BaseRecipeController):
return RecipeZipTokenResponse(token=create_recipe_slug_token(slug)) return RecipeZipTokenResponse(token=create_recipe_slug_token(slug))
@router_exports.get("/{slug}/exports", response_class=FileResponse) @router_exports.get("/{slug}/exports", response_class=FileResponse)
def get_recipe_as_format(self, slug: str, template_name: str, temp_dir=Depends(temporary_dir)): def get_recipe_as_format(self, slug: str, template_name: str):
""" """
## Parameters ## Parameters
`template_name`: The name of the template to use to use in the exports listed. Template type will automatically `template_name`: The name of the template to use to use in the exports listed. Template type will automatically
@@ -111,19 +111,21 @@ class RecipeExportController(BaseRecipeController):
names and formats in the /api/recipes/exports endpoint. names and formats in the /api/recipes/exports endpoint.
""" """
with get_temporary_path(auto_unlink=False) as temp_path:
recipe = self.mixins.get_one(slug) recipe = self.mixins.get_one(slug)
file = self.service.render_template(recipe, temp_dir, template_name) file = self.service.render_template(recipe, temp_path, template_name)
return FileResponse(file) return FileResponse(file, background=BackgroundTask(rmtree, temp_path))
@router_exports.get("/{slug}/exports/zip") @router_exports.get("/{slug}/exports/zip")
def get_recipe_as_zip(self, slug: str, token: str, temp_path=Depends(temporary_zip_path)): def get_recipe_as_zip(self, slug: str, token: str):
"""Get a Recipe and It's Original Image as a Zip File""" """Get a Recipe and Its Original Image as a Zip File"""
slug = validate_recipe_token(token) with get_temporary_zip_path(auto_unlink=False) as temp_path:
validated_slug = validate_recipe_token(token)
if slug != slug: if validated_slug != slug:
raise HTTPException(status_code=400, detail="Invalid Slug") raise HTTPException(status_code=400, detail="Invalid Slug")
recipe: Recipe = self.mixins.get_one(slug) recipe: Recipe = self.mixins.get_one(validated_slug)
image_asset = recipe.image_dir.joinpath(RecipeImageTypes.original.value) image_asset = recipe.image_dir.joinpath(RecipeImageTypes.original.value)
with ZipFile(temp_path, "w") as myzip: with ZipFile(temp_path, "w") as myzip:
myzip.writestr(f"{slug}.json", recipe.model_dump_json()) myzip.writestr(f"{slug}.json", recipe.model_dump_json())
@@ -131,7 +133,9 @@ class RecipeExportController(BaseRecipeController):
if image_asset.is_file(): if image_asset.is_file():
myzip.write(image_asset, arcname=image_asset.name) myzip.write(image_asset, arcname=image_asset.name)
return FileResponse(temp_path, filename=f"{slug}.zip") return FileResponse(
temp_path, filename=f"{recipe.slug}.zip", background=BackgroundTask(temp_path.unlink, missing_ok=True)
)
router = UserAPIRouter(prefix="/recipes", tags=["Recipe: CRUD"], route_class=MealieCrudRoute) router = UserAPIRouter(prefix="/recipes", tags=["Recipe: CRUD"], route_class=MealieCrudRoute)
@@ -219,8 +223,9 @@ class RecipeController(BaseRecipeController):
return "recipe_scrapers was unable to scrape this URL" return "recipe_scrapers was unable to scrape this URL"
@router.post("/create-from-zip", status_code=201) @router.post("/create-from-zip", status_code=201)
def create_recipe_from_zip(self, temp_path=Depends(temporary_zip_path), archive: UploadFile = File(...)): def create_recipe_from_zip(self, archive: UploadFile = File(...)):
"""Create recipe from archive""" """Create recipe from archive"""
with get_temporary_zip_path() as temp_path:
recipe = self.service.create_from_zip(archive, temp_path) recipe = self.service.create_from_zip(archive, temp_path)
self.publish_event( self.publish_event(
event_type=EventTypes.recipe_created, event_type=EventTypes.recipe_created,

View File

@@ -1,10 +1,9 @@
import shutil import shutil
from pathlib import Path
from fastapi import Depends, File, HTTPException, UploadFile, status from fastapi import File, HTTPException, UploadFile, status
from pydantic import UUID4 from pydantic import UUID4
from mealie.core.dependencies.dependencies import temporary_dir from mealie.core.dependencies import get_temporary_path
from mealie.pkgs import cache, img from mealie.pkgs import cache, img
from mealie.routes._base import BaseUserController, controller from mealie.routes._base import BaseUserController, controller
from mealie.routes._base.routers import UserAPIRouter from mealie.routes._base.routers import UserAPIRouter
@@ -21,11 +20,11 @@ class UserImageController(BaseUserController):
self, self,
id: UUID4, id: UUID4,
profile: UploadFile = File(...), profile: UploadFile = File(...),
temp_dir: Path = Depends(temporary_dir),
): ):
"""Updates a User Image""" """Updates a User Image"""
with get_temporary_path() as temp_path:
assert_user_change_allowed(id, self.user) assert_user_change_allowed(id, self.user)
temp_img = temp_dir.joinpath(profile.filename) temp_img = temp_path.joinpath(profile.filename)
with temp_img.open("wb") as buffer: with temp_img.open("wb") as buffer:
shutil.copyfileobj(profile.file, buffer) shutil.copyfileobj(profile.file, buffer)

View File

@@ -18,6 +18,7 @@ class AppInfo(MealieModel):
enable_oidc: bool enable_oidc: bool
oidc_redirect: bool oidc_redirect: bool
oidc_provider_name: str oidc_provider_name: str
enable_openai: bool
class AppTheme(MealieModel): class AppTheme(MealieModel):
@@ -64,6 +65,7 @@ class CheckAppConfig(MealieModel):
email_ready: bool email_ready: bool
ldap_ready: bool ldap_ready: bool
oidc_ready: bool oidc_ready: bool
enable_openai: bool
base_url_set: bool base_url_set: bool
is_up_to_date: bool is_up_to_date: bool

View File

@@ -187,7 +187,7 @@ class RecipeIngredientBase(MealieModel):
qty: float | Fraction qty: float | Fraction
# decimal # decimal
if not self.unit or not self.unit.fraction: if self.unit and not self.unit.fraction:
qty = round(self.quantity or 0, INGREDIENT_QTY_PRECISION) qty = round(self.quantity or 0, INGREDIENT_QTY_PRECISION)
if qty.is_integer(): if qty.is_integer():
return str(int(qty)) return str(int(qty))
@@ -327,6 +327,7 @@ class ParsedIngredient(MealieModel):
class RegisteredParser(str, enum.Enum): class RegisteredParser(str, enum.Enum):
nlp = "nlp" nlp = "nlp"
brute = "brute" brute = "brute"
openai = "openai"
class IngredientsRequest(MealieModel): class IngredientsRequest(MealieModel):

View File

@@ -1,10 +0,0 @@
# This file is auto-generated by gen_schema_exports.py
from .tasks import ServerTask, ServerTaskCreate, ServerTaskNames, ServerTaskPagination, ServerTaskStatus
__all__ = [
"ServerTask",
"ServerTaskCreate",
"ServerTaskNames",
"ServerTaskPagination",
"ServerTaskStatus",
]

View File

@@ -1,50 +0,0 @@
import datetime
import enum
from uuid import UUID
from pydantic import ConfigDict, Field
from mealie.schema._mealie import MealieModel
from mealie.schema.response.pagination import PaginationBase
class ServerTaskNames(str, enum.Enum):
default = "Background Task"
backup_task = "Database Backup"
bulk_recipe_import = "Bulk Recipe Import"
class ServerTaskStatus(str, enum.Enum):
running = "running"
finished = "finished"
failed = "failed"
class ServerTaskCreate(MealieModel):
group_id: UUID
name: ServerTaskNames = ServerTaskNames.default
created_at: datetime.datetime = Field(default_factory=datetime.datetime.now)
status: ServerTaskStatus = ServerTaskStatus.running
log: str = ""
def set_running(self) -> None:
self.status = ServerTaskStatus.running
def set_finished(self) -> None:
self.status = ServerTaskStatus.finished
def set_failed(self) -> None:
self.status = ServerTaskStatus.failed
def append_log(self, message: str) -> None:
# Prefix with Timestamp and append new line and join to log
self.log += f"{datetime.datetime.now()}: {message}\n"
class ServerTask(ServerTaskCreate):
id: int
model_config = ConfigDict(from_attributes=True)
class ServerTaskPagination(PaginationBase):
items: list[ServerTask]

View File

@@ -0,0 +1,6 @@
from .openai import OpenAIDataInjection, OpenAIService
__all__ = [
"OpenAIDataInjection",
"OpenAIService",
]

View File

@@ -0,0 +1,129 @@
import inspect
import json
import os
from pathlib import Path
from textwrap import dedent
from openai import NOT_GIVEN, AsyncOpenAI
from openai.resources.chat.completions import ChatCompletion
from pydantic import BaseModel, field_validator
from mealie.core.config import get_app_settings
from .._base_service import BaseService
class OpenAIDataInjection(BaseModel):
description: str
value: str
@field_validator("value", mode="before")
def parse_value(cls, value):
if not value:
raise ValueError("Value cannot be empty")
if isinstance(value, str):
return value
# convert Pydantic models to JSON
if isinstance(value, BaseModel):
return value.model_dump_json()
# convert Pydantic types to their JSON schema definition
if inspect.isclass(value) and issubclass(value, BaseModel):
value = value.model_json_schema()
# attempt to convert object to JSON
try:
return json.dumps(value, separators=(",", ":"))
except TypeError:
return value
class OpenAIService(BaseService):
PROMPTS_DIR = Path(os.path.dirname(os.path.abspath(__file__))) / "prompts"
def __init__(self) -> None:
settings = get_app_settings()
if not settings.OPENAI_ENABLED:
raise ValueError("OpenAI is not enabled")
self.model = settings.OPENAI_MODEL
self.workers = settings.OPENAI_WORKERS
self.send_db_data = settings.OPENAI_SEND_DATABASE_DATA
self.get_client = lambda: AsyncOpenAI(
base_url=settings.OPENAI_BASE_URL,
api_key=settings.OPENAI_API_KEY,
)
super().__init__()
@classmethod
def get_prompt(cls, name: str, data_injections: list[OpenAIDataInjection] | None = None) -> str:
"""
Load stored prompt and inject data into it.
Access prompts with dot notation.
For example, to access `prompts/recipes/parse-recipe-ingredients.txt`, use
`recipes.parse-recipe-ingredients`
"""
if not name:
raise ValueError("Prompt name cannot be empty")
tree = name.split(".")
prompt_dir = os.path.join(cls.PROMPTS_DIR, *tree[:-1], tree[-1] + ".txt")
try:
with open(prompt_dir) as f:
content = f.read()
except OSError as e:
raise OSError(f"Unable to load prompt {name}") from e
if not data_injections:
return content
content_parts = [content]
for data_injection in data_injections:
content_parts.append(
dedent(
f"""
###
{data_injection.description}
---
{data_injection.value}
"""
)
)
return "\n".join(content_parts)
async def _get_raw_response(
self, prompt: str, message: str, temperature=0.2, force_json_response=True
) -> ChatCompletion:
client = self.get_client()
return await client.chat.completions.create(
messages=[
{
"role": "system",
"content": prompt,
},
{
"role": "user",
"content": message,
},
],
model=self.model,
temperature=temperature,
response_format={"type": "json_object"} if force_json_response else NOT_GIVEN,
)
async def get_response(self, prompt: str, message: str, temperature=0.2, force_json_response=True) -> str | None:
"""Send data to OpenAI and return the response message content"""
try:
response = await self._get_raw_response(prompt, message, temperature, force_json_response)
if not response.choices:
return None
return response.choices[0].message.content
except Exception:
self.logger.exception("OpenAI Request Failed")
return None

View File

@@ -0,0 +1,24 @@
You are a bot that parses user input into recipe ingredients. You will receive a list of one or more ingredients, each containing one or more of the following components:
- Food: the actual physical ingredient used in the recipe. For instance, if you receive "3 cups of onions, chopped", the food is "onions"
- Unit: the unit of measurement for this ingredient. For instance, if you receive "2 lbs chicken breast", the unit is "lbs" (short for "pounds")
- Quantity: the numerical representation of how much of this ingredient. For instance, if you receive "3 1/2 grams of minced garlic", the quantity is "3 1/2". Quantity may be represented as a whole number (integer), a float or decimal, or a fraction. You should output quantity in only whole numbers or floats, converting fractions into floats. Floats longer than 10 decimal places should be rounded to 10 decimal places.
- Note: the rest of the text that represents more detail on how to prepare the ingredient. Anything that is not one of the above should be the note. For instance, if you receive "one can of butter beans, drained" the note would be "drained". If you receive "3 cloves of garlic peeled and finely chopped", the note would be "peeled and finely chopped"
- Input: The input is simply the ingredient string you are processing as-is. It is forbidden to modify this at all, you must provide the input exactly as you received it
While parsing the ingredients, there are some things to keep in mind:
- If you cannot accurately determine the quantity, unit, food, or note, you should place everything into the note field and leave everything else empty. It's better to err on the side of putting everything in the note field than being wrong
- You may receive recipe ingredients from multiple different languages. You should adhere to the grammar rules of the input language when trying to parse the ingredient string
- Sometimes foods or units will be in their singular, plural, or other grammatical forms. You must interpret all of them appropriately
- Sometimes ingredients will have text in parenthesis (like this). Parenthesis typically indicate something that should appear in the notes. For example: an input of "3 potatoes (roughly chopped)" would parse "roughly chopped" into the notes. Notice that when this occurs, the parenthesis are dropped, and you should use "roughly chopped" instead of "(roughly chopped)" in the note
- It's possible for the input to contain typos. For instance, you might see the word "potatos" instead of "potatoes". If it is a common misspelling, you may correct it
- Pay close attention to what can be considered a unit of measurement. There are common measurements such as tablespoon, teaspoon, and gram, abbreviations such as tsp, tbsp, and oz, and others such as sprig, can, bundle, bunch, unit, cube, package, and pinch
- Sometimes quantities can be given a range, such as "3-5" or "1 to 2" or "three or four". In this instance, choose the lower quantity; do not try to average or otherwise calculate the quantity. For instance, if the input it "2-3 lbs of chicken breast" the quantity should be "2"
- Any text that does not appear in the unit or food must appear in the notes. No text should be left off. The only exception for this is if a quantity is converted from text into a number. For instance, if you convert "2 dozen" into the number "24", you should not put the word "dozen" into any other field
It is imperative that you do not create any data or otherwise make up any information. Failure to adhere to this rule is illegal and will result in harsh punishment. If you are unsure, place the entire string into the note section of the response. Do not make things up.
In addition to calculating the recipe ingredient fields, you are also responsible for including a confidence value. This value is a float between 0 - 1, where 1 is full confidence that the result is correct, and 0 is no confidence that the result is correct. If you're unable to parse anything, and you put the entire string in the notes, you should return 0 confidence. If you can easily parse the string into each component, then you should return a confidence of 1. If you have to guess which part is the unit and which part is the food, your confidence should be lower, such as 0.6. Even if there is no unit or note, if you're able to determine the food, you may use a higher confidence. If the entire ingredient consists of only a food, you can use a confidence of 1.
Below you will receive the JSON schema for your response. Your response must be in valid JSON in the below schema as provided. You must respond in this JSON schema; failure to do so is illegal. It is imperative that you follow the schema precisely to avoid punishment. You must follow the JSON schema.
The user message that you receive will be the list of one or more recipe ingredients for you to parse. Your response should have exactly one item for each item provided. For instance, if you receive 12 items to parse, then your response should be an array of 12 parsed items.

View File

@@ -0,0 +1,161 @@
from abc import ABC, abstractmethod
from typing import TypeVar
from pydantic import UUID4, BaseModel
from rapidfuzz import fuzz, process
from sqlalchemy.orm import Session
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
from mealie.repos.all_repositories import get_repositories
from mealie.repos.repository_factory import AllRepositories
from mealie.schema.recipe.recipe_ingredient import (
CreateIngredientFood,
CreateIngredientUnit,
IngredientFood,
IngredientUnit,
ParsedIngredient,
)
from mealie.schema.response.pagination import PaginationQuery
T = TypeVar("T", bound=BaseModel)
class ABCIngredientParser(ABC):
"""
Abstract class for ingredient parsers.
"""
def __init__(self, group_id: UUID4, session: Session) -> None:
self.group_id = group_id
self.session = session
self._foods_by_alias: dict[str, IngredientFood] | None = None
self._units_by_alias: dict[str, IngredientUnit] | None = None
@property
def _repos(self) -> AllRepositories:
return get_repositories(self.session)
@property
def foods_by_alias(self) -> dict[str, IngredientFood]:
if self._foods_by_alias is None:
foods_repo = self._repos.ingredient_foods.by_group(self.group_id)
query = PaginationQuery(page=1, per_page=-1)
all_foods = foods_repo.page_all(query).items
foods_by_alias: dict[str, IngredientFood] = {}
for food in all_foods:
if food.name:
foods_by_alias[IngredientFoodModel.normalize(food.name)] = food
if food.plural_name:
foods_by_alias[IngredientFoodModel.normalize(food.plural_name)] = food
for alias in food.aliases or []:
if alias.name:
foods_by_alias[IngredientFoodModel.normalize(alias.name)] = food
self._foods_by_alias = foods_by_alias
return self._foods_by_alias
@property
def units_by_alias(self) -> dict[str, IngredientUnit]:
if self._units_by_alias is None:
units_repo = self._repos.ingredient_units.by_group(self.group_id)
query = PaginationQuery(page=1, per_page=-1)
all_units = units_repo.page_all(query).items
units_by_alias: dict[str, IngredientUnit] = {}
for unit in all_units:
if unit.name:
units_by_alias[IngredientUnitModel.normalize(unit.name)] = unit
if unit.plural_name:
units_by_alias[IngredientUnitModel.normalize(unit.plural_name)] = unit
if unit.abbreviation:
units_by_alias[IngredientUnitModel.normalize(unit.abbreviation)] = unit
if unit.plural_abbreviation:
units_by_alias[IngredientUnitModel.normalize(unit.plural_abbreviation)] = unit
for alias in unit.aliases or []:
if alias.name:
units_by_alias[IngredientUnitModel.normalize(alias.name)] = unit
self._units_by_alias = units_by_alias
return self._units_by_alias
@property
def food_fuzzy_match_threshold(self) -> int:
"""Minimum threshold to fuzzy match against a database food search"""
return 85
@property
def unit_fuzzy_match_threshold(self) -> int:
"""Minimum threshold to fuzzy match against a database unit search"""
return 70
@abstractmethod
async def parse_one(self, ingredient_string: str) -> ParsedIngredient: ...
@abstractmethod
async def parse(self, ingredients: list[str]) -> list[ParsedIngredient]: ...
@classmethod
def find_match(cls, match_value: str, *, store_map: dict[str, T], fuzzy_match_threshold: int = 0) -> T | None:
# check for literal matches
if match_value in store_map:
return store_map[match_value]
# fuzzy match against food store
fuzz_result = process.extractOne(
match_value, store_map.keys(), scorer=fuzz.ratio, score_cutoff=fuzzy_match_threshold
)
if fuzz_result is None:
return None
return store_map[fuzz_result[0]]
def find_food_match(self, food: IngredientFood | CreateIngredientFood | str) -> IngredientFood | None:
if isinstance(food, IngredientFood):
return food
food_name = food if isinstance(food, str) else food.name
match_value = IngredientFoodModel.normalize(food_name)
return self.find_match(
match_value,
store_map=self.foods_by_alias,
fuzzy_match_threshold=self.food_fuzzy_match_threshold,
)
def find_unit_match(self, unit: IngredientUnit | CreateIngredientUnit | str) -> IngredientUnit | None:
if isinstance(unit, IngredientUnit):
return unit
unit_name = unit if isinstance(unit, str) else unit.name
match_value = IngredientUnitModel.normalize(unit_name)
return self.find_match(
match_value,
store_map=self.units_by_alias,
fuzzy_match_threshold=self.unit_fuzzy_match_threshold,
)
def find_ingredient_match(self, ingredient: ParsedIngredient) -> ParsedIngredient:
if ingredient.ingredient.food and (food_match := self.find_food_match(ingredient.ingredient.food)):
ingredient.ingredient.food = food_match
if ingredient.ingredient.unit and (unit_match := self.find_unit_match(ingredient.ingredient.unit)):
ingredient.ingredient.unit = unit_match
# Parser might have wrongly split a food into a unit and food.
if isinstance(ingredient.ingredient.food, CreateIngredientFood) and isinstance(
ingredient.ingredient.unit, CreateIngredientUnit
):
if food_match := self.find_food_match(
f"{ingredient.ingredient.unit.name} {ingredient.ingredient.food.name}"
):
ingredient.ingredient.food = food_match
ingredient.ingredient.unit = None
return ingredient

View File

@@ -29,24 +29,24 @@ class CRFIngredient(BaseModel):
input: str = "" input: str = ""
name: str = "" name: str = ""
other: str = "" other: str = ""
qty: str = "" qty: Annotated[str, Field(validate_default=True)] = ""
comment: str = "" comment: str = ""
unit: str = "" unit: str = ""
confidence: CRFConfidence confidence: CRFConfidence
@field_validator("qty", mode="before") @field_validator("qty", mode="before")
def validate_qty(qty, info: ValidationInfo): # sourcery skip: merge-nested-ifs def validate_qty(cls, qty, info: ValidationInfo):
if qty is None or qty == "": if qty is not None and qty != "":
return qty
# Check if other contains a fraction # Check if other contains a fraction
try: try:
if info.data["other"] is not None and info.data["other"].find("/") != -1: if info.data["other"] is not None and info.data["other"].find("/") != -1:
return round(float(Fraction(info.data["other"])), 3) return str(round(float(Fraction(info.data["other"])), 3))
else: else:
return 1 return "0"
except Exception: except Exception:
pass return ""
return qty
def _exec_crf_test(input_text): def _exec_crf_test(input_text):

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