Compare commits

..

7 Commits

Author SHA1 Message Date
mealie-commit-bot[bot]
d70978cd8b chore: bump version to v3.19.0 2026-05-24 19:30:20 +00:00
Michael Genson
3b2bcca639 fix: Prevent swiping AND scrolling on shopping list (#7659) 2026-05-24 13:28:58 -05:00
Hayden
16163a9189 chore(l10n): New Crowdin updates (#7653)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-05-24 16:50:04 +00:00
mealie-actions[bot]
5ce448af7a chore(l10n): Crowdin locale sync (#7655)
Co-authored-by: GitHub Action <action@github.com>
2026-05-24 03:11:27 +00:00
Michael Genson
c3f87736d0 feat: In-app AI Provider Configuration (#7650) 2026-05-23 11:13:10 -05:00
Hayden
f6fe92b400 chore(l10n): New Crowdin updates (#7652) 2026-05-23 10:54:10 -05:00
Hayden
823b938a2c fix: enforce organize-group-data permission on food/tag/category mutations (#7651) 2026-05-23 05:32:57 +00:00
144 changed files with 5155 additions and 531 deletions

View File

@@ -58,9 +58,6 @@ load_secrets() {
"OIDC_CONFIGURATION_URL"
"OIDC_CLIENT_ID"
"OIDC_CLIENT_SECRET"
"OPENAI_BASE_URL"
"OPENAI_API_KEY"
)
# If any secrets are set, prefer them over base environment variables.

View File

@@ -1,10 +1,10 @@
!!! info
This guide was submitted by a community member. Find something wrong? Submit a PR to get it fixed!
An easy way to add recipes to Mealie from an Apple device is via an Apple Shortcut. This is a short guide to install an configure a shortcut able to add recipes via a link or image(s).
An easy way to add recipes to Mealie from an Apple device is via an Apple Shortcut. This is a short guide to install an configure a shortcut able to add recipes via a link or image(s).
!!! note
If adding via images make sure to enable [Mealie's OpenAI Integration](https://docs.mealie.io/documentation/getting-started/installation/open-ai/)
If adding via images make sure to enable [Mealie's AI Integration](https://docs.mealie.io/documentation/getting-started/installation/ai-providers)
## Javascript can only be run via Shortcuts on the Safari browser on MacOS and iOS. If you do not use Safari you may skip this section
Some sites have begun blocking AI scraping bots, inadvertently blocking the recipe scraping library Mealie uses as well. To circumvent this, the shortcut uses javascript to capture the raw html loaded in the browser and sends that to mealie when possible.
@@ -23,7 +23,7 @@ An API key is needed to authenticate with mealie. To create an api key for a use
The shortcut can be installed via **[This link](https://www.icloud.com/shortcuts/52834724050b42aebe0f2efd8d067360)**. Upon install, replace "MEALIE_API_KEY" with the API key generated previously and "MEALIE_URI" with the full URL used to access your mealie instance e.g. "http://10.0.0.5:9000" or "https://mealie.domain.com".
## Using the Shortcut
Once installed, the shortcut will automatically appear as an option when sharing an image or webpage. It can also be useful to add the shortcut to the home screen of your device. If selected from the home screen or shortcuts app, a menu will appear with prompts to import via **taking photo(s)**, **selecting photo(s)**, **scanning a URL**, or **pasting a URL**.
Once installed, the shortcut will automatically appear as an option when sharing an image or webpage. It can also be useful to add the shortcut to the home screen of your device. If selected from the home screen or shortcuts app, a menu will appear with prompts to import via **taking photo(s)**, **selecting photo(s)**, **scanning a URL**, or **pasting a URL**.
!!! note
Despite the Mealie API being able to accept multiple recipe images for import it is currently impossible to send multiple files in 1 web request via Shortcuts. Instead, the shortcut combines the images into a singular, vertically-concatenated image to send to mealie. This can result in slightly less-accurate text recognition.
Despite the Mealie API being able to accept multiple recipe images for import it is currently impossible to send multiple files in 1 web request via Shortcuts. Instead, the shortcut combines the images into a singular, vertically-concatenated image to send to mealie. This can result in slightly less-accurate text recognition.

View File

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

View File

@@ -11,7 +11,7 @@ Mealie offers several ways to create recipes:
- **Recipe HTML or JSON:** Copy/paste structured HTML or JSON and Mealie can import it.
- **Manual Editor:** Create recipes from scratch using the integrated editor.
Mealie's [AI integration](./installation/open-ai.md) greatly expands the ways you can create recipes:
Mealie's [AI integration](./installation/ai-providers.md) greatly expands the ways you can create recipes:
- **Image Import:** Upload an image of a written or typed recipe and Mealie will use OCR and AI to import it.
- **Video URL Import:** Provide a video URL (e.g., YouTube) and Mealie will transcribe the audio and turn it into a recipe.

View File

@@ -0,0 +1,29 @@
# AI Integration
:octicons-tag-24: v1.7.0
Mealie's AI integration enables several features and enhancements throughout the application. To enable AI features, you must have access to an AI provider (such as OpenAI). Mealie works with any OpenAI-compatible API.
## Configuration
To set up AI providers, visit your group settings.
[Group Settings Demo](https://demo.mealie.io/group){ .md-button .md-button--primary }
- To enable AI features at all, you *must* set a default provider (e.g. `gpt-5`)
- To enable image recognition features, such as creating a recipe from an image, configure a provider capable of image recognition (e.g. `gpt-5`)
- To enable audio transcription features, such as importing a recipe from a video, configure a provider capable of audio transcriptions (e.g. `whisper-1`)
For most users, choosing an OpenAI model (such as `gpt-5`) and supplying the OpenAI API key is all you need to do. Note that while OpenAI has a free tier, it's not sufficiently capable for Mealie (or most other production use cases). For more information, check out [OpenAI's rate limits](https://platform.openai.com/docs/guides/rate-limits). If you deposit $5 into your OpenAI account, you will be permanently bumped up to Tier 1, which is sufficient for Mealie. Cost per-request is dependant on many factors, but Mealie tries to keep token counts conservative.
If you have another provider you'd like to use, such as Azure, you can configure Mealie to use that instead as long as it has an OpenAI-compatible API. For instance, a common self-hosted alternative to OpenAI is [Ollama](https://ollama.com/). To use Ollama with Mealie, set your `base_url` to `http://localhost:11434/v1` (where `http://localhost:11434` is wherever you're hosting Ollama, and `/v1` enables the OpenAI-compatible endpoints). Note that you *must* provide an API key, even though it is ultimately ignored by Ollama.
Note that some models are capable of handling multiple features (e.g. `gpt-5` can handle both normal chat requests and image recognition requests). You may configure one provider for multiple provider features.
While Mealie has prompts for each AI task, you can override these with your own prompts if you'd like. For more information, check out the [backend configuration](./installation/ai-providers.md).
## AI Features
- The OpenAI Ingredient Parser can be used as an alternative to the NLP and Brute Force parsers. Simply choose the OpenAI parser while parsing ingredients (:octicons-tag-24: v1.7.0)
- When importing a recipe via URL, if the default recipe scraper is unable to read the recipe data from a webpage, the webpage contents will be parsed by OpenAI (:octicons-tag-24: v1.9.0)
- You can import an image of a written recipe, which is sent to OpenAI and imported into Mealie. The recipe can be hand-written or typed, as long as the text is in the picture. You can also optionally have OpenAI translate the recipe into your own language (:octicons-tag-24: v1.12.0)
- You can import a recipe via a video URL (e.g., a YouTube link). The video is transcribed AI, and the transcription is parsed into a recipe (:octicons-tag-24: v3.13.0)

View File

@@ -120,22 +120,10 @@ For usage, see [Usage - OpenID Connect](../authentication/oidc-v2.md)
:octicons-tag-24: v1.7.0
Mealie supports various integrations using OpenAI. For more information, check out our [OpenAI documentation](./open-ai.md).
For custom mapping variables (e.g. OPENAI_CUSTOM_HEADERS) you should pass values as JSON encoded strings (e.g. `OPENAI_CUSTOM_PARAMS='{"k1": "v1", "k2": "v2"}'`)
Mealie supports various integrations using OpenAI. For more information, check out our [OpenAI documentation](./ai-providers.md).
| Variables | Default | Description |
|-------------------------------------------------------------------------|:-----------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| OPENAI_BASE_URL<super>[&dagger;][secrets]</super> | None | The base URL for the OpenAI API. If you're not sure, leave this empty to use the standard OpenAI platform |
| OPENAI_API_KEY<super>[&dagger;][secrets]</super> | None | Your OpenAI API Key. Enables OpenAI-related features |
| OPENAI_MODEL | gpt-4o | Which OpenAI model to use. If you're not sure, leave this empty |
| OPENAI_AUDIO_MODEL <br/> :octicons-tag-24: v3.13.0 | whisper-1 | Which OpenAI model to use for audio transcriptions, if enabled. If you're not sure, leave this empty |
| OPENAI_CUSTOM_HEADERS <br/> :octicons-tag-24: v2.0.0 | None | Custom HTTP headers to add to all OpenAI requests. This should generally be left empty unless your custom service requires them |
| OPENAI_CUSTOM_PARAMS <br/> :octicons-tag-24: v2.0.0 | None | Custom HTTP query params to add to all OpenAI requests. This should generally be left empty unless your custom service requires them |
| OPENAI_ENABLE_IMAGE_SERVICES <br/> :octicons-tag-24: v1.12.0 | True | Whether to enable OpenAI image services, such as creating recipes via image. Leave this enabled unless your custom model doesn't support it, or you want to reduce costs |
| OPENAI_ENABLE_TRANSCRIPTION_SERVICES <br/> :octicons-tag-24: v3.13.0 | True | Whether to enable OpenAI transcription services, such as creating recipes via video URL. Leave this enabled unless your custom model doesn't support it, or you want to reduce costs |
| OPENAI_WORKERS | 2 | Number of OpenAI workers per request. Higher values may increase processing speed, but will incur additional API costs |
| OPENAI_SEND_DATABASE_DATA | True | Whether to send Mealie data to OpenAI to improve request accuracy. This will incur additional API costs |
| OPENAI_REQUEST_TIMEOUT | 300 | The number of seconds to wait for an OpenAI request to complete before cancelling the request. Leave this empty unless you're running into timeout issues on slower hardware |
| OPENAI_CUSTOM_PROMPT_DIR <br/> :octicons-tag-24: v3.10.0 | None. | Path to custom prompt files. Only existing files in your custom directory will override the defaults; any missing or empty custom files will automatically fall back to the system defaults. See https://github.com/mealie-recipes/mealie/tree/mealie-next/mealie/services/openai/prompts for expected file names. |
### Theming
@@ -315,7 +303,6 @@ at least these sensitive environment variables when working within shared enviro
- `POSTGRES_PASSWORD`
- `SMTP_PASSWORD`
- `LDAP_QUERY_PASSWORD`
- `OPENAI_API_KEY`
[docker-secrets]: https://docs.docker.com/compose/use-secrets/
[secrets]: #docker-secrets

View File

@@ -31,7 +31,7 @@ To deploy mealie on your local network, it is highly recommended to use Docker t
We've gone through a few versions of Mealie v1 deployment targets. We have settled on a single container deployment, and we've begun publishing the nightly container on github containers. If you're looking to move from the old nightly (split containers _or_ the omni image) to the new nightly, there are a few things you need to do:
1. Take a backup just in case!
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v3.18.0`
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v3.19.0`
3. Take the external port from the frontend container and set that as the port mapped to port `9000` on the new container. The frontend is now served on port 9000 from the new container, so it will need to be mapped for you to have access.
4. Restart the container

View File

@@ -1,24 +0,0 @@
# OpenAI Integration
:octicons-tag-24: v1.7.0
Mealie's OpenAI integration enables several features and enhancements throughout the application. To enable OpenAI features, you must have an account with OpenAI and configure Mealie to use the OpenAI API key (for more information, check out the [backend configuration](./backend-config.md#openai)).
## Configuration
For most users, supplying the OpenAI API key is all you need to do; you will use the regular OpenAI service with the default language model. Note that while OpenAI has a free tier, it's not sufficiently capable for Mealie (or most other production use cases). For more information, check out [OpenAI's rate limits](https://platform.openai.com/docs/guides/rate-limits). If you deposit $5 into your OpenAI account, you will be permanently bumped up to Tier 1, which is sufficient for Mealie. Cost per-request is dependant on many factors, but Mealie tries to keep token counts conservative.
Alternatively, if you have another service you'd like to use in-place of OpenAI, you can configure Mealie to use that instead, as long as it has an OpenAI-compatible API. For instance, a common self-hosted alternative to OpenAI is [Ollama](https://ollama.com/). To use Ollama with Mealie, change your `OPENAI_BASE_URL` to `http://localhost:11434/v1` (where `http://localhost:11434` is wherever you're hosting Ollama, and `/v1` enables the OpenAI-compatible endpoints). Note that you *must* provide an API key, even though it is ultimately ignored by Ollama.
If you wish to disable image recognition features (to save costs, or because your custom model doesn't support them) you can set `OPENAI_ENABLE_IMAGE_SERVICES` to `False`.
If you wish to disable transcription features (to save costs, or because your custom model doesn't support them) you can set `OPENAI_ENABLE_TRANSCRIPTION_SERVICES` to `False`.
For more information on what configuration options are available, check out the [backend configuration](./backend-config.md#openai).
## OpenAI Features
- The OpenAI Ingredient Parser can be used as an alternative to the NLP and Brute Force parsers. Simply choose the OpenAI parser while parsing ingredients (:octicons-tag-24: v1.7.0)
- When importing a recipe via URL, if the default recipe scraper is unable to read the recipe data from a webpage, the webpage contents will be parsed by OpenAI (:octicons-tag-24: v1.9.0)
- You can import an image of a written recipe, which is sent to OpenAI and imported into Mealie. The recipe can be hand-written or typed, as long as the text is in the picture. You can also optionally have OpenAI translate the recipe into your own language (:octicons-tag-24: v1.12.0)
- You can import a recipe via a video URL (e.g., a YouTube link). The video is transcribed using OpenAI's Whisper model, and the transcription is parsed into a recipe (:octicons-tag-24: v3.13.0)

View File

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

View File

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

View File

@@ -76,7 +76,7 @@ nav:
- Backend Configuration: "documentation/getting-started/installation/backend-config.md"
- Security: "documentation/getting-started/installation/security.md"
- Logs: "documentation/getting-started/installation/logs.md"
- OpenAI: "documentation/getting-started/installation/open-ai.md"
- AI Providers: "documentation/getting-started/installation/ai-providers.md"
- Usage:
- Backup and Restoring: "documentation/getting-started/usage/backups-and-restoring.md"
- Permissions and Public Access: "documentation/getting-started/usage/permissions-and-public-access.md"

View File

@@ -0,0 +1,63 @@
<template>
<div>
<p>AI providers can now be configured directly in Mealie, without managing environment variables or secrets.</p>
<div class="mb-2">
AI providers enable features such as:
<ul class="ml-6">
<li>Creating recipes from images</li>
<li>Importing recipes from videos (YouTube, TikTok, etc.)</li>
<li>Enhanced ingredient parsing</li>
<li>And more!</li>
</ul>
</div>
<hr class="mt-2 mb-4">
<p>
<span v-if="group?.aiProviderSettings?.aiEnabled">
Your group already has AI providers configured.
</span>
<span v-else>
Your group does not currently have any AI providers configured.
</span>
<span v-if="user?.canManage">
You can manage them here:
<br>
<v-btn class="mt-2" color="primary" to="/group">
{{ $t("profile.group-settings") }}
</v-btn>
</span>
<span v-else-if="!group?.aiProviderSettings?.aiEnabled">
Contact a group manager or server admin to set up AI providers for your group.
</span>
</p>
<div v-if="user?.admin">
<br>
<p>
As an admin, you can configure AI providers for any group. Unlike the old environment variable approach, providers are configured per-group:
<br>
<v-btn class="mt-2" color="primary" to="/admin/manage/groups">
{{ $t("group.admin-group-management") }}
</v-btn>
</p>
</div>
</div>
</template>
<script setup lang="ts">
import { useGroupSelf } from "~/composables/use-groups";
import type { AnnouncementMeta } from "~/composables/use-announcements";
const { user } = useMealieAuth();
const { group } = useGroupSelf();
</script>
<script lang="ts">
export const meta: AnnouncementMeta = {
title: "Improved AI Provider Configuration",
};
</script>
<style scoped lang="css">
p {
padding-bottom: 8px;
}
</style>

View File

@@ -0,0 +1,204 @@
<template>
<BaseDialog
v-model="dialog"
:title="isEdit ? $t('group.ai-provider-settings.edit-provider') : $t('group.ai-provider-settings.create-provider')"
:icon="$globals.icons.robot"
:loading="loading"
can-submit
:submit-icon="isEdit ? $globals.icons.save : $globals.icons.createAlt"
:submit-text="isEdit ? $t('general.update') : $t('general.create')"
:submit-disabled="submitDisabled"
@submit="handleSubmit"
@close="resetForm"
>
<v-card-text v-if="init" style="max-height: 70vh; overflow-y: auto;">
<v-form ref="form" v-no-autofill>
<v-text-field
v-model="formData.name"
:label="$t('group.ai-provider-settings.provider-name')"
:rules="[validators.required]"
density="compact"
variant="outlined"
class="mb-4"
/>
<v-text-field
v-model="formData.model"
:label="$t('group.ai-provider-settings.model')"
:hint="$t('group.ai-provider-settings.model-description')"
:rules="[validators.required]"
density="compact"
variant="outlined"
class="mb-4"
/>
<v-text-field
v-model="formData.apiKey"
:label="$t('group.ai-provider-settings.api-key')"
:hint="$t(
isEdit
? 'group.ai-provider-settings.api-key-description-edit'
: 'group.ai-provider-settings.api-key-description-create',
)"
:persistent-hint="isEdit"
:rules="isEdit ? [] : [validators.required]"
density="compact"
variant="outlined"
type="password"
class="mb-4"
/>
<v-text-field
v-model="formData.baseUrl"
:label="$t('group.ai-provider-settings.base-url')"
:hint="$t('group.ai-provider-settings.base-url-description')"
density="compact"
variant="outlined"
class="mb-4"
/>
<v-number-input
v-model.number="formData.timeout"
:label="$t('group.ai-provider-settings.request-timeout-seconds')"
type="number"
:min="0"
hide-details
control-variant="stacked"
density="compact"
variant="outlined"
class="mb-4"
/>
<v-expansion-panels v-model="advancedPanel" variant="accordion">
<v-expansion-panel>
<v-expansion-panel-title class="text-subtitle-2" expand-icon="$expand" collapse-icon="$expand">
{{ $t('search.advanced') }}
</v-expansion-panel-title>
<v-expansion-panel-text class="px-0">
<div class="mb-2 text-subtitle-2">
{{ $t('group.ai-provider-settings.request-headers') }}
</div>
<BaseKeyValueEditor
v-model="formData.requestHeaders"
class="mb-4"
/>
<v-divider class="mb-4" />
<div class="mb-2 text-subtitle-2">
{{ $t('group.ai-provider-settings.request-params') }}
</div>
<BaseKeyValueEditor
v-model="formData.requestParams"
/>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
</v-form>
</v-card-text>
<AppLoader v-else waiting-text="" />
</BaseDialog>
</template>
<script setup lang="ts">
import { useAIProviders } from "~/composables/use-ai-providers";
import { validators } from "~/composables/use-validators";
import type { AIProviderCreate, AIProviderUpdate } from "~/lib/api/types/group";
const props = withDefaults(defineProps<{
providerId?: string;
}>(), {
providerId: undefined,
});
const emit = defineEmits<{
(e: "create", data: AIProviderCreate): void;
(e: "update", id: string, data: AIProviderUpdate): void;
}>();
const dialog = defineModel<boolean>({ default: false });
const { $globals } = useNuxtApp();
const { loading, getOne } = useAIProviders();
const init = ref(false);
const form = ref();
const advancedPanel = ref<number | undefined>(undefined);
const isEdit = computed(() => !!props.providerId);
const defaultForm = () => ({
name: "",
model: "",
apiKey: "",
baseUrl: "",
timeout: 300,
requestHeaders: {} as Record<string, string>,
requestParams: {} as Record<string, string>,
});
const formData = reactive(defaultForm());
const submitDisabled = computed(() => {
return !formData.name?.trim() || !formData.model?.trim() || (!isEdit.value && !formData.apiKey?.trim());
});
// Fetch existing provider when editing; reset form for create mode
watch(
() => [dialog.value, props.providerId] as const,
async ([open, id]) => {
if (!open) return;
if (!id) {
// Create mode — just show the empty form
resetForm();
init.value = true;
return;
}
init.value = false;
const { data } = await getOne(id);
init.value = true;
if (data) {
formData.name = data.name;
formData.model = data.model;
formData.apiKey = "";
formData.baseUrl = data.baseUrl ?? "";
formData.timeout = data.timeout ?? 300;
formData.requestHeaders = { ...(data.requestHeaders ?? {}) };
formData.requestParams = { ...(data.requestParams ?? {}) };
}
},
{ immediate: true },
);
function handleSubmit() {
// Required field guard (button is also disabled, but keep as a safeguard)
if (!formData.name?.trim() || !formData.model?.trim()) return;
if (!isEdit.value && !formData.apiKey?.trim()) return;
if (isEdit.value && props.providerId) {
const payload: AIProviderUpdate & { apiKey?: string } = {
name: formData.name,
model: formData.model,
baseUrl: formData.baseUrl || null,
timeout: formData.timeout,
requestHeaders: Object.keys(formData.requestHeaders).length ? formData.requestHeaders : undefined,
requestParams: Object.keys(formData.requestParams).length ? formData.requestParams : undefined,
};
if (formData.apiKey) {
payload.apiKey = formData.apiKey;
}
emit("update", props.providerId, payload);
}
else {
const createPayload = {
name: formData.name,
model: formData.model,
apiKey: formData.apiKey,
baseUrl: formData.baseUrl || null,
timeout: formData.timeout,
requestHeaders: Object.keys(formData.requestHeaders).length ? formData.requestHeaders : undefined,
requestParams: Object.keys(formData.requestParams).length ? formData.requestParams : undefined,
};
emit("create", createPayload as AIProviderCreate);
}
}
function resetForm() {
Object.assign(formData, defaultForm());
form.value?.reset();
advancedPanel.value = undefined;
}
</script>

View File

@@ -0,0 +1,170 @@
<template>
<div v-if="providerSettings">
<BaseCardSectionTitle v-if="!hideHeader" :title="$t('group.ai-provider-settings.ai-provider-settings')">
<template v-if="noDefaultProviderWarning" #append-title>
<v-tooltip location="bottom" color="warning">
<template #activator="{ props: tooltipProps }">
<v-icon v-bind="tooltipProps" size="small" color="warning" class="ms-2">
{{ $globals.icons.alert }}
</v-icon>
</template>
<span>{{ $t('group.ai-provider-settings.no-default-provider-warning') }}</span>
</v-tooltip>
</template>
</BaseCardSectionTitle>
<v-card-text v-if="!hideHeader" class="pt-0 pb-10 px-0">
{{ $t("group.ai-provider-settings.ai-provider-settings-description") }}
</v-card-text>
<v-row class="mb-4">
<v-col cols="12">
<v-autocomplete
v-model="local.defaultProviderId"
:label="$t('group.ai-provider-settings.default-provider')"
:items="local.providers"
item-title="name"
item-value="id"
clearable
hide-details
density="compact"
variant="outlined"
/>
<v-card-subtitle class="mt-1">
{{ $t("group.ai-provider-settings.default-provider-description") }}
</v-card-subtitle>
</v-col>
<v-col cols="12">
<v-autocomplete
v-model="local.audioProviderId"
:label="$t('group.ai-provider-settings.audio-provider')"
:items="local.providers"
item-title="name"
item-value="id"
clearable
hide-details
density="compact"
variant="outlined"
/>
<v-card-subtitle class="mt-1">
{{ $t("group.ai-provider-settings.audio-provider-description") }}
</v-card-subtitle>
</v-col>
<v-col cols="12">
<v-autocomplete
v-model="local.imageProviderId"
:label="$t('group.ai-provider-settings.image-provider')"
:items="local.providers"
item-title="name"
item-value="id"
clearable
hide-details
density="compact"
variant="outlined"
/>
<v-card-subtitle class="mt-1">
{{ $t("group.ai-provider-settings.image-provider-description") }}
</v-card-subtitle>
</v-col>
</v-row>
<GroupAIProviderDialog
v-model="dialogOpen"
:provider-id="editingProviderId ?? undefined"
@create="(data) => $emit('create', data)"
@update="(id, data) => $emit('update', id, data)"
/>
<BaseCardSectionTitle
:title="$t('group.ai-provider-settings.providers')"
size="medium"
class="pt-2"
>
<template #append-title>
<BaseButton
:text="$t('group.ai-provider-settings.create-provider')"
class="ms-auto my-2"
create
small
@click="openCreate"
/>
</template>
</BaseCardSectionTitle>
<v-card
v-for="provider in local.providers"
:key="provider.id"
variant="tonal"
class="pa-0 mb-4"
>
<v-row no-gutters>
<v-col :cols="10">
<v-card-text>
{{ provider.name }}
</v-card-text>
</v-col>
<v-col :cols="2">
<BaseButtonGroup
:buttons="[
{
icon: $globals.icons.edit,
text: $t('general.edit'),
event: 'edit',
},
{
icon: $globals.icons.delete,
text: $t('general.delete'),
event: 'delete',
},
]"
@edit="openEdit(provider.id)"
@delete="$emit('delete', provider.id)"
/>
</v-col>
</v-row>
</v-card>
</div>
</template>
<script setup lang="ts">
import type { AIProviderCreate, AIProviderUpdate } from "~/lib/api/types/group";
import type { AIProviderSettingsOut } from "~/lib/api/types/user";
const providerSettings = defineModel<AIProviderSettingsOut>({ required: true });
const props = withDefaults(defineProps<{
hideHeader?: boolean;
}>(), {
hideHeader: false,
});
const { hideHeader } = toRefs(props);
const local = reactive({ ...providerSettings.value });
watch(local, (newVal) => { providerSettings.value = { ...newVal }; });
// Sync back when the parent refreshes after create/update/delete
watch(providerSettings, (newVal) => { if (newVal) Object.assign(local, newVal); });
const noDefaultProviderWarning = computed(
() => local.providers.length > 0 && !local.defaultProviderId,
);
defineEmits<{
(e: "create", data: AIProviderCreate): void;
(e: "update", id: string, data: AIProviderUpdate): void;
(e: "delete", id: string): void;
}>();
const dialogOpen = ref(false);
const editingProviderId = ref<string | null>(null);
function openCreate() {
editingProviderId.value = null;
dialogOpen.value = true;
}
function openEdit(id: string) {
editingProviderId.value = id;
dialogOpen.value = true;
}
</script>

View File

@@ -200,6 +200,7 @@ import { useUserApi } from "~/composables/api";
import { useIngredientTextParser } from "~/composables/recipes";
import { useFoodData, useFoodStore, useUnitData, useUnitStore } from "~/composables/store";
import { useGlobalI18n } from "~/composables/use-global-i18n";
import { useGroupSelf } from "~/composables/use-groups";
import { alert } from "~/composables/use-toast";
import { useParsingPreferences } from "~/composables/use-users/preferences";
@@ -215,7 +216,7 @@ const emit = defineEmits<{
(e: "save", value: NoUndefinedField<RecipeIngredient[]>): void;
}>();
const { $appInfo } = useNuxtApp();
const { group } = useGroupSelf();
const i18n = useGlobalI18n();
const api = useUserApi();
const drag = ref(false);
@@ -240,7 +241,7 @@ const availableParsers = computed(() => {
{
text: i18n.t("recipe.parser.openai-parser"),
value: "openai",
hide: !$appInfo.enableOpenai,
hide: !group.value?.aiProviderSettings?.aiEnabled,
},
];
});

View File

@@ -10,24 +10,9 @@
}"
>
<v-row
v-touch="{
move: ({ originalEvent: { touches: [{ screenX, screenY }] } }) => {
swipeInfo.touchendX = screenX;
swipeInfo.touchendY = screenY;
},
start: ({ originalEvent: { touches: [{ screenX, screenY }] } }) => {
swipeInfo.touchstartX = screenX;
swipeInfo.touchstartY = screenY;
},
end: () => {
if (swiping < SWIPE_THRESHOLD) {
swipeInfo = {};
return;
}
swipeInfo = {};
toggleChecked();
},
}"
ref="swipeRowRef"
v-touch="{ move: onSwipeMove, start: onSwipeStart, end: onSwipeEnd }"
style="touch-action: pan-y;"
no-gutters
class="flex-nowrap align-center"
>
@@ -214,9 +199,23 @@ const emit = defineEmits<{
}>();
const SWIPE_THRESHOLD = 50;
const SCROLL_THRESHOLD = 50;
const { isRtl } = useRtl();
const swipeRowRef = ref<InstanceType<typeof import("vuetify/components").VRow> | null>(null);
onMounted(() => {
const el = swipeRowRef.value?.$el as HTMLElement | undefined;
if (!el) return;
el.addEventListener(
"touchmove",
(e: TouchEvent) => {
if (swipeInfo.value.gesture === "swipe") {
e.preventDefault();
}
},
{ passive: false },
);
});
const i18n = useI18n();
const displayRecipeRefs = ref(false);
const itemLabelCols = computed<string>(() => (model.value?.checked ? "auto" : "6"));
@@ -267,22 +266,66 @@ function save() {
edit.value = false;
}
const swipeInfo: Ref<{ touchstartX?: number; touchendX?: number; touchstartY?: number; touchendY?: number }> = ref({});
const swiping = computed(() => {
const { touchstartX, touchendX, touchstartY, touchendY } = swipeInfo.value ?? {};
if (touchstartX === undefined || touchendX === undefined) {
return 0;
}
const deltaX = isRtl.value ? touchstartX - touchendX : touchendX - touchstartX;
type SwipeGesture = null | "scroll" | "swipe";
// If there's significant vertical movement, treat as a scroll gesture and ignore
if (touchstartY !== undefined && touchendY !== undefined) {
const deltaY = Math.abs(touchendY - touchstartY);
if (deltaY > SCROLL_THRESHOLD) {
return 0;
const swipeInfo = ref({
touchstartX: 0,
touchstartY: 0,
touchendX: 0,
touchendY: 0,
gesture: null as SwipeGesture,
});
function getSwipePoint(e: any) {
const touch = e?.touches?.[0] ?? e?.changedTouches?.[0] ?? e;
return { x: touch?.clientX ?? 0, y: touch?.clientY ?? 0 };
}
function resetSwipe() {
swipeInfo.value = { touchstartX: 0, touchstartY: 0, touchendX: 0, touchendY: 0, gesture: null };
}
function onSwipeStart(payload: any) {
const { x, y } = getSwipePoint(payload.originalEvent);
swipeInfo.value = { touchstartX: x, touchstartY: y, touchendX: x, touchendY: y, gesture: null };
}
function onSwipeMove(payload: any) {
const { x, y } = getSwipePoint(payload.originalEvent);
swipeInfo.value.touchendX = x;
swipeInfo.value.touchendY = y;
if (!swipeInfo.value.gesture) {
const deltaX = Math.abs(x - swipeInfo.value.touchstartX);
const deltaY = Math.abs(y - swipeInfo.value.touchstartY);
if (deltaY > 8 && deltaY > deltaX) {
swipeInfo.value.gesture = "scroll";
}
else if (deltaX > 8 && deltaX > deltaY) {
swipeInfo.value.gesture = "swipe";
}
else if (deltaX > 8 || deltaY > 8) {
// Diagonal / ambiguous — default to scroll
swipeInfo.value.gesture = "scroll";
}
}
return Math.min(Math.max(0, deltaX), 100);
}
function onSwipeEnd() {
if (swipeInfo.value.gesture === "swipe" && swiping.value >= SWIPE_THRESHOLD) {
toggleChecked();
}
resetSwipe();
}
const swiping = computed(() => {
if (swipeInfo.value.gesture !== "swipe") {
return 0;
}
const deltaX = isRtl.value
? swipeInfo.value.touchstartX - swipeInfo.value.touchendX
: swipeInfo.value.touchendX - swipeInfo.value.touchstartX;
return Math.max(0, Math.min(deltaX, 100));
});
const recipeList = computed<RecipeSummary[]>(() => {

View File

@@ -4,7 +4,7 @@
style="border-color: lightgrey;"
:to="link.to"
height="100%"
class="d-flex flex-column mt-4"
class="d-flex flex-column mt-4 pa-2"
>
<div
v-if="$vuetify.display.smAndDown"

View File

@@ -96,15 +96,17 @@
<script setup lang="ts">
import { useLoggedInState } from "~/composables/use-logged-in-state";
import type { SideBarLink } from "~/types/application-types";
import { useGroupSelf } from "~/composables/use-groups";
import { useCookbookPreferences } from "~/composables/use-users/preferences";
import { useCookbookStore, usePublicCookbookStore } from "~/composables/store/use-cookbook-store";
import type { ReadCookBook } from "~/lib/api/types/cookbook";
const i18n = useI18n();
const { $appInfo, $globals } = useNuxtApp();
const { $globals } = useNuxtApp();
const display = useDisplay();
const auth = useMealieAuth();
const { isOwnGroup } = useLoggedInState();
const { group } = useGroupSelf();
const route = useRoute();
const groupSlug = computed(() => route.params.groupSlug as string || auth.user.value?.groupSlug || "");
@@ -131,7 +133,7 @@ const cookbooks = computed(() => {
return [];
});
const showImageImport = computed(() => $appInfo.enableOpenaiImageServices);
const showImageImport = computed(() => group.value?.aiProviderSettings?.imageProviderEnabled);
const sidebar = ref<boolean>(false);
onMounted(() => {

View File

@@ -7,7 +7,8 @@
'mt-8': section,
}"
>
<v-card-title class="text-h5 pl-0 py-0" style="font-weight: normal;">
<v-card-title :class="`text-title-${size} pl-0 py-0 d-flex align-center`" style="font-weight: normal;">
<slot name="prepend-title" />
<v-icon
v-if="icon"
size="small"
@@ -16,6 +17,7 @@
{{ icon }}
</v-icon>
{{ title }}
<slot name="append-title" />
</v-card-title>
<v-card-text
v-if="$slots.default"
@@ -30,11 +32,17 @@
</template>
<script setup lang="ts">
type Size = "large" | "medium" | "small";
defineProps({
title: {
type: String,
required: true,
},
size: {
type: String as () => Size,
default: "large",
},
icon: {
type: String,
default: "",

View File

@@ -0,0 +1,127 @@
<template>
<div>
<div
v-for="(value, key) in (modelValue ?? {})"
:key="key"
class="d-flex align-center mb-2 gap-2"
>
<v-text-field
:model-value="key"
:label="resolvedKeyLabel"
density="compact"
variant="outlined"
hide-details
readonly
class="me-3 flex-grow-1"
/>
<v-text-field
:model-value="value"
:label="resolvedValueLabel"
density="compact"
variant="outlined"
hide-details
class="ms-3 flex-grow-1"
@update:model-value="updateValue(key, $event)"
/>
<v-btn
icon
variant="text"
color="error"
size="small"
@click="removeEntry(key)"
>
<v-icon>{{ $globals.icons.delete }}</v-icon>
</v-btn>
</div>
<div class="d-flex align-center mt-2 gap-2" @focusout="onNewEntryFocusOut">
<v-text-field
v-model="newKey"
:label="resolvedKeyLabel"
density="compact"
variant="outlined"
hide-details
class="me-3 flex-grow-1"
@keydown.enter.prevent="addEntry"
/>
<v-text-field
v-model="newValue"
:label="resolvedValueLabel"
density="compact"
variant="outlined"
hide-details
class="ms-3 flex-grow-1"
@keydown.enter.prevent="addEntry"
/>
<v-btn
icon
variant="text"
color="primary"
size="small"
:disabled="!newKey?.trim()"
@click="addEntry"
>
<v-icon>{{ $globals.icons.createAlt }}</v-icon>
</v-btn>
</div>
</div>
</template>
<script setup lang="ts">
import { useGlobalI18n } from "~/composables/use-global-i18n";
const i18n = useGlobalI18n();
const props = defineProps<{
modelValue?: Record<string, string> | null;
keyLabel?: string;
valueLabel?: string;
}>();
const emit = defineEmits<{
(e: "update:modelValue", value: Record<string, string>): void;
}>();
const { $globals } = useNuxtApp();
const resolvedKeyLabel = computed(() => props.keyLabel ?? i18n.t("general.key"));
const resolvedValueLabel = computed(() => props.valueLabel ?? i18n.t("general.value"));
const newKey = ref("");
const newValue = ref("");
function current(): Record<string, string> {
return { ...(props.modelValue ?? {}) };
}
function addEntry() {
const key = newKey.value?.trim();
if (!key) return;
const updated = current();
updated[key] = newValue.value;
emit("update:modelValue", updated);
newKey.value = "";
newValue.value = "";
}
function onNewEntryFocusOut(e: FocusEvent) {
const relatedTarget = e.relatedTarget as HTMLElement | null;
const currentTarget = e.currentTarget as HTMLElement;
if (!relatedTarget || !currentTarget.contains(relatedTarget)) {
addEntry();
}
}
function updateValue(key: string, value: string) {
const updated = current();
updated[key] = value;
emit("update:modelValue", updated);
}
function removeEntry(key: string) {
const updated = current();
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete updated[key];
emit("update:modelValue", updated);
}
</script>

View File

@@ -0,0 +1,55 @@
import { useUserApi } from "~/composables/api";
import type { AIProviderCreate, AIProviderUpdate } from "~/lib/api/types/group";
export function useAIProviders() {
const api = useUserApi();
const loading = ref(false);
async function getOne(id: string) {
loading.value = true;
try {
return await api.aiProviders.getOne(id);
}
finally {
loading.value = false;
}
}
async function createOne(payload: AIProviderCreate) {
loading.value = true;
try {
return await api.aiProviders.createOne(payload);
}
finally {
loading.value = false;
}
}
async function updateOne(id: string, payload: AIProviderUpdate) {
loading.value = true;
try {
return await api.aiProviders.updateOne(id, payload);
}
finally {
loading.value = false;
}
}
async function deleteOne(id: string) {
loading.value = true;
try {
return await api.aiProviders.deleteOne(id);
}
finally {
loading.value = false;
}
}
return {
loading: readonly(loading),
getOne,
createOne,
updateOne,
deleteOne,
};
}

View File

@@ -22,7 +22,10 @@ const allAnnouncements: Announcement[] = Object.entries(_announcementsUnsorted)
.map(([path, mod]) => {
const key = path.split("/").at(-1)!.replace(".vue", "");
const parsed = new Date(key.split("_", 1)[0]!);
const dateParts = key.split("_", 1)[0]!.split("-").map(Number);
const parsed = dateParts.length === 3
? new Date(dateParts[0]!, dateParts[1]! - 1, dateParts[2]!)
: new Date(NaN);
const date = isNaN(parsed.getTime()) ? undefined : parsed;
return {

View File

@@ -42,6 +42,25 @@ export const useGroupSelf = function () {
return data || undefined;
},
async updateAIProviderSettings() {
if (!groupSelfRef.value) {
await refreshGroupSelf();
}
if (!groupSelfRef.value?.aiProviderSettings) {
return;
}
const { data } = await api.groups.setAIProviderSettings(groupSelfRef.value.aiProviderSettings);
if (data) {
groupSelfRef.value.aiProviderSettings = data;
}
return data || undefined;
},
async refresh() {
await refreshGroupSelf();
},
};
const group = actions.get();

View File

@@ -3,7 +3,7 @@ export const LOCALES = [
{
name: "繁體中文 (Chinese traditional)",
value: "zh-TW",
progress: 98,
progress: 97,
dir: "ltr",
pluralFoodHandling: "never",
},
@@ -24,14 +24,14 @@ export const LOCALES = [
{
name: "Українська (Ukrainian)",
value: "uk-UA",
progress: 86,
progress: 85,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Türkçe (Turkish)",
value: "tr-TR",
progress: 54,
progress: 53,
dir: "ltr",
pluralFoodHandling: "never",
},
@@ -52,42 +52,42 @@ export const LOCALES = [
{
name: "Slovenščina (Slovenian)",
value: "sl-SI",
progress: 57,
progress: 56,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Slovenčina (Slovak)",
value: "sk-SK",
progress: 61,
progress: 60,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Pусский (Russian)",
value: "ru-RU",
progress: 59,
progress: 58,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Română (Romanian)",
value: "ro-RO",
progress: 60,
progress: 59,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Português (Portuguese)",
value: "pt-PT",
progress: 57,
progress: 56,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Português do Brasil (Brazilian Portuguese)",
value: "pt-BR",
progress: 99,
progress: 98,
dir: "ltr",
pluralFoodHandling: "always",
},
@@ -101,14 +101,14 @@ export const LOCALES = [
{
name: "Norsk (Norwegian)",
value: "no-NO",
progress: 60,
progress: 59,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Nederlands (Dutch)",
value: "nl-NL",
progress: 98,
progress: 97,
dir: "ltr",
pluralFoodHandling: "always",
},
@@ -129,7 +129,7 @@ export const LOCALES = [
{
name: "한국어 (Korean)",
value: "ko-KR",
progress: 55,
progress: 54,
dir: "ltr",
pluralFoodHandling: "never",
},
@@ -143,35 +143,35 @@ export const LOCALES = [
{
name: "Italiano (Italian)",
value: "it-IT",
progress: 73,
progress: 72,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Íslenska (Icelandic)",
value: "is-IS",
progress: 57,
progress: 56,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Magyar (Hungarian)",
value: "hu-HU",
progress: 61,
progress: 62,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Hrvatski (Croatian)",
value: "hr-HR",
progress: 42,
progress: 41,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "עברית (Hebrew)",
value: "he-IL",
progress: 73,
progress: 72,
dir: "rtl",
pluralFoodHandling: "always",
},
@@ -192,7 +192,7 @@ export const LOCALES = [
{
name: "Français canadien (Canadian French)",
value: "fr-CA",
progress: 90,
progress: 89,
dir: "ltr",
pluralFoodHandling: "always",
},
@@ -206,7 +206,7 @@ export const LOCALES = [
{
name: "Suomi (Finnish)",
value: "fi-FI",
progress: 99,
progress: 98,
dir: "ltr",
pluralFoodHandling: "always",
},
@@ -241,21 +241,21 @@ export const LOCALES = [
{
name: "Ελληνικά (Greek)",
value: "el-GR",
progress: 57,
progress: 58,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Deutsch (German)",
value: "de-DE",
progress: 99,
progress: 98,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Dansk (Danish)",
value: "da-DK",
progress: 100,
progress: 99,
dir: "ltr",
pluralFoodHandling: "always",
},
@@ -269,7 +269,7 @@ export const LOCALES = [
{
name: "Català (Catalan)",
value: "ca-ES",
progress: 60,
progress: 59,
dir: "ltr",
pluralFoodHandling: "always",
},
@@ -283,14 +283,14 @@ export const LOCALES = [
{
name: "العربية (Arabic)",
value: "ar-SA",
progress: 98,
progress: 97,
dir: "rtl",
pluralFoodHandling: "always",
},
{
name: "Afrikaans (Afrikaans)",
value: "af-ZA",
progress: 37,
progress: 36,
dir: "ltr",
pluralFoodHandling: "always",
},

View File

@@ -223,7 +223,9 @@
"show-advanced": "Wys uitgebreide",
"add-field": "Voeg veld by",
"date-created": "Datum Geskep",
"date-updated": "Datum Opgedateer"
"date-updated": "Datum Opgedateer",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Is jy seker jy wil <b>{groupName}<b/> uitvee?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Veranderinge aan hierdie groep sal onmiddellik weerspieël word.",
"group-id-value": "Groep-Id: {0}",
"total-households": "Total Households",
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household"
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Household",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage",
"common-settings-for-new-sites": "Here are some common settings for new sites",
"setup-complete": "Setup Complete!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Here are a few things to help you get started with Mealie",
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.",
"manage-profile-or-get-invite-link": "Manage your own profile, or grab an invite link to share with others."

View File

@@ -223,7 +223,9 @@
"show-advanced": "إظهار متقدمة",
"add-field": "إضافة حقل",
"date-created": "تاريخ الإنشاء",
"date-updated": "تاريخ التحديث"
"date-updated": "تاريخ التحديث",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "هل انت متأكد من رغبتك في حذف <b>{groupName}<b/>؟",
@@ -283,7 +285,40 @@
"admin-group-management-text": "التغييرات التي ستطرأ على هذه المجموعة ستنعكس على الفور.",
"group-id-value": "معرف المجموعة: {0}",
"total-households": "مجموع المنزل",
"you-must-select-a-group-before-selecting-a-household": "يجب عليك تحديد مجموعة قبل تحديد المنزل"
"you-must-select-a-group-before-selecting-a-household": "يجب عليك تحديد مجموعة قبل تحديد المنزل",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "المنزل",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "أنا بالفعل جاهز، فقط أحضر ني إلى الصفحة الرئيسية",
"common-settings-for-new-sites": "فيما يلي بعض الإعدادات الشائعة للمواقع الجديدة",
"setup-complete": "تمت الإعدادات!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "إليك بعض الأشياء لمساعدتك على البدء مع ميالي",
"restore-from-v1-backup": "لديك نسخة احتياطية من مثيل سابق لـ Mealie v1؟ يمكنك استعادتها هنا.",
"manage-profile-or-get-invite-link": "إدارة الملف الشخصي الخاص بك، أو التقط رابط دعوة للمشاركة مع الآخرين."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Разширени настройки",
"add-field": "Добави поле",
"date-created": "Дата на създаване",
"date-updated": "Дата на актуализация"
"date-updated": "Дата на актуализация",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Сигурни ли сте, че искате да изтриете <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Промените по тази група ще бъдат отразени моментално.",
"group-id-value": "ID на Групата: {0}",
"total-households": "Общ брой домакинства",
"you-must-select-a-group-before-selecting-a-household": "Трябва да изберете група, преди да изберете домакинство"
"you-must-select-a-group-before-selecting-a-household": "Трябва да изберете група, преди да изберете домакинство",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Домакинство",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Вече съм настроен, просто ме отведете до началната страница",
"common-settings-for-new-sites": "Ето някои общи настройки за нови сайтове",
"setup-complete": "Настройката е завършена!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Ето няколко неща, които ще Ви помогнат да започнете с Mealie",
"restore-from-v1-backup": "Имате резервно копие от предишна инстанция на Mealie v1? Можете да го възстановите тук.",
"manage-profile-or-get-invite-link": "Управлявайте собствения си профил или вземете връзка за покана, която да споделите с други."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Mostrar els paràmetres avançats",
"add-field": "Afegir camp",
"date-created": "Data de creació",
"date-updated": "Data dactualització"
"date-updated": "Data dactualització",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Esteu segur de voler suprimir el grup <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Els canvis en aquest grup s'actualitzaran immediatament.",
"group-id-value": "ID del grup: {0}",
"total-households": "Llars totals",
"you-must-select-a-group-before-selecting-a-household": "Heu de seleccionar un grup abans de seleccionar una llar"
"you-must-select-a-group-before-selecting-a-household": "Heu de seleccionar un grup abans de seleccionar una llar",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Llar",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Ja ho tinc configurat, porta'm a la pàgina principal",
"common-settings-for-new-sites": "Aquí hi ha algunes configuracions comunes per noves pàgines",
"setup-complete": "Configuració completada!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Aquí hi ha unes quantes coses per ajudar-te a posar Mealie en marxa",
"restore-from-v1-backup": "Tens una còpia de seguretat d'una instància prèvia de Mealie v1? Pots restaurar-la aquí.",
"manage-profile-or-get-invite-link": "Gestiona el teu propi perfil, o agafa un enllaç d'invitació per compartir amb altres."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Zobrazit pokročilé",
"add-field": "Přidat pole",
"date-created": "Datum vytvoření",
"date-updated": "Datum aktualizace"
"date-updated": "Datum aktualizace",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Jste si jisti, že chcete smazat <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Změny v této skupině budou okamžitě zohledněny.",
"group-id-value": "ID skupiny: {0}",
"total-households": "Celkem domácností",
"you-must-select-a-group-before-selecting-a-household": "Před výběrem domácnosti musíte vybrat skupinu"
"you-must-select-a-group-before-selecting-a-household": "Před výběrem domácnosti musíte vybrat skupinu",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Domácnost",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Již mám nastaveno, vezmi mě na domovskou stránku",
"common-settings-for-new-sites": "Zde jsou některá běžná nastavení pro nové stránky",
"setup-complete": "Nastavení dokončeno!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Zde je několik věcí, které vám pomohou začít s Mealie",
"restore-from-v1-backup": "Máte zálohu z předchozí instance Mealie v1? Můžete ji obnovit zde.",
"manage-profile-or-get-invite-link": "Spravujte svůj vlastní profil, nebo přidejte pozvánku ke sdílení s ostatními."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Vis avanceret",
"add-field": "Tilføj felt",
"date-created": "Oprettet",
"date-updated": "Opdateret"
"date-updated": "Opdateret",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Er du sikker på, du vil slette <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Ændringer i denne gruppe vil træde i kraft øjeblikkeligt.",
"group-id-value": "Gruppe-ID: {0}",
"total-households": "Husstande i Alt",
"you-must-select-a-group-before-selecting-a-household": "Du skal vælge en gruppe, før du vælger en husstand"
"you-must-select-a-group-before-selecting-a-household": "Du skal vælge en gruppe, før du vælger en husstand",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Husstand",
@@ -628,7 +663,7 @@
"create-recipe-description": "Opret ny opskrift fra bunden.",
"create-recipes": "Opret opskrift",
"import-with-zip": "Importér fra ZIP-fil",
"create-recipe-from-images": "Create Recipe from Images",
"create-recipe-from-images": "Opret opskrift fra billeder",
"create-recipe-from-an-image-description": "Opret en opskrift ved at overføre et billede af den. Mealie vil forsøge at udtrække teksten fra billedet med AI og oprette en opskrift fra det.",
"crop-and-rotate-the-image": "Beskær og roter billedet, så kun teksten er synlig, og det vises i den rigtige retning.",
"create-from-images": "Opret fra billede",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Jeg er allerede oprettet, bare bringe mig til startsiden",
"common-settings-for-new-sites": "Her er nogle almindelige indstillinger for nye sites",
"setup-complete": "Opsætning færdig!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Her er et par ting, der kan hjælpe dig i gang med Mealie",
"restore-from-v1-backup": "Har du en sikkerhedskopi fra en tidligere udgave af Mealie v1? Du kan gendanne den her.",
"manage-profile-or-get-invite-link": "Administrer din egen profil, eller tag et invitationslink til at dele med andre."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Erweiterte Optionen anzeigen",
"add-field": "Feld Hinzufügen",
"date-created": "Erstellungsdatum",
"date-updated": "Aktualisiert am"
"date-updated": "Aktualisiert am",
"key": "Key",
"value": "Value"
},
"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?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Änderungen an dieser Gruppe sind sofort wirksam.",
"group-id-value": "Gruppen ID: {0}",
"total-households": "Haushalte insgesamt",
"you-must-select-a-group-before-selecting-a-household": "Du musst eine Gruppe auswählen, bevor du einen Haushalt auswählst"
"you-must-select-a-group-before-selecting-a-household": "Du musst eine Gruppe auswählen, bevor du einen Haushalt auswählst",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Haushalt",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Ich habe schon alles eingerichtet, bring mich zur Startseite",
"common-settings-for-new-sites": "Hier sind einige allgemeine Einstellungen für neue Seiten",
"setup-complete": "Einrichtung abgeschlossen!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Hier sind einige Funktionen, die dich beim Start mit Mealie unterstützen",
"restore-from-v1-backup": "Hast du ein Backup von einer früheren v1 Instanz von Mealie? Hier kannst du es wiederherstellen.",
"manage-profile-or-get-invite-link": "Verwalte dein eigenes Profil oder erstelle einen Einladungslink, den du an andere weitergeben kannst."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Εμφάνιση προχωρημένων επιλογών",
"add-field": "Προσθήκη πεδίου",
"date-created": "Ημερομηνία δημιουργίας",
"date-updated": "Ημερομηνία ενημέρωσης"
"date-updated": "Ημερομηνία ενημέρωσης",
"key": "Κλειδί",
"value": "Τιμή"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Θέλετε σίγουρα να διαγράψετε αυτό τον ασφαλή σύνδεσμο <b>{groupName}<b/>;",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Οι αλλαγές σε αυτή την ομάδα θα αντικατοπτρίζονται αμέσως.",
"group-id-value": "ID ομάδας: {0}",
"total-households": "Σύνολο νοικοκυριών",
"you-must-select-a-group-before-selecting-a-household": "Πρέπει να επιλέξετε μια ομάδα πριν επιλέξετε ένα νοικοκυριό"
"you-must-select-a-group-before-selecting-a-household": "Πρέπει να επιλέξετε μια ομάδα πριν επιλέξετε ένα νοικοκυριό",
"ai-provider-settings": {
"ai-provider-settings": "Ρυθμίσεις παρόχου τεχνητής νοημοσύνης",
"ai-provider": "Πάροχος τεχνητής νοημοσύνης",
"ai-providers": "Πάροχοι τεχνητής νοημοσύνης",
"ai-provider-settings-description": "Ρυθμίστε τους παρόχους τεχνητής νοημοσύνης για να ενεργοποιήσετε τις λειτουργίες AI, όπως βελτιωμένη ανάλυση συστατικών, δημιουργία συνταγών από βίντεο και πολλά άλλα!",
"providers": "Πάροχοι",
"create-provider": "Δημιουργία παρόχου",
"edit-provider": "Επεξεργασία παρόχου",
"default-provider": "Προεπιλεγμένος πάροχος",
"default-provider-description": "Απαιτείται για την ενεργοποίηση των χαρακτηριστικών τεχνητής νοημοσύνης",
"audio-provider": "Πάροχος ήχου",
"audio-provider-description": "Ενεργοποιεί λειτουργίες μεταγραφής ήχου, όπως η δημιουργία συνταγών από βίντεο",
"image-provider": "Πάροχος εικόνων",
"image-provider-description": "Ενεργοποιεί δυνατότητες αναγνώρισης εικόνας, όπως η δημιουργία συνταγών από εικόνες",
"provider-name": "Ονομα παρόχου",
"api-key": "Κλειδί API",
"api-key-description-create": "Το κλειδί API του παρόχου σας για έλεγχο ταυτότητας. Αν η υπηρεσία σας (π.χ. Ollama) δεν χρησιμοποιεί ένα κλειδί API, πρέπει να βάλετε κάτι εδώ.",
"api-key-description-edit": "Αφήστε το κενό εκτός αν θέλετε να το αλλάξετε.",
"base-url": "Βασική διεύθυνση URL",
"base-url-description": "Αν χρησιμοποιείτε το OpenAI αφήστε αυτό το κενό. Πρέπει να είναι ένα OpenAI συμβατό endpoint (π.χ. \"http://localhost:11434/v1\").",
"model": "Μοντέλο",
"model-description": "Ποιο μοντέλο θα πρέπει να χρησιμοποιήσει ο πάροχος τεχνητής νοημοσύνης σας (π.χ. \"gpt-5\").",
"request-timeout-seconds": "Χρονικό όριο αιτήματος (δευτερόλεπτα)",
"provider-created": "Ο πάροχος δημιουργήθηκε",
"provider-updated": "Ο πάροχος ενημερώθηκε",
"provider-deleted": "Ο πάροχος διαγράφτηκε",
"provider-create-failed": "Αποτυχία δημιουργίας παρόχου",
"provider-update-failed": "Αποτυχία ενημέρωσης παρόχου",
"provider-delete-failed": "Αποτυχία διαγραφής παρόχου",
"request-headers": "Κεφαλίδες αιτήματος",
"request-params": "Παράμετροι αιτήματος",
"no-default-provider-warning": "Δεν έχετε ορίσει προεπιλεγμένο πάροχο, οπότε οι λειτουργίες τεχνητής νοημοσύνης είναι απενεργοποιημένες"
}
},
"household": {
"household": "Νοικοκυριό",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Εχω ήδη κάνει εγκατάσταση, απλά πήγαινέ με στην αρχική σελίδα",
"common-settings-for-new-sites": "Εδώ είναι μερικές κοινές ρυθμίσεις για νέους ιστότοπους",
"setup-complete": "Η εγκατάσταση ολοκληρώθηκε!",
"ai-providers-description": "Προαιρετικά ρυθμίστε τους παρόχους τεχνητής νοημοσύνης για την ομάδα σας. Οι πάροχοι τεχνητής νοημοσύνης ενεργοποιούν λειτουργίες όπως η δημιουργία συνταγών από εικόνες, η εισαγωγή συνταγών από βίντεο και η βελτιωμένη ανάλυση συστατικών. Αυτό μπορείτε πάντα να το διαμορφώσετε αργότερα από τις ρυθμίσεις της ομάδας σας.",
"here-are-a-few-things-to-help-you-get-started": "Εδώ είναι μερικά πράγματα που θα σας βοηθήσουν να ξεκινήσετε με το Mealie",
"restore-from-v1-backup": "Εχετε ένα αντίγραφο ασφαλείας από μια προηγούμενη υπόσταση του Mealie v1; Μπορείτε να το επαναφέρετε εδώ.",
"manage-profile-or-get-invite-link": "Διαχειριστείτε το δικό σας προφίλ, ή λάβετε έναν σύνδεσμο πρόσκλησης για να μοιραστείτε με άλλους."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Show Advanced",
"add-field": "Add Field",
"date-created": "Date Created",
"date-updated": "Date Updated"
"date-updated": "Date Updated",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Changes to this group will be reflected immediately.",
"group-id-value": "Group ID: {0}",
"total-households": "Total Households",
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household"
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Household",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage",
"common-settings-for-new-sites": "Here are some common settings for new sites",
"setup-complete": "Setup Complete!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Here are a few things to help you get started with Mealie",
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.",
"manage-profile-or-get-invite-link": "Manage your own profile, or grab an invite link to share with others."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Show Advanced",
"add-field": "Add Field",
"date-created": "Date Created",
"date-updated": "Date Updated"
"date-updated": "Date Updated",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Changes to this group will be reflected immediately.",
"group-id-value": "Group Id: {0}",
"total-households": "Total Households",
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household"
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Household",
@@ -1362,6 +1397,8 @@
"already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage",
"common-settings-for-new-sites": "Here are some common settings for new sites",
"setup-complete": "Setup Complete!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Here are a few things to help you get started with Mealie",
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.",
"manage-profile-or-get-invite-link": "Manage your own profile, or grab an invite link to share with others."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Mostrar Avanzado",
"add-field": "Añadir campo",
"date-created": "Fecha de creación",
"date-updated": "Fecha de actualización"
"date-updated": "Fecha de actualización",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Por favor, confirma que deseas eliminar <b>{groupName}<b/>",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Los cambios en este grupo se reflejarán inmediatamente.",
"group-id-value": "Id del Grupo: {0}",
"total-households": "Total de Casas",
"you-must-select-a-group-before-selecting-a-household": "Debe seleccionar un grupo antes de seleccionar un hogar"
"you-must-select-a-group-before-selecting-a-household": "Debe seleccionar un grupo antes de seleccionar un hogar",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Casa",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Estoy bien, solo llévame al Inicio",
"common-settings-for-new-sites": "Aquí hay algunos ajustes comunes para sitios nuevos",
"setup-complete": "¡Configuración completada!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Aquí hay algunas cosas para ayudarte a empezar con Mealie",
"restore-from-v1-backup": "¿Tienes una copia de seguridad de Mealie v1? Puedes restaurarla aquí.",
"manage-profile-or-get-invite-link": "Gestiona tu perfil, o usa un enlace de invitación para compartir con otros."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Kuva täpsemad sätted",
"add-field": "Lisa väli",
"date-created": "Loomise kuupäev",
"date-updated": "Üleslaadimise kuupäev"
"date-updated": "Üleslaadimise kuupäev",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Kas oled kindel, et tahad kustutada <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Selle grupi muudatused on koheselt nähtavad",
"group-id-value": "Grupi ID: {0}",
"total-households": "Kokku leibkondi",
"you-must-select-a-group-before-selecting-a-household": "Sa pead valima grupi enne leibkonna valimist"
"you-must-select-a-group-before-selecting-a-household": "Sa pead valima grupi enne leibkonna valimist",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Leibkond",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Ma olen juba vajalikud asjad seadistanud, vii mind pealehele",
"common-settings-for-new-sites": "Siin on mõned harilikud sätted uute lehekülgede jaoks",
"setup-complete": "Seadistus valmis!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Siin on mõned asjad mis aitavad sul teha algust Mealie-ga",
"restore-from-v1-backup": "Kas sul on tagavarakoopia varasemast Mealie v1 instantsist? Sa saad taastada selle siin.",
"manage-profile-or-get-invite-link": "Halda oma profiili, või haara kutselink teistega jagamiseks."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Näytä Lisäasetukset",
"add-field": "Lisää Kenttä",
"date-created": "Luontipäivä",
"date-updated": "Päivitetty"
"date-updated": "Päivitetty",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Haluatko varmasti poistaa ryhmän <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Muutokset tähän ryhmään tulevat näkymään välittömästi.",
"group-id-value": "Ryhmän tunniste: {0}",
"total-households": "Kotitaloudet Yhteensä",
"you-must-select-a-group-before-selecting-a-household": "Sinun tulee valita ryhmä ennen kuin valitset kotitalouden"
"you-must-select-a-group-before-selecting-a-household": "Sinun tulee valita ryhmä ennen kuin valitset kotitalouden",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Kotitalous",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Olen jo valmis, vie minut kotisivulle",
"common-settings-for-new-sites": "Tässä muutamia yleisiä asetuksia uusille sivustoille",
"setup-complete": "Asennus valmis.",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Näillä muutamilla asioilla pääset alkuun",
"restore-from-v1-backup": "Onko sinulla varmuuskopio aiemmasta Mealie v1 -instanssista? Palauta se tästä.",
"manage-profile-or-get-invite-link": "Hallitse profiiliasi tai hanki kutsulinkki muille."

View File

@@ -169,7 +169,7 @@
"token": "Jeton",
"tuesday": "Mardi",
"type": "Type",
"undo": "Undo",
"undo": "Annuler",
"update": "Mettre à jour",
"updated": "Mis à jour",
"upload": "Importer",
@@ -223,7 +223,9 @@
"show-advanced": "Afficher les paramètres avancés",
"add-field": "Ajouter un champ",
"date-created": "Date de création",
"date-updated": "Date de mise à jour"
"date-updated": "Date de mise à jour",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Voulez-vous vraiment supprimer <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Les modifications apportées à ce groupe seront immédiatement prises en compte.",
"group-id-value": "ID groupe: {0}",
"total-households": "Nombre de foyers",
"you-must-select-a-group-before-selecting-a-household": "Vous devez sélectionner un groupe avant de sélectionner un foyer"
"you-must-select-a-group-before-selecting-a-household": "Vous devez sélectionner un groupe avant de sélectionner un foyer",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Foyer",
@@ -628,7 +663,7 @@
"create-recipe-description": "Créer une nouvelle recette de zéro.",
"create-recipes": "Créer des recettes",
"import-with-zip": "Importer un .zip",
"create-recipe-from-images": "Create Recipe from Images",
"create-recipe-from-images": "Créer une recette depuis une image",
"create-recipe-from-an-image-description": "Créez une recette en téléchargeant une image de celle-ci. Mealie utilisera lIA pour tenter dextraire le texte et de créer une recette.",
"crop-and-rotate-the-image": "Rogner et pivoter limage pour que seul le texte soit visible, et quil soit dans la bonne orientation.",
"create-from-images": "Créer à partir dune image",
@@ -943,7 +978,7 @@
"are-you-sure-you-want-to-uncheck-all-items": "Voulez-vous vraiment désélectionner tous les éléments ?",
"are-you-sure-you-want-to-delete-checked-items": "Voulez-vous vraiment supprimer tous les éléments sélectionnés ?",
"no-shopping-lists-found": "Aucune liste de courses trouvée",
"item-checked-off": "Checked off {item}"
"item-checked-off": "{item} coché"
},
"sidebar": {
"all-recipes": "Recettes",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Jai déjà tout configuré, amenez moi à lécran daccueil",
"common-settings-for-new-sites": "Voici quelques paramètres courants pour les nouveaux sites",
"setup-complete": "Configuration terminée !",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Voici quelques trucs pour vous aider à commencer avec Mealie",
"restore-from-v1-backup": "Vous avez une sauvegarde dune précédente instance de Mealie v1 ? Vous pouvez la restaurer ici.",
"manage-profile-or-get-invite-link": "Gérez votre propre profil, ou récupérez un lien dinvitation à partager avec dautres."

View File

@@ -169,7 +169,7 @@
"token": "Jeton",
"tuesday": "Mardi",
"type": "Type",
"undo": "Undo",
"undo": "Annuler",
"update": "Mettre à jour",
"updated": "Mis à jour",
"upload": "Importer",
@@ -223,7 +223,9 @@
"show-advanced": "Afficher les paramètres avancés",
"add-field": "Ajouter un champ",
"date-created": "Date de création",
"date-updated": "Date de mise à jour"
"date-updated": "Date de mise à jour",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Êtes-vous certain de vouloir supprimer <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Les modifications apportées à ce groupe seront immédiatement prises en compte.",
"group-id-value": "ID groupe: {0}",
"total-households": "Nombre de foyers",
"you-must-select-a-group-before-selecting-a-household": "Vous devez sélectionner un groupe avant de sélectionner un foyer"
"you-must-select-a-group-before-selecting-a-household": "Vous devez sélectionner un groupe avant de sélectionner un foyer",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Foyer",
@@ -628,7 +663,7 @@
"create-recipe-description": "Créer une nouvelle recette à partir de zéro.",
"create-recipes": "Créer des recettes",
"import-with-zip": "Importer un .zip",
"create-recipe-from-images": "Create Recipe from Images",
"create-recipe-from-images": "Créer une recette depuis une image",
"create-recipe-from-an-image-description": "Créez une recette en téléversant une image de celle-ci. Mealie utilisera lIA pour tenter dextraire le texte et de créer une recette.",
"crop-and-rotate-the-image": "Rogner et pivoter limage pour que seul le texte soit visible et quil soit dans la bonne orientation.",
"create-from-images": "Créer à partir dimages",
@@ -943,7 +978,7 @@
"are-you-sure-you-want-to-uncheck-all-items": "Voulez-vous vraiment désélectionner tous les éléments ?",
"are-you-sure-you-want-to-delete-checked-items": "Voulez-vous vraiment supprimer tous les éléments sélectionnés ?",
"no-shopping-lists-found": "Aucune liste de courses trouvée",
"item-checked-off": "Checked off {item}"
"item-checked-off": "{item} coché"
},
"sidebar": {
"all-recipes": "Les recettes",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Jai déjà tout configuré, amenez moi à lécran daccueil",
"common-settings-for-new-sites": "Voici quelques paramètres communs pour les nouveaux sites",
"setup-complete": "Configuration terminée!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Voici quelques trucs pour vous aider à commencer avec Mealie",
"restore-from-v1-backup": "Vous avez une sauvegarde dune précédente instance de Mealie v1 ? Vous pouvez la restaurer ici.",
"manage-profile-or-get-invite-link": "Gérez votre propre profil, ou récupérez un lien dinvitation à partager avec dautres."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Afficher les paramètres avancés",
"add-field": "Ajouter un champ",
"date-created": "Date de création",
"date-updated": "Date de mise à jour"
"date-updated": "Date de mise à jour",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Voulez-vous vraiment supprimer <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Les modifications apportées à ce groupe seront immédiatement prises en compte.",
"group-id-value": "ID groupe: {0}",
"total-households": "Nombre de foyers",
"you-must-select-a-group-before-selecting-a-household": "Vous devez sélectionner un groupe avant de sélectionner un foyer"
"you-must-select-a-group-before-selecting-a-household": "Vous devez sélectionner un groupe avant de sélectionner un foyer",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Foyer",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Jai déjà tout configuré, amenez-moi à lécran daccueil",
"common-settings-for-new-sites": "Voici quelques paramètres courants pour les nouveaux sites",
"setup-complete": "Configuration terminée !",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Voici quelques trucs pour vous aider à commencer avec Mealie",
"restore-from-v1-backup": "Vous avez une sauvegarde dune précédente instance de Mealie v1 ? Vous pouvez la restaurer ici.",
"manage-profile-or-get-invite-link": "Gérez votre propre profil, ou récupérez un lien dinvitation à partager avec dautres."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Mostrar Avanzadas",
"add-field": "Adicionar Campo",
"date-created": "Data de Creación",
"date-updated": "Data de Atualización"
"date-updated": "Data de Atualización",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Estás seguro de que queres eliminar <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Os cambios neste grupo reflectiranse inmediatamente.",
"group-id-value": "Id do grupo: {0}",
"total-households": "Casas Totais",
"you-must-select-a-group-before-selecting-a-household": "Tes que selecionar un grupo antes de selecionar unha casa"
"you-must-select-a-group-before-selecting-a-household": "Tes que selecionar un grupo antes de selecionar unha casa",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Casa",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Xa estou listo, levame para a páxina inicial",
"common-settings-for-new-sites": "Aqui están algunhas configuracións comuns para sites novos",
"setup-complete": "Configuración Concluída!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Aqui están algunhas cousas para axudar a comezar co Mealie",
"restore-from-v1-backup": "Ten unha copia de seguranza dunha instancia do Mealie v1? Pode restaurala aqui.",
"manage-profile-or-get-invite-link": "Xestione o seu proprio perfil ou obteña unha ligazón de convite para compartir con outras persoas."

View File

@@ -223,7 +223,9 @@
"show-advanced": "הצג הגדרות מתקדמות",
"add-field": "הוסף שדה",
"date-created": "תאריך יצירה",
"date-updated": "תאריך עדכון"
"date-updated": "תאריך עדכון",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "למחוק את <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "שינויים לקבוצה זו ישתקפו מיידית.",
"group-id-value": "מזהה קבוצה: {0}",
"total-households": "סך כל משקי בית",
"you-must-select-a-group-before-selecting-a-household": "חובה לבחור קבוצה לפני בחירת משק בית"
"you-must-select-a-group-before-selecting-a-household": "חובה לבחור קבוצה לפני בחירת משק בית",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "משק בית",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "כבר הגדרתי הכל, תעבירו אותי לעמוד הבית",
"common-settings-for-new-sites": "הגדרות נפוצות לאתרים חדשים יופיעו כאן",
"setup-complete": "ההגדרה הושלמה!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "כמה דברים שיעזרו לך להתחיל להשתמש ב-Mealie",
"restore-from-v1-backup": "יש לך גיבוי משרת Mealie v1? ניתן לשחזר אותו כאן.",
"manage-profile-or-get-invite-link": "ניתן לנהל את הפרופיל שלך, או לשתף את לינק ההזמנה לאחרים."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Prikaži napredno",
"add-field": "Dodaj polje",
"date-created": "Datum kreiranja",
"date-updated": "Datum ažuriranja"
"date-updated": "Datum ažuriranja",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Jeste li sigurni da želite izbrisati <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Promjene u ovoj grupi će se odmah odraziti.",
"group-id-value": "Id grupe: {0}",
"total-households": "Ukupno kućanstava",
"you-must-select-a-group-before-selecting-a-household": "Prije odabira kućanstva morate odabrati grupu"
"you-must-select-a-group-before-selecting-a-household": "Prije odabira kućanstva morate odabrati grupu",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Domaćinstvo",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage",
"common-settings-for-new-sites": "Here are some common settings for new sites",
"setup-complete": "Setup Complete!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Here are a few things to help you get started with Mealie",
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.",
"manage-profile-or-get-invite-link": "Manage your own profile, or grab an invite link to share with others."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Haladó beállítások megjelenítése",
"add-field": "Mező hozzáadása",
"date-created": "Létrehozás dátuma",
"date-updated": "Frissítés dátuma"
"date-updated": "Frissítés dátuma",
"key": "Kulcs",
"value": "Érték"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Biztosan törölni szeretnéd ezt: <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "A csoporthoz tartozó változtatások azonnal megjelennek.",
"group-id-value": "Csoport azonosító: {0}",
"total-households": "Háztartások száma",
"you-must-select-a-group-before-selecting-a-household": "A háztartás kiválasztása előtt ki kell választania egy csoportot"
"you-must-select-a-group-before-selecting-a-household": "A háztartás kiválasztása előtt ki kell választania egy csoportot",
"ai-provider-settings": {
"ai-provider-settings": "AI szolgáltató beállítások",
"ai-provider": "AI szolgáltató",
"ai-providers": "AI szolgáltató",
"ai-provider-settings-description": "Állítsa be az AI-szolgáltatókat az AI-alapú funkciók, például a továbbfejlesztett összetevő-elemzés, a videókból történő receptkészítés és még sok más használatához!",
"providers": "Szolgáltatók",
"create-provider": "Szolgáltató létrehozása",
"edit-provider": "Szolgáltató szerkesztése",
"default-provider": "Alapértelmezett szolgáltató",
"default-provider-description": "Az AI-funkciók használatához szükséges",
"audio-provider": "Audió szolgáltató",
"audio-provider-description": "Engedélyezi az audió-átírási funkciókat, például a videókból származó receptek létrehozását",
"image-provider": "Kép szolgáltató",
"image-provider-description": "Engedélyezi a képfelismerési funkciókat, például a képek alapján történő receptkészítést",
"provider-name": "Szolgáltató neve",
"api-key": "API Kulcs",
"api-key-description-create": "A szolgáltató hitelesítési API-kulcsa. Ha a szolgáltatása (pl. Ollama) nem használ API-kulcsot, akkor is be kell írnia ide valamit.",
"api-key-description-edit": "Ha nem szeretné módosítani, hagyja üresen.",
"base-url": "Alap URL",
"base-url-description": "Ha az OpenAI-t használja, hagyja üresen ezt a mezőt. Az endpointnak OpenAI-kompatibilisnek kell lennie (pl. „http://localhost:11434/v1”).",
"model": "Modell",
"model-description": "Melyik modellt kell használnia az AI-szolgáltatónak (pl. „gpt-5”).",
"request-timeout-seconds": "A kérés időtúllépése (másodpercben)",
"provider-created": "Szolgáltató létrehozva",
"provider-updated": "Szolgáltató frissítve",
"provider-deleted": "Szolgáltató törölve",
"provider-create-failed": "A szolgáltató létrehozása sikertelen",
"provider-update-failed": "A szolgáltató frissítése sikertelen",
"provider-delete-failed": "A szolgáltató törlése sikertelen",
"request-headers": "Kérés fejléce",
"request-params": "Kérelem paraméterek",
"no-default-provider-warning": "Mivel nem állított be alapértelmezett szolgáltatót, az AI-funkciók le vannak tiltva"
}
},
"household": {
"household": "Háztartás",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Már kész is vagyok, vigyél az új kezdőoldalamra",
"common-settings-for-new-sites": "Itt van egy pár alap beállítás az új oldaladhoz",
"setup-complete": "Beállítás kész!",
"ai-providers-description": "Opcionálisan konfigurálhatja az AI-szolgáltatókat a csoportjához. Az AI-szolgáltatók olyan funkciókat tesznek lehetővé, mint a képekből történő receptkészítés, a videókból történő receptimportálás és a továbbfejlesztett hozzávalóelemzés. Ezt később bármikor beállíthatja a csoportbeállítások menüpontban.",
"here-are-a-few-things-to-help-you-get-started": "Itt van egy pár dolog ami segíthet a kezdésben a Mealie-vel",
"restore-from-v1-backup": "Van egy korábbi Mealie v1 biztonsági másolatod? Itt visszaállíthatod.",
"manage-profile-or-get-invite-link": "Alakítsa a profilját vagy szerezze meg a meghívó link-jét hogy megoszthassa másokkal."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Ítarlegar stillingar",
"add-field": "Bæta við dálk",
"date-created": "Búið til",
"date-updated": "Dagsetning uppfærð"
"date-updated": "Dagsetning uppfærð",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Ertu viss um að þú viljir eyða <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Breytingar á þessum hóp koma strax fram.",
"group-id-value": "Hóp ID: {0}",
"total-households": "Fjöldi heimila",
"you-must-select-a-group-before-selecting-a-household": "Þú verður að velja hóp áður en þú velur heimili"
"you-must-select-a-group-before-selecting-a-household": "Þú verður að velja hóp áður en þú velur heimili",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Heimili",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Allt er þegar klárt hjá mér, sendu mig á heimasíðuna",
"common-settings-for-new-sites": "Hér eru nokkrar algengar stillingar fyrir nýjar síður",
"setup-complete": "Uppsetningu lokið",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Hér eru nokkur atriði til að hjálpa þér að byrja á því að nota Mealie",
"restore-from-v1-backup": "Ertu með öryggisafrit af eldri útgáfur af Mealie útgáfu 1? Þú getur endurheimt hana hér.",
"manage-profile-or-get-invite-link": "Stilltu eigin prófil, eða sæktu boðs-tengil til að deila með öðrum."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Mostra Avanzate",
"add-field": "Aggiungi campo",
"date-created": "Data di Creazione",
"date-updated": "Data di aggiornamento"
"date-updated": "Data di aggiornamento",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Sei sicuro di volerlo eliminare <b>{groupName}<b/>'?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Le modifiche a questo gruppo si rifletteranno immediatamente.",
"group-id-value": "Id Gruppo: {0}",
"total-households": "Famiglie Totali",
"you-must-select-a-group-before-selecting-a-household": "Devi selezionare un gruppo prima di selezionare una famiglia"
"you-must-select-a-group-before-selecting-a-household": "Devi selezionare un gruppo prima di selezionare una famiglia",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Famiglia",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Ho già configurato tutto, portami alla pagina iniziale",
"common-settings-for-new-sites": "Ecco alcune impostazioni comuni per i nuovi siti",
"setup-complete": "Configurazione completata!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Qui ci sono alcune cose per aiutarvi a iniziare con Mealie",
"restore-from-v1-backup": "Hai un backup da un'istanza precedente di Mealie v1? Puoi ripristinarlo qui.",
"manage-profile-or-get-invite-link": "Gestisci il tuo profilo, o parti da un link di invito per condividere con gli altri."

View File

@@ -223,7 +223,9 @@
"show-advanced": "詳細を表示",
"add-field": "フィールドを追加",
"date-created": "作成日",
"date-updated": "更新日"
"date-updated": "更新日",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "<b>{groupName}<b/> を削除しますか?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "このグループへの変更はすぐに反映されます。",
"group-id-value": "グループID: {0}",
"total-households": "世帯数",
"you-must-select-a-group-before-selecting-a-household": "世帯を選択する前にグループを選択する必要があります"
"you-must-select-a-group-before-selecting-a-household": "世帯を選択する前にグループを選択する必要があります",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "世帯",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "すでに設定は完了しています。ホームページにアクセスしてください",
"common-settings-for-new-sites": "新しいサイトの一般的な設定は次のとおりです",
"setup-complete": "セットアップ完了!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Mealieを始めるのに役立つことがいくつかあります",
"restore-from-v1-backup": "Mealie v1以前のインスタンスからのバックアップはありますかここで復元できます。",
"manage-profile-or-get-invite-link": "自分のプロフィールを管理するか、招待リンクを取得して他の人と共有します。"

View File

@@ -223,7 +223,9 @@
"show-advanced": "고급 표시",
"add-field": "필드 추가",
"date-created": "작성 날짜",
"date-updated": "업데이트 일자"
"date-updated": "업데이트 일자",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "<b>{groupName}<b/>을(를) 삭제하시겠습니까?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "이 그룹에 대한 변경 사항은 즉시 반영됩니다.",
"group-id-value": "그룹 Id: {0}",
"total-households": "전체 가구 수",
"you-must-select-a-group-before-selecting-a-household": "가구를 선택하기 전에 반드시 그룹을 선택해야 합니다."
"you-must-select-a-group-before-selecting-a-household": "가구를 선택하기 전에 반드시 그룹을 선택해야 합니다.",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "가구",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "설정은 이미 완료되었으니, 그냥 홈페이지로 이동해 주세요",
"common-settings-for-new-sites": "새 사이트에 대한 일반적인 설정은 다음과 같습니다",
"setup-complete": "설정 완료",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Mealie를 시작하는 데 도움이 될 몇 가지 사항입니다.",
"restore-from-v1-backup": "이전 버전의 Mealie v1 백업본이 있으신가요? 여기서 복원할 수 있습니다.",
"manage-profile-or-get-invite-link": "자신의 프로필을 관리하거나, 다른 사람들과 공유할 초대 링크를 가져가세요."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Rodyti plačiau",
"add-field": "Pridėti lauką",
"date-created": "Sukūrimo data",
"date-updated": "Atnaujinimo data"
"date-updated": "Atnaujinimo data",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Ar tikrai norite ištrinti <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Pakeitimai šiai grupei pritaikomi iš karto.",
"group-id-value": "Grupės ID: {0}",
"total-households": "Iš viso namų ūkių",
"you-must-select-a-group-before-selecting-a-household": "Prieš pasirinkdami namų ūkį, turite pasirinkti grupę"
"you-must-select-a-group-before-selecting-a-household": "Prieš pasirinkdami namų ūkį, turite pasirinkti grupę",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Namų ūkis",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage",
"common-settings-for-new-sites": "Here are some common settings for new sites",
"setup-complete": "Setup Complete!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Here are a few things to help you get started with Mealie",
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.",
"manage-profile-or-get-invite-link": "Manage your own profile, or grab an invite link to share with others."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Rādīt papildu",
"add-field": "Pievienot lauku",
"date-created": "Izveidošanas datums",
"date-updated": "Atjaunināšanas datums"
"date-updated": "Atjaunināšanas datums",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Vai tiešām vēlaties dzēst <b>{groupName} <b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Izmaiņas šajā grupā tiks atspoguļotas nekavējoties.",
"group-id-value": "Grupas ID: {0}",
"total-households": "Kopējais mājsaimniecību skaits",
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household"
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Mājsaimniecība",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Es jau esmu iestatīts, vienkārši nogādājiet mani mājaslapā",
"common-settings-for-new-sites": "Šeit ir daži kopīgi iestatījumi jaunām vietnēm",
"setup-complete": "Iestatīšana pabeigta!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Šeit ir dažas lietas, kas palīdzēs jums sākt darbu ar Mealie",
"restore-from-v1-backup": "Vai jums ir dublējums no iepriekšējā Mealie v1 gadījuma? Jūs varat to atjaunot šeit.",
"manage-profile-or-get-invite-link": "Pārvaldiet savu profilu vai paņemiet uzaicinājuma saiti, lai kopīgotu ar citiem."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Laat geavanceerde keuzes zien",
"add-field": "Veld toevoegen",
"date-created": "Datum aangemaakt",
"date-updated": "Datum bijgewerkt"
"date-updated": "Datum bijgewerkt",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Weet je zeker dat je <b>{groupName}<b/> wil verwijderen?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Wijzigingen in deze groep zijn meteen zichtbaar.",
"group-id-value": "Groeps-id: {0}",
"total-households": "Totaal aantal huishoudens",
"you-must-select-a-group-before-selecting-a-household": "Kies een groep voordat je een huishouden kiest"
"you-must-select-a-group-before-selecting-a-household": "Kies een groep voordat je een huishouden kiest",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Huishouden",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Ik ben al ingesteld, breng me naar de startpagina",
"common-settings-for-new-sites": "Hier zijn enkele algemene instellingen voor nieuwe sites",
"setup-complete": "Installatie voltooid!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Hier zijn een aantal dingen om je op weg te helpen met Mealie",
"restore-from-v1-backup": "Heb je een back-up van een vorig exemplaar van Mealie v1? Deze kan je hier terugzetten.",
"manage-profile-or-get-invite-link": "Beheer je eigen profiel, of gebruik een uitnodigingslink om te delen met anderen."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Vis avansert",
"add-field": "Legg til felt",
"date-created": "Opprettet dato",
"date-updated": "Dato oppdatert"
"date-updated": "Dato oppdatert",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Er du sikker på at du vil slette <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Endringer i denne gruppen vil gjenspeiles umiddelbart.",
"group-id-value": "Gruppe-ID: {0}",
"total-households": "Husholdninger totalt",
"you-must-select-a-group-before-selecting-a-household": "Du må velge en gruppe før du kan velge en husholdning"
"you-must-select-a-group-before-selecting-a-household": "Du må velge en gruppe før du kan velge en husholdning",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Husholdning",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Jeg er allerede satt opp, bare ta meg med til hjemmesiden",
"common-settings-for-new-sites": "Her er noen vanlige innstillinger for nye sider",
"setup-complete": "Oppsett fullført!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Her er noen ting som kan hjelpe deg å komme i gang med Mealie",
"restore-from-v1-backup": "Har du en sikkerhetskopi fra en tidligere forekomst av Mealie v1? Du kan gjenopprette den her.",
"manage-profile-or-get-invite-link": "Administrer din egen profil, eller hent en invitasjonslenke for å dele med andre."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Pokaż zaawansowane",
"add-field": "Dodaj pole",
"date-created": "Data utworzenia",
"date-updated": "Data aktualizacji"
"date-updated": "Data aktualizacji",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Czy na pewno chcesz usunąć <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Zmiany w tej grupie zostaną natychmiast odzwierciedlone.",
"group-id-value": "Id grupy: {0}",
"total-households": "Gospodarstwa domowe razem",
"you-must-select-a-group-before-selecting-a-household": "Musisz wybrać grupę przed wybraniem gospodarstwa domowego"
"you-must-select-a-group-before-selecting-a-household": "Musisz wybrać grupę przed wybraniem gospodarstwa domowego",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Gospodarstwo domowe",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Już mam skonfigurowane, po prostu przenieś mnie na stronę główną",
"common-settings-for-new-sites": "Oto kilka typowych ustawień dla nowych witryn",
"setup-complete": "Konfiguracja zakończona!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Oto kilka rzeczy, które pomogą Ci zacząć z Mealie",
"restore-from-v1-backup": "Masz kopię zapasową z poprzedniej instancji Mealie v1? Możesz ją tutaj przywrócić.",
"manage-profile-or-get-invite-link": "Zarządzaj własnym profilem lub pobierz link z zaproszeniem, aby udostępnić go innym."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Mostrar Avançado",
"add-field": "Adicionar Campo",
"date-created": "Data de Criação",
"date-updated": "Data de Atualização"
"date-updated": "Data de Atualização",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Tem certeza que deseja excluir o grupo <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "As alterações a este grupo serão refletidas imediatamente.",
"group-id-value": "ID do grupo: {0}",
"total-households": "Total de domicílios",
"you-must-select-a-group-before-selecting-a-household": "Você deve selecionar um grupo antes de selecionar um domicílio"
"you-must-select-a-group-before-selecting-a-household": "Você deve selecionar um grupo antes de selecionar um domicílio",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Domicílio",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Já estou pronto, apenas me leve à página inicial",
"common-settings-for-new-sites": "Estas são algumas configurações comuns para novos sites",
"setup-complete": "Configuração Concluída!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Aqui há algumas coisas para ajudá-lo a começar com o Mealie",
"restore-from-v1-backup": "Tem uma cópia de segurança de uma instância anterior do Mealie v1? Você pode restaurá-la aqui.",
"manage-profile-or-get-invite-link": "Gerencie seu próprio perfil, ou pegue um link de convite para compartilhar."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Mostrar Avançadas",
"add-field": "Adicionar Campo",
"date-created": "Data de Criação",
"date-updated": "Data de Atualização"
"date-updated": "Data de Atualização",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Tem a certeza que quer eliminar <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "As alterações a este grupo serão aplicadas imediatamente.",
"group-id-value": "ID do Grupo: {0}",
"total-households": "Total de Lares",
"you-must-select-a-group-before-selecting-a-household": "Tens de selecionar um grupo antes de selecionar uma casa"
"you-must-select-a-group-before-selecting-a-household": "Tens de selecionar um grupo antes de selecionar uma casa",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Casa",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Já estou pronto, leva-me para a página inicial",
"common-settings-for-new-sites": "Aqui estão algumas configurações comuns para sites novos",
"setup-complete": "Configuração Concluída!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Aqui estão algumas coisas para ajudar a começar com o Mealie",
"restore-from-v1-backup": "Tem uma cópia de segurança de uma instância do Mealie v1? Pode restaurá-la aqui.",
"manage-profile-or-get-invite-link": "Gira o seu próprio perfil ou pegue num convite para partilhar com outros."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Arată avansate",
"add-field": "Adaugă câmp",
"date-created": "Data creării",
"date-updated": "Data actualizată"
"date-updated": "Data actualizată",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Sunteți sigur că doriți să ștergeți <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Modificările la acest grup se vor reflecta imediat.",
"group-id-value": "ID grup: {0}",
"total-households": "Total locuințe",
"you-must-select-a-group-before-selecting-a-household": "Trebuie să selectaţi un grup înainte de a selecta o gospodărie"
"you-must-select-a-group-before-selecting-a-household": "Trebuie să selectaţi un grup înainte de a selecta o gospodărie",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Locuință",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Sunt deja configurat, du-mă direct la pagina principală",
"common-settings-for-new-sites": "Aici sunt câteva setări comune pentru noile site-uri",
"setup-complete": "Configurare finalizată!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Aici sunt câteva lucruri care te ajută să începi cu Mealie",
"restore-from-v1-backup": "Aveţi o copie de rezervă dintr-o instanţă anterioară a lui Mealie v1? O puteţi restaura aici.",
"manage-profile-or-get-invite-link": "Gestionează-ți propriul profil, sau apasă un link de invitație pentru a distribui cu alții."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Показать расширенные",
"add-field": "Добавить поле",
"date-created": "Дата создания",
"date-updated": "Дата обновления"
"date-updated": "Дата обновления",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Вы действительно хотите удалить <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Изменения в этой группе будут отражены немедленно.",
"group-id-value": "Id группы: {0}",
"total-households": "Всего домохозяйств",
"you-must-select-a-group-before-selecting-a-household": "Вы должны выбрать группу, прежде чем выбирать домохозяйство"
"you-must-select-a-group-before-selecting-a-household": "Вы должны выбрать группу, прежде чем выбирать домохозяйство",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Домохозяйство",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Я уже готов, просто открой домашнюю страницу",
"common-settings-for-new-sites": "Ниже приведены общие настройки для новых сайтов",
"setup-complete": "Настройка завершена!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Вот несколько вещей, которые помогут Вам начать работу с Mealie",
"restore-from-v1-backup": "У вас есть резервная копия предыдущего экземпляра Mealie v1? Вы можете восстановить ее здесь.",
"manage-profile-or-get-invite-link": "Управляйте своим профилем или получите ссылку-приглашение, чтобы поделиться ею с другими."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Zobraziť pokročilé",
"add-field": "Pridať pole",
"date-created": "Dátum vytvorenia",
"date-updated": "Dátum aktualizácie"
"date-updated": "Dátum aktualizácie",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Naozaj chcete odstrániť <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Zmeny týkajúce sa tejto skupiny budú vykonané okamžite.",
"group-id-value": "Id skupiny: {0}",
"total-households": "Domácností celkom",
"you-must-select-a-group-before-selecting-a-household": "Pred výberom domácnosti musíte vybrať skupinu"
"you-must-select-a-group-before-selecting-a-household": "Pred výberom domácnosti musíte vybrať skupinu",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Domácnosť",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Som pripravený začať, vezmi ma na hlavnú stránku",
"common-settings-for-new-sites": "Toto sú bežné nastavenia nových inštalácií",
"setup-complete": "Nastavenie je dokončené!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Tu je niekoľko bodov, ktoré vám pomôžu na začiatku v Mealie",
"restore-from-v1-backup": "Máte zálohu z predchádzajúcej inštalácie Mealie v1? Tu ju môžete obnoviť.",
"manage-profile-or-get-invite-link": "Spravujte svoj profil ako súkromný alebo ho s pomocou pozývacieho linku zdieľajte s ostatnými."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Prikaži napredno",
"add-field": "Dodaj polje",
"date-created": "Datum nastanka",
"date-updated": "Datum posodobitve"
"date-updated": "Datum posodobitve",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Ste prepričani, da želite izbrisati <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Spremembe v tej skupini se poznajo takoj.",
"group-id-value": "ID skupine: {0}",
"total-households": "Skupaj gospodinjstev",
"you-must-select-a-group-before-selecting-a-household": "Preden izberete gospodinjstvo, morate izbrati skupino"
"you-must-select-a-group-before-selecting-a-household": "Preden izberete gospodinjstvo, morate izbrati skupino",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Gospodinjstvo",
@@ -628,7 +663,7 @@
"create-recipe-description": "Ustvari nov recept.",
"create-recipes": "Ustvari recepte",
"import-with-zip": "Uvozi z .zip",
"create-recipe-from-images": "Create Recipe from Images",
"create-recipe-from-images": "Ustvari recept iz slik",
"create-recipe-from-an-image-description": "Ustvarite recept tako, da naložite njegovo sliko. Mealie bo s pomočjo umetne inteligence poskušal izluščiti besedilo iz slike in iz njega ustvariti recept.",
"crop-and-rotate-the-image": "Obrežite in zasukajte sliko, tako da bo vidno samo besedilo in da bo v pravilnem položaju.",
"create-from-images": "Ustvari iz slik",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Vse imam že nastavljeno, odpri glavno stran",
"common-settings-for-new-sites": "Tule je par pogostih nastavitev za nove spletne strani",
"setup-complete": "Namestitev je zaključena!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Par stvari, ki to bodo pomagale začeti z Mealie",
"restore-from-v1-backup": "Imaš varnostno kopijo iz predhodnje instance Mealie v1? Obnoviš jo lahko tule.",
"manage-profile-or-get-invite-link": "Upravljaj s svojim profilom ali skopiraj povezavo z vabilom in jo deli z drugimi."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Прикажи напредно",
"add-field": "Додај поље",
"date-created": "Датум креирања",
"date-updated": "Датум измене"
"date-updated": "Датум измене",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Да ли сте сигурни да желите да обришете <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Промене у овој групи биће одмах видљиве.",
"group-id-value": "ИД групе: {0}",
"total-households": "Укупно домаћинстава",
"you-must-select-a-group-before-selecting-a-household": "Морате селектовати групу пре селектовања домаћинства"
"you-must-select-a-group-before-selecting-a-household": "Морате селектовати групу пре селектовања домаћинства",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Домаћинство",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Већ сам подешен, само ме одведи на почетну страницу",
"common-settings-for-new-sites": "Ево неколико уобичајених подешавања за нове сајтове",
"setup-complete": "Подешавање је завршено!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Ево неколико ствари које ће вам помоћи да започнете са Mealieјем",
"restore-from-v1-backup": "Имате резервну копију из претходне инстанце Mealie v1? Можете је вратити овде.",
"manage-profile-or-get-invite-link": "Управљајте сопственим профилом или узмите линк за позивницу да бисте га поделили са другима."

View File

@@ -98,7 +98,7 @@
"dashboard": "Startsida",
"delete": "Ta bort",
"disabled": "Inaktiverad",
"done": "Done",
"done": "Färdig",
"download": "Ladda ner",
"duplicate": "Duplicera",
"edit": "Redigera",
@@ -223,7 +223,9 @@
"show-advanced": "Visa avancerat",
"add-field": "Lägg till fält",
"date-created": "Datum skapad",
"date-updated": "Datum uppdaterat"
"date-updated": "Datum uppdaterat",
"key": "Nyckel",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Är du säker på att du vill radera <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Ändringar i denna grupp kommer att återspeglas omedelbart.",
"group-id-value": "Grupp-Id: {0}",
"total-households": "Totalt antal hushåll",
"you-must-select-a-group-before-selecting-a-household": "Du måste välja en grupp innan du kan välja ett hushåll"
"you-must-select-a-group-before-selecting-a-household": "Du måste välja en grupp innan du kan välja ett hushåll",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI-leverantör",
"ai-providers": "AI-leverantörer",
"ai-provider-settings-description": "Konfigurera AI-leverantörer för att aktivera AI-drivna funktioner, såsom förbättrad ingrediensanalysering, skapa recept från videor och mycket mer!",
"providers": "Leverantörer",
"create-provider": "Skapa leverantör",
"edit-provider": "Ändra leverantör",
"default-provider": "Standard leverantör",
"default-provider-description": "Krävs för att aktivera AI-funktioner",
"audio-provider": "Ljudleverantör",
"audio-provider-description": "Möjliggör funktioner för ljudtranskription, till exempel att skapa recept från videor",
"image-provider": "Bildleverantör",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Leverantörsnamn",
"api-key": "API-nyckel",
"api-key-description-create": "Din leverantörs API-nyckel för autentisering. Om din tjänst (t.ex. Ollama) inte använder en API-nyckel, måste du fortfarande sätta något här.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Hushåll",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Jag har redan gjort inställningarna, ta mig bara till hemsidan",
"common-settings-for-new-sites": "Här är några vanliga inställningar för nya webbplatser",
"setup-complete": "Konfigurationen slutförd!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Här är några saker som hjälper dig att komma igång med Mealie",
"restore-from-v1-backup": "Har du en säkerhetskopia från en tidigare instans av Mealie v1? Du kan återställa den här.",
"manage-profile-or-get-invite-link": "Hantera din egen profil eller hämta en inbjudningslänk för att dela med andra."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Gelişmiş Göster",
"add-field": "Alan Ekle",
"date-created": "Oluşturma Tarihi",
"date-updated": "Güncellenme tarihi"
"date-updated": "Güncellenme tarihi",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "<b>{groupName}<b/>'i silmek istediğine emin misin?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Bu gruptaki değişiklikler hemen yansıtılacaktır.",
"group-id-value": "Grup Kimliği: {0}",
"total-households": "Toplam Hane Halkı",
"you-must-select-a-group-before-selecting-a-household": " Bir hane seçmeden önce bir grup seçmelisiniz"
"you-must-select-a-group-before-selecting-a-household": " Bir hane seçmeden önce bir grup seçmelisiniz",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Ev Halkı",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Kurulumu zaten yaptım, beni yalnızca ana sayfaya götür",
"common-settings-for-new-sites": "Yeni siteler için bazı ortak ayarlar şunlardır",
"setup-complete": "Kurulum tamamlandı!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Mealie'yi kullanmaya başlamanıza yardımcı olacak birkaç şey",
"restore-from-v1-backup": "Mealie v1'in önceki örneğinden bir yedeğiniz mi var? Buradan geri yükleyebilirsiniz.",
"manage-profile-or-get-invite-link": "Kendi profilinizi yönetin veya başkalarıyla paylaşmak için bir davet bağlantısı alın."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Показати розширені",
"add-field": "Додати поле",
"date-created": "Дата Створення",
"date-updated": "Дата Оновлення"
"date-updated": "Дата Оновлення",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Ви дійсно бажаєте видалити <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Зміни до цієї групи будуть відображені негайно.",
"group-id-value": "Id групи: {0}",
"total-households": "Всього сімей",
"you-must-select-a-group-before-selecting-a-household": "Ви маєте вибрати групу перед тим, як вибирати сім'ю"
"you-must-select-a-group-before-selecting-a-household": "Ви маєте вибрати групу перед тим, як вибирати сім'ю",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Сімʼя",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "Вже все налаштовано, просто перенеси мене на домашню сторінку",
"common-settings-for-new-sites": "Ось деякі загальні налаштування для нових сайтів",
"setup-complete": "Налаштування завершене!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Ось кілька речей, які допоможуть вам почати з Mealie",
"restore-from-v1-backup": "Маєте резервну копію з попереднього екземпляра Меаліе v1? Ви можете використати його тут.",
"manage-profile-or-get-invite-link": "Керуйте вашим власним профілем, або отримайте посилання-запрошення щоб поділитися з іншими."

View File

@@ -223,7 +223,9 @@
"show-advanced": "Show Advanced",
"add-field": "Add Field",
"date-created": "Date Created",
"date-updated": "Date Updated"
"date-updated": "Date Updated",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "Changes to this group will be reflected immediately.",
"group-id-value": "Group Id: {0}",
"total-households": "Total Households",
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household"
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "Household",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage",
"common-settings-for-new-sites": "Here are some common settings for new sites",
"setup-complete": "Setup Complete!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "Here are a few things to help you get started with Mealie",
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.",
"manage-profile-or-get-invite-link": "Manage your own profile, or grab an invite link to share with others."

View File

@@ -223,7 +223,9 @@
"show-advanced": "显示进阶设置",
"add-field": "添加项目",
"date-created": "创建日期",
"date-updated": "修改日期"
"date-updated": "修改日期",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "您确定要删除<b>{groupName}<b/>吗?",
@@ -283,7 +285,40 @@
"admin-group-management-text": "对本群组的更改将被立即应用。",
"group-id-value": "群组ID{0}",
"total-households": "总共家庭",
"you-must-select-a-group-before-selecting-a-household": "你必须先选择一个组才能选择一个家庭"
"you-must-select-a-group-before-selecting-a-household": "你必须先选择一个组才能选择一个家庭",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "家庭",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "我已经配置好了,直接跳转到主页",
"common-settings-for-new-sites": "这有一些新站点的常见设置",
"setup-complete": "配置完成!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "以下这些可以帮助你开始使用Mealie",
"restore-from-v1-backup": "有之前Mealie v1实例的备份数据你可以在这里恢复它们。",
"manage-profile-or-get-invite-link": "管理你自己的个人资料,或者获取邀请链接分享给其他人。"

View File

@@ -223,7 +223,9 @@
"show-advanced": "顯示進階選項",
"add-field": "新增欄位",
"date-created": "建立日期",
"date-updated": "更新日期"
"date-updated": "更新日期",
"key": "Key",
"value": "Value"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "確定要刪除 <b>{groupName}</b>",
@@ -283,7 +285,40 @@
"admin-group-management-text": "對此群組的變更將立即生效。",
"group-id-value": "群組 ID{0}",
"total-households": "家庭群組總數",
"you-must-select-a-group-before-selecting-a-household": "請先選擇群組,再選擇家庭群組"
"you-must-select-a-group-before-selecting-a-household": "請先選擇群組,再選擇家庭群組",
"ai-provider-settings": {
"ai-provider-settings": "AI Provider Settings",
"ai-provider": "AI Provider",
"ai-providers": "AI Providers",
"ai-provider-settings-description": "Configure AI providers to enable AI-powered features, such as enhanced ingredient parsing, creating recipes from videos, and more!",
"providers": "Providers",
"create-provider": "Create Provider",
"edit-provider": "Edit Provider",
"default-provider": "Default Provider",
"default-provider-description": "Required to enable AI features",
"audio-provider": "Audio Provider",
"audio-provider-description": "Enables audio transcription features, such as creating recipes from videos",
"image-provider": "Image Provider",
"image-provider-description": "Enables image recognition features, such as creating recipes from images",
"provider-name": "Provider Name",
"api-key": "API Key",
"api-key-description-create": "Your provider's API key for authentication. If your service (e.g. Ollama) doesn't use an API key, you still have to put something here.",
"api-key-description-edit": "Leave this blank unless you want to change it.",
"base-url": "Base URL",
"base-url-description": "If you're using OpenAI leave this blank. Must be an OpenAI-compatible endpoint (e.g. \"http://localhost:11434/v1\").",
"model": "Model",
"model-description": "Which model your AI provider should use (e.g. \"gpt-5\").",
"request-timeout-seconds": "Request Timeout (seconds)",
"provider-created": "Provider created",
"provider-updated": "Provider updated",
"provider-deleted": "Provider deleted",
"provider-create-failed": "Failed to create provider",
"provider-update-failed": "Failed to update provider",
"provider-delete-failed": "Failed to delete provider",
"request-headers": "Request Headers",
"request-params": "Request Parameters",
"no-default-provider-warning": "You have not set a default provider, so AI features are disabled"
}
},
"household": {
"household": "家庭群組",
@@ -1362,6 +1397,7 @@
"already-set-up-bring-to-homepage": "我已完成設定,直接帶我到首頁",
"common-settings-for-new-sites": "以下是新站台的常用設定",
"setup-complete": "設定完成!",
"ai-providers-description": "Optionally configure AI providers for your group. AI providers enable features like creating recipes from images, importing recipes from videos, and enhanced ingredient parsing. You can always configure this later from your group settings.",
"here-are-a-few-things-to-help-you-get-started": "以下幾項可幫助您開始使用 Mealie",
"restore-from-v1-backup": "有舊版 Mealie v1 的備份?您可以在這裡還原。",
"manage-profile-or-get-invite-link": "管理您的個人資料,或取得邀請連結與他人分享。"

View File

@@ -0,0 +1,27 @@
import { BaseAPI } from "../base/base-clients";
import type { AIProviderCreate, AIProviderOut, AIProviderUpdate } from "~/lib/api/types/group";
const prefix = "/api/admin";
const routes = {
providers: (groupId: string) => `${prefix}/groups/${groupId}/ai-providers/providers`,
providersId: (groupId: string, providerId: string) => `${prefix}/groups/${groupId}/ai-providers/providers/${providerId}`,
};
export class AdminAIProvidersApi extends BaseAPI {
async createProvider(groupId: string, payload: AIProviderCreate) {
return await this.requests.post<AIProviderOut>(routes.providers(groupId), payload);
}
async getProvider(groupId: string, providerId: string) {
return await this.requests.get<AIProviderOut>(routes.providersId(groupId, providerId));
}
async updateProvider(groupId: string, providerId: string, payload: AIProviderUpdate) {
return await this.requests.put<AIProviderOut>(routes.providersId(groupId, providerId), payload);
}
async deleteProvider(groupId: string, providerId: string) {
return await this.requests.delete<AIProviderOut>(routes.providersId(groupId, providerId));
}
}

View File

@@ -4,11 +4,11 @@ import type { DebugResponse } from "~/lib/api/types/admin";
const prefix = "/api";
const routes = {
openai: `${prefix}/admin/debug/openai`,
openai: providerId => `${prefix}/admin/debug/openai/${providerId}`,
};
export class AdminDebugAPI extends BaseAPI {
async debugOpenAI(fileObject: Blob | File | undefined = undefined, fileName = "") {
async debugOpenAI(providerId: string, fileObject: Blob | File | undefined = undefined, fileName = "") {
let formData: FormData | null = null;
if (fileObject) {
formData = new FormData();
@@ -16,6 +16,6 @@ export class AdminDebugAPI extends BaseAPI {
formData.append("extension", fileName.split(".").pop() ?? "");
}
return await this.requests.post<DebugResponse>(routes.openai, formData);
return await this.requests.post<DebugResponse>(routes.openai(providerId), formData);
}
}

View File

@@ -6,6 +6,7 @@ import { AdminBackupsApi } from "./admin/admin-backups";
import { AdminMaintenanceApi } from "./admin/admin-maintenance";
import { AdminAnalyticsApi } from "./admin/admin-analytics";
import { AdminDebugAPI } from "./admin/admin-debug";
import { AdminAIProvidersApi } from "./admin/admin-ai-providers";
import type { ApiRequestInstance } from "~/lib/api/types/non-generated";
export class AdminAPI {
@@ -17,6 +18,7 @@ export class AdminAPI {
public maintenance: AdminMaintenanceApi;
public analytics: AdminAnalyticsApi;
public debug: AdminDebugAPI;
public aiProviders: AdminAIProvidersApi;
constructor(requests: ApiRequestInstance) {
this.about = new AdminAboutAPI(requests);
@@ -27,6 +29,7 @@ export class AdminAPI {
this.maintenance = new AdminMaintenanceApi(requests);
this.analytics = new AdminAnalyticsApi(requests);
this.debug = new AdminDebugAPI(requests);
this.aiProviders = new AdminAIProvidersApi(requests);
Object.freeze(this);
}

View File

@@ -24,6 +24,7 @@ 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 { AIProvidersAPI } from "./user/group-ai-providers";
import type { ApiRequestInstance } from "~/lib/api/types/non-generated";
export class UserApiClient {
@@ -53,6 +54,7 @@ export class UserApiClient {
public groupEventNotifier: GroupEventNotifierApi;
public upload: UploadFile;
public seeders: GroupDataSeederApi;
public aiProviders: AIProvidersAPI;
constructor(requests: ApiRequestInstance) {
// Recipes
@@ -80,6 +82,7 @@ export class UserApiClient {
this.shopping = new ShoppingApi(requests);
this.multiPurposeLabels = new MultiPurposeLabelsApi(requests);
this.seeders = new GroupDataSeederApi(requests);
this.aiProviders = new AIProvidersAPI(requests);
// Admin
this.backups = new BackupAPI(requests);

View File

@@ -16,9 +16,6 @@ export interface AdminAboutInfo {
enableOidc: boolean;
oidcRedirect: boolean;
oidcProviderName: string;
enableOpenai: boolean;
enableOpenaiImageServices: boolean;
enableOpenaiTranscriptionServices: boolean;
tokenTime: number;
versionLatest: string;
apiPort: number;
@@ -50,9 +47,6 @@ export interface AppInfo {
enableOidc: boolean;
oidcRedirect: boolean;
oidcProviderName: string;
enableOpenai: boolean;
enableOpenaiImageServices: boolean;
enableOpenaiTranscriptionServices: boolean;
tokenTime: number;
}
export interface AppStartupInfo {
@@ -95,7 +89,6 @@ export interface CheckAppConfig {
emailReady: boolean;
ldapReady: boolean;
oidcReady: boolean;
enableOpenai: boolean;
baseUrlSet: boolean;
isUpToDate: boolean;
}

View File

@@ -17,6 +17,77 @@ export type SupportedMigrations =
| "recipekeeper"
| "cookn";
export interface AIProviderCreate {
name: string;
baseUrl?: string | null;
model: string;
timeout?: number;
requestHeaders?: {
[k: string]: string;
};
requestParams?: {
[k: string]: string;
};
}
export interface AIProviderOut {
name: string;
baseUrl?: string | null;
model: string;
timeout?: number;
requestHeaders?: {
[k: string]: string;
};
requestParams?: {
[k: string]: string;
};
id: string;
}
export interface AIProviderSave {
name: string;
baseUrl?: string | null;
model: string;
timeout?: number;
requestHeaders?: {
[k: string]: string;
};
requestParams?: {
[k: string]: string;
};
settingsId: string;
}
export interface AIProviderSettingsCreate {
groupId: string;
}
export interface AIProviderSettingsOut {
defaultProviderId: string | null;
audioProviderId: string | null;
imageProviderId: string | null;
providers: AIProviderSummary[];
aiEnabled: boolean;
audioProviderEnabled: boolean;
imageProviderEnabled: boolean;
}
export interface AIProviderSummary {
id: string;
name: string;
}
export interface AIProviderSettingsUpdate {
defaultProviderId: string | null;
audioProviderId: string | null;
imageProviderId: string | null;
}
export interface AIProviderUpdate {
name: string;
baseUrl?: string | null;
model: string;
timeout?: number;
requestHeaders?: {
[k: string]: string;
};
requestParams?: {
[k: string]: string;
};
}
export interface CreateGroupPreferences {
privateGroup?: boolean;
showAnnouncements?: boolean;
@@ -29,6 +100,7 @@ export interface GroupAdminUpdate {
id: string;
name: string;
preferences?: UpdateGroupPreferences | null;
aiProviderSettings?: AIProviderSettingsUpdate | null;
}
export interface UpdateGroupPreferences {
privateGroup?: boolean;

View File

@@ -59,6 +59,7 @@ export interface GroupInDB {
households?: GroupHouseholdSummary[] | null;
users?: UserSummary[] | null;
preferences?: ReadGroupPreferences | null;
aiProviderSettings?: AIProviderSettingsOut | null;
}
export interface CategoryBase {
name: string;
@@ -89,11 +90,25 @@ export interface ReadGroupPreferences {
groupId: string;
id: string;
}
export interface AIProviderSettingsOut {
defaultProviderId: string | null;
audioProviderId: string | null;
imageProviderId: string | null;
providers: AIProviderSummary[];
aiEnabled: boolean;
audioProviderEnabled: boolean;
imageProviderEnabled: boolean;
}
export interface AIProviderSummary {
id: string;
name: string;
}
export interface GroupSummary {
name: string;
id: string;
slug: string;
preferences?: ReadGroupPreferences | null;
aiProviderSettings?: AIProviderSettingsOut | null;
}
export interface LongLiveTokenCreateResponse {
name: string;

View File

@@ -0,0 +1,27 @@
import { BaseAPI } from "../base/base-clients";
import type { AIProviderCreate, AIProviderOut, AIProviderUpdate } from "~/lib/api/types/group";
const prefix = "/api/groups/ai-providers";
const routes = {
providers: `${prefix}/providers`,
providersId: (id: string) => `${prefix}/providers/${id}`,
};
export class AIProvidersAPI extends BaseAPI {
async getOne(id: string) {
return await this.requests.get<AIProviderOut>(routes.providersId(id));
}
async createOne(payload: AIProviderCreate) {
return await this.requests.post<AIProviderOut>(routes.providers, payload);
}
async updateOne(id: string, payload: AIProviderUpdate) {
return await this.requests.put<AIProviderOut, AIProviderUpdate>(routes.providersId(id), payload);
}
async deleteOne(id: string) {
return await this.requests.delete<AIProviderOut>(routes.providersId(id));
}
}

View File

@@ -3,6 +3,8 @@ import type { PaginationData } from "../types/non-generated";
import type { QueryValue } from "../base/route";
import type { GroupBase, GroupInDB, GroupSummary, UserSummary } from "~/lib/api/types/user";
import type {
AIProviderSettingsUpdate,
AIProviderSettingsOut,
GroupAdminUpdate,
GroupStorage,
ReadGroupPreferences,
@@ -15,6 +17,7 @@ const routes = {
groups: `${prefix}/admin/groups`,
groupsSelf: `${prefix}/groups/self`,
preferences: `${prefix}/groups/preferences`,
aiProviderSettings: `${prefix}/groups/ai-providers/settings`,
storage: `${prefix}/groups/storage`,
members: `${prefix}/groups/members`,
groupsId: (id: string | number) => `${prefix}/admin/groups/${id}`,
@@ -29,15 +32,15 @@ export class GroupAPI extends BaseCRUDAPI<GroupBase, GroupInDB, GroupAdminUpdate
return await this.requests.get<GroupSummary>(routes.groupsSelf);
}
async getPreferences() {
return await this.requests.get<ReadGroupPreferences>(routes.preferences);
}
async setPreferences(payload: UpdateGroupPreferences) {
// TODO: This should probably be a patch request, which isn't offered by the API currently
return await this.requests.put<ReadGroupPreferences, UpdateGroupPreferences>(routes.preferences, payload);
}
async setAIProviderSettings(payload: AIProviderSettingsUpdate) {
return await this.requests.put<AIProviderSettingsOut, AIProviderSettingsUpdate>(routes.aiProviderSettings, payload);
}
async fetchMembers(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
return await this.requests.get<PaginationData<UserSummary>>(routes.members, { page, perPage, ...params });
}

View File

@@ -43,10 +43,6 @@ export class HouseholdAPI extends BaseCRUDAPIReadOnly<HouseholdSummary> {
return await this.requests.get<HouseholdRecipeSummary>(routes.householdsSelfRecipesSlug(recipeSlug));
}
async getPreferences() {
return await this.requests.get<ReadHouseholdPreferences>(routes.preferences);
}
async setPreferences(payload: UpdateHouseholdPreferences) {
// TODO: This should probably be a patch request, which isn't offered by the API currently
return await this.requests.put<ReadHouseholdPreferences, UpdateHouseholdPreferences>(routes.preferences, payload);

View File

@@ -6,7 +6,7 @@
<br>
<DocLink
class="mt-2"
link="/documentation/getting-started/installation/open-ai"
link="/documentation/getting-started/installation/ai-providers"
/>
</BaseCardSectionTitle>
</v-container>
@@ -17,6 +17,36 @@
<div>
<v-card-text>
<v-container class="pa-0">
<v-row>
<v-col cols="12" md="6">
<v-select
v-if="groups"
v-model="selectedGroupId"
:items="groups"
item-title="name"
item-value="id"
:label="$t('group.group')"
density="compact"
variant="outlined"
clearable
hide-details
/>
</v-col>
<v-col cols="12" md="6">
<v-select
v-model="selectedProviderId"
:items="groupProviders"
item-title="name"
item-value="id"
:label="$t('group.ai-provider-settings.ai-provider')"
density="compact"
variant="outlined"
clearable
hide-details
:disabled="!selectedGroupId"
/>
</v-col>
</v-row>
<v-row>
<v-col
cols="auto"
@@ -61,6 +91,7 @@
<v-card-actions>
<BaseButton
type="submit"
:disabled="!selectedProviderId"
:text="$t('admin.run-test')"
:icon="$globals.icons.check"
:loading="loading"
@@ -85,7 +116,9 @@
<script setup lang="ts">
import { useAdminApi } from "~/composables/api";
import { useGroups } from "~/composables/use-groups";
import { alert } from "~/composables/use-toast";
import type { AIProviderSummary } from "~/lib/api/types/group";
definePageMeta({
layout: "admin",
@@ -106,10 +139,24 @@ const uploadedImage = ref<Blob | File>();
const uploadedImageName = ref<string>("");
const uploadedImagePreviewUrl = ref<string>();
function uploadImage(fileObject: File) {
uploadedImage.value = fileObject;
uploadedImageName.value = fileObject.name;
uploadedImagePreviewUrl.value = URL.createObjectURL(fileObject);
// Group + provider selection
const { groups } = useGroups();
const selectedGroupId = ref<string | null>(null);
const groupProviders = ref<AIProviderSummary[]>([]);
const selectedProviderId = ref<string | null>(null);
watch(selectedGroupId, (id) => {
groupProviders.value = [];
selectedProviderId.value = null;
if (!id) return;
const group = groups.value?.find(g => g.id === id);
groupProviders.value = group?.aiProviderSettings?.providers ?? [];
});
function uploadImage(fileObject: unknown) {
uploadedImage.value = fileObject as File;
uploadedImageName.value = (fileObject as File).name;
uploadedImagePreviewUrl.value = URL.createObjectURL(fileObject as File);
}
function clearImage() {
@@ -119,10 +166,15 @@ function clearImage() {
}
async function testOpenAI() {
if (!selectedProviderId.value) {
alert.error("Please select a provider");
return;
}
response.value = "";
loading.value = true;
const { data } = await api.debug.debugOpenAI(uploadedImage.value);
const { data } = await api.debug.debugOpenAI(selectedProviderId.value, uploadedImage.value);
loading.value = false;
if (!data) {

View File

@@ -33,6 +33,13 @@
v-if="group.preferences"
v-model="group.preferences"
/>
<GroupAIProviderSettingsEditor
v-if="group.aiProviderSettings"
v-model="group.aiProviderSettings"
@create="handleCreateProvider"
@update="handleUpdateProvider"
@delete="handleDeleteProvider"
/>
</v-card-text>
</v-card>
<div class="d-flex pa-2">
@@ -50,8 +57,10 @@
<script setup lang="ts">
import GroupPreferencesEditor from "~/components/Domain/Group/GroupPreferencesEditor.vue";
import GroupAIProviderSettingsEditor from "~/components/Domain/Group/GroupAIProviderSettingsEditor.vue";
import { useAdminApi } from "~/composables/api";
import { alert } from "~/composables/use-toast";
import type { AIProviderCreate, AIProviderUpdate } from "~/lib/api/types/group";
import type { VForm } from "vuetify/components";
definePageMeta({
@@ -72,7 +81,7 @@ const adminApi = useAdminApi();
const userError = ref(false);
const { data: group } = useLazyAsyncData(`get-household-${groupId.value}`, async () => {
const { data: group, refresh } = useLazyAsyncData(`get-household-${groupId.value}`, async () => {
if (!groupId.value) {
return null;
}
@@ -86,7 +95,7 @@ const { data: group } = useLazyAsyncData(`get-household-${groupId.value}`, async
}, { watch: [groupId] });
async function handleSubmit() {
if (!refGroupEditForm.value?.validate() || group.value === null) {
if (!refGroupEditForm.value?.validate() || !group.value) {
return;
}
@@ -103,4 +112,40 @@ async function handleSubmit() {
alert.error(i18n.t("settings.settings-update-failed"));
}
}
async function handleCreateProvider(data: AIProviderCreate) {
if (!group.value) return;
const result = await adminApi.aiProviders.createProvider(group.value.id, data);
if (result.data) {
await refresh();
alert.success(i18n.t("group.ai-provider-settings.provider-created"));
}
else {
alert.error(i18n.t("group.ai-provider-settings.provider-create-failed"));
}
}
async function handleUpdateProvider(id: string, data: AIProviderUpdate) {
if (!group.value) return;
const result = await adminApi.aiProviders.updateProvider(group.value.id, id, data);
if (result.data) {
await refresh();
alert.success(i18n.t("group.ai-provider-settings.provider-updated"));
}
else {
alert.error(i18n.t("group.ai-provider-settings.provider-update-failed"));
}
}
async function handleDeleteProvider(id: string) {
if (!group.value) return;
const result = await adminApi.aiProviders.deleteProvider(group.value.id, id);
if (result.data) {
await refresh();
alert.success(i18n.t("group.ai-provider-settings.provider-deleted"));
}
else {
alert.error(i18n.t("group.ai-provider-settings.provider-delete-failed"));
}
}
</script>

View File

@@ -45,6 +45,14 @@
:title="$t('settings.site-settings')"
/>
<v-divider />
<v-stepper-item
:value="Pages.AI_PROVIDERS"
:icon="$globals.icons.robot"
:complete="currentPage > Pages.AI_PROVIDERS"
:color="getStepperColor(currentPage, Pages.AI_PROVIDERS)"
:title="$t('group.ai-provider-settings.ai-providers')"
/>
<v-divider />
<v-stepper-item
:value="Pages.CONFIRM"
:icon="$globals.icons.chefHat"
@@ -173,6 +181,43 @@
</v-stepper-actions>
</v-stepper-window-item>
<!-- AI PROVIDERS -->
<v-stepper-window-item :value="Pages.AI_PROVIDERS">
<v-container max-width="880">
<v-card-title class="headline pa-0">
{{ $t('group.ai-provider-settings.ai-providers') }}
</v-card-title>
<v-card-subtitle class="px-0 py-2 text-wrap">
{{ $t('group.ai-provider-settings.ai-providers-description') }}
</v-card-subtitle>
<GroupAIProviderSettingsEditor
v-if="group?.aiProviderSettings"
v-model="group.aiProviderSettings"
hide-header
class="mt-4"
@create="handleCreateProvider"
@update="handleUpdateProvider"
@delete="handleDeleteProvider"
/>
</v-container>
<v-stepper-actions
:disabled="isSubmitting"
prev-text="general.back"
@click:prev="onPrev"
>
<template #next>
<v-btn
variant="flat"
color="success"
:disabled="isSubmitting"
:loading="isSubmitting"
:text="$t('general.next')"
@click="onNext"
/>
</template>
</v-stepper-actions>
</v-stepper-window-item>
<!-- CONFIRMATION -->
<v-stepper-window-item :value="Pages.CONFIRM">
<v-container max-width="880">
@@ -252,7 +297,11 @@ import { useLocales } from "~/composables/use-locales";
import { alert } from "~/composables/use-toast";
import { useUserRegistrationForm } from "~/composables/use-users/user-registration-form";
import { useCommonSettingsForm } from "~/composables/use-setup/common-settings-form";
import { useGroupSelf } from "~/composables/use-groups";
import { useAIProviders } from "~/composables/use-ai-providers";
import UserRegistrationForm from "~/components/Domain/User/UserRegistrationForm.vue";
import GroupAIProviderSettingsEditor from "~/components/Domain/Group/GroupAIProviderSettingsEditor.vue";
import type { AIProviderCreate, AIProviderUpdate } from "~/lib/api/types/group";
definePageMeta({
layout: "blank",
@@ -267,6 +316,42 @@ const userApi = useUserApi();
const adminApi = useAdminApi();
const groupSlug = computed(() => auth.user.value?.groupSlug);
const { group, actions: groupActions } = useGroupSelf();
const { createOne, updateOne, deleteOne } = useAIProviders();
async function handleCreateProvider(data: AIProviderCreate) {
const result = await createOne(data);
if (result.data) {
await groupActions.refresh();
alert.success(i18n.t("group.ai-provider-settings.provider-created"));
}
else {
alert.error(i18n.t("group.ai-provider-settings.provider-create-failed"));
}
}
async function handleUpdateProvider(id: string, data: AIProviderUpdate) {
const result = await updateOne(id, data);
if (result.data) {
await groupActions.refresh();
alert.success(i18n.t("group.ai-provider-settings.provider-updated"));
}
else {
alert.error(i18n.t("group.ai-provider-settings.provider-update-failed"));
}
}
async function handleDeleteProvider(id: string) {
const result = await deleteOne(id);
if (result.data) {
await groupActions.refresh();
alert.success(i18n.t("group.ai-provider-settings.provider-deleted"));
}
else {
alert.error(i18n.t("group.ai-provider-settings.provider-delete-failed"));
}
}
const { locale } = useLocales();
const router = useRouter();
const isSubmitting = ref(false);
@@ -281,8 +366,9 @@ enum Pages {
LANDING = 1,
USER_INFO = 2,
PAGE_2 = 3,
CONFIRM = 4,
END = 5,
AI_PROVIDERS = 4,
CONFIRM = 5,
END = 6,
}
function getStepperColor(currentPage: Pages, page: Pages) {
@@ -475,6 +561,7 @@ async function submitAll() {
const tasks = [
submitRegistration(),
submitCommonSettings(),
groupActions.updateAIProviderSettings(),
];
await Promise.all(tasks);

View File

@@ -284,7 +284,6 @@ const appConfig = ref<CheckApp>({
isUpToDate: false,
ldapReady: false,
oidcReady: false,
enableOpenai: false,
});
function isLocalHostOrHttps() {
return window.location.hostname === "localhost" || window.location.protocol === "https:";
@@ -351,15 +350,6 @@ const simpleChecks = computed<SimpleCheck[]>(() => {
color: appConfig.value.oidcReady ? goodColor : warningColor,
icon: appConfig.value.oidcReady ? goodIcon : warningIcon,
},
{
id: "openai-ready",
text: appConfig.value.enableOpenai ? i18n.t("settings.openai-ready") : i18n.t("settings.openai-not-ready"),
status: appConfig.value.enableOpenai,
errorText: i18n.t("settings.openai-ready-error-text"),
successText: i18n.t("settings.openai-ready-success-text"),
color: appConfig.value.enableOpenai ? goodColor : warningColor,
icon: appConfig.value.enableOpenai ? goodIcon : warningIcon,
},
];
return data;
});

View File

@@ -45,6 +45,7 @@
<script setup lang="ts">
import type { MenuItem } from "~/components/global/BaseOverflowButton.vue";
import AdvancedOnly from "~/components/global/AdvancedOnly.vue";
import { useGroupSelf } from "~/composables/use-groups";
definePageMeta({
middleware: ["group-only"],
@@ -52,7 +53,8 @@ definePageMeta({
const i18n = useI18n();
const auth = useMealieAuth();
const { $appInfo, $globals } = useNuxtApp();
const { $globals } = useNuxtApp();
const { group } = useGroupSelf();
useSeoMeta({
title: i18n.t("general.create"),
@@ -78,7 +80,7 @@ const subpages = computed<MenuItem[]>(() => [
icon: $globals.icons.fileImage,
text: i18n.t("recipe.create-from-images"),
value: "image",
hide: !$appInfo.enableOpenaiImageServices,
hide: !group.value?.aiProviderSettings?.imageProviderEnabled,
},
{
icon: $globals.icons.edit,

View File

@@ -25,7 +25,7 @@
persistent-hint
/>
</v-card-text>
<v-card-text v-if="$appInfo.enableOpenai">
<v-card-text v-if="group?.aiProviderSettings?.aiEnabled">
{{ $t('recipe.recipe-debugger-use-openai-description') }}
<v-checkbox
v-model="state.useOpenAI"
@@ -69,6 +69,7 @@
<script setup lang="ts">
import { useUserApi } from "~/composables/api";
import { useGroupSelf } from "~/composables/use-groups";
import { validators } from "~/composables/use-validators";
import type { Recipe } from "~/lib/api/types/recipe";
@@ -80,6 +81,7 @@ const state = reactive({
const api = useUserApi();
const route = useRoute();
const router = useRouter();
const { group } = useGroupSelf();
const recipeUrl = computed({
set(recipe_import_url: string | null) {

View File

@@ -11,7 +11,7 @@
<v-card-text>
<v-card-text class="pa-0">
<p>{{ $t('recipe.scrape-recipe-description') }}</p>
<p v-if="$appInfo.enableOpenaiTranscriptionServices">
<p v-if="group?.aiProviderSettings?.audioProviderEnabled">
{{ $t('recipe.scrape-recipe-description-transcription') }}
</p>
</v-card-text>
@@ -145,6 +145,7 @@
<script setup lang="ts">
import type { AxiosResponse } from "axios";
import { useUserApi } from "~/composables/api";
import { useGroupSelf } from "~/composables/use-groups";
import { useTagStore } from "~/composables/store/use-tag-store";
import { useNewRecipeOptions } from "~/composables/use-new-recipe-options";
import { validators } from "~/composables/use-validators";
@@ -162,6 +163,7 @@ const auth = useMealieAuth();
const api = useUserApi();
const route = useRoute();
const groupSlug = computed(() => route.params.groupSlug as string || auth.user.value?.groupSlug || "");
const { group } = useGroupSelf();
const router = useRouter();
const tags = useTagStore();

View File

@@ -17,25 +17,52 @@
</template>
{{ $t("profile.group-description") }}
</BasePageTitle>
<v-form ref="refGroupEditForm" @submit.prevent="handleSubmit">
<v-card variant="outlined" style="border-color: lightgray;">
<v-card-text>
<GroupPreferencesEditor v-if="group.preferences" v-model="group.preferences" />
</v-card-text>
</v-card>
<div class="d-flex pa-2">
<BaseButton type="submit" edit class="ml-auto">
{{ $t("general.update") }}
</BaseButton>
</div>
</v-form>
<div class="mb-10">
<v-form ref="refGroupPrefsEditForm" @submit.prevent="handlePrefsSubmit">
<v-card variant="outlined" style="border-color: lightgray;">
<v-card-text>
<GroupPreferencesEditor v-if="group.preferences" v-model="group.preferences" />
</v-card-text>
</v-card>
<div class="d-flex pa-2">
<BaseButton type="submit" edit class="ml-auto">
{{ $t("general.update") }}
</BaseButton>
</div>
</v-form>
</div>
<div>
<v-form ref="refGroupAISettingsForm" @submit.prevent="handleAISettingsSubmit">
<v-card variant="outlined" style="border-color: lightgray;">
<v-card-text>
<GroupAIProviderSettingsEditor
v-if="group.aiProviderSettings"
v-model="group.aiProviderSettings"
@create="handleCreateProvider"
@update="handleUpdateProvider"
@delete="handleDeleteProvider"
/>
</v-card-text>
</v-card>
<div class="d-flex pa-2">
<BaseButton type="submit" edit class="ml-auto">
{{ $t("general.update") }}
</BaseButton>
</div>
</v-form>
</div>
</v-container>
</template>
<script setup lang="ts">
import GroupPreferencesEditor from "~/components/Domain/Group/GroupPreferencesEditor.vue";
import GroupAIProviderSettingsEditor from "~/components/Domain/Group/GroupAIProviderSettingsEditor.vue";
import { useGroupSelf } from "~/composables/use-groups";
import { useAIProviders } from "~/composables/use-ai-providers";
import { alert } from "~/composables/use-toast";
import type { AIProviderCreate, AIProviderUpdate } from "~/lib/api/types/group";
import type { VForm } from "~/types/auto-forms";
definePageMeta({
@@ -49,10 +76,11 @@ useSeoMeta({
title: i18n.t("group.group"),
});
const refGroupEditForm = ref<VForm | null>(null);
const refGroupPrefsEditForm = ref<VForm | null>(null);
const refGroupAISettingsForm = ref<VForm | null>(null);
async function handleSubmit() {
if (!refGroupEditForm.value?.validate() || !group.value?.preferences) {
async function handlePrefsSubmit() {
if (!refGroupPrefsEditForm.value?.validate() || !group.value?.preferences) {
return;
}
@@ -64,6 +92,55 @@ async function handleSubmit() {
alert.error(i18n.t("settings.settings-update-failed"));
}
}
async function handleAISettingsSubmit() {
if (!refGroupAISettingsForm.value?.validate() || !group.value?.aiProviderSettings) {
return;
}
const data = await groupActions.updateAIProviderSettings();
if (data) {
alert.success(i18n.t("settings.settings-updated"));
}
else {
alert.error(i18n.t("settings.settings-update-failed"));
}
}
const { createOne, updateOne, deleteOne } = useAIProviders();
async function handleCreateProvider(data: AIProviderCreate) {
const result = await createOne(data);
if (result.data) {
await groupActions.refresh();
alert.success(i18n.t("group.ai-provider-settings.provider-created"));
}
else {
alert.error(i18n.t("group.ai-provider-settings.provider-create-failed"));
}
}
async function handleUpdateProvider(id: string, data: AIProviderUpdate) {
const result = await updateOne(id, data);
if (result.data) {
await groupActions.refresh();
alert.success(i18n.t("group.ai-provider-settings.provider-updated"));
}
else {
alert.error(i18n.t("group.ai-provider-settings.provider-update-failed"));
}
}
async function handleDeleteProvider(id: string) {
const result = await deleteOne(id);
if (result.data) {
await groupActions.refresh();
alert.success(i18n.t("group.ai-provider-settings.provider-deleted"));
}
else {
alert.error(i18n.t("group.ai-provider-settings.provider-delete-failed"));
}
}
</script>
<style lang="css">

View File

@@ -45,7 +45,7 @@
sm="12"
md="12"
>
<v-card variant="outlined" style="border-color: lightgray;" class="mt-4">
<v-card variant="outlined" style="border-color: lightgray;" class="mt-4 pa-2">
<v-card-title class="text-h6 pb-0">
{{ $t('profile.household-statistics') }}
</v-card-title>

View File

@@ -0,0 +1,65 @@
/**
* v-no-autofill directive
*
* Vuetify 3 places data-* attributes on its wrapper div, not the underlying
* <input> element, so password managers still offer to autofill. This directive
* uses a MutationObserver to find and patch every <input> inside the host
* element, even ones rendered asynchronously (dialogs, conditional blocks).
*
* From: https://github.com/vuetifyjs/vuetify/issues/18202
*
* Usage:
* <v-text-field v-no-autofill ... />
* <v-form v-no-autofill>...</v-form>
* <div v-no-autofill>...</div>
*/
import type { Directive, DirectiveBinding } from "vue";
interface ObservedElement extends HTMLElement {
_noAutofillObserver?: MutationObserver;
}
function patchInput(input: HTMLInputElement) {
input.setAttribute("autocomplete", "off");
input.setAttribute("data-1p-ignore", "true");
input.setAttribute("data-lpignore", "true");
input.setAttribute("data-protonpass-ignore", "true");
input.setAttribute("data-bwignore", "true");
input.setAttribute("data-form-type", "other");
}
function patchAll(el: HTMLElement) {
if (el.tagName === "INPUT") {
patchInput(el as HTMLInputElement);
}
el.querySelectorAll<HTMLInputElement>("input").forEach(patchInput);
}
const noAutofill: Directive<ObservedElement> = {
mounted(el: ObservedElement, _binding: DirectiveBinding) {
patchAll(el);
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
patchAll(node as HTMLElement);
}
}
}
});
observer.observe(el, { childList: true, subtree: true });
el._noAutofillObserver = observer;
},
unmounted(el: ObservedElement) {
el._noAutofillObserver?.disconnect();
delete el._noAutofillObserver;
},
};
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive("no-autofill", noAutofill);
});

View File

@@ -14,6 +14,7 @@ import type BaseCardSectionTitle from "@/components/global/BaseCardSectionTitle.
import type BaseDialog from "@/components/global/BaseDialog.vue";
import type BaseDivider from "@/components/global/BaseDivider.vue";
import type BaseExpansionPanels from "@/components/global/BaseExpansionPanels.vue";
import type BaseKeyValueEditor from "@/components/global/BaseKeyValueEditor.vue";
import type BaseOverflowButton from "@/components/global/BaseOverflowButton.vue";
import type BasePageTitle from "@/components/global/BasePageTitle.vue";
import type ButtonLink from "@/components/global/ButtonLink.vue";
@@ -54,6 +55,7 @@ declare module "vue" {
BaseDialog: typeof BaseDialog;
BaseDivider: typeof BaseDivider;
BaseExpansionPanels: typeof BaseExpansionPanels;
BaseKeyValueEditor: typeof BaseKeyValueEditor;
BaseOverflowButton: typeof BaseOverflowButton;
BasePageTitle: typeof BasePageTitle;
ButtonLink: typeof ButtonLink;

View File

@@ -1,6 +1,6 @@
{
"name": "mealie",
"version": "3.18.0",
"version": "3.19.0",
"private": true,
"scripts": {
"dev": "nuxt dev",

View File

@@ -0,0 +1,298 @@
"""add table for ai providers
Revision ID: 2187537c52b8
Revises: c7427796f7b6
Create Date: 2026-05-18 16:27:05.770218
"""
import sqlalchemy as sa
from alembic import op
from pydantic_settings import BaseSettings, SettingsConfigDict
import mealie.db.migration_types
from mealie.core.config import ENV
from mealie.core.root_logger import get_logger
from mealie.core.settings.settings import get_secrets_dir
from mealie.db.models._model_utils.guid import GUID
# revision identifiers, used by Alembic.
revision = "2187537c52b8"
down_revision: str | None = "c7427796f7b6"
branch_labels: str | tuple[str, ...] | None = None
depends_on: str | tuple[str, ...] | None = None
logger = get_logger()
class LegacyOpenAISettings(BaseSettings):
OPENAI_BASE_URL: str | None = None
OPENAI_API_KEY: str | None = None
OPENAI_MODEL: str = "gpt-4o"
OPENAI_AUDIO_MODEL: str = "whisper-1"
OPENAI_CUSTOM_HEADERS: dict[str, str] = {}
OPENAI_CUSTOM_PARAMS: dict[str, str] = {}
OPENAI_ENABLE_IMAGE_SERVICES: bool = True
OPENAI_ENABLE_TRANSCRIPTION_SERVICES: bool = True
OPENAI_REQUEST_TIMEOUT: int = 300
model_config = SettingsConfigDict(extra="ignore")
def get_openai_settings() -> LegacyOpenAISettings:
return LegacyOpenAISettings(
_env_file=ENV,
_env_file_encoding="utf-8",
_secrets_dir=get_secrets_dir(),
)
def is_postgres() -> bool:
return op.get_context().dialect.name == "postgresql"
def generate_id() -> str:
val = GUID.generate()
dialect = op.get_bind().dialect
return GUID.convert_value_to_guid(val, dialect) # type: ignore
def create_provider(settings_id: str, provider_id: str, model: str, openai_settings: LegacyOpenAISettings) -> None:
logger.info(f"Creating provider '{model}'")
conn = op.get_bind()
conn.execute(
sa.text(
"INSERT INTO ai_providers (id, settings_id, name, base_url, api_key, model, timeout) "
"VALUES (:id, :settings_id, :name, :base_url, :api_key, :model, :timeout)"
),
{
"id": provider_id,
"settings_id": settings_id,
# we only create one provider per model in this mirgration script,
# so there's no chance for collisions
"name": model,
"base_url": openai_settings.OPENAI_BASE_URL,
"api_key": openai_settings.OPENAI_API_KEY,
"model": model,
"timeout": openai_settings.OPENAI_REQUEST_TIMEOUT,
},
)
for key, value in openai_settings.OPENAI_CUSTOM_HEADERS.items():
conn.execute(
sa.text(
"INSERT INTO ai_provider_headers (provider_id, key_name, value) "
"VALUES (:provider_id, :key_name, :value)"
),
{"provider_id": provider_id, "key_name": key, "value": value},
)
for key, value in openai_settings.OPENAI_CUSTOM_PARAMS.items():
conn.execute(
sa.text(
"INSERT INTO ai_provider_params (provider_id, key_name, value) VALUES (:provider_id, :key_name, :value)"
),
{"provider_id": provider_id, "key_name": key, "value": value},
)
def create_providers() -> None:
"""Create provider settings and migrate legacy OPEN_AI_... environment variables to a provider"""
openai_settings = get_openai_settings()
create_providers = bool(openai_settings.OPENAI_API_KEY and openai_settings.OPENAI_MODEL)
if create_providers:
logger.info("Found legacy OpenAI configuration, creating new AI providers")
conn = op.get_bind()
groups = conn.execute(sa.text("SELECT id FROM groups")).fetchall()
for (group_id,) in groups:
logger.info(f"Creating provider settings for {group_id=}")
# Create AI provider settings
settings_id = generate_id()
conn.execute(
sa.text("INSERT INTO ai_provider_settings (id, group_id) VALUES (:id, :group_id)"),
{"id": settings_id, "group_id": group_id},
)
if not create_providers:
continue
# Create provider
default_provider_id = generate_id()
model = openai_settings.OPENAI_MODEL
create_provider(settings_id, default_provider_id, model, openai_settings)
# Set the image provider if image services are enabled
if openai_settings.OPENAI_ENABLE_IMAGE_SERVICES:
image_provider_id = default_provider_id
else:
image_provider_id = None
# Set the audio provider if transcription services are enabled
if openai_settings.OPENAI_ENABLE_TRANSCRIPTION_SERVICES:
transcription_model = openai_settings.OPENAI_AUDIO_MODEL or model
if transcription_model == openai_settings.OPENAI_MODEL:
audio_provider_id = default_provider_id
else:
# The transcription model is different than the base model, so create a new provider
audio_provider_id = generate_id()
create_provider(settings_id, audio_provider_id, transcription_model, openai_settings)
else:
audio_provider_id = None
# Update the provider settings to reference new provider(s)
conn.execute(
sa.text(
"""
UPDATE ai_provider_settings
SET
default_provider_id = :default_provider_id,
audio_provider_id = :audio_provider_id,
image_provider_id = :image_provider_id
WHERE id = :id
"""
),
{
"default_provider_id": default_provider_id,
"audio_provider_id": audio_provider_id,
"image_provider_id": image_provider_id,
"id": settings_id,
},
)
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"ai_provider_settings",
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("default_provider_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("audio_provider_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("image_provider_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("created_at", mealie.db.migration_types.NaiveDateTime(), nullable=True),
sa.Column("update_at", mealie.db.migration_types.NaiveDateTime(), nullable=True),
sa.ForeignKeyConstraint(["default_provider_id"], ["ai_providers.id"], use_alter=True),
sa.ForeignKeyConstraint(["audio_provider_id"], ["ai_providers.id"], use_alter=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.ForeignKeyConstraint(["image_provider_id"], ["ai_providers.id"], use_alter=True),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("group_id", name="ai_provider_settings_group_id_key"),
)
with op.batch_alter_table("ai_provider_settings", schema=None) as batch_op:
batch_op.create_index(
batch_op.f("ix_ai_provider_settings_default_provider_id"), ["default_provider_id"], unique=False
)
batch_op.create_index(
batch_op.f("ix_ai_provider_settings_audio_provider_id"), ["audio_provider_id"], unique=False
)
batch_op.create_index(batch_op.f("ix_ai_provider_settings_created_at"), ["created_at"], unique=False)
batch_op.create_index(batch_op.f("ix_ai_provider_settings_group_id"), ["group_id"], unique=False)
batch_op.create_index(
batch_op.f("ix_ai_provider_settings_image_provider_id"), ["image_provider_id"], unique=False
)
op.create_table(
"ai_providers",
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("settings_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("base_url", sa.String(), nullable=True),
sa.Column("api_key", sa.String(), nullable=False),
sa.Column("model", sa.String(), nullable=False),
sa.Column("timeout", sa.Integer(), nullable=False),
sa.Column("created_at", mealie.db.migration_types.NaiveDateTime(), nullable=True),
sa.Column("update_at", mealie.db.migration_types.NaiveDateTime(), nullable=True),
sa.ForeignKeyConstraint(
["settings_id"],
["ai_provider_settings.id"],
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name", "settings_id", name="ai_providers_name_settings_id_key"),
)
with op.batch_alter_table("ai_providers", schema=None) as batch_op:
batch_op.create_index(batch_op.f("ix_ai_providers_created_at"), ["created_at"], unique=False)
batch_op.create_index(batch_op.f("ix_ai_providers_name"), ["name"], unique=False)
batch_op.create_index(batch_op.f("ix_ai_providers_settings_id"), ["settings_id"], unique=False)
op.create_table(
"ai_provider_headers",
sa.Column("provider_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("key_name", sa.String(), nullable=True),
sa.Column("value", sa.String(), nullable=True),
sa.Column("created_at", mealie.db.migration_types.NaiveDateTime(), nullable=True),
sa.Column("update_at", mealie.db.migration_types.NaiveDateTime(), nullable=True),
sa.ForeignKeyConstraint(
["provider_id"],
["ai_providers.id"],
),
sa.PrimaryKeyConstraint("id"),
)
with op.batch_alter_table("ai_provider_headers", schema=None) as batch_op:
batch_op.create_index(batch_op.f("ix_ai_provider_headers_created_at"), ["created_at"], unique=False)
batch_op.create_index(batch_op.f("ix_ai_provider_headers_provider_id"), ["provider_id"], unique=False)
op.create_table(
"ai_provider_params",
sa.Column("provider_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("key_name", sa.String(), nullable=True),
sa.Column("value", sa.String(), nullable=True),
sa.Column("created_at", mealie.db.migration_types.NaiveDateTime(), nullable=True),
sa.Column("update_at", mealie.db.migration_types.NaiveDateTime(), nullable=True),
sa.ForeignKeyConstraint(
["provider_id"],
["ai_providers.id"],
),
sa.PrimaryKeyConstraint("id"),
)
with op.batch_alter_table("ai_provider_params", schema=None) as batch_op:
batch_op.create_index(batch_op.f("ix_ai_provider_params_created_at"), ["created_at"], unique=False)
batch_op.create_index(batch_op.f("ix_ai_provider_params_provider_id"), ["provider_id"], unique=False)
# ### end Alembic commands ###
try:
with op.get_bind().begin_nested():
create_providers()
except Exception:
logger.exception("Failed to migrate legacy OpenAI config")
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("ai_provider_params", schema=None) as batch_op:
batch_op.drop_index(batch_op.f("ix_ai_provider_params_provider_id"))
batch_op.drop_index(batch_op.f("ix_ai_provider_params_created_at"))
op.drop_table("ai_provider_params")
with op.batch_alter_table("ai_provider_headers", schema=None) as batch_op:
batch_op.drop_index(batch_op.f("ix_ai_provider_headers_provider_id"))
batch_op.drop_index(batch_op.f("ix_ai_provider_headers_created_at"))
op.drop_table("ai_provider_headers")
with op.batch_alter_table("ai_providers", schema=None) as batch_op:
batch_op.drop_index(batch_op.f("ix_ai_providers_settings_id"))
batch_op.drop_index(batch_op.f("ix_ai_providers_name"))
batch_op.drop_index(batch_op.f("ix_ai_providers_created_at"))
op.drop_table("ai_providers")
with op.batch_alter_table("ai_provider_settings", schema=None) as batch_op:
batch_op.drop_index(batch_op.f("ix_ai_provider_settings_image_provider_id"))
batch_op.drop_index(batch_op.f("ix_ai_provider_settings_group_id"))
batch_op.drop_index(batch_op.f("ix_ai_provider_settings_created_at"))
batch_op.drop_index(batch_op.f("ix_ai_provider_settings_audio_provider_id"))
batch_op.drop_index(batch_op.f("ix_ai_provider_settings_default_provider_id"))
op.drop_table("ai_provider_settings")
# ### end Alembic commands ###

View File

@@ -88,8 +88,6 @@ async def lifespan_fn(_: FastAPI) -> AsyncGenerator[None, None]:
logger.info(settings.LDAP_FEATURE)
logger.info("--------==OIDC==--------")
logger.info(settings.OIDC_FEATURE)
logger.info("-------==OPENAI==-------")
logger.info(settings.OPENAI_FEATURE)
logger.info("------------------------")
yield

View File

@@ -3,7 +3,7 @@ import os
import secrets
from datetime import UTC, datetime
from pathlib import Path
from typing import Annotated, Any, Literal, NamedTuple
from typing import Annotated, Literal, NamedTuple
from dateutil.tz import tzlocal
from pydantic import PlainSerializer, field_validator
@@ -388,60 +388,12 @@ class AppSettings(AppLoggingSettings):
# ===============================================
# OpenAI Configuration
OPENAI_BASE_URL: str | None = None
"""The base URL for the OpenAI API. Leave this unset for most usecases"""
OPENAI_API_KEY: MaskedNoneString = None
"""Your OpenAI API key. Required to enable OpenAI features"""
OPENAI_MODEL: str = "gpt-4o"
"""Which OpenAI model to send requests to. Leave this unset for most usecases"""
OPENAI_AUDIO_MODEL: str = "whisper-1"
"""Which OpenAI model to use for audio transcription. Leave this unset for most usecases"""
OPENAI_CUSTOM_HEADERS: dict[str, str] = {}
"""Custom HTTP headers to send with each OpenAI request"""
OPENAI_CUSTOM_PARAMS: dict[str, Any] = {}
"""Custom HTTP parameters to send with each OpenAI request"""
OPENAI_ENABLE_IMAGE_SERVICES: bool = True
"""Whether to enable image-related features in OpenAI"""
OPENAI_ENABLE_TRANSCRIPTION_SERVICES: bool = True
"""Whether to enable audio transcription features in OpenAI"""
OPENAI_WORKERS: int = 2
"""
Number of OpenAI workers per request. Higher values may increase
processing speed, but will incur additional API costs
"""
OPENAI_SEND_DATABASE_DATA: bool = True
"""
Sending database data may increase accuracy in certain requests,
but will incur additional API costs
"""
OPENAI_REQUEST_TIMEOUT: int = 300
"""
The number of seconds to wait for an OpenAI request to complete before cancelling the request
"""
OPENAI_CUSTOM_PROMPT_DIR: str | None = None
"""
Path to a folder containing custom prompt files;
files are individually optional, each prompt name will fall back to the default if no custom file exists
"""
@property
def OPENAI_FEATURE(self) -> FeatureDetails:
description = None
if not self.OPENAI_API_KEY:
description = "OPENAI_API_KEY is not set"
elif not self.OPENAI_MODEL:
description = "OPENAI_MODEL is not set"
return FeatureDetails(
enabled=bool(self.OPENAI_API_KEY and self.OPENAI_MODEL),
description=description,
)
@property
def OPENAI_ENABLED(self) -> bool:
"""Validates OpenAI settings are all set"""
return self.OPENAI_FEATURE.enabled
# ===============================================
# Web Concurrency

View File

@@ -1,3 +1,4 @@
from .ai_providers import *
from .exports import *
from .group import *
from .preferences import *

View File

@@ -0,0 +1,145 @@
from typing import TYPE_CHECKING
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
from .._model_base import BaseMixins, SqlAlchemyBase
from .._model_utils.auto_init import auto_init
from .._model_utils.guid import GUID
if TYPE_CHECKING:
from .group import Group
def unwrap_headers_and_params(func):
"""Decorator function to unpack headers and params into dicts"""
def unwrap(value: dict | None) -> list[dict]:
if value is None:
value = {}
return [{"key": k, "value": v} for k, v in value.items()]
def wrapper(*args, **kwargs):
headers = kwargs.pop("request_headers", {})
params = kwargs.pop("request_params", {})
return func(
*args,
request_headers=unwrap(headers),
request_params=unwrap(params),
**kwargs,
)
return wrapper
class AIProviderKV:
"""
Template for key-value pairs
This class is not an actual table, so it does not inherit from SqlAlchemyBase
"""
id: orm.Mapped[int] = orm.mapped_column(sa.Integer, primary_key=True)
key_name: orm.Mapped[str | None] = orm.mapped_column(sa.String)
value: orm.Mapped[str | None] = orm.mapped_column(sa.String)
def __init__(self, key, value) -> None:
self.key_name = key
self.value = value
class AIProviderHeaders(AIProviderKV, SqlAlchemyBase):
__tablename__ = "ai_provider_headers"
provider_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("ai_providers.id"), index=True)
class AIProviderParams(AIProviderKV, SqlAlchemyBase):
__tablename__ = "ai_provider_params"
provider_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("ai_providers.id"), index=True)
class AIProvider(SqlAlchemyBase, BaseMixins):
__tablename__ = "ai_providers"
__table_args__ = (sa.UniqueConstraint("name", "settings_id", name="ai_providers_name_settings_id_key"),)
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
group_id: AssociationProxy[GUID] = association_proxy("settings", "group_id")
settings_id: orm.Mapped[GUID] = orm.mapped_column(
GUID, sa.ForeignKey("ai_provider_settings.id"), nullable=False, index=True
)
settings: orm.Mapped["AIProviderSettings"] = orm.relationship(
"AIProviderSettings", foreign_keys="[AIProvider.settings_id]", back_populates="providers"
)
name: orm.Mapped[str] = orm.mapped_column(sa.String, index=True, nullable=False)
base_url: orm.Mapped[str | None] = orm.mapped_column(sa.String, nullable=True)
api_key: orm.Mapped[str] = orm.mapped_column(sa.String, nullable=False)
model: orm.Mapped[str] = orm.mapped_column(sa.String, nullable=False)
timeout: orm.Mapped[int] = orm.mapped_column(sa.Integer, nullable=False, default=300)
request_headers: orm.Mapped[list[AIProviderHeaders]] = orm.relationship(
"AIProviderHeaders", cascade="all, delete-orphan"
)
request_params: orm.Mapped[list[AIProviderParams]] = orm.relationship(
"AIProviderParams", cascade="all, delete-orphan"
)
@unwrap_headers_and_params
@auto_init()
def __init__(self, **_) -> None:
pass
class AIProviderSettings(SqlAlchemyBase, BaseMixins):
__tablename__ = "ai_provider_settings"
__table_args__ = (sa.UniqueConstraint("group_id", name="ai_provider_settings_group_id_key"),)
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
group_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True)
group: orm.Mapped["Group"] = orm.relationship("Group", back_populates="ai_provider_settings")
providers: orm.Mapped[list[AIProvider]] = orm.relationship(
AIProvider,
foreign_keys="[AIProvider.settings_id]",
back_populates="settings",
uselist=True,
single_parent=True,
cascade="all, delete-orphan",
)
# Configured Providers
default_provider_id: orm.Mapped[GUID | None] = orm.mapped_column(
GUID, sa.ForeignKey("ai_providers.id", use_alter=True), nullable=True, index=True
)
default_provider: orm.Mapped[AIProvider | None] = orm.relationship(
AIProvider,
foreign_keys="[AIProviderSettings.default_provider_id]",
uselist=False,
post_update=True,
)
audio_provider_id: orm.Mapped[GUID | None] = orm.mapped_column(
GUID, sa.ForeignKey("ai_providers.id", use_alter=True), nullable=True, index=True
)
audio_provider: orm.Mapped[AIProvider | None] = orm.relationship(
AIProvider,
foreign_keys="[AIProviderSettings.audio_provider_id]",
uselist=False,
post_update=True,
)
image_provider_id: orm.Mapped[GUID | None] = orm.mapped_column(
GUID, sa.ForeignKey("ai_providers.id", use_alter=True), nullable=True, index=True
)
image_provider: orm.Mapped[AIProvider | None] = orm.relationship(
AIProvider,
foreign_keys="[AIProviderSettings.image_provider_id]",
uselist=False,
post_update=True,
)
@auto_init()
def __init__(self, **_) -> None:
pass

View File

@@ -16,6 +16,7 @@ from ..household.mealplan import GroupMealPlan
from ..household.webhooks import GroupWebhooksModel
from ..recipe.category import Category, group_to_categories
from ..server.task import ServerTaskModel
from .ai_providers import AIProviderSettings
from .preferences import GroupPreferencesModel
if TYPE_CHECKING:
@@ -41,6 +42,8 @@ class Group(SqlAlchemyBase, BaseMixins):
invite_tokens: Mapped[list[GroupInviteToken]] = orm.relationship(
GroupInviteToken, back_populates="group", cascade="all, delete-orphan"
)
# Config
preferences: Mapped[GroupPreferencesModel] = orm.relationship(
GroupPreferencesModel,
back_populates="group",
@@ -48,6 +51,13 @@ class Group(SqlAlchemyBase, BaseMixins):
single_parent=True,
cascade="all, delete-orphan",
)
ai_provider_settings: Mapped[AIProviderSettings] = orm.relationship(
AIProviderSettings,
back_populates="group",
uselist=False,
single_parent=True,
cascade="all, delete-orphan",
)
# Recipes
recipes: Mapped[list["RecipeModel"]] = orm.relationship("RecipeModel", back_populates="group")
@@ -89,6 +99,7 @@ class Group(SqlAlchemyBase, BaseMixins):
"shopping_lists",
"cookbooks",
"preferences",
"ai_provider_settings",
"invite_tokens",
"mealplans",
"data_exports",

View File

@@ -0,0 +1,62 @@
import sqlalchemy as sa
from fastapi import HTTPException, status
from pydantic import UUID4
from mealie.db.models.group.ai_providers import AIProvider, AIProviderSettings
from mealie.schema.group.ai_providers import AIProviderCreate, AIProviderOut
from .repository_generic import GroupRepositoryGeneric
class GroupRepositoryAIProvider(GroupRepositoryGeneric[AIProviderOut, AIProvider]):
def create(self, data: AIProviderCreate | dict):
if isinstance(data, AIProviderCreate):
api_key = data.api_key
data = data.model_dump()
data["api_key"] = api_key
if not data.get("api_key"):
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="API key cannot be empty")
if not data.get("settings_id"):
data["settings_id"] = self.session.execute(
sa.select(AIProviderSettings.id).where(AIProviderSettings.group_id == self.group_id)
).scalar_one()
return super().create(data)
def update(self, match_value: str | int | UUID4, new_data: AIProviderCreate | dict):
if isinstance(new_data, AIProviderCreate):
new_data = new_data.model_dump()
# Merge existing API key into new data
if not new_data.get("api_key"):
existing = self.get_one(match_value)
if existing:
new_data["api_key"] = existing.api_key
return super().update(match_value, new_data)
def delete(self, value, match_key: str | None = None) -> AIProviderOut:
# Null out any settings references to this provider before deleting
self.session.execute(
sa.update(AIProviderSettings)
.where(AIProviderSettings.default_provider_id == value)
.values(default_provider_id=None)
)
self.session.execute(
sa.update(AIProviderSettings)
.where(AIProviderSettings.audio_provider_id == value)
.values(audio_provider_id=None)
)
self.session.execute(
sa.update(AIProviderSettings)
.where(AIProviderSettings.image_provider_id == value)
.values(image_provider_id=None)
)
# Delete
return super().delete(value, match_key)
def update_many(self, data):
raise NotImplementedError

View File

@@ -7,6 +7,7 @@ from sqlalchemy.orm import Session
from mealie.db.models._model_utils.guid import GUID
from mealie.db.models.group import Group, ReportEntryModel, ReportModel
from mealie.db.models.group.ai_providers import AIProvider, AIProviderSettings
from mealie.db.models.group.exports import GroupDataExportsModel
from mealie.db.models.group.preferences import GroupPreferencesModel
from mealie.db.models.household.cookbook import CookBook
@@ -37,12 +38,14 @@ from mealie.db.models.recipe.tool import Tool
from mealie.db.models.users import LongLiveToken, User
from mealie.db.models.users.password_reset import PasswordResetModel
from mealie.db.models.users.user_to_recipe import UserToRecipe
from mealie.repos.repository_ai_provider import GroupRepositoryAIProvider
from mealie.repos.repository_cookbooks import RepositoryCookbooks
from mealie.repos.repository_foods import RepositoryFood
from mealie.repos.repository_household import RepositoryHousehold, RepositoryHouseholdRecipes
from mealie.repos.repository_meal_plan_rules import RepositoryMealPlanRules
from mealie.repos.repository_units import RepositoryUnit
from mealie.schema.cookbook.cookbook import ReadCookBook
from mealie.schema.group.ai_providers import AIProviderOut, AIProviderSettingsOut
from mealie.schema.group.group_exports import GroupDataExport
from mealie.schema.group.group_preferences import ReadGroupPreferences
from mealie.schema.household.group_events import GroupEventNotifierOut
@@ -221,6 +224,16 @@ class AllRepositories:
def group_report_entries(self) -> GroupRepositoryGeneric[ReportEntryOut, ReportEntryModel]:
return GroupRepositoryGeneric(self.session, PK_ID, ReportEntryModel, ReportEntryOut, group_id=self.group_id)
@cached_property
def group_ai_provider_settings(self) -> GroupRepositoryGeneric[AIProviderSettingsOut, AIProviderSettings]:
return GroupRepositoryGeneric(
self.session, PK_GROUP_ID, AIProviderSettings, AIProviderSettingsOut, group_id=self.group_id
)
@cached_property
def group_ai_providers(self) -> GroupRepositoryAIProvider:
return GroupRepositoryAIProvider(self.session, PK_ID, AIProvider, AIProviderOut, group_id=self.group_id)
# ================================================================
# Household

View File

@@ -26,7 +26,7 @@ from mealie.schema.recipe.recipe import RecipePagination, RecipeSummary, create_
from mealie.schema.recipe.recipe_ingredient import IngredientFood
from mealie.schema.recipe.recipe_suggestion import RecipeSuggestionQuery, RecipeSuggestionResponseItem
from mealie.schema.recipe.recipe_tool import RecipeToolOut
from mealie.schema.response.pagination import OrderByNullPosition, OrderDirection, PaginationQuery
from mealie.schema.response.pagination import PaginationQuery
from mealie.services.query_filter.builder import QueryFilterBuilder
from ..db.models._model_base import SqlAlchemyBase
@@ -92,20 +92,6 @@ class RepositoryRecipes(HouseholdRepositoryGeneric[Recipe, RecipeModel]):
)
return sa.cast(effective_rating, sa.Float)
def add_order_attr_to_query(
self,
query: sa.Select,
order_attr: orm.InstrumentedAttribute,
order_dir: OrderDirection,
order_by_null: OrderByNullPosition | None,
) -> sa.Select:
# Sort recipe names by their normalized (accent-folded, lowercased) form so that
# accented and umlaut characters sort next to their base letter (e.g. "Über" near
# "U") instead of after "Z" by raw code point. See GH #6853.
if order_attr is RecipeModel.name:
order_attr = RecipeModel.name_normalized
return super().add_order_attr_to_query(query, order_attr, order_dir, order_by_null)
def create(self, document: Recipe) -> Recipe: # type: ignore
max_retries = 10
original_name: str = document.name # type: ignore

View File

@@ -438,7 +438,7 @@
"aliases": [],
"description": "",
"name": "brócoli tierno",
"plural_name": "tenderstem broccoli"
"plural_name": "brócoli tierno"
},
"plantain": {
"aliases": [],
@@ -484,7 +484,7 @@
},
"maize": {
"aliases": [
"corn husk"
"hoja de maíz"
],
"description": "",
"name": "maíz",
@@ -547,8 +547,8 @@
"microgreens": {
"aliases": [],
"description": "",
"name": "microgreens",
"plural_name": "microgreens"
"name": "microbrotes",
"plural_name": "microbrotes"
},
"boston lettuce": {
"aliases": [],
@@ -591,8 +591,8 @@
"cress": {
"aliases": [],
"description": "",
"name": "cress",
"plural_name": "cress"
"name": "brotes",
"plural_name": "brotes"
},
"broccoli slaw": {
"aliases": [],
@@ -1582,7 +1582,7 @@
"aliases": [],
"description": "",
"name": "amla",
"plural_name": "amlas"
"plural_name": "amla"
},
"elderberry": {
"aliases": [],

View File

@@ -64,7 +64,7 @@
"jalapeño": {
"aliases": [],
"description": "",
"name": "jalapeño",
"name": "jalapenho",
"plural_name": "jalapeños"
},
"avocado": {
@@ -621,7 +621,7 @@
"alfalfa": {
"aliases": [],
"description": "",
"name": "alfalfa",
"name": "alfafa",
"plural_name": "alfafas"
}
}
@@ -3888,7 +3888,7 @@
"aliases": [],
"description": "",
"name": "salame",
"plural_name": "salamis"
"plural_name": "salames"
},
"brisket": {
"aliases": [],
@@ -7182,8 +7182,8 @@
"fleur de sel": {
"aliases": [],
"description": "",
"name": "fleur de sel",
"plural_name": "fleur de sel"
"name": "Flor de sal",
"plural_name": "Flor de sal"
},
"thai red curry paste": {
"aliases": [],
@@ -8630,7 +8630,7 @@
"granola cereal": {
"aliases": [],
"description": "",
"name": "granola cereal",
"name": "Granola",
"plural_name": "granolas"
},
"bulgur": {
@@ -11024,7 +11024,7 @@
"cottonseed oil": {
"aliases": [],
"description": "",
"name": "cottonseed oil",
"name": "Óleo de algodão",
"plural_name": "óleo de algodão"
},
"pork dripping": {
@@ -14143,7 +14143,7 @@
"caramel candy": {
"aliases": [],
"description": "",
"name": "caramel candy",
"name": "balas",
"plural_name": "caramelos de caramelo"
},
"vanilla pudding": {
@@ -14173,13 +14173,13 @@
"peach preserve": {
"aliases": [],
"description": "",
"name": "peach preserve",
"name": "Conserva de pêssego",
"plural_name": "pearch preserves"
},
"chocolate wafer": {
"aliases": [],
"description": "",
"name": "chocolate wafer",
"name": "Wafer de chocolate",
"plural_name": "chocolate wafers"
},
"candied peel": {
@@ -14669,7 +14669,7 @@
"coconut rum": {
"aliases": [],
"description": "",
"name": "coconut rum",
"name": "Rum de coco",
"plural_name": "cocos rum"
},
"dessert wine": {
@@ -16087,7 +16087,7 @@
"fish oil": {
"aliases": [],
"description": "",
"name": "fish oil",
"name": "Óleo de peixe",
"plural_name": "óleo de peixe"
},
"lime essential oil": {

View File

@@ -1295,7 +1295,7 @@
"black fungu": {
"aliases": [],
"description": "",
"name": "black fungus",
"name": "svart träskinnsvampar",
"plural_name": "svart träskinnsvampar"
},
"black truffle": {
@@ -6250,7 +6250,7 @@
"aliases": [],
"description": "",
"name": "Starkt paprikapulver",
"plural_name": "hot paprika"
"plural_name": "Starkt paprikapulver"
},
"lemon thyme": {
"aliases": [],
@@ -6932,14 +6932,14 @@
"honey powder": {
"aliases": [],
"description": "",
"name": "honey powder",
"plural_name": "honey powder"
"name": "honungspulver",
"plural_name": "honungspulver"
},
"berry syrup": {
"aliases": [],
"description": "",
"name": "berry syrup",
"plural_name": "berry syrup"
"name": "bärsirap",
"plural_name": "bärsirap"
},
"grape syrup": {
"aliases": [],
@@ -7081,7 +7081,7 @@
"aliases": [],
"description": "",
"name": "Senapspulver",
"plural_name": "mustard powder"
"plural_name": "senapspulver"
},
"taco seasoning": {
"aliases": [],
@@ -7104,14 +7104,14 @@
"white miso": {
"aliases": [],
"description": "",
"name": "white miso",
"plural_name": "white misoes"
"name": "vit misopasta",
"plural_name": "vit misopasta"
},
"himalayan salt": {
"aliases": [],
"description": "",
"name": "Himalaya salt",
"plural_name": "himalayan salt"
"plural_name": "himalaya salt"
},
"lemon & pepper seasoning": {
"aliases": [],
@@ -7326,8 +7326,8 @@
"smoked salt": {
"aliases": [],
"description": "",
"name": "smoked salt",
"plural_name": "smoked salt"
"name": "rökt salt",
"plural_name": "rökt salt"
},
"dash seasoning": {
"aliases": [],

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