Merge branch 'mealie-next' into fix/translation-issues-when-scraping

This commit is contained in:
Michael Genson 2024-01-02 19:17:26 -06:00 committed by GitHub
commit e90f05d2dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 1047 additions and 2503 deletions

View File

@ -67,7 +67,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install libsasl2-dev libldap2-dev libssl-dev tesseract-ocr-all
sudo apt-get install libsasl2-dev libldap2-dev libssl-dev
poetry install
poetry add "psycopg2-binary==2.8.6"
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' || steps.cache-validate.outputs.cache-hit-success != 'true'

View File

@ -50,7 +50,6 @@ RUN apt-get update \
build-essential \
libpq-dev \
libwebp-dev \
tesseract-ocr-all \
# LDAP Dependencies
libsasl2-dev libldap2-dev libssl-dev \
gnupg gnupg2 gnupg1 \
@ -89,8 +88,8 @@ RUN apt-get update \
&& apt-get install --no-install-recommends -y \
gosu \
iproute2 \
tesseract-ocr-all \
libldap-common \
libldap-2.5 \
&& rm -rf /var/lib/apt/lists/*
# copying poetry and venv into image

View File

@ -64,9 +64,7 @@ No. Due to limitations from the Javascript Framework, mealie doesn't support ser
## Can I install Mealie without docker?
Yes, you can install Mealie on your local machine. HOWEVER, it is recommended that you don't. Managing non-system versions of python, node, and npm is a pain. Moreover, updating and upgrading your system with this configuration is unsupported and will likely require manual interventions. If you insist on installing Mealie on your local machine, you can use the links below to help guide your path.
- [Advanced Installation](../installation/advanced/)
Yes, you can install Mealie on your local machine. HOWEVER, it is recommended that you don't. Managing non-system versions of python, node, and npm is a pain. Moreover, updating and upgrading your system with this configuration is unsupported and will likely require manual interventions.
## What is fuzzy search and how do I use it?
Mealie can use fuzzy search, which is robust to minor typos. For example, searching for "brocolli" will still find your recipe for "broccoli soup". But fuzzy search is only functional on a Postgres database backend. To enable fuzzy search you will need to migrate to Postgres:

View File

@ -46,7 +46,7 @@ services:
image: postgres:15
restart: always
volumes:
- ./mealie-pgdata:/var/lib/postgresql/data
- mealie-pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: mealie
POSTGRES_USER: mealie

File diff suppressed because one or more lines are too long

View File

@ -102,7 +102,6 @@ const SAVE_EVENT = "save";
const DELETE_EVENT = "delete";
const CLOSE_EVENT = "close";
const JSON_EVENT = "json";
const OCR_EVENT = "ocr";
export default defineComponent({
components: { RecipeContextMenu, RecipeFavoriteBadge, RecipeTimerMenu, RecipeTimelineBadge },
@ -139,10 +138,6 @@ export default defineComponent({
type: Boolean,
default: false,
},
showOcrButton: {
type: Boolean,
default: false,
},
},
setup(props, context) {
const deleteDialog = ref(false);
@ -175,15 +170,6 @@ export default defineComponent({
},
];
if (props.showOcrButton) {
editorButtons.splice(2, 0, {
text: i18n.t("ocr-editor.ocr-editor"),
icon: $globals.icons.eye,
event: OCR_EVENT,
color: "accent",
});
}
function emitHandler(event: string) {
switch (event) {
case CLOSE_EVENT:

View File

@ -143,7 +143,7 @@ export default defineComponent({
}
const { share, isSupported: shareIsSupported } = useShare();
const { copy } = useClipboard();
const { copy, copied, isSupported } = useClipboard();
function getRecipeText() {
return i18n.t("recipe.share-recipe-message", [props.name]);
@ -154,8 +154,18 @@ export default defineComponent({
}
async function copyTokenLink(token: string) {
await copy(getTokenLink(token));
alert.success(i18n.t("recipe-share.recipe-link-copied-message") as string);
if (isSupported.value) {
await copy(getTokenLink(token));
if (copied.value) {
alert.success(i18n.t("recipe-share.recipe-link-copied-message") as string);
}
else {
alert.error(i18n.t("general.clipboard-copy-failure") as string);
}
}
else {
alert.error(i18n.t("general.clipboard-not-supported") as string);
}
}
async function shareRecipe(token: string) {

View File

@ -1,390 +0,0 @@
<template>
<v-container
v-if="recipe && recipe.slug && recipe.settings && recipe.recipeIngredient"
:class="{
'pa-0': $vuetify.breakpoint.smAndDown,
}"
>
<BannerExperimental />
<div v-if="loading">
<v-spacer />
<v-progress-circular indeterminate class="" color="primary"> </v-progress-circular>
{{ loadingText }}
<v-spacer />
</div>
<v-row v-if="!loading">
<v-col cols="12" sm="7" md="7" lg="7">
<RecipeOcrEditorPageCanvas
:image="canvasImage"
:tsv="tsv"
@setText="canvasSetText"
@update-recipe="updateRecipe"
@close-editor="closeEditor"
@text-selected="updateSelectedText"
>
</RecipeOcrEditorPageCanvas>
<RecipeOcrEditorPageHelp />
</v-col>
<v-col cols="12" sm="5" md="5" lg="5">
<v-tabs v-model="tab" fixed-tabs>
<v-tab key="header">
{{ $t("general.recipe") }}
</v-tab>
<v-tab key="ingredients">
{{ $t("recipe.ingredients") }}
</v-tab>
<v-tab key="instructions">
{{ $t("recipe.instructions") }}
</v-tab>
</v-tabs>
<v-tabs-items v-model="tab">
<v-tab-item key="header">
<v-text-field
v-model="recipe.name"
class="my-3"
:label="$t('recipe.recipe-name')"
:rules="[validators.required]"
@focus="selectedRecipeField = 'name'"
>
</v-text-field>
<div class="d-flex flex-wrap">
<v-text-field
v-model="recipe.totalTime"
class="mx-2"
:label="$t('recipe.total-time')"
@click="selectedRecipeField = 'totalTime'"
></v-text-field>
<v-text-field
v-model="recipe.prepTime"
class="mx-2"
:label="$t('recipe.prep-time')"
@click="selectedRecipeField = 'prepTime'"
></v-text-field>
<v-text-field
v-model="recipe.performTime"
class="mx-2"
:label="$t('recipe.perform-time')"
@click="selectedRecipeField = 'performTime'"
></v-text-field>
</div>
<v-textarea
v-model="recipe.description"
auto-grow
min-height="100"
:label="$t('recipe.description')"
@click="selectedRecipeField = 'description'"
>
</v-textarea>
<v-text-field
v-model="recipe.recipeYield"
dense
:label="$t('recipe.servings')"
@click="selectedRecipeField = 'recipeYield'"
>
</v-text-field>
</v-tab-item>
<v-tab-item key="ingredients">
<div class="d-flex justify-end mt-2">
<RecipeDialogBulkAdd class="ml-1 mr-1" :input-text-prop="canvasSelectedText" @bulk-data="addIngredient" />
<BaseButton @click="addIngredient"> {{ $t("general.new") }} </BaseButton>
</div>
<draggable
v-if="recipe.recipeIngredient.length > 0"
v-model="recipe.recipeIngredient"
handle=".handle"
v-bind="{
animation: 200,
group: 'description',
disabled: false,
ghostClass: 'ghost',
}"
@start="drag = true"
@end="drag = false"
>
<TransitionGroup type="transition" :name="!drag ? 'flip-list' : ''">
<RecipeIngredientEditor
v-for="(ingredient, index) in recipe.recipeIngredient"
:key="ingredient.referenceId"
v-model="recipe.recipeIngredient[index]"
class="list-group-item"
:disable-amount="recipe.settings.disableAmount"
@delete="recipe.recipeIngredient.splice(index, 1)"
@clickIngredientField="setSingleIngredient($event, index)"
/>
</TransitionGroup>
</draggable>
</v-tab-item>
<v-tab-item key="instructions">
<div class="d-flex justify-end mt-2">
<RecipeDialogBulkAdd class="ml-1 mr-1" :input-text-prop="canvasSelectedText" @bulk-data="addStep" />
<BaseButton @click="addStep()"> {{ $t("general.new") }}</BaseButton>
</div>
<RecipePageInstructions
v-model="recipe.recipeInstructions"
:ingredients="recipe.recipeIngredient"
:disable-amount="recipe.settings.disableAmount"
:edit="true"
:recipe="recipe"
:assets.sync="recipe.assets"
@click-instruction-field="setSingleStep"
/>
</v-tab-item>
</v-tabs-items>
</v-col>
</v-row>
</v-container>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted, reactive, toRefs, useContext, useRouter, computed, useRoute } from "@nuxtjs/composition-api";
import { until } from "@vueuse/core";
import { invoke } from "@vueuse/shared";
import draggable from "vuedraggable";
import RecipePageInstructions from "~/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageInstructions.vue";
import { useUserApi, useStaticRoutes } from "~/composables/api";
import { OcrTsvResponse as NullableOcrTsvResponse } from "~/lib/api/types/ocr";
import { validators } from "~/composables/use-validators";
import { Recipe, RecipeIngredient, RecipeStep } from "~/lib/api/types/recipe";
import { Paths, Leaves, SelectedRecipeLeaves } from "~/types/ocr-types";
import BannerExperimental from "~/components/global/BannerExperimental.vue";
import RecipeDialogBulkAdd from "~/components/Domain/Recipe/RecipeDialogBulkAdd.vue";
import RecipeIngredientEditor from "~/components/Domain/Recipe/RecipeIngredientEditor.vue";
import RecipeOcrEditorPageCanvas from "~/components/Domain/Recipe/RecipeOcrEditorPage/RecipeOcrEditorPageParts/RecipeOcrEditorPageCanvas.vue";
import RecipeOcrEditorPageHelp from "~/components/Domain/Recipe/RecipeOcrEditorPage/RecipeOcrEditorPageParts/RecipeOcrEditorPageHelp.vue";
import { uuid4 } from "~/composables/use-utils";
import { NoUndefinedField } from "~/lib/api/types/non-generated";
// Temporary Shim until we have a better solution
// https://github.com/phillipdupuis/pydantic-to-typescript/issues/28
type OcrTsvResponse = NoUndefinedField<NullableOcrTsvResponse>;
export default defineComponent({
components: {
RecipeIngredientEditor,
draggable,
BannerExperimental,
RecipeDialogBulkAdd,
RecipePageInstructions,
RecipeOcrEditorPageCanvas,
RecipeOcrEditorPageHelp,
},
props: {
recipe: {
type: Object as () => NoUndefinedField<Recipe>,
required: true,
},
},
setup(props) {
const { $auth } = useContext();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const router = useRouter();
const api = useUserApi();
const tsv = ref<OcrTsvResponse[]>([]);
const drag = ref(false);
const { i18n } = useContext();
const { recipeAssetPath } = useStaticRoutes();
function assetURL(assetName: string) {
return recipeAssetPath(props.recipe.id, assetName);
}
const state = reactive({
loading: true,
loadingText: i18n.tc("general.loading-recipe"),
tab: null,
selectedRecipeField: "" as SelectedRecipeLeaves | "",
canvasSelectedText: "",
canvasImage: new Image(),
});
const setPropertyValueByPath = function <T extends Recipe>(object: T, path: Paths<T>, value: any) {
const a = path.split(".");
let nextProperty: any = object;
for (let i = 0, n = a.length - 1; i < n; ++i) {
const k = a[i];
if (k in nextProperty) {
nextProperty = nextProperty[k];
} else {
return;
}
}
nextProperty[a[a.length - 1]] = value;
};
/**
* This function will find the title of a recipe with the assumption that the title
* has the biggest ratio of surface area / number of words on the image.
* @return Returns the text parts of the block with the highest score.
*/
function findRecipeTitle() {
const filtered = tsv.value.filter((element) => element.level === 2 || element.level === 5);
const blocks = [[]] as OcrTsvResponse[][];
let blockNum = 1;
filtered.forEach((element, index, array) => {
if (index !== 0 && array[index - 1].blockNum !== element.blockNum) {
blocks.push([]);
blockNum = element.blockNum;
}
blocks[blockNum - 1].push(element);
});
let bestScore = 0;
let bestBlock = blocks[0];
blocks.forEach((element) => {
// element[0] is the block declaration line containing the blocks total dimensions
// element.length is the number of words (+ 2) contained in that block
const elementScore = (element[0].height * element[0].width) / element.length; // Prettier is adding useless parenthesis for a mysterious reason
const elementText = element.map((element) => element.text).join(""); // Identify empty blocks and don't count them
if (elementScore > bestScore && elementText !== "") {
bestBlock = element;
bestScore = elementScore;
}
});
return bestBlock
.filter((element) => element.level === 5 && element.conf >= 40)
.map((element) => {
return element.text.trim();
})
.join(" ");
}
onMounted(() => {
invoke(async () => {
await until(props.recipe).not.toBeNull();
state.loadingText = i18n.tc("general.loading-ocr-data");
const assetName = props.recipe.assets[0].fileName;
const imagesrc = assetURL(assetName);
state.canvasImage.src = imagesrc;
const res = await api.ocr.assetToTsv(props.recipe.slug, assetName);
tsv.value = res.data as OcrTsvResponse[];
state.loading = false;
if (props.recipe.name.match(/New\sOCR\sRecipe(\s\([0-9]+\))?/g)) {
props.recipe.name = findRecipeTitle();
}
});
});
function addIngredient(ingredients: Array<string> | null = null) {
if (ingredients?.length) {
const newIngredients = ingredients.map((x) => {
return {
referenceId: uuid4(),
title: "",
note: x,
unit: undefined,
food: undefined,
disableAmount: true,
quantity: 1,
originalText: "",
};
});
if (newIngredients) {
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
props.recipe.recipeIngredient.push(...newIngredients);
}
} else {
props.recipe.recipeIngredient.push({
referenceId: uuid4(),
title: "",
note: "",
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
unit: undefined,
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
food: undefined,
disableAmount: true,
quantity: 1,
});
}
}
function addStep(steps: Array<string> | null = null) {
if (!props.recipe.recipeInstructions) {
return;
}
if (steps) {
const cleanedSteps = steps.map((step) => {
return { id: uuid4(), text: step, title: "", ingredientReferences: [] };
});
props.recipe.recipeInstructions.push(...cleanedSteps);
} else {
props.recipe.recipeInstructions.push({ id: uuid4(), text: "", title: "", ingredientReferences: [] });
}
}
// EVENT HANDLERS
// Canvas component event handlers
async function updateRecipe() {
const { data } = await api.recipes.updateOne(props.recipe.slug, props.recipe);
if (data?.slug) {
router.push(`/g/${groupSlug.value}/r/${data.slug}`);
}
}
function closeEditor() {
router.push(`/g/${groupSlug.value}/r/${props.recipe.slug}`);
}
const canvasSetText = function () {
if (state.selectedRecipeField !== "") {
setPropertyValueByPath<Recipe>(props.recipe, state.selectedRecipeField, state.canvasSelectedText);
}
};
function updateSelectedText(value: string) {
state.canvasSelectedText = value;
}
// Recipe field selection event handlers
function setSingleIngredient(f: keyof RecipeIngredient, index: number) {
state.selectedRecipeField = `recipeIngredient.${index}.${f}` as SelectedRecipeLeaves;
}
// Leaves<RecipeStep[]> will return some function types making eslint very unhappy
type RecipeStepsLeaves = `${number}.${Leaves<RecipeStep>}`;
function setSingleStep(path: RecipeStepsLeaves) {
state.selectedRecipeField = `recipeInstructions.${path}` as SelectedRecipeLeaves;
}
return {
...toRefs(state),
addIngredient,
addStep,
drag,
assetURL,
updateRecipe,
closeEditor,
updateSelectedText,
tsv,
validators,
setSingleIngredient,
setSingleStep,
canvasSetText,
};
},
});
</script>
<style lang="css">
.ghost {
opacity: 0.5;
}
</style>

View File

@ -1,488 +0,0 @@
<template>
<v-card flat tile>
<v-toolbar v-for="(section, idx) in toolbarIcons" :key="section.sectionTitle" dense style="float: left">
<v-toolbar-title bottom>
{{ section.sectionTitle }}
</v-toolbar-title>
<v-tooltip v-for="icon in section.icons" :key="icon.name" bottom>
<template #activator="{ on, attrs }">
<v-btn icon @click="section.eventHandler(icon.name)">
<v-icon :color="section.highlight === icon.name ? 'primary' : 'default'" v-bind="attrs" v-on="on">
{{ icon.icon }}
</v-icon>
</v-btn>
</template>
<span>{{ icon.tooltip }}</span>
</v-tooltip>
<v-divider v-if="idx != toolbarIcons.length - 1" vertical class="mx-2" />
</v-toolbar>
<v-toolbar dense style="float: right">
<BaseButton class="ml-1 mr-1" save @click="updateRecipe()">
{{ $t("general.save") }}
</BaseButton>
<BaseButton cancel @click="closeEditor()">
{{ $t("general.close") }}
</BaseButton>
</v-toolbar>
<canvas
ref="canvas"
@mousedown="handleMouseDown"
@mouseup="handleMouseUp"
@mousemove="handleMouseMove"
@wheel="handleMouseScroll"
>
</canvas>
<span style="white-space: pre-wrap">
{{ selectedText.trim() }}
</span>
</v-card>
</template>
<script lang="ts">
import { defineComponent, reactive, useContext, ref, toRefs, watch, onMounted } from "@nuxtjs/composition-api";
import { NoUndefinedField } from "~/lib/api/types/non-generated";
import { OcrTsvResponse as NullableOcrTsvResponse } from "~/lib/api/types/ocr";
import { CanvasModes, SelectedTextSplitModes, ImagePosition, Mouse, CanvasRect, ToolbarIcons } from "~/types/ocr-types";
// Temporary Shim until we have a better solution
// https://github.com/phillipdupuis/pydantic-to-typescript/issues/28
type OcrTsvResponse = NoUndefinedField<NullableOcrTsvResponse>;
export default defineComponent({
props: {
image: {
type: HTMLImageElement,
required: true,
},
tsv: {
type: Array as () => OcrTsvResponse[],
required: true,
},
},
setup(props, context) {
const state = reactive({
canvas: null as HTMLCanvasElement | null,
ctx: null as CanvasRenderingContext2D | null,
canvasRect: null as DOMRect | null,
rect: {
startX: 0,
startY: 0,
w: 0,
h: 0,
},
mouse: {
current: {
x: 0,
y: 0,
},
down: false,
},
selectedText: "",
canvasMode: "selection" as CanvasModes,
imagePosition: {
sx: 0,
sy: 0,
sWidth: 0,
sHeight: 0,
dx: 0,
dy: 0,
dWidth: 0,
dHeight: 0,
scale: 1,
panStartPoint: {
x: 0,
y: 0,
},
} as ImagePosition,
isImageSmallerThanCanvas: false,
selectedTextSplitMode: "lineNum" as SelectedTextSplitModes,
});
watch(
() => state.selectedText,
(value) => {
context.emit("text-selected", value);
}
);
onMounted(() => {
if (state.canvas === null) return; // never happens because the ref "canvas" is in the template
state.ctx = state.canvas.getContext("2d") as CanvasRenderingContext2D;
state.ctx.imageSmoothingEnabled = false;
state.canvasRect = state.canvas.getBoundingClientRect();
state.canvas.width = state.canvasRect.width;
if (props.image.width < state.canvas.width) {
state.isImageSmallerThanCanvas = true;
}
state.imagePosition.dWidth = state.canvas.width;
updateImageScale();
state.canvas.height = Math.min(props.image.height * state.imagePosition.scale, 700); // Max height of 700px
state.imagePosition.sWidth = props.image.width;
state.imagePosition.sHeight = props.image.height;
state.imagePosition.dWidth = state.canvas.width;
drawImage(state.ctx);
drawWordBoxesOnCanvas(props.tsv);
});
function handleMouseDown(event: MouseEvent) {
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
state.mouse.down = true;
updateMousePos(event);
if (state.canvasMode === "selection") {
if (isMouseInRect(state.mouse, state.rect)) {
context.emit("setText", state.selectedText);
} else {
state.ctx.fillStyle = "rgb(255, 255, 255)";
state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height);
drawImage(state.ctx);
state.rect.startX = state.mouse.current.x;
state.rect.startY = state.mouse.current.y;
resetSelection();
}
return;
}
if (state.canvasMode === "panAndZoom") {
state.imagePosition.panStartPoint.x = state.mouse.current.x - state.imagePosition.dx;
state.imagePosition.panStartPoint.y = state.mouse.current.y - state.imagePosition.dy;
resetSelection();
}
}
function handleMouseUp(_event: MouseEvent) {
if (state.canvasRect === null) return;
state.mouse.down = false;
state.selectedText = getWordsInSelection(props.tsv, state.rect);
}
function handleMouseMove(event: MouseEvent) {
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
updateMousePos(event);
if (state.mouse.down) {
if (state.canvasMode === "selection") {
state.rect.w = state.mouse.current.x - state.rect.startX;
state.rect.h = state.mouse.current.y - state.rect.startY;
draw();
return;
}
if (state.canvasMode === "panAndZoom") {
state.canvas.style.cursor = "move";
state.imagePosition.dx = state.mouse.current.x - state.imagePosition.panStartPoint.x;
state.imagePosition.dy = state.mouse.current.y - state.imagePosition.panStartPoint.y;
keepImageInCanvas();
state.ctx.fillStyle = "rgb(255, 255, 255)";
state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height);
drawImage(state.ctx);
return;
}
}
if (isMouseInRect(state.mouse, state.rect) && state.canvasMode === "selection") {
state.canvas.style.cursor = "pointer";
} else {
state.canvas.style.cursor = "default";
}
}
const scrollSensitivity = 0.05;
function handleMouseScroll(event: WheelEvent) {
if (state.isImageSmallerThanCanvas) return;
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
if (state.canvasMode === "panAndZoom") {
event.preventDefault();
updateMousePos(event);
const m = Math.sign(event.deltaY);
const ndx = state.imagePosition.dx + m * state.imagePosition.dWidth * scrollSensitivity;
const ndy = state.imagePosition.dy + m * state.imagePosition.dHeight * scrollSensitivity;
const ndw = state.imagePosition.dWidth + -m * state.imagePosition.dWidth * scrollSensitivity * 2;
const ndh = state.imagePosition.dHeight + -m * state.imagePosition.dHeight * scrollSensitivity * 2;
if (ndw < props.image.width) {
state.imagePosition.dx = ndx;
state.imagePosition.dy = ndy;
state.imagePosition.dWidth = ndw;
state.imagePosition.dHeight = ndh;
}
keepImageInCanvas();
updateImageScale();
state.ctx.fillStyle = "rgb(255, 255, 255)";
state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height);
drawImage(state.ctx);
}
}
function draw() {
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
if (state.mouse.down) {
state.ctx.imageSmoothingEnabled = false;
state.ctx.fillStyle = "rgb(255, 255, 255)";
state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height);
drawImage(state.ctx);
state.ctx.fillStyle = "rgba(255, 255, 255, 0.1)";
state.ctx.setLineDash([6]);
state.ctx.fillRect(state.rect.startX, state.rect.startY, state.rect.w, state.rect.h);
state.ctx.strokeRect(state.rect.startX, state.rect.startY, state.rect.w, state.rect.h);
}
}
function drawImage(ctx: CanvasRenderingContext2D) {
ctx.drawImage(
props.image,
state.imagePosition.sx,
state.imagePosition.sy,
state.imagePosition.sWidth,
state.imagePosition.sHeight,
state.imagePosition.dx,
state.imagePosition.dy,
state.imagePosition.dWidth,
state.imagePosition.dHeight
);
}
function keepImageInCanvas() {
if (state.canvasRect === null || state.canvas === null) return;
// Prevent image from being smaller than the canvas width
if (state.imagePosition.dWidth - state.canvas.width < 0) {
state.imagePosition.dWidth = state.canvas.width;
}
// Prevent image from being smaller than the canvas height
if (state.imagePosition.dHeight - state.canvas.height < 0) {
state.imagePosition.dHeight = props.image.height * state.imagePosition.scale;
}
// Prevent to move the image too much to the left
if (state.canvas.width - state.imagePosition.dx - state.imagePosition.dWidth > 0) {
state.imagePosition.dx = state.canvas.width - state.imagePosition.dWidth;
}
// Prevent to move the image too much to the top
if (state.canvas.height - state.imagePosition.dy - state.imagePosition.dHeight > 0) {
state.imagePosition.dy = state.canvas.height - state.imagePosition.dHeight;
}
// Prevent to move the image too much to the right
if (state.imagePosition.dx > 0) {
state.imagePosition.dx = 0;
}
// Prevent to move the image too much to the bottom
if (state.imagePosition.dy > 0) {
state.imagePosition.dy = 0;
}
}
function updateImageScale() {
state.imagePosition.scale = state.imagePosition.dWidth / props.image.width;
// force the original ratio to be respected
state.imagePosition.dHeight = props.image.height * state.imagePosition.scale;
// Don't let images bigger than the canvas be zoomed in more than 1:1 scale
// Meaning only let images smaller than the canvas to have a scale > 1
if (!state.isImageSmallerThanCanvas && state.imagePosition.scale > 1) {
state.imagePosition.scale = 1;
}
}
function resetSelection() {
if (state.canvasRect === null) return;
state.rect.w = 0;
state.rect.h = 0;
state.selectedText = "";
}
function updateMousePos<T extends MouseEvent>(event: T) {
if (state.canvas === null) return;
state.canvasRect = state.canvas.getBoundingClientRect();
state.mouse.current = {
x: event.clientX - state.canvasRect.left,
y: event.clientY - state.canvasRect.top,
};
}
function isMouseInRect(mouse: Mouse, rect: CanvasRect) {
if (state.canvasRect === null) return;
const correctRect = correctRectCoordinates(rect);
return (
mouse.current.x > correctRect.startX &&
mouse.current.x < correctRect.startX + correctRect.w &&
mouse.current.y > correctRect.startY &&
mouse.current.y < correctRect.startY + correctRect.h
);
}
/**
* Returns rectangle coordinates with positive dimensions
* @param rect A rectangle
* @returns An equivalent rectangle with width and height > 0
*/
function correctRectCoordinates(rect: CanvasRect) {
if (rect.w < 0) {
rect.startX = rect.startX + rect.w;
rect.w = -rect.w;
}
if (rect.h < 0) {
rect.startY = rect.startY + rect.h;
rect.h = -rect.h;
}
return rect;
}
function drawWordBoxesOnCanvas(tsv: OcrTsvResponse[]) {
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
state.ctx.fillStyle = "rgb(255, 255, 255, 0.3)";
tsv
.filter((element) => element.level === 5)
.forEach((element) => {
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
state.ctx.fillRect(
element.left * state.imagePosition.scale,
element.top * state.imagePosition.scale,
element.width * state.imagePosition.scale,
element.height * state.imagePosition.scale
);
});
}
// Event emitters
const updateRecipe = function () {
context.emit("update-recipe");
};
const closeEditor = function () {
context.emit("close-editor");
};
// TOOLBAR STUFF
const { $globals, i18n } = useContext();
const toolbarIcons = ref<ToolbarIcons<CanvasModes | SelectedTextSplitModes>>([
{
sectionTitle: i18n.tc("ocr-editor.toolbar"),
eventHandler: switchCanvasMode,
highlight: state.canvasMode,
icons: [
{
name: "selection",
icon: $globals.icons.selectMode,
tooltip: i18n.tc("ocr-editor.selection-mode"),
},
{
name: "panAndZoom",
icon: $globals.icons.panAndZoom,
tooltip: i18n.tc("ocr-editor.pan-and-zoom-picture"),
},
],
},
{
sectionTitle: i18n.tc("ocr-editor.split-text"),
eventHandler: switchSplitTextMode,
highlight: state.selectedTextSplitMode,
icons: [
{
name: "lineNum",
icon: $globals.icons.preserveLines,
tooltip: i18n.tc("ocr-editor.preserve-line-breaks"),
},
{
name: "blockNum",
icon: $globals.icons.preserveBlocks,
tooltip: i18n.tc("ocr-editor.split-by-block"),
},
{
name: "flatten",
icon: $globals.icons.flatten,
tooltip: i18n.tc("ocr-editor.flatten"),
},
],
},
]);
function switchCanvasMode(mode: CanvasModes) {
if (state.canvasRect === null || state.canvas === null) return;
state.canvasMode = mode;
toolbarIcons.value[0].highlight = mode;
if (mode === "panAndZoom") {
state.canvas.style.cursor = "pointer";
} else {
state.canvas.style.cursor = "default";
}
}
function switchSplitTextMode(mode: SelectedTextSplitModes) {
if (state.canvasRect === null) return;
state.selectedTextSplitMode = mode;
toolbarIcons.value[1].highlight = mode;
state.selectedText = getWordsInSelection(props.tsv, state.rect);
}
/**
* Using rectangle coordinates, filters the tsv to get text elements contained
* inside the rectangle
* Additionaly adds newlines depending on the current "text split" mode
* @param tsv An Object containing tesseracts tsv fields
* @param rect Coordinates of a rectangle
* @returns Text from tsv contained in the rectangle
*/
function getWordsInSelection(tsv: OcrTsvResponse[], rect: CanvasRect) {
const correctedRect = correctRectCoordinates(rect);
return tsv
.filter(
(element) =>
element.level === 5 &&
correctedRect.startY - state.imagePosition.dy < element.top * state.imagePosition.scale &&
correctedRect.startX - state.imagePosition.dx < element.left * state.imagePosition.scale &&
correctedRect.startX + correctedRect.w >
(element.left + element.width) * state.imagePosition.scale + state.imagePosition.dx &&
correctedRect.startY + correctedRect.h >
(element.top + element.height) * state.imagePosition.scale + state.imagePosition.dy
)
.map((element, index, array) => {
let separator = " ";
if (
state.selectedTextSplitMode !== "flatten" &&
index !== array.length - 1 &&
element[state.selectedTextSplitMode] !== array[index + 1][state.selectedTextSplitMode]
) {
separator = "\n";
}
return element.text + separator;
})
.join("");
}
return {
...toRefs(state),
handleMouseDown,
handleMouseUp,
handleMouseMove,
handleMouseScroll,
toolbarIcons,
updateRecipe,
closeEditor,
};
},
});
</script>

View File

@ -1,54 +0,0 @@
<template>
<v-card>
<v-app-bar dense dark color="primary" class="mb-2">
<v-icon large left>
{{ $globals.icons.help }}
</v-icon>
<v-toolbar-title class="headline"> {{ $t("ocr-editor.help.help") }} </v-toolbar-title>
<v-spacer></v-spacer>
</v-app-bar>
<v-card-text>
<h1> {{ $t("ocr-editor.help.mouse-modes") }}</h1>
<v-divider class="mb-2 mt-1" />
<h2 class="my-2">
<v-icon> {{ $globals.icons.selectMode }} </v-icon>{{ $t("ocr-editor.help.selection-mode") }}
</h2>
<p class="my-1">{{ $t("ocr-editor.help.selection-mode") }}</p>
<ol>
<li>{{ $t("ocr-editor.help.selection-mode-steps.draw") }}</li>
<li>{{ $t("ocr-editor.help.selection-mode-steps.click") }}</li>
<li>{{ $t("ocr-editor.help.selection-mode-steps.result") }}</li>
</ol>
<h2 class="my-2">
<v-icon> {{ $globals.icons.panAndZoom }} </v-icon>{{ $t("ocr-editor.help.pan-and-zoom-mode") }}
</h2>
{{ $t("ocr-editor.help.pan-and-zoom-desc") }}
<h1 class="mt-5">{{ $t("ocr-editor.help.split-text-mode") }}</h1>
<v-divider class="mb-2 mt-1" />
<h2 class="my-2">
<v-icon> {{ $globals.icons.preserveLines }} </v-icon>
{{ $t("ocr-editor.help.split-modes.line-mode") }}
</h2>
<p>
{{ $t("ocr-editor.help.split-modes.line-mode-desc") }}
</p>
<h2 class="my-2">
<v-icon> {{ $globals.icons.preserveBlocks }} </v-icon>
{{ $t("ocr-editor.help.split-modes.block-mode") }}
</h2>
<p>
{{ $t("ocr-editor.help.split-modes.block-mode-desc") }}
</p>
<h2 class="my-2">
<v-icon> {{ $globals.icons.flatten }} </v-icon> {{ $t("ocr-editor.help.split-modes.flat-mode") }}
</h2>
<p>{{ $t("ocr-editor.help.split-modes.flat-mode-desc") }}</p>
</v-card-text>
</v-card>
</template>
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api";
export default defineComponent({});
</script>

View File

@ -1,3 +0,0 @@
import RecipeOcrEditorPage from "./RecipeOcrEditorPage.vue";
export default RecipeOcrEditorPage;

View File

@ -50,7 +50,6 @@
:logged-in="isOwnGroup"
:open="isEditMode"
:recipe-id="recipe.id"
:show-ocr-button="recipe.isOcrRecipe"
class="ml-auto mt-n8 pb-4"
@close="setMode(PageMode.VIEW)"
@json="toggleEditMode()"
@ -58,13 +57,12 @@
@save="$emit('save')"
@delete="$emit('delete')"
@print="printRecipe"
@ocr="goToOcrEditor"
/>
</div>
</template>
<script lang="ts">
import { defineComponent, useContext, computed, ref, watch, useRouter, useRoute } from "@nuxtjs/composition-api";
import { defineComponent, useContext, computed, ref, watch } from "@nuxtjs/composition-api";
import { useLoggedInState } from "~/composables/use-logged-in-state";
import RecipeRating from "~/components/Domain/Recipe/RecipeRating.vue";
import RecipeLastMade from "~/components/Domain/Recipe/RecipeLastMade.vue";
@ -96,16 +94,12 @@ export default defineComponent({
},
},
setup(props) {
const { $auth, $vuetify } = useContext();
const { $vuetify } = useContext();
const { recipeImage } = useStaticRoutes();
const { imageKey, pageMode, editMode, setMode, toggleEditMode, isEditMode } = usePageState(props.recipe.slug);
const { user } = usePageUser();
const { isOwnGroup } = useLoggedInState();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const router = useRouter();
function printRecipe() {
window.print();
}
@ -119,10 +113,6 @@ export default defineComponent({
return recipeImage(props.recipe.id, props.recipe.image, imageKey.value);
});
function goToOcrEditor() {
router.push(`/g/${groupSlug.value}/r/${props.recipe.slug}/ocr-editor`);
}
watch(
() => recipeImageUrl.value,
() => {
@ -146,7 +136,6 @@ export default defineComponent({
hideImage,
isEditMode,
recipeImageUrl,
goToOcrEditor,
};
},
});

View File

@ -2,7 +2,7 @@
<v-tooltip
ref="copyToolTip"
v-model="show"
color="success lighten-1"
:color="copied? 'success lighten-1' : 'red lighten-1'"
top
:open-on-hover="false"
:open-on-click="true"
@ -29,12 +29,14 @@
<v-icon left dark>
{{ $globals.icons.clipboardCheck }}
</v-icon>
<slot> {{ $t("general.copied_message") }} </slot>
<slot v-if="!isSupported"> {{ $t("general.your-browser-does-not-support-clipboard") }} </slot>
<slot v-else> {{ copied ? $t("general.copied_message") : $t("general.clipboard-copy-failure") }} </slot>
</span>
</v-tooltip>
</template>
<script lang="ts">
import { useClipboard } from "@vueuse/core"
import { defineComponent, ref } from "@nuxtjs/composition-api";
import { VTooltip } from "~/types/vuetify";
@ -58,6 +60,7 @@ export default defineComponent({
},
},
setup(props) {
const { copy, copied, isSupported } = useClipboard()
const show = ref(false);
const copyToolTip = ref<VTooltip | null>(null);
@ -65,13 +68,21 @@ export default defineComponent({
copyToolTip.value?.deactivate();
}
function textToClipboard() {
async function textToClipboard() {
if (isSupported.value) {
await copy(props.copyText);
if (copied.value) {
console.log(`Copied\n${props.copyText}`)
}
else {
console.warn("Copy failed: ", copied.value);
}
}
else {
console.warn("Clipboard is currently not supported by your browser. Ensure you're on a secure (https) site.");
}
show.value = true;
const copyText = props.copyText;
navigator.clipboard.writeText(copyText).then(
() => console.log(`Copied\n${copyText}`),
() => console.log(`Copied Failed\n${copyText}`)
);
setTimeout(() => {
toggleBlur();
}, 500);
@ -81,6 +92,8 @@ export default defineComponent({
show,
copyToolTip,
textToClipboard,
copied,
isSupported,
};
},
});

View File

@ -7,23 +7,30 @@ export function useCopy() {
const { i18n } = useContext();
function copyText(text: string) {
if (!isSupported) {
if (!isSupported.value) {
alert.error(i18n.tc("general.clipboard-not-supported"));
return;
}
copy(text);
alert.success(i18n.tc("general.copied-to-clipboard"));
copy(text).then(() => {
// Verify copy success as no error is thrown on failure.
if (copied.value) {
alert.success(i18n.tc("general.copied-to-clipboard"));
}
else {
alert.error(i18n.tc("general.clipboard-copy-failure"));
}
});
}
return { copyText, copied };
}
export function useCopyList() {
const { copy, isSupported } = useClipboard();
const { copy, isSupported, copied } = useClipboard();
const { i18n } = useContext();
function checkClipboard() {
if (!isSupported) {
if (!isSupported.value) {
alert.error(i18n.tc("general.your-browser-does-not-support-clipboard"));
return false;
}
@ -54,7 +61,13 @@ export function useCopyList() {
function copyText(text: string, len: number) {
copy(text).then(() => {
alert.success(i18n.tc("general.copied-items-to-clipboard", len));
// Verify copy success as no error is thrown on failure.
if (copied.value) {
alert.success(i18n.tc("general.copied-items-to-clipboard", len));
}
else {
alert.error(i18n.tc("general.clipboard-copy-failure"));
}
});
}

View File

@ -198,7 +198,8 @@
"refresh": "Verfris",
"upload-file": "Laai dokument op",
"created-on-date": "Geskep op: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Is jy seker jy wil <b>{groupName}<b/> uitvee?",

View File

@ -198,7 +198,8 @@
"refresh": "تحديث",
"upload-file": "تحميل الملف",
"created-on-date": "تم الإنشاء في {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "هل انت متأكد من رغبتك في حذف <b>{groupName}<b/>؟",

View File

@ -198,7 +198,8 @@
"refresh": "Опресни",
"upload-file": "Качване на файл",
"created-on-date": "Създадено на {0}",
"unsaved-changes": "Имате незапазени промени. Желаете ли да ги запазите преди да излезете? Натиснете Ок за запазване и Отказ за отхвърляне на промените."
"unsaved-changes": "Имате незапазени промени. Желаете ли да ги запазите преди да излезете? Натиснете Ок за запазване и Отказ за отхвърляне на промените.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Сигурни ли сте, че искате да изтриете <b>{groupName}<b/>?",
@ -510,7 +511,7 @@
"how-did-it-turn-out": "Как се получи?",
"user-made-this": "{user} направи това",
"last-made-date": "Последно приготвена на {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"api-extras-description": "Екстрите за рецепти са ключова характеристика на Mealie API. Те Ви позволяват да създавате персонализирани JSON двойки ключ/стойност в рамките на рецепта, за да ги препращате към други приложения. Можете да използвате тези ключове, за да предоставите информация за задействане на автоматизация или персонализирани съобщения, за препращане към желаното от Вас устройство.",
"message-key": "Ключ на съобщението",
"parse": "Анализирай",
"attach-images-hint": "Прикачете снимки като ги влачете и пуснете в редактора",

View File

@ -198,7 +198,8 @@
"refresh": "Actualitza",
"upload-file": "Puja un fitxer",
"created-on-date": "Creat el: {0}",
"unsaved-changes": "Tens canvis que no estan guardats. Vols guardar-los abans de sortir? Clica d'acord per guardar-los o cancel·lar per descartar els canvis."
"unsaved-changes": "Tens canvis que no estan guardats. Vols guardar-los abans de sortir? Clica d'acord per guardar-los o cancel·lar per descartar els canvis.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Esteu segur de voler suprimir el grup <b>{groupName}<b/>?",
@ -510,7 +511,7 @@
"how-did-it-turn-out": "Com ha sortit?",
"user-made-this": "{user} ha fet això",
"last-made-date": "Última vegada feta {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Message Key",
"parse": "Analitzar",
"attach-images-hint": "Afegeix imatges arrossegant i deixant anar la imatge a l'editor",

View File

@ -198,7 +198,8 @@
"refresh": "Obnovit",
"upload-file": "Nahrát soubor",
"created-on-date": "Vytvořeno dne: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Jste si jisti, že chcete smazat <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Opdater",
"upload-file": "Upload Fil",
"created-on-date": "Oprettet den: {0}",
"unsaved-changes": "Du har ændringer som ikke er gemt. Vil du gemme før du forlader? Vælg \"Okay\" for at gemme, eller \"Annullér\" for at kassere ændringer."
"unsaved-changes": "Du har ændringer som ikke er gemt. Vil du gemme før du forlader? Vælg \"Okay\" for at gemme, eller \"Annullér\" for at kassere ændringer.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Er du sikker på, du vil slette <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Aktualisieren",
"upload-file": "Datei hochladen",
"created-on-date": "Erstellt am: {0}",
"unsaved-changes": "Du hast ungespeicherte Änderungen. Möchtest du vor dem Verlassen speichern? OK um zu speichern, Cancel um Änderungen zu verwerfen."
"unsaved-changes": "Du hast ungespeicherte Änderungen. Möchtest du vor dem Verlassen speichern? OK um zu speichern, Cancel um Änderungen zu verwerfen.",
"clipboard-copy-failure": "Fehler beim Kopieren in die Zwischenablage."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Bist du dir sicher, dass du die Gruppe <b>{groupName}<b/> löschen möchtest?",

View File

@ -198,7 +198,8 @@
"refresh": "Ανανέωση",
"upload-file": "Μεταφόρτωση αρχείου",
"created-on-date": "Δημιουργήθηκε στις: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό τον ασφαλή σύνδεσμο <b>{groupName}<b/>;",

View File

@ -198,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Actualizar",
"upload-file": "Subir Archivo",
"created-on-date": "Creado el {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Por favor, confirma que deseas eliminar <b>{groupName}<b/>",

View File

@ -198,7 +198,8 @@
"refresh": "Päivitä",
"upload-file": "Tuo tiedosto",
"created-on-date": "Luotu {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Haluatko varmasti poistaa ryhmän <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Actualiser",
"upload-file": "Transférer un fichier",
"created-on-date": "Créé le {0}",
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer avant de partir? OK pour enregistrer, Annuler pour ignorer les modifications."
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer avant de partir? OK pour enregistrer, Annuler pour ignorer les modifications.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Êtes-vous certain de vouloir supprimer <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Actualiser",
"upload-file": "Transférer un fichier",
"created-on-date": "Créé le {0}",
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer avant de partir? OK pour enregistrer, Annuler pour ignorer les modifications."
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer avant de partir? OK pour enregistrer, Annuler pour ignorer les modifications.",
"clipboard-copy-failure": "Échec de la copie dans le presse-papiers."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Voulez-vous vraiment supprimer <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",

View File

@ -77,7 +77,7 @@
"tag-events": "אירועי תגיות",
"category-events": "אירועי קטגוריות",
"when-a-new-user-joins-your-group": "כאשר משתמש חדש מצטרף לקבוצה",
"recipe-events": "Recipe Events"
"recipe-events": "אירועי מתכון"
},
"general": {
"cancel": "ביטול",
@ -114,10 +114,10 @@
"json": "JSON",
"keyword": "מילת מפתח",
"link-copied": "קישור הועתק",
"loading": "Loading",
"loading": "טעינה",
"loading-events": "טוען",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipe": "טוען מתכון...",
"loading-ocr-data": "טוען נתוני OCR...",
"loading-recipes": "מתכונים בטעינה",
"message": "הודעה",
"monday": "שני",
@ -128,7 +128,7 @@
"no-recipe-found": "לא נמצא מתכון",
"ok": "אישור",
"options": "אפשרויות:",
"plural-name": "Plural Name",
"plural-name": "שם ברבים",
"print": "הדפסה",
"print-preferences": "העדפות הדפסה",
"random": "אקראי",
@ -198,7 +198,8 @@
"refresh": "רענן",
"upload-file": "העלאת קבצים",
"created-on-date": "נוצר ב-{0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "יש שינויים שלא נשמרו. לצאת לפני שמירה? אשר לשמירה, בטל למחיקת שינויים.",
"clipboard-copy-failure": "כשלון בהעתקה ללוח ההדבקה."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "האם את/ה בטוח/ה שברצונך למחוק את <b>{groupName}<b/>?",
@ -213,7 +214,7 @@
"group-id-with-value": "מזהה קבוצה: {groupID}",
"group-name": "שם קבוצה",
"group-not-found": "קבוצה לא נמצאה",
"group-token": "Group Token",
"group-token": "טוקן קבוצתי",
"group-with-value": "קבוצה: {groupID}",
"groups": "קבוצות",
"manage-groups": "ניהול קבוצות",
@ -249,7 +250,7 @@
"general-preferences": "העדפות כלליות",
"group-recipe-preferences": "העדפות קבוצה",
"report": "דיווח",
"report-with-id": "Report ID: {id}",
"report-with-id": "מספר דוח: {id}",
"group-management": "ניהול קבוצה",
"admin-group-management": "ניהול קבוצת מנהל",
"admin-group-management-text": "השינויים לקבוצה זו יתבצעו מיידית.",
@ -470,9 +471,9 @@
"add-to-plan": "הוספה לתכנית",
"add-to-timeline": "הוסף לציר הזמן",
"recipe-added-to-list": "מתכון נוסף לרשימה",
"recipes-added-to-list": "Recipes added to list",
"recipes-added-to-list": "מתכונים הוספו לרשימה",
"recipe-added-to-mealplan": "מתכון נוסף לתכנון ארוחות",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipes-to-list": "כשלון בהוספת מתכון לרשימה",
"failed-to-add-recipe-to-mealplan": "הוספת מתכון לתכנון ארוחות נכשלה",
"yield": "תשואה",
"quantity": "כמות",
@ -514,7 +515,7 @@
"message-key": "מפתח הודעה",
"parse": "ניתוח",
"attach-images-hint": "הוסף תמונות ע\"י גרירה ושחרור אל תוך העורך",
"drop-image": "Drop image",
"drop-image": "גרור תמונה",
"enable-ingredient-amounts-to-use-this-feature": "אפשר לכמות המרכיבים להשתמש בפונקציה",
"recipes-with-units-or-foods-defined-cannot-be-parsed": "לא ניתן לפענך מתכונים בהם יחידות או אוכל מוגדרים.",
"parse-ingredients": "חילוץ רכיבים",
@ -573,16 +574,16 @@
"search-hint": "לחץ '/'",
"advanced": "מתקדם",
"auto-search": "חיפוש אוטומטי",
"no-results": "No results found"
"no-results": "לא נמצאו תוצאות"
},
"settings": {
"add-a-new-theme": "הוסף ערכת נושא חדשה",
"admin-settings": "הגדרות מנהל",
"backup": {
"backup-created": "Backup created successfully",
"backup-created": "הגיבוי בוצע בהצלחה",
"backup-created-at-response-export_path": "גיבוי נוצר ב {path}",
"backup-deleted": "גיבוי נמחק",
"restore-success": "Restore successful",
"restore-success": "השחזור הצליח",
"backup-tag": "תגית גיבוי",
"create-heading": "Create a Backup",
"delete-backup": "מחיקת גיבוי",
@ -691,13 +692,13 @@
"configuration": "הגדרות",
"docker-volume": "Docker Volume",
"docker-volume-help": "מילי דורשת שקונטיינר הקצה הקדמי והקצה האחורי חולקים את באותו כונן / שטח אחסון בדוקר.\nכל זאת כל מנת לוודא שלקונטיינר תהיה גישה לתמונות הגיבוי או לנכסים בדיסק.",
"volumes-are-misconfigured": "Volumes are misconfigured.",
"volumes-are-misconfigured": "כוננים אינם מוגדרים בצורה תקינה.",
"volumes-are-configured-correctly": "ה-Volumeים מוגדרים תקין.",
"status-unknown-try-running-a-validation": "מצב לא ידוע. נסה להריץ אימות.",
"validate": "אימות",
"email-configuration-status": "מצב הגדרות דוא״ל",
"email-configured": "Email Configured",
"email-test-results": "Email Test Results",
"email-configured": "דואר אלקטרוני הוגדר",
"email-test-results": "תוצאות בדיקת דואר אלקטרוני",
"ready": "מוכן",
"not-ready": "לא מוכן - בדוק משתני סביבה",
"succeeded": "הצליח",
@ -832,7 +833,7 @@
"password-updated": "הסיסמה עודכנה",
"password": "סיסמה",
"password-strength": "חוזק הסיסמה {strength}",
"please-enter-password": "Please enter your new password.",
"please-enter-password": "אנא הזן סיסמה חדשה.",
"register": "הרשמה",
"reset-password": "איפוס סיסמה",
"sign-in": "התחברות",
@ -853,7 +854,7 @@
"username": "שם משתמש",
"users-header": "משתמשים",
"users": "משתמשים",
"user-not-found": "User not found",
"user-not-found": "משתמש לא נמצא",
"webhook-time": "זמן Webhook",
"webhooks-enabled": "הפעלת Webhooks",
"you-are-not-allowed-to-create-a-user": "אין הרשאה ליצור משתמש",
@ -876,7 +877,7 @@
"user-management": "ניהול משתמשים",
"reset-locked-users": "אתחל משתמשים נעולים",
"admin-user-creation": "יצירת משתמש אדמין",
"admin-user-management": "Admin User Management",
"admin-user-management": "ניהול משתמשים",
"user-details": "פרטי משתמש",
"user-name": "שם משתמש",
"authentication-method": "שיטת אימות",
@ -887,9 +888,9 @@
"user-can-manage-group": "משתמש יכול לנהל קבוצה",
"user-can-organize-group-data": "משתמש יכול לשנות מידע של קבוצה",
"enable-advanced-features": "אפשר אפשרויות מתקדמות",
"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": "נראה שזו ההתחברות הראשונה שלך.",
"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!",
"forgot-password": "Forgot Password",
"forgot-password": "שכחתי סיסמא",
"forgot-password-text": "Please enter your email address and we will send you a link to reset your password.",
"changes-reflected-immediately": "Changes to this user will be reflected immediately."
},
@ -965,8 +966,8 @@
"delete-recipes": "מחיקת מתכונים",
"source-unit-will-be-deleted": "יחידת המקור תמחק"
},
"create-alias": "Create Alias",
"manage-aliases": "Manage Aliases",
"create-alias": "יצירת שם נרדף",
"manage-aliases": "נהל שמות נרדפים",
"seed-data": "אכלס נתונים",
"seed": "אכלס",
"data-management": "ניהול מידע",
@ -976,24 +977,24 @@
"columns": "עמודות",
"combine": "שילוב",
"categories": {
"edit-category": "Edit Category",
"new-category": "New Category",
"category-data": "Category Data"
"edit-category": "ערוך קטגוריה",
"new-category": "קטגוריה חדשה",
"category-data": "נתוני קטגוריה"
},
"tags": {
"new-tag": "New Tag",
"edit-tag": "Edit Tag",
"tag-data": "Tag Data"
"new-tag": "טאג חדש",
"edit-tag": "ערוך טאג",
"tag-data": "נתוני טאגים"
},
"tools": {
"new-tool": "New Tool",
"edit-tool": "Edit Tool",
"tool-data": "Tool Data"
"new-tool": "כלי חדש",
"edit-tool": "ערוך כלי",
"tool-data": "נתוני כלים"
}
},
"user-registration": {
"user-registration": "רישום משתמשים",
"registration-success": "Registration Success",
"registration-success": "ההרשמה הושלמה",
"join-a-group": "הצטרפות לקבוצה",
"create-a-new-group": "יצירת קבוצה חדשה",
"provide-registration-token-description": "ספק בבקשה את טוקן הרישום המשוייך לקבוצה אליה ברצונך להצטרף. בכדי לקבל אותו יהיה צורך להשיג אותו מאחד מחברה הקבוצה הקיימים.",
@ -1040,7 +1041,7 @@
},
"ocr-editor": {
"ocr-editor": "עורך סריקת תווים",
"toolbar": "Toolbar",
"toolbar": "סרגל כלים",
"selection-mode": "מצב בחירה",
"pan-and-zoom-picture": "סובב והגדל תמונה",
"split-text": "פיצול טקסט",
@ -1048,8 +1049,8 @@
"split-by-block": "פצל לפי מבנה המלל",
"flatten": "שטח בלי קשר למבנה המקורי",
"help": {
"help": "Help",
"mouse-modes": "Mouse modes",
"help": "עזרה",
"mouse-modes": "מצבי עכבר",
"selection-mode": "מצב בחירה (ברירת מחדל)",
"selection-mode-desc": "מצב בחירה זה הינו הראשי אשר ישמש להכנסת מידע:",
"selection-mode-steps": {

View File

@ -198,7 +198,8 @@
"refresh": "Osvježi",
"upload-file": "Prenesi Datoteku",
"created-on-date": "Kreirano dana: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Jeste li sigurni da želite izbrisati <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Frissít",
"upload-file": "Fájl feltöltése",
"created-on-date": "Létrehozva: {0}",
"unsaved-changes": "El nem mentett módosításai vannak. Szeretné elmenteni, mielőtt kilép? A mentéshez kattintson az Ok, a módosítások elvetéséhez a Mégsem gombra."
"unsaved-changes": "El nem mentett módosításai vannak. Szeretné elmenteni, mielőtt kilép? A mentéshez kattintson az Ok, a módosítások elvetéséhez a Mégsem gombra.",
"clipboard-copy-failure": "Nem sikerült a vágólapra másolás."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Biztosan törölni szeretnéd ezt: <b>{groupName}<b/>?",
@ -1113,7 +1114,7 @@
"brute": "Brute",
"show-individual-confidence": "",
"ingredient-text": "Hozzávaló szöveg",
"average-confident": "{0} Confident",
"average-confident": "{0}-os bizonyosság",
"try-an-example": "Próbáljon ki egy példát",
"parser": "Szintaxis elemző",
"background-tasks": "Háttér folyamatok",

View File

@ -77,7 +77,7 @@
"tag-events": "Tag Eventi",
"category-events": "Categoria Eventi",
"when-a-new-user-joins-your-group": "Quando un nuovo utente entra nel tuo gruppo",
"recipe-events": "Recipe Events"
"recipe-events": "Eventi di ricette"
},
"general": {
"cancel": "Cancella",
@ -114,10 +114,10 @@
"json": "JSON",
"keyword": "Parola chiave",
"link-copied": "Link Copiato",
"loading": "Loading",
"loading": "Caricamento in corso",
"loading-events": "Caricamento eventi",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipe": "Caricamento della ricetta...",
"loading-ocr-data": "Caricamento dati OCR...",
"loading-recipes": "Caricamento Ricette",
"message": "Messaggio",
"monday": "Lunedì",
@ -128,7 +128,7 @@
"no-recipe-found": "Nessuna Ricetta Trovata",
"ok": "OK",
"options": "Opzioni:",
"plural-name": "Plural Name",
"plural-name": "Nome Plurale",
"print": "Stampa",
"print-preferences": "Preferenze Di Stampa",
"random": "Casuale",
@ -198,7 +198,8 @@
"refresh": "Ricarica",
"upload-file": "Carica file",
"created-on-date": "Creato il: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "Sono state apportate modifiche non salvate. Salvare prima di uscire? Premi Ok per salvare, Annulla per scartare le modifiche.",
"clipboard-copy-failure": "Impossibile copiare negli appunti."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Sei sicuro di volerlo eliminare <b>{groupName}<b/>'?",
@ -213,7 +214,7 @@
"group-id-with-value": "ID Gruppo:{groupID}",
"group-name": "Nome Gruppo",
"group-not-found": "Gruppo non trovato",
"group-token": "Group Token",
"group-token": "Token del gruppo",
"group-with-value": "Gruppo: {groupID}",
"groups": "Gruppi",
"manage-groups": "Gestisci Gruppi",
@ -249,7 +250,7 @@
"general-preferences": "Impostazioni Generali",
"group-recipe-preferences": "Impostazioni per le ricette del gruppo",
"report": "Report",
"report-with-id": "Report ID: {id}",
"report-with-id": "ID Report: {id}",
"group-management": "Gestione Gruppo",
"admin-group-management": "Gestione Gruppo Amministratore",
"admin-group-management-text": "Le modifiche a questo gruppo si rifletteranno immediatamente.",
@ -304,7 +305,7 @@
"for-type-meal-types": "per {0} tipi di pasto",
"meal-plan-rules": "Regole del piano alimentare",
"new-rule": "Nuova regola",
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the categories of the rules will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
"meal-plan-rules-description": "Puoi creare regole per la selezione automatica delle ricette per i tuoi piani alimentari. Queste regole vengono utilizzate dal server per determinare le ricette da selezionare durante la creazione dei piani alimentari. Tieni presente che se le regole hanno gli stessi vincoli di giorno/tipo, le categorie delle regole verranno unite. In pratica non è necessario creare regole duplicate, ma è possibile farlo.",
"new-rule-description": "Quando si crea una nuova regola per un piano alimentare è possibile limitare la sua applicazione ad un giorno specifico della settimana e/o un tipo specifico di pasto. Per applicare una regola a tutti i giorni o a tutti i tipi di pasto, è possibile impostare la regola a \"Qualsiasi\", applicandola a tutti i possibili valori per il tipo di giorno e/o di pasto.",
"recipe-rules": "Regole per le ricette",
"applies-to-all-days": "Si applica a ogni giorno",
@ -356,7 +357,7 @@
"mealie-text": "Mealie può importare ricette dall'applicazione Mealie da un versione pre v1.0. Esporta le tue ricette dalla tua vecchia istanza e carica il file zip qui sotto. Nota che solo le ricette possono essere importate dall'esportazione.",
"plantoeat": {
"title": "Plan to Eat",
"description-long": "Mealie can import recipies from Plan to Eat."
"description-long": "Mealie può importare le ricette da Plan to Eat."
}
},
"new-recipe": {
@ -468,11 +469,11 @@
"date-format-hint-yyyy-mm-dd": "Formato YYYY-MM-DD",
"add-to-list": "Aggiungi alla lista",
"add-to-plan": "Aggiungi al piano giornaliero",
"add-to-timeline": "Add to Timeline",
"add-to-timeline": "Aggiungi alla linea temporale",
"recipe-added-to-list": "Ricetta aggiunta alla lista",
"recipes-added-to-list": "Recipes added to list",
"recipes-added-to-list": "Ricette aggiunte alla lista",
"recipe-added-to-mealplan": "Ricetta aggiunta al piano alimentare",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipes-to-list": "Impossibile aggiungere la ricetta alla lista",
"failed-to-add-recipe-to-mealplan": "Impossibile aggiungere la ricetta al piano alimentare",
"yield": "Porzioni",
"quantity": "Quantità",
@ -495,26 +496,26 @@
"locked": "Bloccato",
"public-link": "Link Pubblico",
"timer": {
"kitchen-timer": "Kitchen Timer",
"start-timer": "Start Timer",
"pause-timer": "Pause Timer",
"resume-timer": "Resume Timer",
"stop-timer": "Stop Timer"
"kitchen-timer": "Contaminuti da cucina",
"start-timer": "Avvia timer",
"pause-timer": "Metti in pausa il contaminuti",
"resume-timer": "Riprendi il contaminuti",
"stop-timer": "Arresta il Timer"
},
"edit-timeline-event": "Modifica Evento Timeline",
"timeline": "Timeline",
"timeline-is-empty": "Niente sulla timeline. Prova a fare questa ricetta!",
"group-global-timeline": "{groupName} Sequenza Temporale Globale",
"open-timeline": "Apri la Timeline",
"edit-timeline-event": "Modifica evento sulla linea temporale",
"timeline": "Linea temporale",
"timeline-is-empty": "Niente sulla linea temporale. Prova a fare questa ricetta!",
"group-global-timeline": "{groupName} Linea temporale globale",
"open-timeline": "Apri la linea temporale",
"made-this": "L'Ho Preparato",
"how-did-it-turn-out": "Come è venuto?",
"user-made-this": "{user} l'ha preparato",
"last-made-date": "Ultima Preparazione {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"api-extras-description": "Le opzioni extra delle ricette sono una caratteristica fondamentale dell'API Mealie. Consentono di creare json personalizzati con coppie di chiavi/valore all'interno di una ricetta a cui fare riferimento tramite applicazioni terze. È possibile utilizzare queste chiavi per inserire informazioni, per esempio per attivare automazioni oppure per inoltrare messaggi personalizzati al dispositivo desiderato.",
"message-key": "Chiave Messaggio",
"parse": "Analizza",
"attach-images-hint": "Allega immagini trascinandole nell'editor",
"drop-image": "Drop image",
"drop-image": "Trascina l'immagine",
"enable-ingredient-amounts-to-use-this-feature": "Abilita le quantità degli ingredienti per utilizzare questa funzione",
"recipes-with-units-or-foods-defined-cannot-be-parsed": "Le ricette con unità o alimenti definiti non possono essere analizzate.",
"parse-ingredients": "Analizza ingredienti",
@ -553,8 +554,8 @@
"recipe-yield": "Resa Ricetta",
"unit": "Unità",
"upload-image": "Carica immagine",
"screen-awake": "Keep Screen Awake",
"remove-image": "Remove image"
"screen-awake": "Mantieni lo schermo acceso",
"remove-image": "Rimuovi immagine"
},
"search": {
"advanced-search": "Ricerca Avanzata",
@ -573,16 +574,16 @@
"search-hint": "Premi '/'",
"advanced": "Ricerca Avanzata",
"auto-search": "Ricerca automatica",
"no-results": "No results found"
"no-results": "Nessun risultato trovato"
},
"settings": {
"add-a-new-theme": "Aggiungi un Nuovo Tema",
"admin-settings": "Impostazioni Amministratore",
"backup": {
"backup-created": "Backup created successfully",
"backup-created": "Backup creato con successo",
"backup-created-at-response-export_path": "Backup Creato in {path}",
"backup-deleted": "Backup eliminato",
"restore-success": "Restore successful",
"restore-success": "Ripristino riuscito",
"backup-tag": "Tag Backup",
"create-heading": "Crea un Backup",
"delete-backup": "Elimina Backup",
@ -691,13 +692,13 @@
"configuration": "Configurazione",
"docker-volume": "Docker Volume",
"docker-volume-help": "Mealie richiede che il frontend e il backend condividano lo stesso volume docker o archiviazione. Ciò assicura che il frontend possa accedere correttamente alle immagini e alle risorse memorizzate sul disco.",
"volumes-are-misconfigured": "Volumes are misconfigured.",
"volumes-are-misconfigured": "I volumi sono configurati male.",
"volumes-are-configured-correctly": "I volumi sono stati configurati correttamente.",
"status-unknown-try-running-a-validation": "Stato sconosciuto. Prova ad eseguire una convalida.",
"validate": "Convalida",
"email-configuration-status": "Configurazione e-mail",
"email-configured": "Email Configured",
"email-test-results": "Email Test Results",
"email-configured": "Email configurata",
"email-test-results": "Risultati del test email",
"ready": "Pronto",
"not-ready": "Non Pronto - Verifica Variabili Di Ambiente",
"succeeded": "Operazione riuscita",
@ -832,7 +833,7 @@
"password-updated": "Password aggiornata",
"password": "Password",
"password-strength": "La password è {strength}",
"please-enter-password": "Please enter your new password.",
"please-enter-password": "Si prega di inserire la nuova password.",
"register": "Registrati",
"reset-password": "Reimposta Password",
"sign-in": "Accedi",
@ -853,7 +854,7 @@
"username": "Nome Utente",
"users-header": "UTENTI",
"users": "Utenti",
"user-not-found": "User not found",
"user-not-found": "Utente non trovato",
"webhook-time": "Ora Webhook",
"webhooks-enabled": "Webhooks Abilitati",
"you-are-not-allowed-to-create-a-user": "Non sei autorizzato per la creazione di utenti",
@ -876,7 +877,7 @@
"user-management": "Gestione Utenti",
"reset-locked-users": "Ripristina Utenti Bloccati",
"admin-user-creation": "Creazione Utente Amministratore",
"admin-user-management": "Admin User Management",
"admin-user-management": "Gestione Utente Amministratore",
"user-details": "Dettagli Utente",
"user-name": "Nome Utente",
"authentication-method": "Metodo di autenticazione",
@ -887,11 +888,11 @@
"user-can-manage-group": "L'utente può gestire il gruppo",
"user-can-organize-group-data": "L'utente può organizzare i dati del gruppo",
"enable-advanced-features": "Abilita funzionalità avanzate",
"it-looks-like-this-is-your-first-time-logging-in": "It looks like this is your first time logging in.",
"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!",
"forgot-password": "Forgot Password",
"forgot-password-text": "Please enter your email address and we will send you a link to reset your password.",
"changes-reflected-immediately": "Changes to this user will be reflected immediately."
"it-looks-like-this-is-your-first-time-logging-in": "Sembra che questa sia la tua prima volta che accedi.",
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "Non vuoi più vederlo? Assicurati di modificare la tua email nelle impostazioni utente!",
"forgot-password": "Password Dimenticata",
"forgot-password-text": "Inserisci il tuo indirizzo email e ti invieremo un collegamento per reimpostare la tua password.",
"changes-reflected-immediately": "Le modifiche a questo utente diventeranno immediatamente effettive."
},
"language-dialog": {
"translated": "tradotto",
@ -913,8 +914,8 @@
"food-label": "Etichetta Alimento",
"edit-food": "Modifica Alimento",
"food-data": "Dati Alimento",
"example-food-singular": "ex: Onion",
"example-food-plural": "ex: Onions"
"example-food-singular": "esempio: Cipolla",
"example-food-plural": "esempio: Cipolle"
},
"units": {
"seed-dialog-text": "Riempie il database con unità comuni basate sulla lingua.",
@ -925,7 +926,7 @@
"merging-unit-into-unit": "Unione di {0} in {1}",
"create-unit": "Crea Unità",
"abbreviation": "Abbreviazione",
"plural-abbreviation": "Plural Abbreviation",
"plural-abbreviation": "Abbreviazione Plurale",
"description": "Descrizione",
"display-as-fraction": "Mostra come Frazione",
"use-abbreviation": "Usa Abbreviazione",
@ -933,10 +934,10 @@
"unit-data": "Dati Unità",
"use-abbv": "Utilizzare Abbrev.",
"fraction": "Frazione",
"example-unit-singular": "ex: Tablespoon",
"example-unit-plural": "ex: Tablespoons",
"example-unit-abbreviation-singular": "ex: Tbsp",
"example-unit-abbreviation-plural": "ex: Tbsps"
"example-unit-singular": "esempio: Cucchiaino",
"example-unit-plural": "esempio: Cucchiaini",
"example-unit-abbreviation-singular": "esempio: Ccno",
"example-unit-abbreviation-plural": "esempio: Ccni"
},
"labels": {
"seed-dialog-text": "Riempie il database con etichette comuni basate sulla lingua.",
@ -965,8 +966,8 @@
"delete-recipes": "Elimina Ricette",
"source-unit-will-be-deleted": "L'unità di origine verrà eliminata"
},
"create-alias": "Create Alias",
"manage-aliases": "Manage Aliases",
"create-alias": "Crea Alias",
"manage-aliases": "Gestisci Alias",
"seed-data": "Dati Predefiniti",
"seed": "Inizializza",
"data-management": "Gestione Dati",
@ -976,24 +977,24 @@
"columns": "Colonne",
"combine": "Unisci",
"categories": {
"edit-category": "Edit Category",
"new-category": "New Category",
"category-data": "Category Data"
"edit-category": "Modifica categoria",
"new-category": "Nuova categoria",
"category-data": "Categoria Dati"
},
"tags": {
"new-tag": "New Tag",
"edit-tag": "Edit Tag",
"tag-data": "Tag Data"
"new-tag": "Nuovo Tag",
"edit-tag": "Modifica Tag",
"tag-data": "Dati del Tag"
},
"tools": {
"new-tool": "New Tool",
"edit-tool": "Edit Tool",
"tool-data": "Tool Data"
"new-tool": "Nuovo strumento",
"edit-tool": "Modifica Strumento",
"tool-data": "Dati dello Strumento"
}
},
"user-registration": {
"user-registration": "Registrazione Utente",
"registration-success": "Registration Success",
"registration-success": "Registrazione completata",
"join-a-group": "Unisciti a un Gruppo",
"create-a-new-group": "Crea un Nuovo Gruppo",
"provide-registration-token-description": "Fornisci il token di registrazione associato al gruppo a cui desideri partecipare. Dovrai ottenerlo da un membro di gruppo esistente.",
@ -1040,7 +1041,7 @@
},
"ocr-editor": {
"ocr-editor": "Editor Ocr",
"toolbar": "Toolbar",
"toolbar": "Barra degli strumenti",
"selection-mode": "Modalità di selezione",
"pan-and-zoom-picture": "Sposta e Ingrandisci l'immagine",
"split-text": "Dividi testo",
@ -1048,8 +1049,8 @@
"split-by-block": "Dividi per blocco di testo",
"flatten": "Appiattire indipendentemente dalla formattazione originale",
"help": {
"help": "Help",
"mouse-modes": "Mouse modes",
"help": "Aiuto",
"mouse-modes": "Modalità Mouse",
"selection-mode": "Modalità di Selezione (Predefinito)",
"selection-mode-desc": "La modalità di selezione è la modalità principale per inserire i dati:",
"selection-mode-steps": {
@ -1106,26 +1107,26 @@
"mainentance": {
"actions-title": "Azioni"
},
"ingredients-natural-language-processor": "Ingredients Natural Language Processor",
"ingredients-natural-language-processor": "Processore Di Lingua Naturale degli Ingredienti",
"ingredients-natural-language-processor-explanation": "Mealie utilizza campi casuali condizionali (CRFs) per l'analisi e la lavorazione di ingredienti. Il modello utilizzato per gli ingredienti è basato su una serie di dati di oltre 100.000 ingredienti da un set di dati compilato dal New York Times. Si noti che poiché il modello è addestrato solo in inglese, si possono avere risultati vari quando si utilizza il modello in altre lingue. Questa pagina è un campo di gioco per testare il modello.",
"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": "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",
"brute": "Brute",
"show-individual-confidence": "Show individual confidence",
"ingredient-text": "Ingredient Text",
"average-confident": "{0} Confident",
"try-an-example": "Try an example",
"parser": "Parser",
"brute": "Bruto",
"show-individual-confidence": "Mostra fiducia individuale",
"ingredient-text": "Testo Ingrediente",
"average-confident": "{0} Fiducia",
"try-an-example": "Prova un esempio",
"parser": "Analizzatore",
"background-tasks": "Attività in Background",
"background-tasks-description": "Qui puoi visualizzare tutte le attività in background in esecuzione e il loro stato",
"no-logs-found": "Nessun Log Trovato",
"tasks": "Tasks"
"tasks": "Compiti"
},
"profile": {
"welcome-user": "👋 Benvenuto, {0}",
"description": "Gestisci il tuo profilo, le ricette e le impostazioni di gruppo.",
"get-invite-link": "Ottieni Link Di Invito",
"get-public-link": "Get Public Link",
"get-public-link": "Ottieni link pubblico",
"account-summary": "Riepilogo Account",
"account-summary-description": "Ecco un riepilogo delle informazioni del tuo gruppo",
"group-statistics": "Statistiche Gruppo",
@ -1157,13 +1158,13 @@
"show-advanced-description": "Mostra funzionalità avanzate (Chiavi API, Webhook e Gestione dati)",
"back-to-profile": "Torna al Profilo",
"looking-for-privacy-settings": "Stai cercando le Impostazioni Della Privacy?",
"manage-your-api-tokens": "Manage Your API Tokens",
"manage-user-profile": "Manage User Profile",
"manage-cookbooks": "Manage Cookbooks",
"manage-members": "Manage Members",
"manage-webhooks": "Manage Webhooks",
"manage-notifiers": "Manage Notifiers",
"manage-data-migrations": "Manage Data Migrations"
"manage-your-api-tokens": "Gestisci i tuoi Token Api",
"manage-user-profile": "Gestisci Profilo Utente",
"manage-cookbooks": "Gestisci Libri Di Cucina",
"manage-members": "Gestisci i membri",
"manage-webhooks": "Gestisci i Webhook",
"manage-notifiers": "Gestisci Notifiche",
"manage-data-migrations": "Gestione Migrazioni Dei Dati"
},
"cookbook": {
"cookbooks": "Ricettari",

View File

@ -198,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "ファイルのアップロード",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "<b>{groupName}<b/> を削除しますか?",

View File

@ -198,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Atnaujinti",
"upload-file": "Įkelti failą",
"created-on-date": "Sukurta: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Ar tikrai norite ištrinti <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Verversen",
"upload-file": "Bestand uploaden",
"created-on-date": "Gemaakt op {0}",
"unsaved-changes": "Er zijn niet-opgeslagen wijzigingen. Wil je eerst opslaan voordat je vertrekt? Okay om op te slaan, Annuleren om wijzigingen ongedaan te maken."
"unsaved-changes": "Er zijn niet-opgeslagen wijzigingen. Wil je eerst opslaan voordat je vertrekt? Okay om op te slaan, Annuleren om wijzigingen ongedaan te maken.",
"clipboard-copy-failure": "Kopiëren naar klembord mislukt."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Weet je zeker dat je <b>{groupName}<b/> wil verwijderen?",

View File

@ -6,7 +6,7 @@
"api-port": "API-port",
"application-mode": "Programmodus",
"database-type": "Databasetype",
"database-url": "Database-URL",
"database-url": "URL til database",
"default-group": "Standardgruppe",
"demo": "Demo",
"demo-status": "Demostatus",
@ -46,7 +46,7 @@
"category-updated": "Kategori oppdatert",
"uncategorized-count": "Ukategorisert {count}",
"create-a-category": "Opprett en kategori",
"category-name": "Katergorinavn",
"category-name": "Navn på kategori",
"category": "Kategori"
},
"events": {
@ -65,10 +65,10 @@
"subscribed-events": "Abonnerte hendelser",
"test-message-sent": "Testmelding sendt",
"new-notification": "Ny varsel",
"event-notifiers": "Hendelsesvarslere",
"event-notifiers": "Hendelsesvarsler",
"apprise-url-skipped-if-blank": "Apprise URL (hoppes over hvis tom)",
"enable-notifier": "Aktiver varsleren",
"what-events": "Hvilke hendelser denne varsleren abonnere på?",
"enable-notifier": "Aktiver varslingsagenten",
"what-events": "Hvilke hendelser skal denne varslingsagenten abonnere på?",
"user-events": "Brukerhendelser",
"mealplan-events": "Måltidsplanleggerhendelser",
"when-a-user-in-your-group-creates-a-new-mealplan": "Når en bruker i gruppen din oppretter en ny måltidsplan",
@ -122,13 +122,13 @@
"message": "Melding",
"monday": "Mandag",
"name": "Navn",
"new": "Ny",
"new": "Legg til",
"never": "Aldri",
"no": "Nei",
"no-recipe-found": "Ingen oppskrift funnet",
"ok": "OK",
"options": "Alternativer:",
"plural-name": "Flertallsnavn",
"plural-name": "Navn på flertallsform",
"print": "Skriv ut",
"print-preferences": "Skriv ut innstillinger",
"random": "Tilfeldig",
@ -180,7 +180,7 @@
"delete-with-name": "Slett {name}",
"confirm-delete-generic-with-name": "Er du sikker på at du vil slette denne {name}?",
"confirm-delete-own-admin-account": "Vær oppmerksom på at du holder på å slette din egen administrator-konto! Dette kan ikke angres og vil slette kontoen din permanent!",
"organizer": "Planlegger",
"organizer": "Organisator",
"transfer": "Overfør",
"copy": "Kopiér",
"color": "Farge",
@ -188,7 +188,7 @@
"last-made": "Sist laget",
"learn-more": "Lær mer",
"this-feature-is-currently-inactive": "Denne funksjonen er for øyeblikket deaktivert",
"clipboard-not-supported": "Utklippstavlen støttes ikke",
"clipboard-not-supported": "Utklippstavle støttes ikke",
"copied-to-clipboard": "Kopiert til utklippstavle",
"your-browser-does-not-support-clipboard": "Nettleseren din støtter ikke kopiering til utklippstavle",
"copied-items-to-clipboard": "Ingen elementer kopiert til utklippstavlen|Ett element kopiert til utklippstavlen|{count} elementer til kopiert til utklippstavlen",
@ -198,7 +198,8 @@
"refresh": "Oppdater",
"upload-file": "Last opp fil",
"created-on-date": "Opprettet: {0}",
"unsaved-changes": "Du har ulagrede endringer. Ønsker du å lagre før du forlater? Trykk 'OK' for å lagre, 'Avbryt' for å forkaste endringene."
"unsaved-changes": "Du har ulagrede endringer. Ønsker du å lagre før du forlater? Trykk 'OK' for å lagre, 'Avbryt' for å forkaste endringene.",
"clipboard-copy-failure": "Kunne ikke kopiere til utklippstavlen."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Er du sikker på at du vil slette <b>{groupName}<b/>?",
@ -225,25 +226,25 @@
"keep-my-recipes-private-description": "Setter gruppen din og alle oppskrifter som standard til privat. Du kan alltids endre dette senere."
},
"manage-members": "Administrer medlemmer",
"manage-members-description": "Administrere rettighetene til medlemmene i dine grupper. {manage} gir brukeren tilgang til data-administrasjonssiden {invite} mulighet til å generere invitasjonslenker for andre brukere. Gruppeeiere kan ikke endre egne tillatelser.",
"manage-members-description": "Administrer rettighetene til medlemmene i gruppene dine. {manage} lar brukeren få tilgang til dataadministrasjonssiden. {invite} lar brukeren generere invitasjonslenker for andre brukere. Gruppeeiere kan ikke endre egne tillatelser.",
"manage": "Administrer",
"invite": "Inviter",
"looking-to-update-your-profile": "Ønsker du å oppdatere profilen din?",
"default-recipe-preferences-description": "Dette er standardinnstillingene når en ny oppskrift blir opprettet i gruppen din. Disse kan endres for individuelle oppskrifter i oppskriftsmenyen.",
"default-recipe-preferences": "Standard Oppskriftsinnstillinger",
"default-recipe-preferences": "Standard oppskriftsinnstillinger",
"group-preferences": "Gruppeinnstillinger",
"private-group": "Privat gruppe",
"private-group-description": "Hvis din gruppe settes til privat, vil alle instillinger setter til standard. Dette overstyrer en individuell oppskrifter offentlige innstillinger.",
"private-group-description": "Når du setter gruppen din til privat, vil alle offentlige visningsalternativer tilbakestilles til standardverdiene. Dette overskriver individuelle oppskrifters offentlige visningsinnstillinger.",
"allow-users-outside-of-your-group-to-see-your-recipes": "Tillat brukere utenfor gruppen å se oppskriftene dine",
"allow-users-outside-of-your-group-to-see-your-recipes-description": "Når aktivert kan du bruke en lenke for offentlig deling til å dele bestemte oppskrifter uten å autorisere brukeren. Når deaktivert kan du bare dele oppskrifter med brukere i gruppen din eller med en forhåndsgenerert privat link",
"allow-users-outside-of-your-group-to-see-your-recipes-description": "Når aktivert, kan du bruke en offentlig lenke for å dele spesifikke oppskrifter uten å autorisere brukeren. Når deaktivert, kan du kun dele oppskrifter med brukere som er i gruppen din eller med en forhåndsgenerert privat lenke",
"show-nutrition-information": "Vis ernæringsinformasjon",
"show-nutrition-information-description": "Når aktivert vil ernæringsinformasjonen bli vist på oppskriften hvis tilgjengelig. Hvis det ikke finnes noen ernæringsinformasjon tilgjengelig vil ikke ernæringsinformasjonen vises",
"show-recipe-assets": "Vis oppskriftsressurser",
"show-recipe-assets-description": "Når aktivert, vil oppskriftsressurser bli vist på oppskriften hvis de er tilgjengelige",
"default-to-landscape-view": "Standard liggende visning",
"default-to-landscape-view": "Sett landskapsvisning som standard",
"default-to-landscape-view-description": "Når aktivert, vil oppskriftsoverskriften vises i landskapsvisning",
"disable-users-from-commenting-on-recipes": "Deaktiver brukere fra å kommentere på oppskrifter",
"disable-users-from-commenting-on-recipes-description": "Skjuler kommentarfeltet på oppskriftssiden og deaktiverer kommentarer",
"disable-users-from-commenting-on-recipes": "Deaktiver muligheten for at brukere kan kommentere på oppskrifter",
"disable-users-from-commenting-on-recipes-description": "Skjuler kommentarfeltet på oppskriftssiden og deaktiverer kommentering",
"disable-organizing-recipe-ingredients-by-units-and-food": "Deaktiver organisering av oppskriftsingredienser ved enheter og matvarer",
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Skjuler matvare-, enhet- og mengdefelt for ingredienser og behandler ingredienser som rene tekstfelt.",
"general-preferences": "Generelle innstillinger",
@ -251,8 +252,8 @@
"report": "Rapport",
"report-with-id": "Rapport-ID: {id}",
"group-management": "Gruppeadministrasjon",
"admin-group-management": "Admin gruppeadministrasjon",
"admin-group-management-text": "Endringer i denne gruppen vil umiddelbart bli reflektert.",
"admin-group-management": "Gruppeadministrasjon",
"admin-group-management-text": "Endringer i denne gruppen vil gjenspeiles umiddelbart.",
"group-id-value": "Gruppe-ID: {0}"
},
"meal-plan": {
@ -264,12 +265,12 @@
"end-date": "Sluttdato",
"group": "Gruppe (Beta)",
"main": "Hovedrett",
"meal-planner": "Planlegg måltid",
"meal-planner": "Måltidsplanlegger",
"meal-plans": "Måltidsplaner",
"mealplan-categories": "MÅLTIDSPLANKATEGORIER",
"mealplan-created": "Måltidsplan opprettet",
"mealplan-creation-failed": "Opprettelse av måltidsplan mislyktes",
"mealplan-deleted": "Måltidsplan Slettet",
"mealplan-deleted": "Måltidsplan slettet",
"mealplan-deletion-failed": "Sletting av måltidsplan mislyktes",
"mealplan-settings": "Måltidsplaninnstillinger",
"mealplan-update-failed": "Oppdatering av måltidsplan mislyktes",
@ -282,81 +283,81 @@
"side": "Tilbehør",
"sides": "Tilbehør",
"start-date": "Startdato",
"rule-day": "Regeldag",
"rule-day": "Regel for dager",
"meal-type": "Type måltid",
"breakfast": "Frokost",
"lunch": "Lunsj",
"dinner": "Middag",
"type-any": "Enhver",
"day-any": "Enhver",
"editor": "Redigering",
"meal-recipe": "Måltid oppskrift",
"meal-title": "Navn på måltid",
"meal-note": "Måltid Notat",
"editor": "Redigeringsverktøy",
"meal-recipe": "Oppskrift på måltid",
"meal-title": "Tittel på måltid",
"meal-note": "Notat for måltid",
"note-only": "Kun notat",
"random-meal": "Tilfeldig måltid",
"random-dinner": "Tilfeldig Middag",
"random-side": "Tilfeldig Tilbehør",
"random-dinner": "Tilfeldig middag",
"random-side": "Tilfeldig tilbehør",
"this-rule-will-apply": "Denne regelen vil gjelde for {dayCriteria} {mealTypeCriteria}.",
"to-all-days": "til alle dager",
"on-days": "på {0}s",
"on-days": "på {0}er",
"for-all-meal-types": "for alle måltidstyper",
"for-type-meal-types": "for {0} måltidstyper",
"meal-plan-rules": "Regler for måltidsplan",
"new-rule": "Ny regel",
"meal-plan-rules-description": "Du kan opprette regler for automatisk valg av oppskrifter for dine måltidsplaner. Disse reglene brukes av serveren til å bestemme det tilfeldige utvalget av oppskrifter som skal velges fra når det opprettes måltidsplaner. Vær oppmerksom på at hvis regler har de samme begrensningene for dag/type, vil kategoriene til reglene bli slått sammen. I praksis er det unødvendig å opprette duplikater av regler, men det er mulig å gjøre det.",
"new-rule-description": "Ved opprettelse av en ny regel for en måltidsplan kan du begrense regelen til å gjelde for en bestemt ukedag og/eller en bestemt type måltid. For å ta i bruk en regen alle dager eller alle måltider kan du sette regelen til \"Enhver\" som gjelder for alle mulige verdier for dag- og/eller måltidstyper.",
"recipe-rules": "Oppskrift Regler",
"recipe-rules": "Regler for oppskrifter",
"applies-to-all-days": "Gjelder for alle dager",
"applies-on-days": "Gjelder på {0}s",
"applies-on-days": "Gjelder på {0}er",
"meal-plan-settings": "Innstillinger for måltidsplan"
},
"migration": {
"migration-data-removed": "Migrasjonsdata er fjernet",
"new-migration": "Ny migrering",
"migration-data-removed": "Overføringsdata er fjernet",
"new-migration": "Ny overføring",
"no-file-selected": "Ingen fil valgt",
"no-migration-data-available": "Ingen migrasjonsdata tilgjengelig",
"previous-migrations": "Tidligere migreringer",
"recipe-migration": "Oppskriftsmigrering",
"no-migration-data-available": "Ingen overføringsdata tilgjengelig",
"previous-migrations": "Tidligere overføringer",
"recipe-migration": "Oppskriftsoverføring",
"chowdown": {
"description": "Overfør data fra Chowdown",
"description-long": "Mealie nativt støtter chowdown depotet. Last ned kodelageret som en .zip-fil og last det opp nedenfor.",
"description-long": "Mealie støtter Chowdown-arkivformatet. Last ned kodearkivet som en .zip-fil og last den opp nedenfor.",
"title": "Chowdown"
},
"nextcloud": {
"description": "Migrer data fra en Nextcloud Cookbook instans",
"description-long": "Nextcloud oppskrifter kan importeres fra en zip-fil som inneholder dataene lagret i Nextcloud. Se mappestrukturen for eksempel nedenfor for å sikre at oppskriftene kan importeres.",
"description": "Overfør data fra en Nextcloud Cookbook-instans",
"description-long": "Oppskrifter fra Nextcloud kan importeres fra en zip-fil som inneholder dataene lagret i Nextcloud. Se eksempelet på mappestrukture nedenfor for å sikre at oppskriftene kan importeres.",
"title": "Nextcloud Cookbook"
},
"copymethat": {
"description-long": "Mealie kan importere oppskrifter fra Copy Me. Eksporter oppskrifter i HTML-format, deretter last opp .zip-filen under.",
"title": "Copy Me That oppskriftsbehandlingen"
"description-long": "Mealie kan importere oppskrifter fra Copy Me That. Eksporter oppskrifter i HTML-format, last deretter opp .zip-filen under.",
"title": "Copy Me That Recipe Manager"
},
"paprika": {
"description-long": "Mealie kan importere oppskrifter fra Paprika. Eksporter oppskriftene fra Paprika, endre filnavnutvidelsen til .zip og last den opp nedenfor.",
"title": "Paprika Oppskriftsbehandler"
"title": "Paprika Recipe Manager"
},
"mealie-pre-v1": {
"description-long": "Mealie kan importere oppskrifter fra Mealie fra versjoner eldre enn v1.0. Eksporter oppskriftene fra din gamle versjon og last opp zip-filen under. Merk at bare oppskrifter kan importeres fra eksporten.",
"title": "Mealie Pre v1.0"
},
"tandoor": {
"description-long": "Mealie kan importere oppskrifter fra Tandoor. Eksporter dataene i \"Standard\"-formatet, deretter last opp .zip-formatet nedenfor.",
"title": "Tandoor oppskrifter"
"description-long": "Mealie kan importere oppskrifter fra Tandoor. Eksporter dataene i \"Standard\"-formatet, last deretter opp zip-filen nedenfor.",
"title": "Tandoor Recipes"
},
"recipe-data-migrations": "Oppskriftsmigrering",
"recipe-data-migrations": "Overføring av oppskrifter",
"recipe-data-migrations-explanation": "Oppskrifter kan overføres fra et annet støttet program til Mealie. Dette er en flott måte å komme i gang med Mealie på.",
"choose-migration-type": "Velg migrasjonstype",
"choose-migration-type": "Velg type overføring",
"tag-all-recipes": "Merk alle oppskrifter med {tag-name}-emneord",
"nextcloud-text": "Nextcloud oppskrifter kan importeres fra en zip-fil som inneholder dataene lagret i Nextcloud. Se mappestrukturen for eksempel nedenfor for å sikre at oppskriftene kan importeres.",
"chowdown-text": "Mealie nativt støtter chowdown depotet. Last ned kodelageret som en .zip-fil og last det opp nedenfor",
"nextcloud-text": "Oppskrifter fra Nextcloud kan importeres fra en zip-fil som inneholder dataene lagret i Nextcloud. Se eksempelet på mappestrukture nedenfor for å sikre at oppskriftene kan importeres.",
"chowdown-text": "Mealie støtter Chowdown-arkivformatet. Last ned kodearkivet som en .zip-fil og last den opp nedenfor",
"recipe-1": "Oppskrift 1",
"recipe-2": "Oppskrift 2",
"paprika-text": "Mealie kan importere oppskrifter fra Paprika. Eksporter oppskriftene fra Paprika, endre filnavnutvidelsen til .zip og last den opp nedenfor.",
"mealie-text": "Mealie kan importere oppskrifter fra Mealie fra versjoner eldre enn v1.0. Eksporter oppskriftene fra din gamle versjon og last opp zip-filen under. Merk at bare oppskrifter kan importeres fra eksporten.",
"plantoeat": {
"title": "Planlegg å spise",
"description-long": "Mealie kan importere oppskrifter fra plan å spise."
"title": "Plan to Eat",
"description-long": "Mealie kan importere oppskrifter fra Plan to Eat."
}
},
"new-recipe": {
@ -366,18 +367,18 @@
"from-url": "Importer en oppskrift",
"github-issues": "GitHub-problemer",
"google-ld-json-info": "Google ld+json-informasjon",
"must-be-a-valid-url": "Må være en gyldig URL-adresse",
"must-be-a-valid-url": "Må være en gyldig nettadresse",
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Lim inn oppskriftsdataene. Hver linje blir behandlet som et element i en liste",
"recipe-markup-specification": "Spesifikasjon for oppskriftsmerking",
"recipe-url": "Oppskrifts-URL",
"recipe-url": "Nettadresse til oppskrift",
"upload-a-recipe": "Last opp oppskrift",
"upload-individual-zip-file": "Last opp en individuell .zip-fil eksportert fra en annen Mealie-instans.",
"url-form-hint": "Kopier og lim inn en link fra favorittoppskriftsnettstedet ditt",
"url-form-hint": "Kopier og lim inn en lenke fra nettstedet med favorittoppskriftene dine",
"view-scraped-data": "Vis skrapte data",
"trim-whitespace-description": "Trim innledende og etterfølgende mellomrom i tillegg til tomme linjer",
"trim-whitespace-description": "Fjern innledende og etterfølgende mellomrom i tillegg til tomme linjer",
"trim-prefix-description": "Fjern første tegn fra hver linje",
"split-by-numbered-line-description": "Forsøk å dele et avsnitt ved å matche mønsteret '1)' eller '1.'",
"import-by-url": "Importer en oppskrift via URL",
"split-by-numbered-line-description": "Forsøk å dele opp et avsnitt ved å matche mønsteret '1)' eller '1.'",
"import-by-url": "Importer en oppskrift via nettadresse",
"create-manually": "Opprett en oppskrift manuelt",
"make-recipe-image": "Bruk dette som oppskriftsbilde"
},
@ -412,7 +413,7 @@
"description": "Beskrivelse",
"disable-amount": "Deaktiver ingrediensmengde",
"disable-comments": "Deaktiver kommentarer",
"duplicate": "Duplikat-oppskrift",
"duplicate": "Dupliser oppskrift",
"duplicate-name": "Navn på den nye oppskriften",
"edit-scale": "Rediger skala",
"fat-content": "Fett",
@ -423,7 +424,7 @@
"insert-ingredient": "Sett inn ingrediens",
"insert-section": "Sett inn avsnitt",
"instructions": "Instruksjoner",
"key-name-required": "Nøkkelnavn påkrevd",
"key-name-required": "Navn på nøkkel er påkrevd",
"landscape-view-coming-soon": "Landskapsvisning",
"milligrams": "mg",
"new-key-name": "Nytt nøkkelnavn",
@ -432,7 +433,7 @@
"nutrition": "Ernæring",
"object-key": "Objektnøkkel",
"object-value": "Objektverdi",
"original-url": "Opprinnlig URL",
"original-url": "Nettadresse til oppskrift",
"perform-time": "Koketid",
"prep-time": "Forberedelsestid",
"protein-content": "Protein",
@ -465,10 +466,10 @@
"add-recipe-to-mealplan": "Legg til oppskrift i måltidsplan",
"entry-type": "Oppføringstype",
"date-format-hint": "MM/DD/ÅÅÅÅ-format",
"date-format-hint-yyyy-mm-dd": "ÅÅÅÅ-MM-DD format",
"date-format-hint-yyyy-mm-dd": "ÅÅÅÅ-MM-DD-format",
"add-to-list": "Legg til i liste",
"add-to-plan": "Legg til i plan",
"add-to-timeline": "Legg til tidslinjen",
"add-to-timeline": "Legg til tidslinje",
"recipe-added-to-list": "Oppskrift er lagt til i liste",
"recipes-added-to-list": "Oppskrifter lagt til listen",
"recipe-added-to-mealplan": "Oppskrift er lagt til i måltidsplan",
@ -483,11 +484,11 @@
"toggle-section": "Legg til seksjon",
"see-original-text": "Se opprinnelig tekst",
"original-text-with-value": "Opprinnelig tekst: {originalText}",
"ingredient-linker": "Ingredienskobler",
"linked-to-other-step": "Koblet til et annet trinn",
"ingredient-linker": "Tilknytt ingredienser",
"linked-to-other-step": "Tilknyttet et annet steg",
"auto": "Automatisk",
"cook-mode": "Tilberedelsesmodus",
"link-ingredients": "Koble på ingredienser",
"link-ingredients": "Tilknytt ingredienser",
"merge-above": "Slå sammen med steget over",
"reset-scale": "Nullstill skala",
"decrease-scale-label": "Reduser skala med 1",
@ -497,23 +498,23 @@
"timer": {
"kitchen-timer": "Kjøkkentimer",
"start-timer": "Start nedtelling",
"pause-timer": "Pause nedtellingen",
"resume-timer": "Fortsett nedtellingen",
"stop-timer": "Stopp nedtellingen"
"pause-timer": "Pause nedtelling",
"resume-timer": "Fortsett nedtelling",
"stop-timer": "Stopp nedtelling"
},
"edit-timeline-event": "Endre hendelsestidslinjen",
"edit-timeline-event": "Endre tidslinjehendelser",
"timeline": "Tidslinje",
"timeline-is-empty": "Ingenting på tidslinjen ennå. Prøv å lage denne oppskriften!",
"group-global-timeline": "{groupName} Global tidslinje",
"open-timeline": "Åpne tidslinje",
"made-this": "Jeg har lagd dette",
"how-did-it-turn-out": "Hvordan ble det?",
"user-made-this": "{user} har lagd denne",
"last-made-date": "Sist lagd {date}",
"made-this": "Jeg har laget denne",
"how-did-it-turn-out": "Hvordan ble den?",
"user-made-this": "{user} har laget denne",
"last-made-date": "Sist laget: {date}",
"api-extras-description": "Ekstramaterialer til oppskrifter er en viktig funksjon i Mealie API-en. De lar deg opprette egendefinerte JSON-nøkkel/verdi-par innenfor en oppskrift for å referere fra tredjepartsapplikasjoner. Du kan bruke disse nøklene til å gi informasjon for eksempel for å utløse automatiseringer eller egendefinerte meldinger som skal videreformidles til ønsket enhet.",
"message-key": "Meldingsnøkkel",
"parse": "Del opp",
"attach-images-hint": "Legg til bilder ved å dra og slippe dem i editoren",
"parse": "Analyser",
"attach-images-hint": "Fest bilder ved å dra og slippe dem inn i redigereringsverktøyet",
"drop-image": "Slipp bilde",
"enable-ingredient-amounts-to-use-this-feature": "Aktiver ingrediensmengder for å bruke denne funksjonen",
"recipes-with-units-or-foods-defined-cannot-be-parsed": "Oppskrifter med enheter eller matvarer som er definert kan ikke tolkes.",
@ -521,39 +522,39 @@
"edit-markdown": "Rediger Markdown",
"recipe-creation": "Opprett oppskrift",
"select-one-of-the-various-ways-to-create-a-recipe": "Velg en av de ulike måtene å opprette en oppskrift",
"looking-for-migrations": "Ser du etter migrasjoner?",
"import-with-url": "Importer med URL",
"looking-for-migrations": "Ser du etter overføringer?",
"import-with-url": "Importer via nettadresse",
"create-recipe": "Opprett oppskrift",
"import-with-zip": "Importer med .zip",
"import-with-zip": "Importer fra .zip-fil",
"create-recipe-from-an-image": "Opprett oppskrift fra et bilde",
"bulk-url-import": "Importer flere URL'er",
"bulk-url-import": "Importer flere nettadresser",
"debug-scraper": "Feilsøk skraper",
"create-a-recipe-by-providing-the-name-all-recipes-must-have-unique-names": "Opprett en oppskrift ved å angi navnet. Alle oppskrifter må ha unike navn.",
"new-recipe-names-must-be-unique": "Oppskriftsnavn må være unike",
"scrape-recipe": "Hent oppskrift",
"scrape-recipe-description": "Scrape en oppskrift ved url. Angi Url-adressen for nettstedet du vil skrape, Og Mealie vil forsøke å skrape oppskriften fra det området og legge den til i samlingen din.",
"new-recipe-names-must-be-unique": "Navn på oppskrift må være unike",
"scrape-recipe": "Skrap oppskrift",
"scrape-recipe-description": "Skrap en oppskrift ved bruk av nettadresse. Oppgi nettadressen til nettstedet du vil skrape, så vil Mealie forsøke å skrape oppskriften fra den siden og legge den til i samlingen din.",
"import-original-keywords-as-tags": "Importer originale søkeord som emneord",
"stay-in-edit-mode": "Bli i skrivemodus",
"import-from-zip": "Importer fra Zip",
"stay-in-edit-mode": "Forbli i redigeringsmodus",
"import-from-zip": "Importer fra zip-fil",
"import-from-zip-description": "Importer en enkelt oppskrift som ble eksportert fra en annen Mealie-instans.",
"zip-files-must-have-been-exported-from-mealie": ".zip-filer må ha blitt eksportert fra Mealie",
"create-a-recipe-by-uploading-a-scan": "Lag en oppskrift ved å laste opp en skanning.",
"upload-a-png-image-from-a-recipe-book": "Last opp et png bilde fra en oppskriftsbok",
"recipe-bulk-importer": "Oppskrift masse-import",
"recipe-bulk-importer-description": "Masse-oppskriftsimporten lar deg importere flere oppskrifter samtidig ved å sette i kø på kontrollstedene på bakgrunnen og kjøre oppgaven i bakgrunnen. Dette kan være nyttig ved først å migrere til Mealie, eller når du vil importere et stort antall oppskrifter.",
"set-categories-and-tags": "Sett kategorier og emneord",
"create-a-recipe-by-uploading-a-scan": "Opprett en oppskrift ved å laste opp en skanning.",
"upload-a-png-image-from-a-recipe-book": "Last opp et png-bilde fra en oppskriftsbok",
"recipe-bulk-importer": "Masseimport av oppskrifter",
"recipe-bulk-importer-description": "Masseimport av oppskrifter lar deg importere flere oppskrifter samtidig ved å sette opp nettstedene i kø på serveren og kjøre oppgaven i bakgrunnen. Dette kan være nyttig når du først migrerer til Mealie, eller når du ønsker å importere et stort antall oppskrifter.",
"set-categories-and-tags": "Angi kategorier og emneord",
"bulk-imports": "Masseimport",
"bulk-import-process-has-started": "Masseimport har startet",
"bulk-import-process-has-failed": "Masseimport mislyktes",
"report-deletion-failed": "Raport sletting feilet",
"recipe-debugger": "Oppskrift Feilsøking",
"recipe-debugger-description": "Hent URL-adressen til oppskriften du vil feilsøke og lim den inn her. URL-adressen vil bli skrapt av oppskrifts-skraperen og resultatene vil bli vist. Hvis du ikke ser noen data returnert, er ikke nettstedet du prøver å skrape støttet av Mealie eller skraper-biblioteket.",
"report-deletion-failed": "Sletting av rapport mislyktes",
"recipe-debugger": "Oppskriftsfeilsøker",
"recipe-debugger-description": "Hent nettadressen til oppskriften du vil feilsøke og lim den inn her. Nettsiden vil bli skrapt og resultatene vil bli vist. Hvis du ikke ser noen data returnert, er ikke nettstedet du prøver å skrape støttet av Mealie eller skraper-biblioteket.",
"debug": "Feilsøk",
"tree-view": "Trevisning",
"recipe-yield": "Oppskrift uttak",
"recipe-yield": "Utbytte av oppskrift",
"unit": "Enhet",
"upload-image": "Last opp bilde",
"screen-awake": "Hold skjerm på",
"screen-awake": "Hold skjermen på",
"remove-image": "Slett bilde"
},
"search": {
@ -592,13 +593,13 @@
"partial-backup": "Delvis sikkerhetskopi",
"unable-to-delete-backup": "Kan ikke slette sikkerhetskopi.",
"experimental-description": "Sikkerhetskopier er komplette øyeblikksbilder av databasen og datamappen til nettstedet. Dette inkluderer all data og kan ikke settes til å ekskludere delsett av data. Du kan tenke på dette som et øyeblikksbilde av Mealie på et bestemt tidspunkt. Disse fungerer som en databasesystemuavhengig måte å eksportere og importere data på, eller sikkerhetskopiere nettstedet til en ekstern plassering.",
"backup-restore": "Sikkerhetskopiering / gjenoppretting",
"backup-restore": "Gjenoppretting av sikkerhetskopi",
"back-restore-description": "Gjenoppretting av denne sikkerhetskopien vil overskrive alle gjeldende data i databasen og i datamappen og erstatte dem med innholdet i denne sikkerhetskopien. {cannot-be-undone} Hvis gjenopprettingen er vellykket, vil du bli logget ut.",
"cannot-be-undone": "Denne handlingen kan ikke angres brukes med forsiktighet.",
"cannot-be-undone": "Denne handlingen kan ikke angres bruk med forsiktighet.",
"postgresql-note": "Hvis du bruker PostGreSQL, vennligst gå igjennom {backup-restore-process} før du gjenoppretter.",
"backup-restore-process-in-the-documentation": "sikkerhetskopiering/gjenoppretting prosess i dokumentasjonen",
"irreversible-acknowledgment": "Jeg forstår at dette er irreversibel, destruktiv og kan føre til tap av data",
"restore-backup": "Gjenopprett sikkerhetskopien"
"irreversible-acknowledgment": "Jeg forstår at denne handlingen er irreversibel, destruktiv og kan føre til tap av data",
"restore-backup": "Gjenopprett sikkerhetskopi"
},
"backup-and-exports": "Sikkerhetskopier",
"change-password": "Endre passord",
@ -623,7 +624,7 @@
"new-page": "Ny side",
"notify": "Varsle",
"organize": "Organiser",
"page-name": "Sidenavn",
"page-name": "Navn på side",
"pages": "Sider",
"profile": "Profil",
"remove-existing-entries-matching-imported-entries": "Fjern eksisterende oppføringer som samsvarer med de importerte oppføringene",
@ -632,9 +633,9 @@
"settings-updated": "Innstillinger oppdatert",
"site-settings": "Sideinnstillinger",
"theme": {
"accent": "Utheving",
"accent": "Uthevet",
"dark": "Mørk",
"default-to-system": "Standard",
"default-to-system": "Systemstandard",
"error": "Feil",
"error-creating-theme-see-log-file": "Feil under oppretting av tema. Se loggfil.",
"error-deleting-theme": "Feil ved sletting av tema",
@ -643,17 +644,17 @@
"light": "Lys",
"primary": "Primær",
"secondary": "Sekundær",
"success": "Fullført",
"success": "Suksess",
"switch-to-dark-mode": "Bytt til mørk modus",
"switch-to-light-mode": "Bytt til lys modus",
"theme-deleted": "Tema slettet",
"theme-name": "Temanavn",
"theme-name": "Navn på tema",
"theme-name-is-required": "Navn på tema er påkrevd.",
"theme-saved": "Tema lagret",
"theme-updated": "Tema oppdatert",
"warning": "Advarsel",
"light-mode": "Lys modus",
"dark-mode": "Mørkemodus"
"dark-mode": "Mørk modus"
},
"token": {
"active-tokens": "AKTIVE TOKENS",
@ -661,13 +662,13 @@
"api-tokens": "API-tokener",
"copy-this-token-for-use-with-an-external-application-this-token-will-not-be-viewable-again": "Kopier dette tokenet til bruk med et eksternt program. Dette tokenet vil ikke kunne ses igjen.",
"create-an-api-token": "Opprett et API-token",
"token-name": "Tokennavn",
"token-name": "Navn på token",
"generate": "Opprett",
"you-have-token-count": "Du har ingen aktive token.|Du har ett aktivt token.|Du har {count} aktive tokener."
},
"toolbox": {
"assign-all": "Tildel alle",
"bulk-assign": "Tilordne flere",
"bulk-assign": "Tildel flere",
"new-name": "Nytt navn",
"no-unused-items": "Ingen ubrukte elementer",
"recipes-affected": "Ingen oppskrifter påvirket|En oppskrift påvirket|{count} Oppskrifter påvirket",
@ -677,29 +678,29 @@
"unorganized": "Uorganisert"
},
"webhooks": {
"test-webhooks": "Test Webhooks",
"test-webhooks": "Test webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "URL-adressene som er oppført nedenfor vil motta webhooks med oppskriftsdata for måltidsplanen på dens planlagte dag. For øyeblikket vil webhooks utføres på",
"webhook-url": "Webhook-URL",
"webhooks-caps": "WEBHOOKS",
"webhooks": "Webhooks",
"webhook-name": "Webhook navn",
"description": "Webhooks definert nedenfor vil utføres når et måltid defineres for dagen. På det planlagte tidspunktet blir webhook sendt med data fra oppskriften som er planlagt for dagen. Merke at av tidspunktet ikke er nøyaktig, webhooks utføres med et intervall på 5 minutter, dette betyr at webhooks utføres innen +/- 5 minutter etter planlagt tidspunkt."
"webhook-name": "Navn på webhook",
"description": "Webhooks definert nedenfor vil utføres når et måltid defineres for dagen. På det planlagte tidspunktet blir webhook sendt med data fra oppskriften som er planlagt for dagen. Merk at tidspunktet ikke er nøyaktig og webhooks utføres med et intervall på 5 minutter. Dette betyr at webhooks utføres innenfor +/- 5 minutter etter planlagt tidspunkt."
},
"bug-report": "Feilrapport",
"bug-report-information": "Bruk denne informasjonen til å rapportere en feil. Å gi detaljer om installasjonen din til utviklerne er den beste måten å få løst problemene dine raskt på.",
"tracker": "Sporingsagent",
"configuration": "Konfigurasjon",
"docker-volume": "Docker volum",
"docker-volume": "Docker-volum",
"docker-volume-help": "Mealie krever at frontend- og backend-konteinerene deler samme docker-volum eller lagringssted. Dette sikrer at frontend-kontaineren får tilgang til bilder og ressurser lagret på harddisken.",
"volumes-are-misconfigured": "Volumene er feilkonfigurert.",
"volumes-are-misconfigured": "Volumene er konfigurert feil.",
"volumes-are-configured-correctly": "Volumene er riktig konfigurert.",
"status-unknown-try-running-a-validation": "Statusen er ukjent. Prøv å validere.",
"status-unknown-try-running-a-validation": "Statusen er ukjent. Prøv å kjør en validering.",
"validate": "Valider",
"email-configuration-status": "E-postkonfigurasjonsstatus",
"email-configuration-status": "Status på konfigurasjon av e-post",
"email-configured": "E-post konfigurert",
"email-test-results": "Email Test Results",
"email-test-results": "Testresultater for e-post",
"ready": "Klar",
"not-ready": "Ikke klar - sjekk miljøvariabler",
"not-ready": "Ikke klar - kontroller konfigurasjonen",
"succeeded": "Lyktes",
"failed": "Mislyktes",
"general-about": "Generelt om",
@ -708,15 +709,15 @@
"mealie-is-up-to-date": "Mealie er oppdatert",
"secure-site": "Sikkert nettsted",
"secure-site-error-text": "Server via localhost eller sikre med https. Utklippstavle og ytterlige nettleser-API-er vil muligens ikke fungere.",
"secure-site-success-text": "Tjenesten er tilgjengelig localhost eller https",
"server-side-base-url": "Serverens sidebase URL",
"secure-site-success-text": "Tjenesten er tilgjengelig via localhost eller https",
"server-side-base-url": "Serverens URL",
"server-side-base-url-error-text": "`BASE_URL` er fortsatt standardverdien på API-serveren. Dette vil forårsake problemer med varslingslenker som genereres på serveren for e-post osv.",
"server-side-base-url-success-text": "Serverside-URL samsvarer ikke med standard",
"ldap-ready": "LDAP klar",
"ldap-ready-error-text": "Ikke alle LDAP-verdier er konfigurert. Dette kan ignoreres hvis du ikke bruker LDAP-autentisering.",
"ldap-ready-success-text": "Alle obligratoriske LDAP-variabler er satt.",
"build": "Bygg",
"recipe-scraper-version": "OppskriftsScraper versjon"
"build": "Build",
"recipe-scraper-version": "Versjon på oppskrift-scraper"
},
"shopping-list": {
"all-lists": "Alle lister",
@ -736,15 +737,15 @@
"are-you-sure-you-want-to-delete-this-item": "Er du sikker på at du vil slette dette elementet?",
"copy-as-text": "Kopier som tekst",
"copy-as-markdown": "Kopier som Markdown",
"delete-checked": "Slett avkrysset",
"delete-checked": "Fjern markerte elementer",
"toggle-label-sort": "Bytt etikettsortering",
"reorder-labels": "Omorganiser etiketter",
"uncheck-all-items": "Fjern merking av alle elementer",
"check-all-items": "Velg alle elementer",
"linked-recipes-count": "Ingen lenkede oppskrifter|En lenket oppskrift|{count} Lenkede oppskrifter",
"uncheck-all-items": "Fjern markering av alle elementer",
"check-all-items": "Marker alle elementer",
"linked-recipes-count": "Ingen tilknyttede oppskrifter|En tilknyttet oppskrift|{count} tilknyttede oppskrifter",
"items-checked-count": "Ingen elementer krysset av|Ett element krysset av|{count} elementer krysset av",
"no-label": "Ingen etikett",
"completed-on": "Fullført {date}"
"completed-on": "Fullført den {date}"
},
"sidebar": {
"all-recipes": "Alle oppskrifter",
@ -791,24 +792,24 @@
"tag-name": "Navn på emneord"
},
"tool": {
"tools": "Utstyr",
"tools": "Kjøkkenredskap",
"on-hand": "Tillgjengelig",
"create-a-tool": "Opprett utstyr",
"tool-name": "Navn på utstyr",
"create-new-tool": "Opprett nytt utstyr",
"create-a-tool": "Opprett kjøkkenredskap",
"tool-name": "Navn på kjøkkenredskap",
"create-new-tool": "Opprett nytt kjøkkenredskap",
"on-hand-checkbox-label": "Vis som tilgjengelig (avmerket)",
"required-tools": "Påkrevd utstyr"
"required-tools": "Påkrevde kjøkkenredskaper"
},
"user": {
"admin": "Administrator",
"are-you-sure-you-want-to-delete-the-link": "Er du sikker på at du vil slette lenken <b>{link}<b/>?",
"are-you-sure-you-want-to-delete-the-user": "Er du sikker på at du vil slette brukeren <b>{activeName} ID: {activeId}<b/>?",
"auth-method": "Autentisering metode",
"auth-method": "Autentiseringsmetode",
"confirm-link-deletion": "Bekreft sletting av lenke",
"confirm-password": "Bekreft passord",
"confirm-user-deletion": "Bekreft sletting av bruker",
"could-not-validate-credentials": "Kunne ikke validere påloggingsinformasjon",
"create-link": "Opprett link",
"create-link": "Opprett lenke",
"create-user": "Opprett bruker",
"current-password": "Gjeldende passord",
"e-mail-must-be-valid": "E-post må være gyldig",
@ -817,10 +818,10 @@
"error-cannot-delete-super-user": "Feil! Kan ikke slette superbruker",
"existing-password-does-not-match": "Eksisterende passord samsvarer ikke",
"full-name": "Fullt navn",
"generate-password-reset-link": "Generer kobling for tilbakestilling av passord",
"generate-password-reset-link": "Generer lenke for tilbakestilling av passord",
"invite-only": "Kun inviterte",
"link-id": "Lenke-ID",
"link-name": "Lenkenavn",
"link-name": "Navn på lenke",
"login": "Logg på",
"logout": "Logg ut",
"manage-users": "Administrer brukere",
@ -832,7 +833,7 @@
"password-updated": "Passord oppdatert",
"password": "Passord",
"password-strength": "Passordet er {strength}",
"please-enter-password": "Angi nytt passord.",
"please-enter-password": "Vennligst angi nytt passord.",
"register": "Registrér",
"reset-password": "Tilbakestill passord",
"sign-in": "Logg inn",
@ -854,7 +855,7 @@
"users-header": "BRUKERE",
"users": "Brukere",
"user-not-found": "Bruker ikke funnet",
"webhook-time": "Webhooks Tidsbruk",
"webhook-time": "Webhook-tid",
"webhooks-enabled": "Webhooks aktivert",
"you-are-not-allowed-to-create-a-user": "Du har ikke rettigheter til å opprette en bruker",
"you-are-not-allowed-to-delete-this-user": "Du har ikke rettigheter til å slette denne brukeren",
@ -863,7 +864,7 @@
"favorite-recipes": "Favorittoppskrifter",
"email-or-username": "E-post eller brukernavn",
"remember-me": "Husk meg",
"please-enter-your-email-and-password": "Vennligst angi brukernavnet og passordet ditt",
"please-enter-your-email-and-password": "Vennligst angi ditt brukernavn og passord",
"invalid-credentials": "Ugyldig brukerinformasjon",
"account-locked-please-try-again-later": "Kontoen er låst. Prøv igjen senere",
"user-favorites": "Brukerfavoritter",
@ -874,14 +875,14 @@
"very-strong": "Veldig sterkt"
},
"user-management": "Brukeradministrasjon",
"reset-locked-users": "Tilbakestille låste brukere",
"admin-user-creation": "Admin bruker oppretting",
"admin-user-management": "Admin User Management",
"reset-locked-users": "Tilbakestill låste brukere",
"admin-user-creation": "Opprett administratorkonto",
"admin-user-management": "Brukeradministrasjon",
"user-details": "Brukerdetaljer",
"user-name": "Brukernavn",
"authentication-method": "Autentiseringsmetode",
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Tillatelser",
"permissions": "Rettigheter",
"administrator": "Administrator",
"user-can-invite-other-to-group": "Brukeren kan invitere andre til gruppe",
"user-can-manage-group": "Brukeren kan administrere gruppe",
@ -902,7 +903,7 @@
},
"data-pages": {
"foods": {
"merge-dialog-text": "Ved å kombinere de valgte matvarene, vil de bli slått sammen til én matvare. Den første matvaren vil bli slettet, og alle referanser til denne vil bli oppdatert til å peke til den nye matvaren.",
"merge-dialog-text": "Ved å kombinere de valgte matvarene vil de bli slått sammen til én matvare. Den første matvaren vil bli slettet, og alle referanser til denne vil bli oppdatert til å peke til den nye matvaren.",
"merge-food-example": "Slår sammen {food1} til {food2}",
"seed-dialog-text": "Legg til matvarer i databasen basert på ditt lokale språk. Dette vil opprette 200+ vanlige matvarer som kan brukes til å organisere databasen din. Matvarene oversettes via en fellesinnsats fra samfunnet.",
"seed-dialog-warning": "Du har allerede noen elementer i databasen din. Denne handlingen vil ikke forene duplikater, du må håndtere dem manuelt.",
@ -913,30 +914,30 @@
"food-label": "Matvareetikett",
"edit-food": "Rediger matvare",
"food-data": "Matvaredata",
"example-food-singular": "f. eks. løk",
"example-food-plural": "f. eks. løk"
"example-food-singular": "f.eks: tomat",
"example-food-plural": "f.eks: tomater"
},
"units": {
"seed-dialog-text": "Tilfør typiske enheter i databasen basert på ditt lokale språk.",
"combine-unit-description": "Ved å kombinere de valgte enhetene vil kildeenheten og målenheten slås sammen i en enkelt enhet. {source-unit-will-be-deleted} og alle referansene til kildeenheten vil bli oppdatert til å peke til målenheten.",
"combine-unit-description": "Ved å kombinere de valgte enhetene, vil de bli slått sammen til én enkelt enhet. {source-unit-will-be-deleted} og alle referanser til denne vil bli oppdatert til å peke til den nye enheten.",
"combine-unit": "Kombiner enhet",
"source-unit": "Kildeenhet",
"target-unit": "Målenhet",
"merging-unit-into-unit": "Slår sammen {0} til {1}",
"create-unit": "Opprett enhet",
"abbreviation": "Forkortelse",
"plural-abbreviation": "Flertallsforkortelse",
"plural-abbreviation": "Forkortet flertallsform",
"description": "Beskrivelse",
"display-as-fraction": "Vis som brøkdel",
"display-as-fraction": "Vis som brøk",
"use-abbreviation": "Bruk forkortelse",
"edit-unit": "Rediger enhet",
"unit-data": "Enhetsdata",
"use-abbv": "Bruk forkortelse.",
"use-abbv": "Bruk forkortelse",
"fraction": "Brøk",
"example-unit-singular": "Feks: Spiseskje",
"example-unit-plural": "Feks: Spiseskjeer",
"example-unit-abbreviation-singular": "Feks: SS",
"example-unit-abbreviation-plural": "Feks: SS"
"example-unit-singular": "f.eks: spiseskje",
"example-unit-plural": "f.eks: spiseskjeer",
"example-unit-abbreviation-singular": "f.eks: ss",
"example-unit-abbreviation-plural": "f.eks: ss"
},
"labels": {
"seed-dialog-text": "Tilfør typiske etiketter i databasen basert på ditt lokale språk.",
@ -946,16 +947,16 @@
},
"recipes": {
"purge-exports": "Fjern eksporter",
"are-you-sure-you-want-to-delete-all-export-data": "Er du sikker på at du vil slette all historikk?",
"confirm-delete-recipes": "Er du sikker på at du vil slette denne oppskriften? Denne handlingen kan ikke angres.",
"are-you-sure-you-want-to-delete-all-export-data": "Er du sikker på at du vil slette alle eksporterte data?",
"confirm-delete-recipes": "Er du sikker på at du vil slette følgende oppskrifter? Denne handlingen kan ikke angres.",
"the-following-recipes-selected-length-will-be-exported": "Følgende oppskrifter ({0}) vil bli eksportert.",
"settings-chosen-explanation": "Innstillinger valgt her, bortsett fra det låste alternativet, vil bli brukt på alle valgte oppskrifter.",
"settings-chosen-explanation": "Innstillinger som valgt her, bortsett fra det låste alternativet, vil bli brukt på alle valgte oppskrifter.",
"selected-length-recipe-s-settings-will-be-updated": "Innstillingene til {count} oppskrift(er) vil bli oppdatert.",
"recipe-data": "Oppskriftsdata",
"recipe-data-description": "Bruk denne delen til å administrere dataene knyttet til oppskriftene dine. Du kan utføre flere massehandlinger på oppskriftene dine, inkludert eksportering, sletting, merking og tildeling av kategorier.",
"recipe-columns": "Oppskriftskolonner",
"data-exports-description": "Dette avsnittet gir lenker til tilgjengelige eksportfiler som er klare til nedlasting. Disse eksportfilene utløper, så sørg for å laste dem ned mens de fortsatt er tilgjengelige.",
"data-exports": "Data eksport",
"data-exports-description": "Her finner du lenker til tilgjengelige eksportfiler som er klare til nedlasting. Disse eksportfilene utløper, så sørg for å laste dem ned mens de fortsatt er tilgjengelige.",
"data-exports": "Dataeksport",
"tag": "Emneord",
"categorize": "Kategoriser",
"update-settings": "Oppdater innstillinger",
@ -968,16 +969,16 @@
"create-alias": "Opprett alias",
"manage-aliases": "Administrer aliaser",
"seed-data": "Tilføringsdata",
"seed": "Nøkkel",
"seed": "Tilfør",
"data-management": "Databehandling",
"data-management-description": "Velg hvilke data du vil gjøre endringer i.",
"data-management-description": "Velg hvilke data du vil gjøre endringer .",
"select-data": "Velg data",
"select-language": "Velg språk",
"columns": "Kolonner",
"combine": "Kombiner",
"categories": {
"edit-category": "Rediger kategori",
"new-category": "Ny Kategori",
"new-category": "Ny kategori",
"category-data": "Kategoridata"
},
"tags": {
@ -986,9 +987,9 @@
"tag-data": "Emneorddata"
},
"tools": {
"new-tool": "Nytt utstyr",
"edit-tool": "Rediger utstyr",
"tool-data": "Utstyrsdata"
"new-tool": "Nytt kjøkkenredskap",
"edit-tool": "Rediger kjøkkenredskap",
"tool-data": "Kjøkkenredskapsdata"
}
},
"user-registration": {
@ -1020,12 +1021,12 @@
"share-recipe": "Del oppskrift",
"default-30-days": "Standard 30 dager",
"expires-at": "Utløper den",
"recipe-link-copied-message": "Lenke kopiert til utklippstavle"
"recipe-link-copied-message": "Oppskriftslenke kopiert til utklippstavle"
},
"banner-experimental": {
"title": "Eksperimentelle Funksjoner",
"title": "Eksperimentelle funksjoner",
"description": "Denne siden inneholder eksperimentelle og uferdige funksjoner. Beklager rotet.",
"issue-link-text": "Følg fremgangen din her"
"issue-link-text": "Følg vår fremgang her"
},
"form": {
"quantity-label-abbreviated": "Ant"
@ -1039,34 +1040,34 @@
"demo_password": "Passord: {password}"
},
"ocr-editor": {
"ocr-editor": "redigere OCR",
"ocr-editor": "OCR-redigering",
"toolbar": "Verktøylinje",
"selection-mode": "Velg modus",
"selection-mode": "Markeringsmodus",
"pan-and-zoom-picture": "Panorer og zoom bilde",
"split-text": "Splitt tekst",
"preserve-line-breaks": "Behold opprinnelige linjeskift",
"split-by-block": "Del tekstblokk",
"flatten": "Flat ut, uavhengig av orginalformatering.",
"preserve-line-breaks": "Bevar opprinnelig linjeskift",
"split-by-block": "Del etter tekstblokk",
"flatten": "Flat ut uavhengig av orginalformatering",
"help": {
"help": "Hjelp",
"mouse-modes": "Musemodus",
"selection-mode": "Valgmodus (standard)",
"selection-mode-desc": "Utvalgsmodus er hovedmodus som kan brukes til å legge inn data:",
"mouse-modes": "Musemoduser",
"selection-mode": "Markeringsmodus (standard)",
"selection-mode-desc": "Markeringsmodusen er hovedmodusen som kan brukes til å legge inn data:",
"selection-mode-steps": {
"draw": "Tegn et rektangel på teksten du vil velge.",
"draw": "Tegn en firkant på teksten du vil velge.",
"click": "Klikk på valgfritt felt til høyre, deretter klikk tilbake på firkanten over bildet.",
"result": "Den markerte teksten vil bli lagt inn i det tidligere valgte feltet."
},
"pan-and-zoom-mode": "Panér og Zoom modus",
"pan-and-zoom-desc": "Velg pan og zoom ved å klikke ikonet. Denne modusen gjør det lettere å bruke store bilder, ved at du kan zoome og flytte rundt på bildet.",
"split-text-mode": "Splitt tekstmodus",
"pan-and-zoom-mode": "Panorer- og zoommodus",
"pan-and-zoom-desc": "Velg panorer og zoom ved å klikke ikonet. Denne modusen gjør det lettere å bruke store bilder ved at du kan zoome og flytte rundt på bildet.",
"split-text-mode": "Splitt-tekst-moduser",
"split-modes": {
"line-mode": "Linjemodus (standard)",
"line-mode-desc": "I linjemodus, vil teksten bli oppdelt ut i fra de orginale linjeskiftene. Denne modusen er nytting når man legger til ingredienslister i bulk der hver ingrediens er på egen linje.",
"block-mode": "Blokk modus",
"block-mode-desc": "I blokkmodus vil teksten bli delt i blokker. Denne modusen er nyttig når man legger inn istruksjoner i bulk, som vanligvis er skrevet i avsnitt.",
"line-mode-desc": "I linjemodus vil teksten bli oppdelt ut i fra de orginale linjeskiftene. Denne modusen er nyttig når man legger til ingredienslister i bulk der hver ingrediens er på egen linje.",
"block-mode": "Blokkmodus",
"block-mode-desc": "I blokkmodus vil teksten bli delt inn i blokker. Denne modusen er nyttig når du legger til instruksjoner i bulk som vanligvis er skrevet i avsnitt.",
"flat-mode": "Flat modus",
"flat-mode-desc": "I flat modus vil teksten bli lagt til i det valgte oppskriftsfeltet uten noen linjeskift."
"flat-mode-desc": "I flat modus vil teksten bli lagt til i det valgte oppskriftsfeltet uten linjeskift."
}
}
},
@ -1078,9 +1079,9 @@
"button-label-get-summary": "Hent sammendrag",
"button-label-open-details": "Detaljer",
"info-description-data-dir-size": "Størrelse på datamappe",
"info-description-log-file-size": "Loggfil størrelse",
"info-description-cleanable-directories": "Rensbare kataloger",
"info-description-cleanable-images": "Rydbare bilder",
"info-description-log-file-size": "Størrelse på loggfil",
"info-description-cleanable-directories": "Mapper som kan ryddes i",
"info-description-cleanable-images": "Ryddbare bilder",
"storage": {
"title-temporary-directory": "Midlertidig mappe (.temp)",
"title-backups-directory": "Mappe for sikkerhetskopier (backups)",
@ -1091,7 +1092,7 @@
"action-delete-log-files-name": "Slett loggfiler",
"action-delete-log-files-description": "Sletter alle loggfilene",
"action-clean-directories-name": "Fjern kataloger",
"action-clean-directories-description": "Fjerner alle oppskriftsmapper som ikke er gyldige UUIDer",
"action-clean-directories-description": "Fjerner alle oppskriftsmapper som ikke er gyldige UUID-er",
"action-clean-temporary-files-name": "Fjern midlertidige filer",
"action-clean-temporary-files-description": "Fjerner alle filer og mapper i .temp-mappen",
"action-clean-images-name": "Fjern bilder",
@ -1100,70 +1101,70 @@
"actions-description-destructive": "destruktiv",
"actions-description-irreversible": "irreversibel",
"logs-action-refresh": "Oppdater logger",
"logs-page-title": "Mealie Log",
"logs-page-title": "Mealie log",
"logs-tail-lines-label": "Siste linjer"
},
"mainentance": {
"actions-title": "Handlinger"
},
"ingredients-natural-language-processor": "Ingredients Natural Language Processor",
"ingredients-natural-language-processor-explanation": "Mealie bruker Conditional Random Fields (CRFs) for å analysere og behandle ingredienser. Modellen som brukes til ingredienser, er basert på et datasett med over 100 000 ingredienser satt sammen av New York Times. Vær oppmerksom på at siden modellen kun er trent på engelsk, kan resultatene variere når du bruker modellen på andre språk. Denne siden er en lekeplass for å teste modellen.",
"ingredients-natural-language-processor": "Naturlig språkprossesering for ingredienser",
"ingredients-natural-language-processor-explanation": "Mealie bruker Conditional Random Fields (CRFs) for å analysere og behandle ingredienser. Modellen som brukes til ingredienser er basert på et datasett med over 100 000 ingredienser satt sammen av New York Times. Vær oppmerksom på at siden modellen kun er trent på engelsk, kan resultatene variere når du bruker modellen på andre språk. På denne siden kan du teste modellen.",
"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",
"brute": "Brute",
"show-individual-confidence": "Vis individuell tillit",
"ingredient-text": "Ingredient tekst",
"show-individual-confidence": "Vis individuell konfidens",
"ingredient-text": "Ingredienstekst",
"average-confident": "{0} Troverdig",
"try-an-example": "Prøv et eksempel",
"parser": "Parser",
"background-tasks": "Bakgrunnsoppgaver",
"background-tasks-description": "Her kan du se alle aktive bakgrunnsoppgaver og deres status",
"background-tasks-description": "Her kan du se alle de aktive bakgrunnsoppgavene og statusen deres",
"no-logs-found": "Ingen logger funnet",
"tasks": "Oppgaver"
},
"profile": {
"welcome-user": "👋 Velkommen, {0}",
"description": "Behandle din profil, oppskrifter og gruppeinnstillinger.",
"get-invite-link": "Få invitasjonslink",
"description": "Administrer din profil, oppskrifter og gruppeinnstillinger.",
"get-invite-link": "Få invitasjonslenke",
"get-public-link": "Få offentlig lenke",
"account-summary": "Kontosammendrag",
"account-summary-description": "Her er en oppsummering av gruppens informasjon",
"account-summary-description": "Her er en oppsummering av informasjonen til gruppen din",
"group-statistics": "Gruppestatistikk",
"group-statistics-description": "Gruppestatistikken din gir deg et innblikk i hvordan du bruker Mealie.",
"storage-capacity": "Lagringskapasitet",
"storage-capacity-description": "Lagringskapasiteten er en beregning av bildene og ressursene du har lastet opp.",
"personal": "Personlig",
"personal-description": "Dette er innstillingene som er personlige for deg. Endringer her påvirker ikke andre brukere",
"personal-description": "Dette er innstillingene som er personlige for deg. Endringer gjort her påvirker ikke andre brukere",
"user-settings": "Brukerinnstillinger",
"user-settings-description": "Administrer innstillingene, endre passordet og oppdater e-postadressen din",
"api-tokens-description": "Håndter dine API-tokens for tilgang fra eksterne programmer",
"group-description": "Disse elementene deles innad i gruppen din. Å redigere ett av dem vil endre det for hele gruppen!",
"api-tokens-description": "Administrer dine API-tokens for tilgang fra eksterne programmer",
"group-description": "Disse elementene deles innad i gruppen din. Redigering av elementenee vil føre til endringer for hele gruppen!",
"group-settings": "Gruppeinnstillinger",
"group-settings-description": "Administrer felles gruppeinnstillinger som måltidsplan- og personverninnstillinger.",
"group-settings-description": "Administrer felles gruppeinnstillinger som innstillinger for måltidsplaner og personvern.",
"cookbooks-description": "Administrer en samling av oppskriftskategorier og generer sider for dem.",
"members": "Medlemmer",
"members-description": "Se hvem som er i gruppen din og adminstrer deres tillatelser.",
"webhooks-description": "Setup webhooks that trigger on days that you have have mealplan scheduled.",
"notifiers": "Varslere",
"members-description": "Se hvem som er medlem av gruppen din og adminstrer deres rettigheter.",
"webhooks-description": "Sett opp webhooks som utløses på dager du har planlagt måltidsplaner.",
"notifiers": "Varslingsagenter",
"notifiers-description": "Sett opp e-post- og pushvarsler som utløses av spesifikke hendelser.",
"manage-data": "Administrer data",
"manage-data-description": "Administrer dine matvarer og enheter (flere alternativer kommer snart)",
"data-migrations": "Datamigrering",
"data-migrations": "Dataoverføringer",
"data-migrations-description": "Overfør eksisterende data fra andre programmer som Nextcloud Recipes og Chowdown",
"email-sent": "Epost sendt",
"email-sent": "E-post sendt",
"error-sending-email": "Feil ved sending av e-post",
"personal-information": "Personlig Informasjon",
"personal-information": "Personlig informasjon",
"preferences": "Preferanser",
"show-advanced-description": "Vis avanserte funksjoner (API-nøkler, webhooks og databehandling)",
"back-to-profile": "Tilbake til profil",
"looking-for-privacy-settings": "Leter du etter personverninnstillinger?",
"manage-your-api-tokens": "Håndter dine API-tokens",
"manage-your-api-tokens": "Administrer dine API-tokens",
"manage-user-profile": "Administrer brukerprofil",
"manage-cookbooks": "Administrere kokebøker",
"manage-cookbooks": "Administrer kokebøker",
"manage-members": "Administrer medlemmer",
"manage-webhooks": "Administrer webhooks",
"manage-notifiers": "Behandle varslere",
"manage-data-migrations": "Håndter datamigreringer"
"manage-notifiers": "Administrer varslingsagenter",
"manage-data-migrations": "Administrer dataoverføringer"
},
"cookbook": {
"cookbooks": "Kokebøker",
@ -1174,7 +1175,7 @@
"filter-options-description": "Når krev alle er valgt, vil kokeboken bare inkludere oppskrifter som har alle de valgte elementene. Dette gjelder for hvert delsett av valgene og ikke et tverrsnitt av de valgte elementene.",
"require-all-categories": "Krev alle kategorier",
"require-all-tags": "Krev alle emneord",
"require-all-tools": "Krev alt utstyr",
"require-all-tools": "Krev alle kjøkkenredskaper",
"cookbook-name": "Navn på kokebok",
"cookbook-with-name": "Kokebok {0}"
}

View File

@ -114,9 +114,9 @@
"json": "JSON",
"keyword": "Słowo kluczowe",
"link-copied": "Odnośnik skopiowany",
"loading": "Loading",
"loading": "Ładowanie",
"loading-events": "Ładowanie wydarzeń",
"loading-recipe": "Loading recipe...",
"loading-recipe": "Ładowanie przepisów...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipes": "Ładowanie przepisów",
"message": "Wiadomość",
@ -128,7 +128,7 @@
"no-recipe-found": "Nie znaleziono przepisu",
"ok": "OK",
"options": "Opcje:",
"plural-name": "Plural Name",
"plural-name": "Nazwa w liczbie mnogiej",
"print": "Drukuj",
"print-preferences": "Właściwości drukowania",
"random": "Losowa",
@ -198,7 +198,8 @@
"refresh": "Odśwież",
"upload-file": "Prześlij plik",
"created-on-date": "Utworzono dnia: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Czy na pewno chcesz usunąć <b>{groupName}<b/>?",
@ -213,7 +214,7 @@
"group-id-with-value": "ID grupy: {groupID}",
"group-name": "Nazwa grupy",
"group-not-found": "Nie znaleziono grupy",
"group-token": "Group Token",
"group-token": "Token grupy",
"group-with-value": "Grupa: {groupID}",
"groups": "Grupy",
"manage-groups": "Zarządzaj grupami",
@ -249,7 +250,7 @@
"general-preferences": "Ustawienia ogólne",
"group-recipe-preferences": "Ustawienia grupy przepisów",
"report": "Zgłoś",
"report-with-id": "Report ID: {id}",
"report-with-id": "ID zgłoszenia: {id}",
"group-management": "Zarządzanie grupą",
"admin-group-management": "Administracja Zarządzanie Grupami",
"admin-group-management-text": "Zmiany w tej grupie zostaną natychmiast odzwierciedlone.",
@ -304,7 +305,7 @@
"for-type-meal-types": "dla {0} rodzajów posiłków",
"meal-plan-rules": "Zasady planu posiłków",
"new-rule": "Nowa reguła",
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the categories of the rules will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
"meal-plan-rules-description": "Możesz tworzyć reguły automatycznego wyboru przepisów dla planów posiłkowych. Te reguły są używane przez serwer do określenia losowej puli przepisów do wyboru podczas tworzenia planów posiłków. Zauważ, że jeśli reguły mają ograniczenia tego samego dnia/typu, kategorie reguł zostaną połączone. W praktyce tworzenie duplikatów reguł jest zbędne, ale jest to możliwe.",
"new-rule-description": "Przy tworzeniu nowej reguły dotyczącej planu posiłków można ograniczyć regułę, tak aby miała zastosowanie do określonego dnia tygodnia i/lub określonego rodzaju posiłku. Aby zastosować regułę do wszystkich dni lub wszystkich rodzajów posiłków można ustawić regułę na \"Dowolny\", co będzie miało zastosowanie do wszystkich możliwych wartości dla dnia i/lub typu posiłku.",
"recipe-rules": "Reguły przepisów",
"applies-to-all-days": "Dotyczy wszystkich dni",
@ -341,8 +342,8 @@
"title": "Mealie Pre v1.0"
},
"tandoor": {
"description-long": "Mealie can import recipes from Tandoor. Export your data in the \"Default\" format, then upload the .zip below.",
"title": "Tandoor Recipes"
"description-long": "Mealie może zaimportować przepisy z Tandoor. Wyeksportuj swoje przepisy w formacie HTML, a następnie prześlij plik .zip poniżej.",
"title": "Przepisy Tandoor"
},
"recipe-data-migrations": "Migracja danych przepisów",
"recipe-data-migrations-explanation": "Receptury mogą być przeniesione z innej wspieranej aplikacji na Mealie. To świetny sposób na rozpoczęcie z Mealie.",
@ -355,8 +356,8 @@
"paprika-text": "Mealie może importować przepisy z aplikacji Paprika. Eksportuj swoje przepisy z papriki, zmień nazwę rozszerzenia eksportu na .zip i prześlij je poniżej.",
"mealie-text": "Mealie może importować receptury z aplikacji Mealie z wersji przed v1.0. Wyeksportuj swoje przepisy ze starej instancji i prześlij plik zip poniżej. Pamiętaj, że tylko receptury mogą być zaimportowane z eksportu.",
"plantoeat": {
"title": "Plan to Eat",
"description-long": "Mealie can import recipies from Plan to Eat."
"title": "Zaplanuj jedzenie",
"description-long": "Mealie może importować przepisy z Plan to Eat."
}
},
"new-recipe": {
@ -468,11 +469,11 @@
"date-format-hint-yyyy-mm-dd": "Format RRRR-MM-DD",
"add-to-list": "Dodaj do listy",
"add-to-plan": "Dodaj do planu",
"add-to-timeline": "Add to Timeline",
"add-to-timeline": "Dodaj do osi czasu",
"recipe-added-to-list": "Przepis dodany do listy",
"recipes-added-to-list": "Przepisy dodane do listy",
"recipe-added-to-mealplan": "Przepis dodany do planu posiłków",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipes-to-list": "Nie udało się dodać przepisu do listy",
"failed-to-add-recipe-to-mealplan": "Nie udało się dodać przepisu do planu posiłków",
"yield": "Wydajność",
"quantity": "Ilość",
@ -495,10 +496,10 @@
"locked": "Zablokowany",
"public-link": "Link publiczny",
"timer": {
"kitchen-timer": "Kitchen Timer",
"kitchen-timer": "Minutnik",
"start-timer": "Włącz minutnik",
"pause-timer": "Pause Timer",
"resume-timer": "Resume Timer",
"pause-timer": "Zatrzymaj minutnik",
"resume-timer": "Wznów minutnik",
"stop-timer": "Zatrzymaj minutnik"
},
"edit-timeline-event": "Edytuj zdarzenie osi czasu",
@ -510,11 +511,11 @@
"how-did-it-turn-out": "Jak się to udało?",
"user-made-this": "{user} ugotował(a) to",
"last-made-date": "Ostatnio ugotowano {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"api-extras-description": "Dodatki w przepisach są kluczową cechą API Mealie. Pozwalają na tworzenie niestandardowych par kluczy/wartości JSON w przepisie do odwoływania się przez zewnętrzne aplikacje. Możesz użyć tych kluczy do wyzwalania automatyzacji lub przekazywania niestandardowych wiadomości do twoich wybranych urządzeń.",
"message-key": "Klucz Wiadomości",
"parse": "Analizuj",
"attach-images-hint": "Dołącz obrazy przeciągając i upuszczając je do edytora",
"drop-image": "Drop image",
"drop-image": "Upuść zdjęcie",
"enable-ingredient-amounts-to-use-this-feature": "Włącz ilości składników, aby użyć tej funkcji",
"recipes-with-units-or-foods-defined-cannot-be-parsed": "Nie można przeanalizować przepisów z już zdefiniowanymi jednostkami lub żywnością.",
"parse-ingredients": "Analizuj tekst składników",
@ -553,7 +554,7 @@
"recipe-yield": "Wydajność przepisu",
"unit": "Jednostka",
"upload-image": "Prześlij obraz",
"screen-awake": "Keep Screen Awake",
"screen-awake": "Pozostaw ekran włączony",
"remove-image": "Usuń obraz"
},
"search": {
@ -573,7 +574,7 @@
"search-hint": "Naciśnij '/'",
"advanced": "Zaawansowane",
"auto-search": "Auto wyszukiwanie",
"no-results": "No results found"
"no-results": "Nie znaleziono wyników"
},
"settings": {
"add-a-new-theme": "Dodaj nowy motyw",
@ -582,7 +583,7 @@
"backup-created": "Kopia zapasowa utworzona pomyślnie",
"backup-created-at-response-export_path": "Kopia zapasowa została utworzona w {path}",
"backup-deleted": "Kopia zapasowa została usunięta",
"restore-success": "Restore successful",
"restore-success": "Przywracanie zakończone sukcesem",
"backup-tag": "Etykieta kopii zapasowej",
"create-heading": "Utwórz kopię zapasową",
"delete-backup": "Usuń kopię zapasową",
@ -691,13 +692,13 @@
"configuration": "Konfiguracja",
"docker-volume": "Docker Volume",
"docker-volume-help": "Mealie wymaga, aby kontener frontendu i backendu współdzieliły ten sam wolumen docker lub pamięć. Zapewnia to prawidłowy dostęp do zdjęć i zasobów przechowywanych na dysku.",
"volumes-are-misconfigured": "Volumes are misconfigured.",
"volumes-are-misconfigured": "Woluminy są skonfigurowane nieprawidłowo.",
"volumes-are-configured-correctly": "Wolumeny są skonfigurowane poprawnie.",
"status-unknown-try-running-a-validation": "Status nieznany. Spróbuj wykonać walidację.",
"validate": "Sprawdź",
"email-configuration-status": "Status konfiguracji Email",
"email-configured": "Email Configured",
"email-test-results": "Email Test Results",
"email-configured": "E-mail skonfigurowany",
"email-test-results": "Wyniki testu e-mail",
"ready": "Gotowe",
"not-ready": "Niegotowy - Sprawdź zmienne środowiskowe",
"succeeded": "Powiodło się",
@ -876,7 +877,7 @@
"user-management": "Zarządzanie użytkownikami",
"reset-locked-users": "Zresetuj zablokowanych użytkowników",
"admin-user-creation": "Administracja Tworzenie Użytkownika",
"admin-user-management": "Admin User Management",
"admin-user-management": "Zarządzanie użytkownikami",
"user-details": "Dane użytkownika",
"user-name": "Nazwa użytkownika",
"authentication-method": "Sposób uwierzytelniania",
@ -887,11 +888,11 @@
"user-can-manage-group": "Użytkownik może zarządzać grupą",
"user-can-organize-group-data": "Użytkownik może organizować dane grupy",
"enable-advanced-features": "Włącz zaawansowane funkcje",
"it-looks-like-this-is-your-first-time-logging-in": "It looks like this is your first time logging in.",
"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!",
"forgot-password": "Forgot Password",
"forgot-password-text": "Please enter your email address and we will send you a link to reset your password.",
"changes-reflected-immediately": "Changes to this user will be reflected immediately."
"it-looks-like-this-is-your-first-time-logging-in": "Wygląda na to, że to jest twoje pierwsze logowanie.",
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "Nie chcesz już tego widzieć? Pamiętaj, aby zmienić swój adres e-mail w ustawieniach użytkownika!",
"forgot-password": "Nie pamiętam hasła",
"forgot-password-text": "Podaj adres e-mail konta, do którego chcesz odzyskać hasło.",
"changes-reflected-immediately": "Zmiany dla tego użytkownika będą zastosowane natychmiastowo."
},
"language-dialog": {
"translated": "przetłumaczone",
@ -913,8 +914,8 @@
"food-label": "Etykieta Żywności",
"edit-food": "Edytuj Żywność",
"food-data": "Dane Żywności",
"example-food-singular": "ex: Onion",
"example-food-plural": "ex: Onions"
"example-food-singular": "np. Cebula",
"example-food-plural": "np. Cebule"
},
"units": {
"seed-dialog-text": "Wypełnij bazę zwyczajowymi jednostkami dla wybranego języka.",
@ -933,8 +934,8 @@
"unit-data": "Dane Jednostki",
"use-abbv": "Użyj Skr.",
"fraction": "Ułamki",
"example-unit-singular": "ex: Tablespoon",
"example-unit-plural": "ex: Tablespoons",
"example-unit-singular": "np. Łyżka stołowa",
"example-unit-plural": "np. Łyżki stołowe",
"example-unit-abbreviation-singular": "ex: Tbsp",
"example-unit-abbreviation-plural": "ex: Tbsps"
},
@ -965,8 +966,8 @@
"delete-recipes": "Usuń Przepisy",
"source-unit-will-be-deleted": "Jednostka źródłowa zostanie usunięta"
},
"create-alias": "Create Alias",
"manage-aliases": "Manage Aliases",
"create-alias": "Utwórz alias",
"manage-aliases": "Zarządzaj aliasami",
"seed-data": "Dane przykładowe",
"seed": "Seed",
"data-management": "Zarządzanie Danymi",
@ -982,18 +983,18 @@
},
"tags": {
"new-tag": "Nowy Tag",
"edit-tag": "Edit Tag",
"tag-data": "Tag Data"
"edit-tag": "Edytuj tag",
"tag-data": "Dane tagu"
},
"tools": {
"new-tool": "New Tool",
"edit-tool": "Edit Tool",
"tool-data": "Tool Data"
"new-tool": "Nowe Narzędzie",
"edit-tool": "Edytuj Narzędzie",
"tool-data": "Dane narzędzia"
}
},
"user-registration": {
"user-registration": "Rejestracja użytkownika",
"registration-success": "Registration Success",
"registration-success": "Rejestracja przebiegła pomyślnie",
"join-a-group": "Dołącz do grupy",
"create-a-new-group": "Stwórz nową grupę",
"provide-registration-token-description": "Podaj kod rejestracyjny powiązany z grupą do której chcesz dołączyć. Taki kod uzyskać możesz od użytkownika który przynależy już do owej grupy.",
@ -1040,7 +1041,7 @@
},
"ocr-editor": {
"ocr-editor": "Edytor OCR",
"toolbar": "Toolbar",
"toolbar": "Pasek narzędzi",
"selection-mode": "Tryb wyboru",
"pan-and-zoom-picture": "Przesuwanie i powiększanie obrazu",
"split-text": "Podziel tekst",
@ -1048,8 +1049,8 @@
"split-by-block": "Podziel według bloku tekstowego",
"flatten": "Wyrównaj niezależnie od oryginalnego formatowania",
"help": {
"help": "Help",
"mouse-modes": "Mouse modes",
"help": "Pomoc",
"mouse-modes": "Tryb myszy",
"selection-mode": "Tryb wyboru (domyślny)",
"selection-mode-desc": "Tryb wyboru jest głównym trybem, który służy do wprowadzenia danych:",
"selection-mode-steps": {
@ -1125,7 +1126,7 @@
"welcome-user": "👋 Witaj, {0}",
"description": "Zarządzaj swoim profilem, przepisami i ustawieniami grupy.",
"get-invite-link": "Uzyskaj link z zaproszeniem",
"get-public-link": "Get Public Link",
"get-public-link": "Uzyskaj link publiczny",
"account-summary": "Podsumowanie konta",
"account-summary-description": "Oto podsumowanie informacji o Twojej grupie",
"group-statistics": "Statystyki Grupy",

View File

@ -198,7 +198,8 @@
"refresh": "Recarregar",
"upload-file": "Enviar arquivo",
"created-on-date": "Criado em {0}",
"unsaved-changes": "Você possui alterações não salvas. Deseja salvar antes de sair? Ok para salvar, Cancelar para descartar alterações."
"unsaved-changes": "Você possui alterações não salvas. Deseja salvar antes de sair? Ok para salvar, Cancelar para descartar alterações.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Tem certeza que deseja excluir o grupo <b>{groupName}<b/>?",

View File

@ -114,7 +114,7 @@
"json": "JSON",
"keyword": "Palavra-chave",
"link-copied": "Ligação copiada",
"loading": "Loading",
"loading": "A carregar",
"loading-events": "A carregar Eventos",
"loading-recipe": "A carregar receita...",
"loading-ocr-data": "A carregar dados OCR...",
@ -198,7 +198,8 @@
"refresh": "Atualizar",
"upload-file": "Carregar ficheiro",
"created-on-date": "Criado em: {0}",
"unsaved-changes": "Tem alterações por gravar. Quer gravar antes de sair? OK para gravar, Cancelar para descartar alterações."
"unsaved-changes": "Tem alterações por gravar. Quer gravar antes de sair? OK para gravar, Cancelar para descartar alterações.",
"clipboard-copy-failure": "Erro ao copiar para a área de transferência."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Tem a certeza que quer eliminar <b>{groupName}</b>?",
@ -510,7 +511,7 @@
"how-did-it-turn-out": "Que tal ficou?",
"user-made-this": "{user} fez isto",
"last-made-date": "Última vez {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"api-extras-description": "Extras para receitas são funcionalidades chave da API Mealie. Estas permitem criar, dentro de uma receita, pares personalizados de chave/valor em JSON, para referência a partir de aplicações de terceiros. Pode usar essas chaves para fornecer informações, por exemplo, para acionar automações ou mensagens personalizadas para transmitir a um determinado dispositivo.",
"message-key": "Chave de Mensagem",
"parse": "Interpretar",
"attach-images-hint": "Anexe imagens arrastando e soltando-as no editor",

View File

@ -198,7 +198,8 @@
"refresh": "Reîncarcă",
"upload-file": "Încărcă fișier",
"created-on-date": "Creat pe {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Sunteți sigur că doriți să ștergeți <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Обновить",
"upload-file": "Загрузить файл",
"created-on-date": "Создано: {0}",
"unsaved-changes": "У вас есть несохраненные изменения. Вы хотите сохранить их перед выходом?"
"unsaved-changes": "У вас есть несохраненные изменения. Вы хотите сохранить их перед выходом?",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Вы действительно хотите удалить <b>{groupName}<b/>?",

View File

@ -114,10 +114,10 @@
"json": "JSON",
"keyword": "Kľučové slovo",
"link-copied": "Odkaz bol skopírovaný",
"loading": "Loading",
"loading": "Načítavam",
"loading-events": "Načítanie udalostí",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipe": "Načítavam recept...",
"loading-ocr-data": "Načítavam OCR dáta...",
"loading-recipes": "Nahrávanie receptu",
"message": "Správa",
"monday": "Pondelok",
@ -128,7 +128,7 @@
"no-recipe-found": "Žiadny recept nenájdený",
"ok": "OK",
"options": "Možnosti:",
"plural-name": "Plural Name",
"plural-name": "Meno v množnom čísle",
"print": "Tlačiť",
"print-preferences": "Nastavenia tlače",
"random": "Náhodné",
@ -198,7 +198,8 @@
"refresh": "Obnoviť",
"upload-file": "Nahrať súbor",
"created-on-date": "Vytvorené: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Naozaj chcete odstrániť <b>{groupName}<b/>?",
@ -495,11 +496,11 @@
"locked": "Zamknuté",
"public-link": "Verejný odkaz",
"timer": {
"kitchen-timer": "Kitchen Timer",
"start-timer": "Start Timer",
"pause-timer": "Pause Timer",
"resume-timer": "Resume Timer",
"stop-timer": "Stop Timer"
"kitchen-timer": "Kuchynský časovač",
"start-timer": "Spustiť časovač",
"pause-timer": "Pozastaviť časovač",
"resume-timer": "Znova spustiť časovač",
"stop-timer": "Zastaviť časovač"
},
"edit-timeline-event": "Upraviť udalosť na časovej osi",
"timeline": "Časová os",

View File

@ -198,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Ste prepričani, da želite izbrisati <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Освежи",
"upload-file": "Учитај датотеку",
"created-on-date": "Крерирано: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Да ли сте сигурни да желите да обришете <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Uppdatera",
"upload-file": "Ladda upp fil",
"created-on-date": "Skapad {0}",
"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."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Är du säker på att du vill radera <b>{groupName}<b/>?",
@ -249,7 +250,7 @@
"general-preferences": "Generella inställningar",
"group-recipe-preferences": "Inställningar för receptgrupper",
"report": "Rapport",
"report-with-id": "Report ID: {id}",
"report-with-id": "RapportID: {id}",
"group-management": "Grupphantering",
"admin-group-management": "Hantering av administratörsgrupp",
"admin-group-management-text": "Ändringar i denna grupp kommer att återspeglas omedelbart.",
@ -355,7 +356,7 @@
"paprika-text": "Mealie kan importera recept från Paprika-applikationen. Exportera dina recept från Paprika, byt namn på filnamnstillägget på exporten till .zip och ladda upp det nedan.",
"mealie-text": "Mealie kan importera recept från Mealieapplikationen från en före v1.0 version. Exportera dina recept från din gamla instans, och ladda upp zip-filen nedan. Observera att endast recept kan importeras från exporten.",
"plantoeat": {
"title": "Plan to Eat",
"title": "Planera att äta",
"description-long": "Mealie kan importera recept från Plan to Eat."
}
},
@ -510,7 +511,7 @@
"how-did-it-turn-out": "Hur blev rätten?",
"user-made-this": "{user} lagade detta",
"last-made-date": "Senast lagad {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"api-extras-description": "Recept extras är en viktig funktion i Mealie's API. Med hjälp av dem kan du skapa anpassade JSON-nyckel/värdepar i ett recept, som du kan referera till från tredjepartsapplikationer. Du kan använda dessa nycklar för att tillhandahålla information, till exempel för att trigga automatiseringar eller anpassade meddelanden som ska vidarebefordras till önskad enhet.",
"message-key": "Meddelandenyckel",
"parse": "Läs in",
"attach-images-hint": "Bifoga bilder genom att dra och släppa dem i redigeraren",
@ -596,7 +597,7 @@
"back-restore-description": "Återställning av den här backuppen kommer att skriva över all information i databasen och datakatalogen och ersätta allt med innehållet i nackuppen. {cannot-be-undone} Om återställningen går bra kommer du att loggas ut.",
"cannot-be-undone": "Denna åtgärd kan inte ångras - använd med försiktighet.",
"postgresql-note": "Om du använder PostGreSQL, vänligen granska {backup-restore-process} innan du återställer.",
"backup-restore-process-in-the-documentation": "backup/restore process in the documentation",
"backup-restore-process-in-the-documentation": "säkerhetskopiering/återställning i dokumentationen",
"irreversible-acknowledgment": "Jag förstår att denna åtgärd är oåterkallelig, destruktiv och kan orsaka dataförlust",
"restore-backup": "Återställ säkerhetskopia"
},
@ -683,21 +684,21 @@
"webhooks-caps": "WEBHOOKS",
"webhooks": "Webhooks",
"webhook-name": "Namn på webhook",
"description": "The webhooks defined below will be executed when a meal is defined for the day. At the scheduled time the webhooks will be sent with the data from the recipe that is scheduled for the day. Note that webhook execution is not exact. The webhooks are executed on a 5 minutes interval so the webhooks will be executed within 5 +/- minutes of the scheduled."
"description": "De webhooks som definieras nedan kommer att exekveras när en måltid definieras för dagen. Vid den schemalagda tiden skickas webhooks med data från receptet som är schemalagt för dagen. Observera att exekveringen av webhooks inte är exakt. Webhooks exekveras med ett 5 minuters intervall så att webhooks kommer att exekveras inom 5 +/- minuter från den schemalagda tiden."
},
"bug-report": "Buggrapport",
"bug-report-information": "Use this information to report a bug. Providing details of your instance to developers is the best way to get your issues resolved quickly.",
"bug-report-information": "Använd denna information för att rapportera ett fel. Att ge utvecklarna information om din installation är det bästa sättet att få dina problem lösta snabbt.",
"tracker": "Spårare",
"configuration": "Konfiguration",
"docker-volume": "Docker volym",
"docker-volume-help": "Mealie requires that the frontend container and the backend share the same docker volume or storage. This ensures that the frontend container can properly access the images and assets stored on disk.",
"volumes-are-misconfigured": "Volumes are misconfigured.",
"volumes-are-configured-correctly": "Volumes are configured correctly.",
"status-unknown-try-running-a-validation": "Status Unknown. Try running a validation.",
"docker-volume-help": "Mealie kräver att frontend behållare och backend dela samma docker volym eller lagring. Detta säkerställer att frontend-behållaren på rätt sätt kan komma åt bilder och tillgångar som lagras på disken.",
"volumes-are-misconfigured": "Volymer är felkonfigurerade.",
"volumes-are-configured-correctly": "Volymer är konfigurerade korrekt.",
"status-unknown-try-running-a-validation": "Status okänd. Försök köra en validering.",
"validate": "Validera",
"email-configuration-status": "Email Configuration Status",
"email-configured": "Email Configured",
"email-test-results": "Email Test Results",
"email-configuration-status": "E-post konfigurationsstatus",
"email-configured": "E-post konfigurerad",
"email-test-results": "Testresultat för e-post",
"ready": "Redo",
"not-ready": "Inte redo - Kontrollera miljövariabler",
"succeeded": "Lyckades",
@ -707,16 +708,16 @@
"application-version-error-text": "Din nuvarande version ({0}) matchar inte den senaste utgåvan. Överväg att uppdatera till den senaste versionen ({1}).",
"mealie-is-up-to-date": "Mealie är uppdaterad",
"secure-site": "Säker webbplats",
"secure-site-error-text": "Serve via localhost or secure with https. Clipboard and additional browser APIs may not work.",
"secure-site-error-text": "Servera via localhost eller säker med https. Urklipp och ytterligare webbläsare API: er kanske inte fungerar.",
"secure-site-success-text": "Webbplatsen nås av localhost eller https",
"server-side-base-url": "Serverns bas-URL",
"server-side-base-url-error-text": "`BASE_URL` is still the default value on API Server. This will cause issues with notifications links generated on the server for emails, etc.",
"server-side-base-url-success-text": "Server Side URL does not match the default",
"server-side-base-url-error-text": "`BASE_URL` är fortfarande standardvärdet på API-servern. Detta kommer att orsaka problem med meddelanden som genereras på servern för e-postmeddelanden, etc.",
"server-side-base-url-success-text": "Serversidans URL matchar inte standard",
"ldap-ready": "LDAP Redo",
"ldap-ready-error-text": "Not all LDAP Values are configured. This can be ignored if you are not using LDAP Authentication.",
"ldap-ready-success-text": "Required LDAP variables are all set.",
"ldap-ready-error-text": "Alla LDAP-värden är inte konfigurerade. Detta kan ignoreras om du inte använder LDAP-autentisering.",
"ldap-ready-success-text": "Alla obligatoriska LDAP-variabler är satta.",
"build": "Bygg",
"recipe-scraper-version": "Recipe Scraper Version"
"recipe-scraper-version": "Version av Recept-scraper"
},
"shopping-list": {
"all-lists": "Visa alla listor",
@ -730,19 +731,19 @@
"food": "Mat",
"note": "Anteckning",
"label": "Etikett",
"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": "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",
"manage-labels": "Hantera etiketter",
"are-you-sure-you-want-to-delete-this-item": "Är du säker på att du vill ta bort detta objekt?",
"copy-as-text": "Kopiera som text",
"copy-as-markdown": "Kopiera som Markdown",
"delete-checked": "Ta bort markerade",
"toggle-label-sort": "Toggle Label Sort",
"toggle-label-sort": "Växla etikettsortering",
"reorder-labels": "Ordna om etiketter",
"uncheck-all-items": "Avmarkera alla objekt",
"check-all-items": "Markera alla objekt",
"linked-recipes-count": "No Linked Recipes|One Linked Recipe|{count} Linked Recipes",
"items-checked-count": "No items checked|One item checked|{count} items checked",
"linked-recipes-count": "Inga länkade recept|Ett länkat recept|{count} länkade recept",
"items-checked-count": "Inga artiklar markerade|En artikel markerad|{count} artiklar markerade",
"no-label": "Ingen etikett",
"completed-on": "Slutförd på {date}"
},
@ -876,7 +877,7 @@
"user-management": "Användarhantering",
"reset-locked-users": "Återställ låsta användare",
"admin-user-creation": "Skapande av adminanvändare",
"admin-user-management": "Admin User Management",
"admin-user-management": "Användarhantering",
"user-details": "Användarinformation",
"user-name": "Användarnamn",
"authentication-method": "Autentiseringsmetod",
@ -907,8 +908,8 @@
"seed-dialog-text": "Fyll databasen med livsmedel baserade på ditt språk. Detta kommer att skapa 200+ vanliga livsmedel som kan användas för att organisera din databas. Livsmedlen översätts via ett gemenskapsinsats.",
"seed-dialog-warning": "Du har redan några objekt i din databas. Denna åtgärd kommer inte att förena dubbletter, du kommer att behöva hantera dem manuellt.",
"combine-food": "Kombinera mat",
"source-food": "Source Food",
"target-food": "Target Food",
"source-food": "Matens källa",
"target-food": "Matens mål",
"create-food": "Skapa mat",
"food-label": "Mat etikett",
"edit-food": "Redigera mat",
@ -918,7 +919,7 @@
},
"units": {
"seed-dialog-text": "Fyll databasen med vanliga enheter baserade på ditt språk.",
"combine-unit-description": "Combining the selected units will merge the Source Unit and Target Unit into a single unit. The {source-unit-will-be-deleted} and all of the references to the Source Unit will be updated to point to the Target Unit.",
"combine-unit-description": "Genom att kombinera de valda enheterna slås källenheten och målenheten samman till en enda enhet. {source-unit-will-be-deleted} och alla referenser till enhetens källa kommer att uppdateras så att de pekar på enhetens mål.",
"combine-unit": "Kombinera enhet",
"source-unit": "Källenhet",
"target-unit": "Målenhet",
@ -954,7 +955,7 @@
"recipe-data": "Recept data",
"recipe-data-description": "Använd det här avsnittet för att hantera data som är kopplade till dina recept. Du kan utföra flera massåtgärder på dina recept, inklusive export, radering, taggning och tilldelning av kategorier.",
"recipe-columns": "Receptkolumner",
"data-exports-description": "This section provides links to available exports that are ready to download. These exports do expire, so be sure to grab them while they're still available.",
"data-exports-description": "I det här avsnittet finns länkar till tillgängliga exporter som är redo att laddas ner. Dessa exporter upphör att gälla, så se till att hämta dem medan de fortfarande är tillgängliga.",
"data-exports": "Dataexport",
"tag": "Tagg",
"categorize": "Kategorisera",
@ -963,14 +964,14 @@
"categorize-recipes": "Kategorisera recept",
"export-recipes": "Exportera recept",
"delete-recipes": "Radera recept",
"source-unit-will-be-deleted": "Source Unit will be deleted"
"source-unit-will-be-deleted": "Källenheten kommer att raderas"
},
"create-alias": "Skapa alias",
"manage-aliases": "Manage Aliases",
"manage-aliases": "Hantera alias",
"seed-data": "Exempeldata",
"seed": "Seed",
"seed": "Frö",
"data-management": "Datahantering",
"data-management-description": "Select which data set you want to make changes to.",
"data-management-description": "Välj vilken data du vill göra ändringar i.",
"select-data": "Välj data",
"select-language": "Välj språk",
"columns": "Kolumner",
@ -978,7 +979,7 @@
"categories": {
"edit-category": "Redigera kategori",
"new-category": "Ny kategori",
"category-data": "Category Data"
"category-data": "Kategoridata"
},
"tags": {
"new-tag": "Ny Tag",
@ -988,7 +989,7 @@
"tools": {
"new-tool": "Nytt verktyg",
"edit-tool": "Lägg till/ta bort verktyg",
"tool-data": "Tool Data"
"tool-data": "Verktygsdata"
}
},
"user-registration": {
@ -1039,52 +1040,52 @@
"demo_password": "Lösenord: {password}"
},
"ocr-editor": {
"ocr-editor": "Ocr editor",
"toolbar": "Toolbar",
"ocr-editor": "Ocr redigerare",
"toolbar": "Verktygsfält",
"selection-mode": "Markeringsläge",
"pan-and-zoom-picture": "Pan and zoom picture",
"pan-and-zoom-picture": "Panorera och zooma bild",
"split-text": "Dela text",
"preserve-line-breaks": "Preserve original line breaks",
"split-by-block": "Split by text block",
"flatten": "Flatten regardless of original formating",
"preserve-line-breaks": "Bevara ursprungliga radbrytningar",
"split-by-block": "Dela med textblock",
"flatten": "Platta till oavsett ursprunglig formatering",
"help": {
"help": "Hjälp",
"mouse-modes": "Mouse modes",
"mouse-modes": "Musläge",
"selection-mode": "Markeringsläge (standard)",
"selection-mode-desc": "The selection mode is the main mode that can be used to enter data:",
"selection-mode-desc": "Urvalsläget är det huvudsakliga läget som kan användas för att mata in data:",
"selection-mode-steps": {
"draw": "Draw a rectangle on the text you want to select.",
"click": "Click on any field on the right and then click back on the rectangle above the image.",
"result": "The selected text will appear inside the previously selected field."
"draw": "Rita en rektangel på den text du vill markera.",
"click": "Klicka på valfritt fält till höger och klicka sedan tillbaka på rektangeln ovanför bilden.",
"result": "Den markerade texten visas i det tidigare markerade fältet."
},
"pan-and-zoom-mode": "Pan and Zoom Mode",
"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.",
"split-text-mode": "Split Text modes",
"pan-and-zoom-mode": "Panorerings- och zoomläge",
"pan-and-zoom-desc": "Välj panorera och zooma genom att klicka på ikonen. I det här läget kan du zooma in i bilden och flytta runt den för att göra det enklare att använda stora bilder.",
"split-text-mode": "Dela upp textlägen",
"split-modes": {
"line-mode": "Line mode (default)",
"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.",
"block-mode": "Block mode",
"block-mode-desc": "In block mode, the text will be split in blocks. This mode is useful when bulk adding instructions that are usually written in paragraphs.",
"flat-mode": "Flat mode",
"flat-mode-desc": "In flat mode, the text will be added to the selected recipe field with no line breaks."
"line-mode": "Radläge (standard)",
"line-mode-desc": "I radläget kommer texten att spridas genom att behålla de ursprungliga radbrytningarna. Detta läge är användbart när du använder bulk add på en lista med ingredienser där en ingrediens är en rad.",
"block-mode": "Block läge",
"block-mode-desc": "I blockläget kommer texten att delas upp i block. Detta läge är användbart när du lägger till instruktioner som vanligtvis är skrivna i stycken.",
"flat-mode": "Platt läge",
"flat-mode-desc": "I platt läge läggs texten till i det valda receptfältet utan radbrytningar."
}
}
},
"admin": {
"maintenance": {
"storage-details": "Storage Details",
"page-title": "Site Maintenance",
"summary-title": "Summary",
"button-label-get-summary": "Get Summary",
"button-label-open-details": "Details",
"info-description-data-dir-size": "Data Directory Size",
"storage-details": "Lagringsdetaljer",
"page-title": "Underhåll av webbplatsen",
"summary-title": "Sammanfattning",
"button-label-get-summary": "Hämta sammanfattning",
"button-label-open-details": "Detaljer",
"info-description-data-dir-size": "Datakatalogens storlek",
"info-description-log-file-size": "Storlek på loggfil",
"info-description-cleanable-directories": "Rensbara mappar",
"info-description-cleanable-images": "Rensbara bilder",
"storage": {
"title-temporary-directory": "Tillfällig mapp (.temp)",
"title-backups-directory": "Backups Directory (backups)",
"title-groups-directory": "Groups Directory (groups)",
"title-backups-directory": "Katalog för säkerhetskopior (säkerhetskopior)",
"title-groups-directory": "Gruppkatalog (grupper)",
"title-recipes-directory": "Recipes Directory (recipes)",
"title-user-directory": "User Directory (user)"
},

View File

@ -198,7 +198,8 @@
"refresh": "Yenile",
"upload-file": "Dosya Yükle",
"created-on-date": "{0} tarihinde oluşturuldu",
"unsaved-changes": "Kaydedilmemiş değişiklikleriniz mevcut. Ayrılmadan önce kaydetmek ister misiniz? Kaydetmek için Tamam'ı, değişiklikleri iptal etmek için İptal'i seçin."
"unsaved-changes": "Kaydedilmemiş değişiklikleriniz mevcut. Ayrılmadan önce kaydetmek ister misiniz? Kaydetmek için Tamam'ı, değişiklikleri iptal etmek için İptal'i seçin.",
"clipboard-copy-failure": "Panoya kopyalanamadı."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "<b>{groupName}<b/>'i silmek istediğine emin misin?",

View File

@ -198,7 +198,8 @@
"refresh": "Оновити",
"upload-file": "Вивантажити файл",
"created-on-date": "Створено: {0}",
"unsaved-changes": "У вас є незбережені зміни. Ви хочете зберегти їх перед виходом? Гаразд, щоб зберегти, Скасувати, щоб скасувати."
"unsaved-changes": "У вас є незбережені зміни. Ви хочете зберегти їх перед виходом? Гаразд, щоб зберегти, Скасувати, щоб скасувати.",
"clipboard-copy-failure": "Не вдалося скопіювати до буфера обміну."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Ви дійсно бажаєте видалити <b>{groupName}<b/>?",

View File

@ -198,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",

File diff suppressed because it is too large Load Diff

View File

@ -177,28 +177,29 @@
"run": "運行",
"menu": "選單",
"a-name-is-required": "名稱必填",
"delete-with-name": "Delete {name}",
"delete-with-name": "刪除 {name}",
"confirm-delete-generic-with-name": "Are you sure you want to delete this {name}?",
"confirm-delete-own-admin-account": "Please note that you are trying to delete your own admin account! This action cannot be undone and will permanently delete your account?",
"organizer": "Organizer",
"transfer": "Transfer",
"copy": "複製",
"color": "Color",
"color": "色彩",
"timestamp": "時間戳",
"last-made": "最後製作的",
"learn-more": "Learn More",
"learn-more": "學習更多",
"this-feature-is-currently-inactive": "該功能目前處於非活動狀態",
"clipboard-not-supported": "Clipboard not supported",
"copied-to-clipboard": "Copied to clipboard",
"copied-to-clipboard": "複製到剪貼簿",
"your-browser-does-not-support-clipboard": "Your browser does not support clipboard",
"copied-items-to-clipboard": "No item copied to clipboard|One item copied to clipboard|Copied {count} items to clipboard",
"actions": "Actions",
"selected-count": "Selected: {count}",
"export-all": "Export All",
"refresh": "Refresh",
"refresh": "更新",
"upload-file": "上傳文件",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "確定要刪除<b>{groupName}<b/>",
@ -224,7 +225,7 @@
"keep-my-recipes-private": "將我的食譜保密",
"keep-my-recipes-private-description": "Sets your group and all recipes defaults to private. You can always change this later."
},
"manage-members": "Manage Members",
"manage-members": "管理成員",
"manage-members-description": "Manage the permissions of the members in your groups. {manage} allows the user to access the data-management page {invite} allows the user to generate invitation links for other users. Group owners cannot change their own permissions.",
"manage": "Manage",
"invite": "邀請",

View File

@ -4,9 +4,7 @@
<v-banner v-if="isDemo" sticky>
<div class="text-center">
<b> {{ $t("demo.info_message_with_version", { version: version }) }} </b> |
{{ $t("demo.demo_username", { username: "changeme@example.com" }) }} |
{{ $t("demo.demo_password", { password: "demo" }) }}
<b> {{ $t("demo.info_message_with_version", { version: version }) }} </b>
</div>
</v-banner>

View File

@ -23,7 +23,6 @@ import { MultiPurposeLabelsApi } from "./user/group-multiple-purpose-labels";
import { GroupEventNotifierApi } from "./user/group-event-notifier";
import { MealPlanRulesApi } from "./user/group-mealplan-rules";
import { GroupDataSeederApi } from "./user/group-seeder";
import { OcrAPI } from "./user/ocr";
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
export class UserApiClient {
@ -52,7 +51,6 @@ export class UserApiClient {
public groupEventNotifier: GroupEventNotifierApi;
public upload: UploadFile;
public seeders: GroupDataSeederApi;
public ocr: OcrAPI;
constructor(requests: ApiRequestInstance) {
// Recipes
@ -91,9 +89,6 @@ export class UserApiClient {
this.bulk = new BulkActionsAPI(requests);
this.groupEventNotifier = new GroupEventNotifierApi(requests);
// ocr
this.ocr = new OcrAPI(requests);
Object.freeze(this);
}
}

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 interface OcrAssetReq {
recipeSlug: string;
assetName: string;
}
export interface OcrTsvResponse {
level?: number;
pageNum?: number;
blockNum?: number;
parNum?: number;
lineNum?: number;
wordNum?: number;
left?: number;
top?: number;
width?: number;
height?: number;
conf?: number;
text?: string;
}

View File

@ -244,7 +244,6 @@ export interface Recipe {
extras?: {
[k: string]: unknown;
};
isOcrRecipe?: boolean;
comments?: RecipeCommentOut[];
}
export interface RecipeTool {

View File

@ -1,16 +0,0 @@
import { BaseAPI } from "../base/base-clients";
const prefix = "/api";
export class OcrAPI extends BaseAPI {
// Currently unused in favor for the endpoint using asset names
async fileToTsv(file: File) {
const formData = new FormData();
formData.append("file", file);
return await this.requests.post(`${prefix}/ocr/file-to-tsv`, formData);
}
async assetToTsv(recipeSlug: string, assetName: string) {
return await this.requests.post(`${prefix}/ocr/asset-to-tsv`, { recipeSlug, assetName });
}
}

View File

@ -38,7 +38,6 @@ const routes = {
recipesCategory: `${prefix}/recipes/category`,
recipesParseIngredient: `${prefix}/parser/ingredient`,
recipesParseIngredients: `${prefix}/parser/ingredients`,
recipesCreateFromOcr: `${prefix}/recipes/create-ocr`,
recipesTimelineEvent: `${prefix}/recipes/timeline/events`,
recipesRecipeSlug: (recipe_slug: string) => `${prefix}/recipes/${recipe_slug}`,
@ -159,15 +158,6 @@ export class RecipeAPI extends BaseCRUDAPI<CreateRecipe, Recipe, Recipe> {
return `${routes.recipesRecipeSlugExportZip(recipeSlug)}?token=${token}`;
}
async createFromOcr(file: File, makeFileRecipeImage: boolean) {
const formData = new FormData();
formData.append("file", file);
formData.append("extension", file.name.split(".").pop() ?? "");
formData.append("makefilerecipeimage", String(makeFileRecipeImage));
return await this.requests.post(routes.recipesCreateFromOcr, formData);
}
async updateLastMade(recipeSlug: string, timestamp: string) {
return await this.requests.patch<Recipe, RecipeLastMade>(routes.recipesSlugLastMade(recipeSlug), { timestamp })
}

View File

@ -1,51 +0,0 @@
<template>
<div>
<RecipeOcrEditorPage v-if="recipe" :recipe="recipe" />
</div>
</template>
<script lang="ts">
import { defineComponent, useRoute } from "@nuxtjs/composition-api";
import RecipeOcrEditorPage from "~/components/Domain/Recipe/RecipeOcrEditorPage/RecipeOcrEditorPage.vue";
import { useRecipe } from "~/composables/recipes";
export default defineComponent({
components: { RecipeOcrEditorPage },
setup() {
const route = useRoute();
const slug = route.value.params.slug;
const { recipe, loading } = useRecipe(slug);
return {
recipe,
loading,
};
},
});
</script>
<style lang="css">
.ghost {
opacity: 0.5;
}
body {
background: #eee;
}
canvas {
background: white;
box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.2);
width: 100%;
image-rendering: optimizeQuality;
}
.box {
position: absolute;
border: 2px #90ee90 solid;
background-color: #90ee90;
z-index: 3;
}
</style>

View File

@ -52,11 +52,6 @@ export default defineComponent({
text: i18n.tc("recipe.import-with-zip"),
value: "zip",
},
{
icon: $globals.icons.fileImage,
text: i18n.tc("recipe.create-recipe-from-an-image"),
value: "ocr",
},
{
icon: $globals.icons.link,
text: i18n.tc("recipe.bulk-url-import"),

View File

@ -1,85 +0,0 @@
<template>
<div>
<v-card-title class="headline"> {{ $t('recipe.create-recipe-from-an-image') }} </v-card-title>
<v-card-text>
{{ $t('recipe.create-a-recipe-by-uploading-a-scan') }}
<v-form ref="domCreateByOcr"> </v-form>
</v-card-text>
<v-card-actions class="justify-center">
<v-file-input
v-model="imageUpload"
accept=".png"
label="recipe.png"
filled
clearable
class="rounded-lg mt-2"
rounded
truncate-length="100"
:hint="$t('recipe.upload-a-png-image-from-a-recipe-book')"
persistent-hint
prepend-icon=""
:prepend-inner-icon="$globals.icons.fileImage"
/>
</v-card-actions>
<v-card-actions class="justify-center">
<v-checkbox v-model="makeFileRecipeImage" :label="$t('new-recipe.make-recipe-image')" />
</v-card-actions>
<v-card-actions class="justify-center">
<div style="width: 250px">
<BaseButton :disabled="imageUpload === null" large rounded block :loading="loading" @click="createByOcr" />
</div>
</v-card-actions>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref, useRouter, computed, useContext, useRoute } from "@nuxtjs/composition-api";
import { AxiosResponse } from "axios";
import { useUserApi } from "~/composables/api";
import { validators } from "~/composables/use-validators";
import { VForm } from "~/types/vuetify";
export default defineComponent({
setup() {
const state = reactive({
error: false,
loading: false,
makeFileRecipeImage: false,
});
const { $auth } = useContext();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const api = useUserApi();
const router = useRouter();
const imageUpload = ref<File | null>(null);
function handleResponse(response: AxiosResponse<string> | null) {
if (response?.status !== 201) {
state.error = true;
state.loading = false;
return;
}
router.push(`/g/${groupSlug.value}/r/${response.data}/ocr-editor`);
}
const domCreateByOcr = ref<VForm | null>(null);
async function createByOcr() {
if (imageUpload.value === null) return; // Should never be true due to circumstances
state.loading = true;
const { response } = await api.recipes.createFromOcr(imageUpload.value, state.makeFileRecipeImage);
// @ts-ignore returns a string and not a full Recipe
handleResponse(response);
}
return {
domCreateByOcr,
createByOcr,
...toRefs(state),
validators,
imageUpload,
};
},
});
</script>

View File

@ -1,73 +0,0 @@
import { OcrTsvResponse } from "~/lib/api/types/ocr";
import { Recipe } from "~/lib/api/types/recipe";
export type CanvasRect = {
startX: number;
startY: number;
w: number;
h: number;
};
export type ImagePosition = {
sx: number;
sy: number;
sWidth: number;
sHeight: number;
dx: number;
dy: number;
dWidth: number;
dHeight: number;
scale: number;
panStartPoint: {
x: number;
y: number;
};
};
export type Mouse = {
current: {
x: number;
y: number;
};
down: boolean;
};
// https://stackoverflow.com/questions/58434389/export typescript-deep-keyof-of-a-nested-object/58436959#58436959
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]];
type Join<K, P> = K extends string | number
? P extends string | number
? `${K}${"" extends P ? "" : "."}${P}`
: never
: never;
export type Leaves<T, D extends number = 10> = [D] extends [never]
? never
: T extends object
? { [K in keyof T]-?: Join<K, Leaves<T[K], Prev[D]>> }[keyof T]
: "";
export type Paths<T, D extends number = 10> = [D] extends [never]
? never
: T extends object
? {
[K in keyof T]-?: K extends string | number ? `${K}` | Join<K, Paths<T[K], Prev[D]>> : never;
}[keyof T]
: "";
export type SelectedRecipeLeaves = Leaves<Recipe>;
export type CanvasModes = "selection" | "panAndZoom";
export type SelectedTextSplitModes = keyof OcrTsvResponse | "flatten";
export type ToolbarIcons<T extends string> = {
sectionTitle: string;
eventHandler(mode: T): void;
highlight: T;
icons: {
name: T;
icon: string;
tooltip: string;
}[];
}[];

View File

@ -11,7 +11,7 @@
"user": {
"user-updated": "Bruker oppdatert",
"password-updated": "Passord oppdatert",
"invalid-current-password": "Feil nåværende passord",
"invalid-current-password": "Nåværende passord er feil",
"ldap-update-password-unavailable": "Kan ikke oppdatere passordet, brukeren kontrolleres av LDAP"
},
"group": {
@ -29,7 +29,7 @@
"generic-updated": "{name} ble oppdatert",
"generic-created-with-url": "{name} har blitt opprettet, {url}",
"generic-updated-with-url": "{name} har blitt oppdatert, {url}",
"generic-duplicated": "Det er blitt laget kopi av {name}",
"generic-duplicated": "{name} har blitt duplisert",
"generic-deleted": "{name} har blitt slettet"
}
}

View File

@ -23,7 +23,7 @@
"broccolini": "broccolini",
"broccoli-rabe": "broccoli rabe",
"brussels-sprouts": "λαχανάκια Βρυξελλών",
"cabbage": "cabbage",
"cabbage": "λάχανο",
"cauliflower": "κουνουπίδι",
"chinese-leaves": "κινέζικα φύλλα",
"collard-greens": "collard greens",
@ -52,34 +52,34 @@
"chicory": "chicory",
"chilli-peppers": "chilli peppers",
"chives": "chives",
"chocolate": "chocolate",
"cilantro": "cilantro",
"cinnamon": "cinnamon",
"chocolate": "σοκολάτα",
"cilantro": "κόλιανδρος",
"cinnamon": "κανέλα",
"clarified-butter": "clarified butter",
"coconut": "coconut",
"coconut-milk": "coconut milk",
"coffee": "coffee",
"coconut": "καρύδα",
"coconut-milk": "γάλα καρύδας",
"coffee": "καφές",
"confectioners-sugar": "confectioners' sugar",
"coriander": "coriander",
"corn": "corn",
"coriander": "κόλιανδρος",
"corn": "καλαμπόκι",
"corn-syrup": "corn syrup",
"cottonseed-oil": "cottonseed oil",
"courgette": "courgette",
"cream-of-tartar": "cream of tartar",
"cucumber": "cucumber",
"cumin": "cumin",
"cucumber": "αγγούρι",
"cumin": "κύμινο",
"daikon": "daikon",
"dairy-products-and-dairy-substitutes": "dairy products and dairy substitutes",
"eggs": "eggs",
"eggs": "αυγά",
"ghee": "ghee",
"milk": "milk",
"milk": "γάλα",
"dandelion": "dandelion",
"demerara-sugar": "demerara sugar",
"dough": "dough",
"dough": "ζυμάρι",
"edible-cactus": "edible cactus",
"eggplant": "eggplant",
"eggplant": "μελιτζάνα",
"endive": "endive",
"fats": "fats",
"fats": "λιπαρά",
"speck": "speck",
"fava-beans": "fava beans",
"fiddlehead": "fiddlehead",

View File

@ -36,7 +36,7 @@
"butternut-pumpkin": "flaskegresskar",
"butternut-squash": "butternut squash",
"cactus-edible": "kaktus, spiselig",
"calabrese": "calabrese Fersk Pølse",
"calabrese": "calabrese",
"cannabis": "cannabis",
"capsicum": "chilipepper",
"caraway": "karve",
@ -45,7 +45,7 @@
"cayenne-pepper": "kayenne pepper",
"celeriac": "sellerirot",
"celery": "selleri",
"cereal-grains": "frokostblanding korn",
"cereal-grains": "frokostblandingkorn",
"rice": "ris",
"chard": "bladbete",
"cheese": "ost",

View File

@ -3,22 +3,22 @@
"name": "Produce"
},
{
"name": "Grains"
"name": "Σιτηρά"
},
{
"name": "Fruits"
"name": "Φρούτα"
},
{
"name": "Vegetables"
"name": "Λαχανικά"
},
{
"name": "Meat"
"name": "Κρέας"
},
{
"name": "Seafood"
"name": "Θαλασσινά"
},
{
"name": "Beverages"
"name": "Ποτά"
},
{
"name": "Baked Goods"
@ -33,10 +33,10 @@
"name": "Confectionary"
},
{
"name": "Dairy Products"
"name": "Γαλακτοκομικά"
},
{
"name": "Frozen Foods"
"name": "Κατεψυγμένα Φαγητά"
},
{
"name": "Health Foods"
@ -48,18 +48,18 @@
"name": "Meat Products"
},
{
"name": "Snacks"
"name": "Σνακ"
},
{
"name": "Spices"
"name": "Μπαχαρικά"
},
{
"name": "Sweets"
"name": "Γλυκά"
},
{
"name": "Alcohol"
"name": "Αλκοόλ"
},
{
"name": "Other"
"name": "Άλλα"
}
]

View File

@ -1,6 +1,6 @@
[
{
"name": "Produce"
"name": "农产品"
},
{
"name": "谷物"
@ -30,7 +30,7 @@
"name": "调味品"
},
{
"name": "Confectionary"
"name": "糖果类"
},
{
"name": "乳制品"
@ -54,7 +54,7 @@
"name": "调味品"
},
{
"name": "甜味剂"
"name": "甜"
},
{
"name": "酒类"

View File

@ -2,17 +2,17 @@
"teaspoon": {
"name": "teaspoon",
"description": "",
"abbreviation": "tsp"
"abbreviation": "κ.γ."
},
"tablespoon": {
"name": "tablespoon",
"description": "",
"abbreviation": "tbsp"
"abbreviation": "κ.σ."
},
"cup": {
"name": "cup",
"name": "φλιτζάνι",
"description": "",
"abbreviation": "cup"
"abbreviation": "φλ."
},
"fluid-ounce": {
"name": "fluid ounce",
@ -42,7 +42,7 @@
"liter": {
"name": "λίτρο",
"description": "",
"abbreviation": "λ"
"abbreviation": "l"
},
"pound": {
"name": "pound",
@ -57,17 +57,17 @@
"gram": {
"name": "γραμ",
"description": "",
"abbreviation": "γ"
"abbreviation": "γρ."
},
"kilogram": {
"name": "κιλό",
"description": "",
"abbreviation": "κιλ"
"abbreviation": "kg"
},
"milligram": {
"name": "χιλιοστόγραμμο",
"description": "",
"abbreviation": "μιλιγκράμ"
"abbreviation": "mg"
},
"splash": {
"name": "splash",

View File

@ -15,17 +15,17 @@
"abbreviation": "kopp"
},
"fluid-ounce": {
"name": "us væske unse",
"name": "væskeunse",
"description": "",
"abbreviation": "fl oz"
},
"pint": {
"name": "halvliter",
"name": "pint",
"description": "",
"abbreviation": "pt"
},
"quart": {
"name": "quart",
"name": "kvart",
"description": "",
"abbreviation": "qt"
},
@ -60,27 +60,27 @@
"abbreviation": "g"
},
"kilogram": {
"name": "kilo",
"name": "kilogram",
"description": "",
"abbreviation": "kg"
},
"milligram": {
"name": "mg",
"name": "milligram",
"description": "",
"abbreviation": "mg"
},
"splash": {
"name": "splash",
"name": "skvett",
"description": "",
"abbreviation": ""
},
"dash": {
"name": "dash",
"name": "klype",
"description": "",
"abbreviation": ""
},
"serving": {
"name": "servering",
"name": "porsjon",
"description": "",
"abbreviation": ""
},

View File

@ -7,7 +7,6 @@ from . import (
comments,
explore,
groups,
ocr,
organizers,
parser,
recipe,
@ -32,4 +31,3 @@ router.include_router(unit_and_foods.router)
router.include_router(admin.router)
router.include_router(validators.router)
router.include_router(explore.router)
router.include_router(ocr.router)

View File

@ -1,7 +0,0 @@
from fastapi import APIRouter
from . import pytesseract
router = APIRouter(prefix="/ocr")
router.include_router(pytesseract.router)

View File

@ -1,37 +0,0 @@
from fastapi import APIRouter, File
from mealie.routes._base import BaseUserController, controller
from mealie.schema.ocr.ocr import OcrAssetReq, OcrTsvResponse
from mealie.services.ocr.pytesseract import OcrService
from mealie.services.recipe.recipe_data_service import RecipeDataService
from mealie.services.recipe.recipe_service import RecipeService
router = APIRouter()
@controller(router)
class OCRController(BaseUserController):
def __init__(self):
self.ocr_service = OcrService()
@router.post("/", response_model=str)
def image_to_string(self, file: bytes = File(...)):
return self.ocr_service.image_to_string(file)
@router.post("/file-to-tsv", response_model=list[OcrTsvResponse])
def file_to_tsv(self, file: bytes = File(...)):
tsv = self.ocr_service.image_to_tsv(file)
return self.ocr_service.format_tsv_output(tsv)
@router.post("/asset-to-tsv", response_model=list[OcrTsvResponse])
def asset_to_tsv(self, req: OcrAssetReq):
recipe_service = RecipeService(self.repos, self.user, self.group)
recipe = recipe_service._get_recipe(req.recipe_slug)
if recipe.id is None:
return []
data_service = RecipeDataService(recipe.id, recipe.group_id)
asset_path = data_service.dir_assets.joinpath(req.asset_name)
file = open(asset_path, "rb")
tsv = self.ocr_service.image_to_tsv(file.read())
return self.ocr_service.format_tsv_output(tsv)

View File

@ -1,7 +1,7 @@
from functools import cached_property
from pathlib import Path
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException
from mealie.core.dependencies.dependencies import temporary_zip_path
from mealie.core.security import create_file_token
@ -50,6 +50,10 @@ class RecipeBulkActionsController(BaseUserController):
@router.get("/export/download")
def get_exported_data_token(self, path: Path):
"""Returns a token to download a file"""
path = Path(path).resolve()
if not path.is_relative_to(self.folders.DATA_DIR):
raise HTTPException(400, "path must be relative to data directory")
return {"fileToken": create_file_token(path)}

View File

@ -27,10 +27,7 @@ from mealie.schema.make_dependable import make_dependable
from mealie.schema.recipe import Recipe, RecipeImageTypes, ScrapeRecipe
from mealie.schema.recipe.recipe import CreateRecipe, CreateRecipeByUrlBulk, RecipeLastMade, RecipeSummary
from mealie.schema.recipe.recipe_asset import RecipeAsset
from mealie.schema.recipe.recipe_ingredient import RecipeIngredient
from mealie.schema.recipe.recipe_scraper import ScrapeRecipeTest
from mealie.schema.recipe.recipe_settings import RecipeSettings
from mealie.schema.recipe.recipe_step import RecipeStep
from mealie.schema.recipe.request_helpers import RecipeDuplicate, RecipeZipTokenResponse, UpdateImageResponse
from mealie.schema.response import PaginationBase, PaginationQuery
from mealie.schema.response.pagination import RecipeSearchQuery
@ -489,37 +486,3 @@ class RecipeController(BaseRecipeController):
self.mixins.update_one(recipe, slug)
return asset_in
# ==================================================================================================================
# OCR
@router.post("/create-ocr", status_code=201, response_model=str)
def create_recipe_ocr(
self, extension: str = Form(...), file: UploadFile = File(...), makefilerecipeimage: bool = Form(...)
):
"""Takes an image and creates a recipe based on the image"""
slug = self.service.create_one(
Recipe(
name="New OCR Recipe",
recipe_ingredient=[RecipeIngredient(note="", title=None, unit=None, food=None, original_text=None)],
recipe_instructions=[RecipeStep(text="")],
is_ocr_recipe=True,
settings=RecipeSettings(show_assets=True),
id=None,
image=None,
recipe_yield=None,
rating=None,
orgURL=None,
date_added=None,
date_updated=None,
created_at=None,
update_at=None,
nutrition=None,
)
).slug
RecipeController.upload_recipe_asset(self, slug, "Original recipe image", "", extension, file)
if makefilerecipeimage:
# Get the pointer to the beginning of the file to read it once more
file.file.seek(0)
self.update_recipe_image(slug, file.file.read(), extension)
return slug

View File

@ -3,6 +3,7 @@ from pathlib import Path
from fastapi import APIRouter, Depends, HTTPException, status
from starlette.responses import FileResponse
from mealie.core.config import get_app_dirs
from mealie.core.dependencies import validate_file_token
router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True)
@ -12,6 +13,14 @@ router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True)
async def download_file(file_path: Path = Depends(validate_file_token)):
"""Uses a file token obtained by an active user to retrieve a file from the operating
system."""
file_path = Path(file_path).resolve()
dirs = get_app_dirs()
if not file_path.is_relative_to(dirs.DATA_DIR):
raise HTTPException(status.HTTP_400_BAD_REQUEST)
if not file_path.is_file():
raise HTTPException(status.HTTP_400_BAD_REQUEST)

View File

@ -1,7 +0,0 @@
# This file is auto-generated by gen_schema_exports.py
from .ocr import OcrAssetReq, OcrTsvResponse
__all__ = [
"OcrAssetReq",
"OcrTsvResponse",
]

View File

@ -1,21 +0,0 @@
from mealie.schema._mealie import MealieModel
class OcrTsvResponse(MealieModel):
level: int = 0
page_num: int = 0
block_num: int = 0
par_num: int = 0
line_num: int = 0
word_num: int = 0
left: int = 0
top: int = 0
width: int = 0
height: int = 0
conf: float = 0.0
text: str = ""
class OcrAssetReq(MealieModel):
recipe_slug: str
asset_name: str

View File

@ -128,7 +128,6 @@ class Recipe(RecipeSummary):
assets: list[RecipeAsset] | None = []
notes: list[RecipeNote] | None = []
extras: dict | None = {}
is_ocr_recipe: bool | None = False
comments: list[RecipeCommentOut] | None = []

View File

@ -1,56 +0,0 @@
from io import BytesIO
import pytesseract
from PIL import Image
from mealie.schema.ocr.ocr import OcrTsvResponse
from mealie.services._base_service import BaseService
class OcrService(BaseService):
"""
Class for ocr engines.
"""
def image_to_string(self, image_data):
"""
Returns a plain text translation of an image
"""
return pytesseract.image_to_string(Image.open(image_data))
def image_to_tsv(self, image_data, lang=None):
"""
Returns the pytesseract default tsv output
"""
if lang is not None:
return pytesseract.image_to_data(Image.open(BytesIO(image_data)), lang=lang)
return pytesseract.image_to_data(Image.open(BytesIO(image_data)))
def format_tsv_output(self, tsv: str) -> list[OcrTsvResponse]:
"""
Returns a OcrTsvResponse from a default pytesseract tsv output
"""
lines = tsv.split("\n")
titles = [t.strip() for t in lines[0].split("\t")]
response: list[OcrTsvResponse] = []
for i in range(1, len(lines)):
if lines[i] == "":
continue
line = OcrTsvResponse()
for key, value in zip(titles, lines[i].split("\t"), strict=False):
if key == "text":
setattr(line, key, value.strip())
elif key == "conf":
setattr(line, key, float(value.strip()))
elif key in OcrTsvResponse.__fields__:
setattr(line, key, int(value.strip()))
else:
continue
if isinstance(line, OcrTsvResponse):
response.append(line)
return response

47
poetry.lock generated
View File

@ -605,6 +605,7 @@ files = [
{file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"},
{file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"},
{file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"},
{file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d967650d3f56af314b72df7089d96cda1083a7fc2da05b375d2bc48c82ab3f3c"},
{file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"},
{file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"},
{file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"},
@ -613,6 +614,7 @@ files = [
{file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"},
{file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"},
{file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"},
{file = "greenlet-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d4606a527e30548153be1a9f155f4e283d109ffba663a15856089fb55f933e47"},
{file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"},
{file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"},
{file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"},
@ -642,6 +644,7 @@ files = [
{file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"},
{file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"},
{file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"},
{file = "greenlet-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1087300cf9700bbf455b1b97e24db18f2f77b55302a68272c56209d5587c12d1"},
{file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"},
{file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"},
{file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"},
@ -650,6 +653,7 @@ files = [
{file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"},
{file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"},
{file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"},
{file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8512a0c38cfd4e66a858ddd1b17705587900dd760c6003998e9472b77b56d417"},
{file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"},
{file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"},
{file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"},
@ -1862,21 +1866,6 @@ files = [
html5lib = "*"
rdflib = "*"
[[package]]
name = "pytesseract"
version = "0.3.10"
description = "Python-tesseract is a python wrapper for Google's Tesseract-OCR"
optional = false
python-versions = ">=3.7"
files = [
{file = "pytesseract-0.3.10-py3-none-any.whl", hash = "sha256:8f22cc98f765bf13517ead0c70effedb46c153540d25783e04014f28b55a5fc6"},
{file = "pytesseract-0.3.10.tar.gz", hash = "sha256:f1c3a8b0f07fd01a1085d451f5b8315be6eec1d5577a6796d46dc7a62bd4120f"},
]
[package.dependencies]
packaging = ">=21.3"
Pillow = ">=8.0.0"
[[package]]
name = "pytest"
version = "7.2.2"
@ -2024,6 +2013,7 @@ files = [
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
@ -2031,8 +2021,15 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
@ -2049,6 +2046,7 @@ files = [
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
@ -2056,6 +2054,7 @@ files = [
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
@ -2205,13 +2204,13 @@ tests = ["html5lib", "pytest", "pytest-cov"]
[[package]]
name = "recipe-scrapers"
version = "14.52.0"
version = "14.53.0"
description = "Python package, scraping recipes from all over the internet"
optional = false
python-versions = ">=3.8"
files = [
{file = "recipe_scrapers-14.52.0-py3-none-any.whl", hash = "sha256:3514048808c7b7de467bfa56bea3921ecff637441cde9085186345e3ce7cabdc"},
{file = "recipe_scrapers-14.52.0.tar.gz", hash = "sha256:3d1d2cf7ad8c5fd73b5a0e921b3505daeddb42da705ef5c68523a465ccd8cd8f"},
{file = "recipe_scrapers-14.53.0-py3-none-any.whl", hash = "sha256:330353dc824f9d77a089e4830722fef940a04259a1a59a6578a162378ed6bc72"},
{file = "recipe_scrapers-14.53.0.tar.gz", hash = "sha256:916e1182fb497b89df8ac29cb816a9566aafc6d2bba73f60462aef42fc1bba22"},
]
[package.dependencies]
@ -2972,6 +2971,16 @@ files = [
{file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"},
{file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"},
{file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"},
{file = "wrapt-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ecee4132c6cd2ce5308e21672015ddfed1ff975ad0ac8d27168ea82e71413f55"},
{file = "wrapt-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2020f391008ef874c6d9e208b24f28e31bcb85ccff4f335f15a3251d222b92d9"},
{file = "wrapt-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2feecf86e1f7a86517cab34ae6c2f081fd2d0dac860cb0c0ded96d799d20b335"},
{file = "wrapt-1.14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:240b1686f38ae665d1b15475966fe0472f78e71b1b4903c143a842659c8e4cb9"},
{file = "wrapt-1.14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9008dad07d71f68487c91e96579c8567c98ca4c3881b9b113bc7b33e9fd78b8"},
{file = "wrapt-1.14.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6447e9f3ba72f8e2b985a1da758767698efa72723d5b59accefd716e9e8272bf"},
{file = "wrapt-1.14.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:acae32e13a4153809db37405f5eba5bac5fbe2e2ba61ab227926a22901051c0a"},
{file = "wrapt-1.14.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49ef582b7a1152ae2766557f0550a9fcbf7bbd76f43fbdc94dd3bf07cc7168be"},
{file = "wrapt-1.14.1-cp311-cp311-win32.whl", hash = "sha256:358fe87cc899c6bb0ddc185bf3dbfa4ba646f05b1b0b9b5a27c2cb92c2cea204"},
{file = "wrapt-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:26046cd03936ae745a502abf44dac702a5e6880b2b01c29aea8ddf3353b68224"},
{file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"},
{file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"},
{file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"},
@ -3025,4 +3034,4 @@ pgsql = ["psycopg2-binary"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "5e0cc403c1ec022e6a75a4969dd97c55ce312daafd7d2024a3617eca25b2129f"
content-hash = "d7537958ae2ddbf8e2b350cb7b1189492a62b02becfe087efbc37a3b0115ff13"

View File

@ -29,7 +29,6 @@ passlib = "^1.7.4"
psycopg2-binary = { version = "^2.9.1", optional = true }
pydantic = "^1.10.4"
pyhumps = "^3.5.3"
pytesseract = "^0.3.9"
python = "^3.10"
python-dateutil = "^2.8.2"
python-dotenv = "^1.0.0"
@ -37,7 +36,7 @@ python-jose = "^3.3.0"
python-ldap = "^3.3.1"
python-multipart = "^0.0.6"
python-slugify = "^8.0.0"
recipe-scrapers = "^14.52.0"
recipe-scrapers = "^14.53.0"
requests = "^2.31.0"
tzdata = "^2022.7"
uvicorn = { extras = ["standard"], version = "^0.21.0" }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,73 +0,0 @@
level page_num block_num par_num line_num word_num left top width height conf text
1 1 0 0 0 0 0 0 640 480 -1
2 1 1 0 0 0 36 92 582 269 -1
3 1 1 1 0 0 36 92 582 92 -1
4 1 1 1 1 0 36 92 544 30 -1
5 1 1 1 1 1 36 92 60 24 87.137558 This
5 1 1 1 1 2 109 92 20 24 87.137558 is
5 1 1 1 1 3 141 98 15 18 87.823906 a
5 1 1 1 1 4 169 92 32 24 87.823906 lot
5 1 1 1 1 5 212 92 28 24 92.965874 of
5 1 1 1 1 6 251 92 31 24 93.247513 12
5 1 1 1 1 7 296 92 68 30 92.734741 point
5 1 1 1 1 8 374 93 53 23 92.996040 text
5 1 1 1 1 9 437 93 26 23 93.160057 to
5 1 1 1 1 10 474 93 52 23 92.312637 test
5 1 1 1 1 11 536 92 44 24 92.312637 the
4 1 1 1 2 0 36 126 582 31 -1
5 1 1 1 2 1 36 132 45 18 90.505524 ocr
5 1 1 1 2 2 91 126 69 24 90.505524 code
5 1 1 1 2 3 172 126 51 24 91.169167 and
5 1 1 1 2 4 236 132 50 18 89.765854 see
5 1 1 1 2 5 299 126 15 24 85.827324 if
5 1 1 1 2 6 325 126 14 24 93.116241 it
5 1 1 1 2 7 348 126 85 24 92.394562 works
5 1 1 1 2 8 445 132 33 18 30.119690 on
5 1 1 1 2 9 500 126 29 24 30.119690 all
5 1 1 1 2 10 541 127 77 30 92.090988 types
4 1 1 1 3 0 36 160 187 24 -1
5 1 1 1 3 1 36 160 28 24 92.476135 of
5 1 1 1 3 2 72 160 41 24 90.919365 file
5 1 1 1 3 3 123 160 100 24 91.360367 format.
3 1 1 2 0 0 36 194 561 167 -1
4 1 1 2 1 0 36 194 549 31 -1
5 1 1 2 1 1 36 194 55 24 89.098892 The
5 1 1 2 1 2 102 194 75 30 89.098892 quick
5 1 1 2 1 3 189 194 85 24 91.415680 brown
5 1 1 2 1 4 287 194 52 31 91.943085 dog
5 1 1 2 1 5 348 194 108 31 92.167969 jumped
5 1 1 2 1 6 468 200 63 18 91.970985 over
5 1 1 2 1 7 540 194 45 24 92.843704 the
4 1 1 2 2 0 37 228 548 31 -1
5 1 1 2 2 1 37 228 55 31 92.262550 lazy
5 1 1 2 2 2 103 228 50 24 92.693161 fox.
5 1 1 2 2 3 165 228 55 24 92.947639 The
5 1 1 2 2 4 232 228 75 30 90.589806 quick
5 1 1 2 2 5 319 228 85 24 91.051247 brown
5 1 1 2 2 6 417 228 51 31 91.925011 dog
5 1 1 2 2 7 478 228 107 31 91.471077 jumped
4 1 1 2 3 0 36 262 561 31 -1
5 1 1 2 3 1 36 268 63 18 90.210129 over
5 1 1 2 3 2 109 262 44 24 90.210129 the
5 1 1 2 3 3 165 262 56 31 91.178192 lazy
5 1 1 2 3 4 231 262 50 24 92.794647 fox.
5 1 1 2 3 5 294 262 55 24 91.388016 The
5 1 1 2 3 6 360 262 75 30 92.525742 quick
5 1 1 2 3 7 447 262 85 24 90.425552 brown
5 1 1 2 3 8 545 262 52 31 90.425552 dog
4 1 1 2 4 0 43 296 518 31 -1
5 1 1 2 4 1 43 296 107 31 91.759590 jumped
5 1 1 2 4 2 162 302 64 18 92.923576 over
5 1 1 2 4 3 235 296 44 24 92.017929 the
5 1 1 2 4 4 292 296 55 31 91.558884 lazy
5 1 1 2 4 5 357 296 50 24 92.687485 fox.
5 1 1 2 4 6 420 296 55 24 91.922661 The
5 1 1 2 4 7 486 296 75 30 91.870224 quick
4 1 1 2 5 0 37 330 524 31 -1
5 1 1 2 5 1 37 330 85 24 92.923935 brown
5 1 1 2 5 2 135 330 52 31 91.468765 dog
5 1 1 2 5 3 196 330 108 31 91.425491 jumped
5 1 1 2 5 4 316 336 63 18 91.489830 over
5 1 1 2 5 5 388 330 45 24 91.740379 the
5 1 1 2 5 6 445 330 55 31 92.110054 lazy
5 1 1 2 5 7 511 330 50 24 93.180054 fox.
1 level page_num block_num par_num line_num word_num left top width height conf text
2 1 1 0 0 0 0 0 0 640 480 -1
3 2 1 1 0 0 0 36 92 582 269 -1
4 3 1 1 1 0 0 36 92 582 92 -1
5 4 1 1 1 1 0 36 92 544 30 -1
6 5 1 1 1 1 1 36 92 60 24 87.137558 This
7 5 1 1 1 1 2 109 92 20 24 87.137558 is
8 5 1 1 1 1 3 141 98 15 18 87.823906 a
9 5 1 1 1 1 4 169 92 32 24 87.823906 lot
10 5 1 1 1 1 5 212 92 28 24 92.965874 of
11 5 1 1 1 1 6 251 92 31 24 93.247513 12
12 5 1 1 1 1 7 296 92 68 30 92.734741 point
13 5 1 1 1 1 8 374 93 53 23 92.996040 text
14 5 1 1 1 1 9 437 93 26 23 93.160057 to
15 5 1 1 1 1 10 474 93 52 23 92.312637 test
16 5 1 1 1 1 11 536 92 44 24 92.312637 the
17 4 1 1 1 2 0 36 126 582 31 -1
18 5 1 1 1 2 1 36 132 45 18 90.505524 ocr
19 5 1 1 1 2 2 91 126 69 24 90.505524 code
20 5 1 1 1 2 3 172 126 51 24 91.169167 and
21 5 1 1 1 2 4 236 132 50 18 89.765854 see
22 5 1 1 1 2 5 299 126 15 24 85.827324 if
23 5 1 1 1 2 6 325 126 14 24 93.116241 it
24 5 1 1 1 2 7 348 126 85 24 92.394562 works
25 5 1 1 1 2 8 445 132 33 18 30.119690 on
26 5 1 1 1 2 9 500 126 29 24 30.119690 all
27 5 1 1 1 2 10 541 127 77 30 92.090988 types
28 4 1 1 1 3 0 36 160 187 24 -1
29 5 1 1 1 3 1 36 160 28 24 92.476135 of
30 5 1 1 1 3 2 72 160 41 24 90.919365 file
31 5 1 1 1 3 3 123 160 100 24 91.360367 format.
32 3 1 1 2 0 0 36 194 561 167 -1
33 4 1 1 2 1 0 36 194 549 31 -1
34 5 1 1 2 1 1 36 194 55 24 89.098892 The
35 5 1 1 2 1 2 102 194 75 30 89.098892 quick
36 5 1 1 2 1 3 189 194 85 24 91.415680 brown
37 5 1 1 2 1 4 287 194 52 31 91.943085 dog
38 5 1 1 2 1 5 348 194 108 31 92.167969 jumped
39 5 1 1 2 1 6 468 200 63 18 91.970985 over
40 5 1 1 2 1 7 540 194 45 24 92.843704 the
41 4 1 1 2 2 0 37 228 548 31 -1
42 5 1 1 2 2 1 37 228 55 31 92.262550 lazy
43 5 1 1 2 2 2 103 228 50 24 92.693161 fox.
44 5 1 1 2 2 3 165 228 55 24 92.947639 The
45 5 1 1 2 2 4 232 228 75 30 90.589806 quick
46 5 1 1 2 2 5 319 228 85 24 91.051247 brown
47 5 1 1 2 2 6 417 228 51 31 91.925011 dog
48 5 1 1 2 2 7 478 228 107 31 91.471077 jumped
49 4 1 1 2 3 0 36 262 561 31 -1
50 5 1 1 2 3 1 36 268 63 18 90.210129 over
51 5 1 1 2 3 2 109 262 44 24 90.210129 the
52 5 1 1 2 3 3 165 262 56 31 91.178192 lazy
53 5 1 1 2 3 4 231 262 50 24 92.794647 fox.
54 5 1 1 2 3 5 294 262 55 24 91.388016 The
55 5 1 1 2 3 6 360 262 75 30 92.525742 quick
56 5 1 1 2 3 7 447 262 85 24 90.425552 brown
57 5 1 1 2 3 8 545 262 52 31 90.425552 dog
58 4 1 1 2 4 0 43 296 518 31 -1
59 5 1 1 2 4 1 43 296 107 31 91.759590 jumped
60 5 1 1 2 4 2 162 302 64 18 92.923576 over
61 5 1 1 2 4 3 235 296 44 24 92.017929 the
62 5 1 1 2 4 4 292 296 55 31 91.558884 lazy
63 5 1 1 2 4 5 357 296 50 24 92.687485 fox.
64 5 1 1 2 4 6 420 296 55 24 91.922661 The
65 5 1 1 2 4 7 486 296 75 30 91.870224 quick
66 4 1 1 2 5 0 37 330 524 31 -1
67 5 1 1 2 5 1 37 330 85 24 92.923935 brown
68 5 1 1 2 5 2 135 330 52 31 91.468765 dog
69 5 1 1 2 5 3 196 330 108 31 91.425491 jumped
70 5 1 1 2 5 4 316 336 63 18 91.489830 over
71 5 1 1 2 5 5 388 330 45 24 91.740379 the
72 5 1 1 2 5 6 445 330 55 31 92.110054 lazy
73 5 1 1 2 5 7 511 330 50 24 93.180054 fox.

View File

@ -1,9 +0,0 @@
This is a lot of 12 point text to test the
ocr code and see if it works on all types
of file format.
The quick brown dog jumped over the
lazy fox. The quick brown dog jumped
over the lazy fox. The quick brown dog
jumped over the lazy fox. The quick
brown dog jumped over the lazy fox.

View File

@ -1,58 +0,0 @@
from pathlib import Path
import pytest
from mealie.services.ocr.pytesseract import OcrService
ocr_service = OcrService()
@pytest.mark.skip("Tesseract is not reliable between environments")
def test_image_to_string():
with open(Path("tests/data/images/test-ocr.png"), "rb") as image:
result = ocr_service.image_to_string(image)
with open(Path("tests/data/text/test-ocr.txt"), encoding="utf-8") as expected_result:
assert result == expected_result.read()
@pytest.mark.skip("Tesseract is not reliable between environments")
def test_image_to_tsv():
with open(Path("tests/data/images/test-ocr.png"), "rb") as image:
result = ocr_service.image_to_tsv(image.read())
with open(Path("tests/data/text/test-ocr.tsv"), encoding="utf-8") as expected_result:
assert result == expected_result.read()
def test_format_tsv_output():
tsv = " level\tpage_num\tblock_num\tpar_num\tline_num\tword_num\tleft\ttop\twidth\theight\tconf\ttext \n1\t1\t0\t0\t0\t0\t0\t0\t640\t480\t-1\t\n5\t1\t1\t1\t1\t1\t36\t92\t60\t24\t87.137558\tThis"
expected_result = [
{
"level": 1,
"page_num": 1,
"block_num": 0,
"par_num": 0,
"line_num": 0,
"word_num": 0,
"left": 0,
"top": 0,
"width": 640,
"height": 480,
"conf": -1.0,
"text": "",
},
{
"level": 5,
"page_num": 1,
"block_num": 1,
"par_num": 1,
"line_num": 1,
"word_num": 1,
"left": 36,
"top": 92,
"width": 60,
"height": 24,
"conf": 87.137558,
"text": "This",
},
]
assert ocr_service.format_tsv_output(tsv) == expected_result

View File

@ -113,12 +113,6 @@ groups_webhooks_rerun = "/api/groups/webhooks/rerun"
"""`/api/groups/webhooks/rerun`"""
media_docker_validate_txt = "/api/media/docker/validate.txt"
"""`/api/media/docker/validate.txt`"""
ocr = "/api/ocr/"
"""`/api/ocr/`"""
ocr_asset_to_tsv = "/api/ocr/asset-to-tsv"
"""`/api/ocr/asset-to-tsv`"""
ocr_file_to_tsv = "/api/ocr/file-to-tsv"
"""`/api/ocr/file-to-tsv`"""
organizers_categories = "/api/organizers/categories"
"""`/api/organizers/categories`"""
organizers_categories_empty = "/api/organizers/categories/empty"
@ -151,8 +145,6 @@ recipes_bulk_actions_tag = "/api/recipes/bulk-actions/tag"
"""`/api/recipes/bulk-actions/tag`"""
recipes_create_from_zip = "/api/recipes/create-from-zip"
"""`/api/recipes/create-from-zip`"""
recipes_create_ocr = "/api/recipes/create-ocr"
"""`/api/recipes/create-ocr`"""
recipes_create_url = "/api/recipes/create-url"
"""`/api/recipes/create-url`"""
recipes_create_url_bulk = "/api/recipes/create-url/bulk"