Compare commits

..

4 Commits

Author SHA1 Message Date
ShihChi Huang
413d082f90 test: cover queue display formatting 2026-06-23 23:58:10 -07:00
ShihChi Huang
3400438dfb test: cover fuse search ranking 2026-06-23 23:45:11 -07:00
ShihChi Huang
3088754bb8 test: fix coverage run 2026-06-23 23:41:54 -07:00
Simon Pinfold
f076106ca5 fix: expand grouped assets when downloading multi-selection on OSS backend (#13079)
*PR Created by the Glary-Bot Agent*

---

## Summary

When using the ComfyUI (non-cloud) backend, selecting a grouped asset
(`outputCount > 1`) and clicking Download only downloaded the
cover/preview image instead of every output in the group.

The cloud backend already handles this correctly by exporting a ZIP
server-side. The OSS path called `downloadFile(asset.preview_url, ...)`
once per selected `AssetItem` and never expanded grouped assets into
their individual outputs.

## Fix

In `useMediaAssetActions.downloadAssets`, when any selected asset has
`outputCount > 1` and we're on the OSS path, resolve each grouped asset
into its individual outputs via the existing `resolveOutputAssetItems`
utility and trigger one direct download per file. Non-grouped selections
keep the original single-shot behaviour. After expansion the file list
is deduplicated by `AssetItem.id` so a user who selects both an expanded
stack parent and one of its children does not download the child twice.
The success toast now reflects the actual number of files downloaded.

- Single asset, single output → unchanged (1 download).
- Multi-select of single-output assets → unchanged (N downloads).
- Any selection containing a grouped asset → expanded via
`resolveOutputAssetItems` (same code path the cloud ZIP and
stack-expansion UI use). If resolution returns nothing, falls back to
the preview download so the user still gets something.
- Grouped parent + one of its expanded children selected → deduped, no
double download.

## Tests

Added unit tests in `useMediaAssetActions.test.ts` for the OSS path:

- Expands a grouped asset into individual downloads.
- Mixes grouped and single-output assets in one selection.
- Falls back to the original asset when `resolveOutputAssetItems`
returns empty.
- Does not call `resolveOutputAssetItems` when no grouped assets are
selected.
- Deduplicates downloads when an expanded child is also selected
alongside its parent.
- Shows an error toast when resolution rejects.

All 40 tests in the file pass; all 508 tests under `src/platform/assets`
pass. `pnpm typecheck`, `pnpm exec eslint`, `pnpm exec oxfmt --check`
all clean.

## Manual verification
Tested against a `master` ComfyUI instance with default settings.
Not tested against cloud - feature is gated to non cloud

Performed by @synap5e 
<img width="448" height="618" alt="image"
src="https://github.com/user-attachments/assets/daf32fa0-c5ec-47ca-bab3-d5ea3fb3d7cc"
/>


https://github.com/user-attachments/assets/a87ae1aa-836f-4cbc-9ef7-a35ed4f94ee7


https://github.com/user-attachments/assets/49d833bf-7b4e-4c53-b0d5-f16ff2108185

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
2026-06-24 02:25:02 +00:00
18 changed files with 721 additions and 896 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@comfyorg/comfyui-frontend",
"version": "1.47.4",
"version": "1.47.3",
"private": true,
"description": "Official front-end implementation of ComfyUI",
"homepage": "https://comfy.org",

View File

@@ -8,12 +8,12 @@ export function useWorkflowStatusDismissal() {
const executionStore = useExecutionStore()
watch(
() => workflowStore.activeWorkflow,
(workflow) => {
if (
workflow &&
executionStore.getWorkflowStatus(workflow) !== 'running'
) {
() => {
const workflow = workflowStore.activeWorkflow
return [workflow, executionStore.getWorkflowStatus(workflow)] as const
},
([workflow, status]) => {
if (workflow && status !== undefined && status !== 'running') {
executionStore.clearWorkflowStatus(workflow)
}
},

View File

@@ -26,6 +26,7 @@ vi.mock('@/scripts/app', () => ({
}))
vi.mock('@/extensions/core/load3d', () => ({}))
vi.mock('@/extensions/core/load3dAdvanced', () => ({}))
vi.mock('@/extensions/core/load3dPreviewExtensions', () => ({}))
vi.mock('@/extensions/core/saveMesh', () => ({}))

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "رمز API الخاص بك لا يملك صلاحية الوصول إلى هذا المورد. يرجى التحقق من أذونات الرمز.",
"errorUserTokenInvalid": "رمز API المخزن غير صالح أو منتهي الصلاحية. يرجى تحديث الرمز في الإعدادات.",
"failedToCreateNode": "فشل إنشاء العقدة. يرجى المحاولة مرة أخرى أو التحقق من وحدة التحكم للحصول على التفاصيل.",
"failedToSetModelValue": "تمت إضافة العقدة، ولكن لم يتم تعيين النموذج تلقائيًا. تحقق من وحدة التحكم لمزيد من التفاصيل.",
"fileFormats": "تنسيقات الملفات",
"fileName": "اسم الملف",
"fileSize": "حجم الملف",
@@ -242,8 +241,7 @@
"auth/user-not-found": "لم يتم العثور على حساب بهذا البريد الإلكتروني. هل ترغب في إنشاء حساب جديد؟",
"auth/weak-password": "كلمة المرور ضعيفة جداً. يرجى استخدام كلمة مرور أقوى تحتوي على 6 أحرف على الأقل.",
"auth/wrong-password": "كلمة المرور التي أدخلتها غير صحيحة. يرجى المحاولة مرة أخرى.",
"generic": "حدث خطأ أثناء تسجيل الدخول. يرجى المحاولة مرة أخرى.",
"signupBlocked": "تعذر إنشاء حسابك في الوقت الحالي. يرجى المحاولة مرة أخرى لاحقًا. إذا استمرت المشكلة، راسل الدعم على support@comfy.org."
"generic": "حدث خطأ أثناء تسجيل الدخول. يرجى المحاولة مرة أخرى."
},
"login": {
"andText": "و",
@@ -757,13 +755,6 @@
"creditsAvailable": "الرصيد المتاح",
"details": "التفاصيل",
"eventType": "نوع الحدث",
"eventTypes": {
"accountCreated": "تم إنشاء الحساب",
"apiNodeUsage": "استخدام عقدة الشريك",
"apiUsage": "استخدام API",
"creditAdded": "تمت إضافة الأرصدة",
"gpuUsage": "استخدام GPU"
},
"faqs": "الأسئلة المتكررة",
"invoiceHistory": "تاريخ الفواتير",
"lastUpdated": "آخر تحديث",
@@ -3711,27 +3702,6 @@
"customLoRAsLabel": "استيراد LoRAs الخاصة بك",
"description": "اختر الخطة الأنسب لك",
"descriptionWorkspace": "اختر أفضل خطة لمساحة العمل الخاصة بك",
"downgrade": {
"body": "سيتم إزالة جميع الأعضاء الآخرين من مساحة العمل هذه فورًا.",
"confirm": "تغيير الخطة",
"confirmationPhrase": "أنا أفهم",
"confirmationPrompt": "اكتب \"{phrase}\" للتأكيد.",
"failed": "فشل في تغيير الخطة",
"failedAfterMemberRemoval": "تمت إزالة أعضاء الفريق، لكن لم يكتمل تغيير الخطة — يرجى المحاولة مرة أخرى أو التواصل مع الدعم",
"memberRemovalFailed": "تعذر إزالة {email} من الفريق — قد يكون بعض الأعضاء قد أزيلوا بالفعل ولم يتم تغيير خطتك",
"notAllowed": "هذا التغيير في الخطة غير متاح",
"paymentMethodRequired": "مطلوب وسيلة دفع لتغيير الخطط",
"paymentPageBlocked": "تعذر فتح صفحة الدفع — يرجى المحاولة مرة أخرى",
"title": "التغيير إلى خطة {plan}؟"
},
"enterprise": {
"cta": "اعرف المزيد",
"flexibility": "تبحث عن مزيد من المرونة أو ميزات مخصصة؟",
"name": "المؤسسات",
"needMoreMembers": "تحتاج إلى مزيد من الأعضاء؟",
"reachOut": "تواصل معنا ولنحدد موعدًا للحديث."
},
"everythingInPlus": "كل ما في {plan}، بالإضافة إلى:",
"expiresDate": "ينتهي في {date}",
"freeTier": {
"description": "تشمل خطتك المجانية {credits} رصيد شهري لتجربة Comfy Cloud.",
@@ -3778,7 +3748,6 @@
"messageSupport": "مراسلة الدعم",
"monthly": "شهري",
"monthlyBonusDescription": "مكافأة الرصيد الشهرية",
"monthlyCredits": "أرصدة شهرية",
"monthlyCreditsInfo": "يتم تحديث هذا الرصيد شهريًا ولا ينتقل للشهر التالي",
"monthlyCreditsLabel": "الرصيد الشهري",
"monthlyCreditsPerMemberLabel": "الرصيد الشهري / عضو",
@@ -3791,13 +3760,7 @@
"partnerNodesCredits": "رصيد العقد الشريكة",
"partnerNodesDescription": "لتشغيل النماذج التجارية/المملوكة",
"perMonth": "دولار أمريكي / شهر",
"personalHeader": "الخطط الشخصية مخصصة للاستخدام الفردي فقط. {action}",
"personalHeaderAction": "لإضافة زملاء، اشترك في خطة الفريق.",
"personalWorkspace": "مساحة العمل الشخصية",
"planScope": {
"personal": "للاستخدام الشخصي",
"team": "للفرق"
},
"plansAndPricing": "الخطط والأسعار",
"plansForWorkspace": "الخطط لمساحة العمل {workspace}",
"prepaidCreditsInfo": "رصيد تم شراؤه بشكل منفصل ولا ينتهي صلاحيته",
@@ -3808,10 +3771,8 @@
"confirm": "تأكيد",
"confirmPayment": "تأكيد الدفع",
"confirmPlanChange": "تأكيد تغيير الخطة",
"creditsRefillTo": "إعادة تعبئة الأرصدة إلى",
"eachMonthCreditsRefill": "يتم إعادة تعبئة الرصيد كل شهر إلى",
"ends": "ينتهي في {date}",
"everyMonthStarting": "كل شهر ابتداءً من {date}",
"hideFeatures": "إخفاء الميزات",
"nextPaymentDue": "الدفع القادم مستحق في {date}. يمكنك الإلغاء في أي وقت.",
"perMember": "/ عضو",
@@ -3821,18 +3782,10 @@
"showMoreFeatures": "عرض المزيد من الميزات",
"starting": "يبدأ في {date}",
"startingToday": "يبدأ اليوم",
"subscribeToPlan": "اشترك في خطة {plan}",
"switchToPlan": "التبديل إلى خطة {plan}",
"terms": "الشروط",
"termsAgreement": "بالمتابعة، أنت توافق على {terms} و{privacy} الخاصة بـ Comfy Org.",
"totalDueToday": "الإجمالي المستحق اليوم",
"youllBeCharged": "سيتم خصم"
"totalDueToday": "الإجمالي المستحق اليوم"
},
"pricingBlurb": "*استنادًا إلى هذا القالب، {seeDetails}. تواصل معنا لـ {questions} أو {enterpriseDiscussions}. لمزيد من تفاصيل التسعير، {clickHere}.",
"pricingBlurbClickHere": "اضغط هنا",
"pricingBlurbEnterprise": "مناقشات المؤسسات",
"pricingBlurbQuestions": "الاستفسارات",
"pricingBlurbSeeDetails": "اعرف التفاصيل",
"refreshCredits": "تحديث الرصيد",
"renewsDate": "تجديد في {date}",
"required": {
@@ -3846,8 +3799,6 @@
"resubscribe": "إعادة الاشتراك",
"resubscribeSuccess": "تمت إعادة تفعيل الاشتراك بنجاح",
"resubscribeTo": "إعادة الاشتراك في {plan}",
"saveYearly": "وفر ٢٠٪",
"saveYearlyUpTo": "وفر حتى ٢٠٪",
"soloUseOnly": "للاستخدام الفردي فقط",
"subscribeForMore": "ترقية",
"subscribeNow": "اشترك الآن",
@@ -3856,34 +3807,10 @@
"subscribeToRun": "اشتراك",
"subscribeToRunFull": "الاشتراك للتشغيل",
"subscriptionRequiredMessage": "الاشتراك مطلوب للأعضاء لتشغيل سير العمل على السحابة",
"success": {
"allSet": "تم كل شيء بنجاح",
"planUpdated": "تم تحديث خطتك بنجاح.",
"receiptEmailed": "تم إرسال إيصال إلى بريدك الإلكتروني."
},
"teamHeader": "للفرق التي ترغب في التعاون. تحتاج إلى مزيد من الأعضاء؟ {learnMore} حول المؤسسات.",
"teamHeaderLearnMore": "اعرف المزيد",
"teamPlan": {
"changePlan": "تغيير الخطة",
"checkoutComingSoon": "الدفع لخطة الفريق قادم قريبًا.",
"comingSoonLabel": "قريبًا:",
"cta": "اشترك في خطة الفريق السنوية",
"ctaMonthly": "اشترك في خطة الفريق الشهرية",
"currentPlan": "الخطة الحالية",
"detailsTitle": "التفاصيل",
"name": "خطة الفريق",
"perkConcurrentRuns": "يمكن للأعضاء تشغيل سير العمل في نفس الوقت",
"perkInviteMembers": "دعوة أعضاء الفريق",
"perkProjectAssets": "إدارة المشاريع والأصول",
"perkRolePermissions": "أذونات حسب الدور",
"perkSharedPool": "رصيد مشترك لجميع الأعضاء",
"tagline": "اختر اشتراك الأرصدة الشهري الخاص بك. احصل على خصم أكبر مع اشتراك أرصدة أكبر."
},
"teamWorkspace": "مساحة عمل الفريق",
"tierNameYearly": "{name} سنوي",
"tiers": {
"creator": {
"feature1": "استيراد نماذجك الخاصة",
"name": "المُبدع"
},
"founder": {
@@ -3893,12 +3820,9 @@
"name": "مجاني"
},
"pro": {
"feature1": "مدة تشغيل سير العمل أطول (حتى ساعة واحدة)",
"name": "احترافي"
},
"standard": {
"feature1": "مدة تشغيل سير العمل القصوى ٣٠ دقيقة",
"feature2": "إضافة المزيد من الأرصدة في أي وقت",
"name": "قياسي"
}
},
@@ -3911,7 +3835,6 @@
"upgradeToAddCredits": "قم بالترقية لإضافة أرصدة",
"usdPerMonth": "دولار أمريكي / شهريًا",
"usdPerMonthPerMember": "دولار أمريكي / شهر / عضو",
"videoEstimate": "ينتج تقريبًا ~{count} فيديوهات لمدة ٥ ثوانٍ*",
"videoEstimateExplanation": "هذه التقديرات مبنية على قالب Wan 2.2 لتحويل الصورة إلى فيديو باستخدام الإعدادات الافتراضية (5 ثوانٍ، 640x640، 16 إطار/ثانية، 4 خطوات أخذ عينات).",
"videoEstimateHelp": "مزيد من التفاصيل حول هذا القالب",
"videoEstimateLabel": "العدد التقريبي لمقاطع الفيديو 5 ثوانٍ التي يتم إنشاؤها باستخدام قالب Wan 2.2 لتحويل الصورة إلى فيديو",
@@ -3921,7 +3844,6 @@
"viewMoreDetails": "عرض المزيد من التفاصيل",
"viewMoreDetailsPlans": "عرض المزيد من التفاصيل حول الخطط والأسعار",
"viewUsageHistory": "عرض سجل الاستخدام",
"whatsIncluded": "ما يتضمنه:",
"workspaceNotSubscribed": "هذه مساحة العمل ليست مشتركة",
"yearly": "سنوي",
"yearlyCreditsLabel": "إجمالي الرصيد السنوي",

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "Tu token de API no tiene acceso a este recurso. Por favor, revisa los permisos de tu token.",
"errorUserTokenInvalid": "Tu token de API almacenado no es válido o ha expirado. Por favor, actualiza tu token en la configuración.",
"failedToCreateNode": "No se pudo crear el nodo. Inténtalo de nuevo o revisa la consola para más detalles.",
"failedToSetModelValue": "Nodo añadido, pero no se pudo establecer su modelo automáticamente. Consulta la consola para más detalles.",
"fileFormats": "Formatos de archivo",
"fileName": "Nombre del archivo",
"fileSize": "Tamaño del archivo",
@@ -242,8 +241,7 @@
"auth/user-not-found": "No se encontró ninguna cuenta con este correo electrónico. ¿Te gustaría crear una nueva cuenta?",
"auth/weak-password": "La contraseña es demasiado débil. Por favor, usa una contraseña más segura con al menos 6 caracteres.",
"auth/wrong-password": "La contraseña que ingresaste es incorrecta. Por favor, inténtalo de nuevo.",
"generic": "Ocurrió un error al iniciar sesión. Por favor, inténtalo de nuevo.",
"signupBlocked": "No pudimos crear tu cuenta en este momento. Por favor, inténtalo de nuevo más tarde. Si el problema persiste, envía un correo a support@comfy.org."
"generic": "Ocurrió un error al iniciar sesión. Por favor, inténtalo de nuevo."
},
"login": {
"andText": "y",
@@ -757,13 +755,6 @@
"creditsAvailable": "Créditos disponibles",
"details": "Detalles",
"eventType": "Tipo de evento",
"eventTypes": {
"accountCreated": "Cuenta creada",
"apiNodeUsage": "Uso de nodo asociado",
"apiUsage": "Uso de API",
"creditAdded": "Créditos añadidos",
"gpuUsage": "Uso de GPU"
},
"faqs": "Preguntas frecuentes",
"invoiceHistory": "Historial de facturas",
"lastUpdated": "Última actualización",
@@ -3711,27 +3702,6 @@
"customLoRAsLabel": "Importa tus propios LoRAs",
"description": "Elige el mejor plan para ti",
"descriptionWorkspace": "Elige el mejor plan para tu espacio de trabajo",
"downgrade": {
"body": "Todos los demás miembros de este espacio de trabajo serán eliminados inmediatamente.",
"confirm": "Cambiar plan",
"confirmationPhrase": "Entiendo",
"confirmationPrompt": "Escribe \"{phrase}\" para confirmar.",
"failed": "No se pudo cambiar el plan",
"failedAfterMemberRemoval": "Los miembros del equipo fueron eliminados, pero el cambio de plan no se completó — por favor, inténtalo de nuevo o contacta con soporte",
"memberRemovalFailed": "No se pudo eliminar a {email} del equipo — algunos miembros pueden haber sido eliminados y tu plan no fue cambiado",
"notAllowed": "Este cambio de plan no está disponible",
"paymentMethodRequired": "Se requiere un método de pago para cambiar de plan",
"paymentPageBlocked": "No se pudo abrir la página de pago — por favor, inténtalo de nuevo",
"title": "¿Cambiar al plan {plan}?"
},
"enterprise": {
"cta": "Saber más",
"flexibility": "¿Buscas más flexibilidad o funciones personalizadas?",
"name": "Enterprise",
"needMoreMembers": "¿Necesitas más miembros?",
"reachOut": "Contáctanos y agendemos una charla."
},
"everythingInPlus": "Todo en {plan}, además:",
"expiresDate": "Caduca el {date}",
"freeTier": {
"description": "Tu plan gratuito incluye {credits} créditos cada mes para probar Comfy Cloud.",
@@ -3778,7 +3748,6 @@
"messageSupport": "Contactar con soporte",
"monthly": "Mensual",
"monthlyBonusDescription": "Bono de créditos mensual",
"monthlyCredits": "créditos mensuales",
"monthlyCreditsInfo": "Estos créditos se renuevan mensualmente y no se acumulan",
"monthlyCreditsLabel": "Créditos mensuales",
"monthlyCreditsPerMemberLabel": "Créditos mensuales / miembro",
@@ -3791,13 +3760,7 @@
"partnerNodesCredits": "Créditos de Nodos de Socio",
"partnerNodesDescription": "Para ejecutar modelos comerciales/propietarios",
"perMonth": "USD / mes",
"personalHeader": "Los planes personales son solo para uso individual. {action}",
"personalHeaderAction": "Para agregar compañeros, suscríbete al plan de equipo.",
"personalWorkspace": "Espacio de trabajo personal",
"planScope": {
"personal": "Para uso personal",
"team": "Para equipos"
},
"plansAndPricing": "Planes y precios",
"plansForWorkspace": "Planes para {workspace}",
"prepaidCreditsInfo": "Créditos comprados por separado que no expiran",
@@ -3808,10 +3771,8 @@
"confirm": "Confirmar",
"confirmPayment": "Confirma tu pago",
"confirmPlanChange": "Confirma el cambio de plan",
"creditsRefillTo": "Los créditos se recargan hasta",
"eachMonthCreditsRefill": "Cada mes los créditos se recargan a",
"ends": "Finaliza el {date}",
"everyMonthStarting": "Cada mes a partir de {date}",
"hideFeatures": "Ocultar funciones",
"nextPaymentDue": "Próximo pago el {date}. Cancela en cualquier momento.",
"perMember": "/ miembro",
@@ -3821,18 +3782,10 @@
"showMoreFeatures": "Mostrar más funciones",
"starting": "Comienza el {date}",
"startingToday": "Comienza hoy",
"subscribeToPlan": "Suscribirse al plan {plan}",
"switchToPlan": "Cambiar al plan {plan}",
"terms": "Términos",
"termsAgreement": "Al continuar, aceptas los {terms} y la {privacy} de Comfy Org.",
"totalDueToday": "Total a pagar hoy",
"youllBeCharged": "Se te cobrará"
"totalDueToday": "Total a pagar hoy"
},
"pricingBlurb": "*Basado en esta plantilla, {seeDetails}. Contáctanos para {questions} o {enterpriseDiscussions}. Para más detalles sobre precios, {clickHere}.",
"pricingBlurbClickHere": "haz clic aquí",
"pricingBlurbEnterprise": "discusiones enterprise",
"pricingBlurbQuestions": "preguntas",
"pricingBlurbSeeDetails": "ver detalles",
"refreshCredits": "Actualizar créditos",
"renewsDate": "Se renueva el {date}",
"required": {
@@ -3846,8 +3799,6 @@
"resubscribe": "Volver a suscribirse",
"resubscribeSuccess": "¡Suscripción reactivada correctamente!",
"resubscribeTo": "Volver a suscribirse a {plan}",
"saveYearly": "Ahorra 20%",
"saveYearlyUpTo": "Ahorra hasta un 20%",
"soloUseOnly": "Solo para uso individual",
"subscribeForMore": "Mejorar",
"subscribeNow": "Suscribirse Ahora",
@@ -3856,34 +3807,10 @@
"subscribeToRun": "Suscribirse",
"subscribeToRunFull": "Suscribirse a Ejecutar",
"subscriptionRequiredMessage": "Se requiere una suscripción para que los miembros ejecuten flujos de trabajo en la nube",
"success": {
"allSet": "Todo listo",
"planUpdated": "Tu plan se ha actualizado correctamente.",
"receiptEmailed": "Se ha enviado un recibo a tu correo electrónico."
},
"teamHeader": "Para equipos que desean colaborar. ¿Necesitas más miembros? {learnMore} sobre enterprise.",
"teamHeaderLearnMore": "Saber más",
"teamPlan": {
"changePlan": "Cambiar plan",
"checkoutComingSoon": "El pago del plan de equipo estará disponible próximamente.",
"comingSoonLabel": "Próximamente:",
"cta": "Suscribirse al plan anual de equipo",
"ctaMonthly": "Suscribirse al plan mensual de equipo",
"currentPlan": "Plan actual",
"detailsTitle": "Detalles",
"name": "Plan de equipo",
"perkConcurrentRuns": "Los miembros pueden ejecutar flujos de trabajo simultáneamente",
"perkInviteMembers": "Invita a miembros del equipo",
"perkProjectAssets": "Gestión de proyectos y recursos",
"perkRolePermissions": "Permisos basados en roles",
"perkSharedPool": "Bolsa de créditos compartida para todos los miembros",
"tagline": "Elige tu propia suscripción mensual de créditos. Obtén un mayor descuento con una suscripción de más créditos."
},
"teamWorkspace": "Espacio de trabajo en equipo",
"tierNameYearly": "{name} Anual",
"tiers": {
"creator": {
"feature1": "Importa tus propios modelos",
"name": "Creador"
},
"founder": {
@@ -3893,12 +3820,9 @@
"name": "Gratis"
},
"pro": {
"feature1": "Mayor tiempo de ejecución de flujo de trabajo (hasta 1 hora)",
"name": "Pro"
},
"standard": {
"feature1": "Tiempo máximo de ejecución de flujo de trabajo de 30 minutos",
"feature2": "Agrega más créditos en cualquier momento",
"name": "Estándar"
}
},
@@ -3911,7 +3835,6 @@
"upgradeToAddCredits": "Mejorar para añadir créditos",
"usdPerMonth": "USD / mes",
"usdPerMonthPerMember": "USD / mes / miembro",
"videoEstimate": "Genera ~{count} videos de 5s*",
"videoEstimateExplanation": "Estas estimaciones se basan en la plantilla Wan 2.2 Imagen a Video usando la configuración predeterminada (5 segundos, 640x640, 16fps, muestreo de 4 pasos).",
"videoEstimateHelp": "Más detalles sobre esta plantilla",
"videoEstimateLabel": "Cantidad aprox. de videos de 5s generados con la plantilla Wan 2.2 Imagen a Video",
@@ -3921,7 +3844,6 @@
"viewMoreDetails": "Ver más detalles",
"viewMoreDetailsPlans": "Ver más detalles sobre planes y precios",
"viewUsageHistory": "Ver historial de uso",
"whatsIncluded": "Qué incluye:",
"workspaceNotSubscribed": "Este espacio de trabajo no tiene una suscripción",
"yearly": "Anual",
"yearlyCreditsLabel": "Total de créditos anuales",

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "توکن API شما به این منبع دسترسی ندارد. لطفاً مجوزهای توکن خود را بررسی کنید.",
"errorUserTokenInvalid": "توکن API ذخیره‌شده شما نامعتبر یا منقضی شده است. لطفاً توکن خود را در تنظیمات به‌روزرسانی کنید.",
"failedToCreateNode": "ایجاد node ناموفق بود. لطفاً دوباره تلاش کنید یا کنسول را بررسی کنید.",
"failedToSetModelValue": "نود اضافه شد، اما مدل آن به‌صورت خودکار تنظیم نشد. برای جزئیات بیشتر، کنسول را بررسی کنید.",
"fileFormats": "فرمت‌های فایل",
"fileName": "نام فایل",
"fileSize": "اندازه فایل",
@@ -242,8 +241,7 @@
"auth/user-not-found": "حسابی با این ایمیل یافت نشد. مایل به ایجاد حساب جدید هستید؟",
"auth/weak-password": "رمز عبور خیلی ضعیف است. لطفاً از رمز عبور قوی‌تر با حداقل ۶ کاراکتر استفاده کنید.",
"auth/wrong-password": "رمز عبور وارد شده نادرست است. لطفاً دوباره تلاش کنید.",
"generic": "در ورود شما مشکلی پیش آمد. لطفاً دوباره تلاش کنید.",
"signupBlocked": "در حال حاضر امکان ایجاد حساب کاربری وجود ندارد. لطفاً بعداً دوباره تلاش کنید. اگر این مشکل ادامه داشت، با آدرس support@comfy.org تماس بگیرید."
"generic": "در ورود شما مشکلی پیش آمد. لطفاً دوباره تلاش کنید."
},
"login": {
"andText": "و",
@@ -757,13 +755,6 @@
"creditsAvailable": "اعتبار موجود",
"details": "جزئیات",
"eventType": "نوع رویداد",
"eventTypes": {
"accountCreated": "حساب کاربری ایجاد شد",
"apiNodeUsage": "استفاده از node شریک",
"apiUsage": "استفاده از API",
"creditAdded": "اعتبار افزوده شد",
"gpuUsage": "استفاده از GPU"
},
"faqs": "سؤالات متداول",
"invoiceHistory": "تاریخچه فاکتورها",
"lastUpdated": "آخرین به‌روزرسانی",
@@ -3723,27 +3714,6 @@
"customLoRAsLabel": "LoRAهای خود را وارد کنید",
"description": "بهترین طرح را برای خود انتخاب کنید",
"descriptionWorkspace": "بهترین طرح را برای فضای کاری خود انتخاب کنید",
"downgrade": {
"body": "تمام اعضای دیگر این workspace بلافاصله حذف خواهند شد.",
"confirm": "تغییر پلن",
"confirmationPhrase": "متوجه شدم",
"confirmationPrompt": "برای تأیید، عبارت «{phrase}» را وارد کنید.",
"failed": "تغییر پلن انجام نشد",
"failedAfterMemberRemoval": "اعضای تیم حذف شدند، اما تغییر پلن کامل نشد — لطفاً دوباره تلاش کنید یا با پشتیبانی تماس بگیرید",
"memberRemovalFailed": "امکان حذف {email} از تیم وجود ندارد — برخی اعضا ممکن است قبلاً حذف شده باشند و پلن شما تغییر نکرده است",
"notAllowed": "این تغییر پلن در دسترس نیست",
"paymentMethodRequired": "برای تغییر پلن، روش پرداخت لازم است",
"paymentPageBlocked": "امکان باز کردن صفحه پرداخت وجود ندارد — لطفاً دوباره تلاش کنید",
"title": "تغییر به پلن {plan}؟"
},
"enterprise": {
"cta": "بیشتر بدانید",
"flexibility": "به دنبال امکانات یا انعطاف‌پذیری بیشتر هستید؟",
"name": "سازمانی",
"needMoreMembers": "به اعضای بیشتری نیاز دارید؟",
"reachOut": "با ما تماس بگیرید تا جلسه‌ای هماهنگ کنیم."
},
"everythingInPlus": "همه امکانات {plan}، به‌علاوه:",
"expiresDate": "انقضا در {date}",
"freeTier": {
"description": "طرح رایگان شما شامل {credits} اعتبار در هر ماه برای استفاده از Comfy Cloud است.",
@@ -3790,7 +3760,6 @@
"messageSupport": "پیام به پشتیبانی",
"monthly": "ماهانه",
"monthlyBonusDescription": "پاداش ماهانه اعتبار",
"monthlyCredits": "اعتبار ماهانه",
"monthlyCreditsInfo": "این اعتبارها هر ماه شارژ می‌شوند و منتقل نمی‌شوند",
"monthlyCreditsLabel": "اعتبار ماهانه",
"monthlyCreditsPerMemberLabel": "اعتبار ماهانه / هر عضو",
@@ -3803,13 +3772,7 @@
"partnerNodesCredits": "قیمت‌گذاری Partner Nodes",
"partnerNodesDescription": "برای اجرای مدل‌های تجاری/اختصاصی",
"perMonth": "/ ماه",
"personalHeader": "پلن‌های شخصی فقط برای استفاده فردی هستند. {action}",
"personalHeaderAction": "برای افزودن اعضا، به پلن تیمی ارتقا دهید.",
"personalWorkspace": "فضای کاری شخصی",
"planScope": {
"personal": "برای استفاده شخصی",
"team": "برای تیم‌ها"
},
"plansAndPricing": "طرح‌ها و قیمت‌ها",
"plansForWorkspace": "طرح‌ها برای {workspace}",
"prepaidCreditsInfo": "اعتبارهای پیش‌پرداخت تا یک سال پس از تاریخ خرید منقضی می‌شوند.",
@@ -3820,10 +3783,8 @@
"confirm": "تأیید",
"confirmPayment": "تأیید پرداخت",
"confirmPlanChange": "تأیید تغییر طرح",
"creditsRefillTo": "اعتبارها تا این مقدار شارژ می‌شوند",
"eachMonthCreditsRefill": "هر ماه اعتبار به این مقدار بازنشانی می‌شود",
"ends": "پایان در {date}",
"everyMonthStarting": "هر ماه از {date} شروع می‌شود",
"hideFeatures": "مخفی کردن امکانات",
"nextPaymentDue": "پرداخت بعدی در {date}. هر زمان می‌توانید لغو کنید.",
"perMember": "/ هر عضو",
@@ -3833,18 +3794,10 @@
"showMoreFeatures": "نمایش امکانات بیشتر",
"starting": "شروع از {date}",
"startingToday": "شروع از امروز",
"subscribeToPlan": "اشتراک پلن {plan}",
"switchToPlan": "تغییر به پلن {plan}",
"terms": "شرایط",
"termsAgreement": "با ادامه، شما با {terms} و {privacy} Comfy Org موافقت می‌کنید.",
"totalDueToday": "مبلغ قابل پرداخت امروز",
"youllBeCharged": "شما مبلغ زیر را پرداخت خواهید کرد"
"totalDueToday": "مبلغ قابل پرداخت امروز"
},
"pricingBlurb": "*بر اساس این قالب، {seeDetails}. برای {questions} یا {enterpriseDiscussions} با ما تماس بگیرید. برای جزئیات بیشتر قیمت‌گذاری، {clickHere}.",
"pricingBlurbClickHere": "اینجا کلیک کنید",
"pricingBlurbEnterprise": "بحث‌های سازمانی",
"pricingBlurbQuestions": "سوالات",
"pricingBlurbSeeDetails": "جزئیات را ببینید",
"refreshCredits": "به‌روزرسانی اعتبارها",
"renewsDate": "تمدید در {date}",
"required": {
@@ -3858,8 +3811,6 @@
"resubscribe": "تمدید اشتراک",
"resubscribeSuccess": "اشتراک با موفقیت فعال شد",
"resubscribeTo": "تمدید اشتراک {plan}",
"saveYearly": "صرفه‌جویی ۲۰٪",
"saveYearlyUpTo": "تا ۲۰٪ صرفه‌جویی",
"soloUseOnly": "فقط برای استفاده فردی",
"subscribeForMore": "ارتقاء",
"subscribeNow": "هم‌اکنون اشتراک بگیرید",
@@ -3868,34 +3819,10 @@
"subscribeToRun": "اشتراک",
"subscribeToRunFull": "اشتراک برای اجرا",
"subscriptionRequiredMessage": "برای اجرای workflowها در Cloud، اشتراک لازم است.",
"success": {
"allSet": "همه چیز آماده است",
"planUpdated": "پلن شما با موفقیت به‌روزرسانی شد.",
"receiptEmailed": "رسید به ایمیل شما ارسال شد."
},
"teamHeader": "برای تیم‌هایی که قصد همکاری دارند. به اعضای بیشتری نیاز دارید؟ {learnMore} درباره پلن سازمانی.",
"teamHeaderLearnMore": "بیشتر بدانید",
"teamPlan": {
"changePlan": "تغییر پلن",
"checkoutComingSoon": "پرداخت پلن تیمی به‌زودی فعال می‌شود.",
"comingSoonLabel": "به‌زودی:",
"cta": "اشتراک سالانه تیمی",
"ctaMonthly": "اشتراک ماهانه تیمی",
"currentPlan": "پلن فعلی",
"detailsTitle": "جزئیات",
"name": "پلن تیمی",
"perkConcurrentRuns": "امکان اجرای همزمان workflow برای اعضا",
"perkInviteMembers": "دعوت از اعضای تیم",
"perkProjectAssets": "مدیریت پروژه و دارایی‌ها",
"perkRolePermissions": "دسترسی مبتنی بر نقش",
"perkSharedPool": "استخر اعتبار مشترک برای همه اعضا",
"tagline": "اشتراک ماهانه اعتبار را خودتان انتخاب کنید. با اعتبار بیشتر، تخفیف بیشتری دریافت کنید."
},
"teamWorkspace": "فضای کاری تیمی",
"tierNameYearly": "{name} سالانه",
"tiers": {
"creator": {
"feature1": "امکان وارد کردن مدل‌های شخصی",
"name": "خالق"
},
"founder": {
@@ -3905,12 +3832,9 @@
"name": "رایگان"
},
"pro": {
"feature1": "زمان اجرای workflow طولانی‌تر (تا ۱ ساعت)",
"name": "حرفه‌ای"
},
"standard": {
"feature1": "حداکثر زمان اجرای workflow: ۳۰ دقیقه",
"feature2": "امکان افزودن اعتبار بیشتر در هر زمان",
"name": "استاندارد"
}
},
@@ -3923,7 +3847,6 @@
"upgradeToAddCredits": "برای افزودن اعتبار ارتقاء دهید",
"usdPerMonth": "دلار آمریکا / ماه",
"usdPerMonthPerMember": "دلار آمریکا / ماه / هر عضو",
"videoEstimate": "تولید حدود ~{count} ویدیو ۵ ثانیه‌ای*",
"videoEstimateExplanation": "این تخمین‌ها بر اساس قالب Wan 2.2 Image-to-Video با تنظیمات پیش‌فرض (۵ ثانیه، ۶۴۰×۶۴۰، ۱۶ فریم بر ثانیه، ۴ مرحله نمونه‌گیری) است.",
"videoEstimateHelp": "جزئیات بیشتر درباره این قالب",
"videoEstimateLabel": "تخمین تعداد ویدیوهای ۵ ثانیه‌ای تولید شده با قالب Wan 2.2 Image-to-Video",
@@ -3933,7 +3856,6 @@
"viewMoreDetails": "مشاهده جزئیات بیشتر",
"viewMoreDetailsPlans": "مشاهده جزئیات بیشتر درباره طرح‌ها و قیمت‌ها",
"viewUsageHistory": "مشاهده تاریخچه استفاده",
"whatsIncluded": "شامل موارد زیر:",
"workspaceNotSubscribed": "این محیط کاری اشتراک فعال ندارد",
"yearly": "سالانه",
"yearlyCreditsLabel": "کل اعتبار سالانه",

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "Votre jeton API na pas accès à cette ressource. Veuillez vérifier les autorisations de votre jeton.",
"errorUserTokenInvalid": "Votre jeton API enregistré est invalide ou expiré. Veuillez le mettre à jour dans les paramètres.",
"failedToCreateNode": "Échec de la création du nœud. Veuillez réessayer ou consulter la console pour plus de détails.",
"failedToSetModelValue": "Nœud ajouté, mais son modèle na pas pu être défini automatiquement. Consultez la console pour plus de détails.",
"fileFormats": "Formats de fichier",
"fileName": "Nom du fichier",
"fileSize": "Taille du fichier",
@@ -242,8 +241,7 @@
"auth/user-not-found": "Aucun compte trouvé avec cette adresse e-mail. Souhaitez-vous créer un nouveau compte ?",
"auth/weak-password": "Le mot de passe est trop faible. Veuillez utiliser un mot de passe plus fort avec au moins 6 caractères.",
"auth/wrong-password": "Le mot de passe que vous avez saisi est incorrect. Veuillez réessayer.",
"generic": "Une erreur s'est produite lors de votre connexion. Veuillez réessayer.",
"signupBlocked": "Nous ne pouvons pas créer votre compte pour le moment. Veuillez réessayer plus tard. Si le problème persiste, contactez support@comfy.org."
"generic": "Une erreur s'est produite lors de votre connexion. Veuillez réessayer."
},
"login": {
"andText": "et",
@@ -757,13 +755,6 @@
"creditsAvailable": "Crédits disponibles",
"details": "Détails",
"eventType": "Type d'événement",
"eventTypes": {
"accountCreated": "Compte créé",
"apiNodeUsage": "Utilisation du nœud partenaire",
"apiUsage": "Utilisation de l'API",
"creditAdded": "Crédits ajoutés",
"gpuUsage": "Utilisation du GPU"
},
"faqs": "FAQ",
"invoiceHistory": "Historique des factures",
"lastUpdated": "Dernière mise à jour",
@@ -3711,27 +3702,6 @@
"customLoRAsLabel": "Importer vos propres LoRAs",
"description": "Choisissez le forfait qui vous convient",
"descriptionWorkspace": "Choisissez la meilleure offre pour votre espace de travail",
"downgrade": {
"body": "Tous les autres membres de cet espace de travail seront immédiatement supprimés.",
"confirm": "Changer de forfait",
"confirmationPhrase": "Je comprends",
"confirmationPrompt": "Tapez « {phrase} » pour confirmer.",
"failed": "Échec du changement de forfait",
"failedAfterMemberRemoval": "Les membres de léquipe ont été retirés, mais le changement de forfait na pas abouti — veuillez réessayer ou contacter le support",
"memberRemovalFailed": "Impossible de retirer {email} de léquipe — certains membres ont peut-être déjà été retirés et votre forfait na pas été modifié",
"notAllowed": "Ce changement de forfait n'est pas disponible",
"paymentMethodRequired": "Un moyen de paiement est requis pour changer de forfait",
"paymentPageBlocked": "Impossible douvrir la page de paiement — veuillez réessayer",
"title": "Passer au forfait {plan} ?"
},
"enterprise": {
"cta": "En savoir plus",
"flexibility": "Vous cherchez plus de flexibilité ou des fonctionnalités personnalisées ?",
"name": "Entreprise",
"needMoreMembers": "Besoin de plus de membres ?",
"reachOut": "Contactez-nous pour planifier un échange."
},
"everythingInPlus": "Tout ce qui est dans {plan}, plus :",
"expiresDate": "Expire le {date}",
"freeTier": {
"description": "Votre plan gratuit inclut {credits} crédits chaque mois pour essayer Comfy Cloud.",
@@ -3778,7 +3748,6 @@
"messageSupport": "Contacter le support",
"monthly": "Mensuel",
"monthlyBonusDescription": "Bonus de crédits mensuel",
"monthlyCredits": "crédits mensuels",
"monthlyCreditsInfo": "Ces crédits se renouvellent chaque mois et ne sont pas reportés",
"monthlyCreditsLabel": "Crédits mensuels",
"monthlyCreditsPerMemberLabel": "Crédits mensuels / membre",
@@ -3791,13 +3760,7 @@
"partnerNodesCredits": "Crédits Nœuds Partenaires",
"partnerNodesDescription": "Pour exécuter des modèles commerciaux/propriétaires",
"perMonth": "USD / mois",
"personalHeader": "Les forfaits personnels sont réservés à un usage individuel. {action}",
"personalHeaderAction": "Pour ajouter des coéquipiers, abonnez-vous au forfait équipe.",
"personalWorkspace": "Espace de travail personnel",
"planScope": {
"personal": "Pour usage personnel",
"team": "Pour les équipes"
},
"plansAndPricing": "Forfaits & tarifs",
"plansForWorkspace": "Formules pour {workspace}",
"prepaidCreditsInfo": "Crédits achetés séparément qui n'expirent pas",
@@ -3808,10 +3771,8 @@
"confirm": "Confirmer",
"confirmPayment": "Confirmer votre paiement",
"confirmPlanChange": "Confirmer le changement d'offre",
"creditsRefillTo": "Les crédits sont réinitialisés à",
"eachMonthCreditsRefill": "Chaque mois, les crédits sont réinitialisés à",
"ends": "Se termine le {date}",
"everyMonthStarting": "Chaque mois à partir du {date}",
"hideFeatures": "Masquer les fonctionnalités",
"nextPaymentDue": "Prochain paiement dû le {date}. Annulez à tout moment.",
"perMember": "/ membre",
@@ -3821,18 +3782,10 @@
"showMoreFeatures": "Afficher plus de fonctionnalités",
"starting": "À partir du {date}",
"startingToday": "À partir d'aujourd'hui",
"subscribeToPlan": "Sabonner à {plan}",
"switchToPlan": "Passer à {plan}",
"terms": "Conditions",
"termsAgreement": "En continuant, vous acceptez les {terms} et la {privacy} de Comfy Org.",
"totalDueToday": "Total dû aujourd'hui",
"youllBeCharged": "Vous serez facturé"
"totalDueToday": "Total dû aujourd'hui"
},
"pricingBlurb": "*Basé sur ce modèle, {seeDetails}. Contactez-nous pour {questions} ou {enterpriseDiscussions}. Pour plus de détails sur les tarifs, {clickHere}.",
"pricingBlurbClickHere": "cliquez ici",
"pricingBlurbEnterprise": "discussions entreprise",
"pricingBlurbQuestions": "questions",
"pricingBlurbSeeDetails": "voir les détails",
"refreshCredits": "Actualiser les crédits",
"renewsDate": "Renouvellement le {date}",
"required": {
@@ -3846,8 +3799,6 @@
"resubscribe": "Se réabonner",
"resubscribeSuccess": "Abonnement réactivé avec succès",
"resubscribeTo": "Se réabonner à {plan}",
"saveYearly": "Économisez 20 %",
"saveYearlyUpTo": "Économisez jusquà 20 %",
"soloUseOnly": "Usage solo uniquement",
"subscribeForMore": "Mettre à niveau",
"subscribeNow": "S'abonner maintenant",
@@ -3856,34 +3807,10 @@
"subscribeToRun": "S'abonner",
"subscribeToRunFull": "S'abonner pour exécuter",
"subscriptionRequiredMessage": "Un abonnement est requis pour que les membres puissent exécuter des workflows sur le Cloud",
"success": {
"allSet": "Tout est prêt",
"planUpdated": "Votre forfait a été mis à jour avec succès.",
"receiptEmailed": "Un reçu vous a été envoyé par e-mail."
},
"teamHeader": "Pour les équipes souhaitant collaborer. Besoin de plus de membres ? {learnMore} sur loffre entreprise.",
"teamHeaderLearnMore": "En savoir plus",
"teamPlan": {
"changePlan": "Changer de forfait",
"checkoutComingSoon": "Le paiement du forfait équipe arrive bientôt.",
"comingSoonLabel": "Bientôt disponible :",
"cta": "Sabonner à loffre Équipe annuelle",
"ctaMonthly": "Sabonner à loffre Équipe mensuelle",
"currentPlan": "Forfait actuel",
"detailsTitle": "Détails",
"name": "Forfait Équipe",
"perkConcurrentRuns": "Les membres peuvent exécuter des workflows simultanément",
"perkInviteMembers": "Invitez des membres dans léquipe",
"perkProjectAssets": "Gestion des projets et des ressources",
"perkRolePermissions": "Permissions basées sur les rôles",
"perkSharedPool": "Crédits partagés pour tous les membres",
"tagline": "Choisissez votre propre abonnement mensuel de crédits. Bénéficiez dune remise plus importante avec un abonnement de crédits plus élevé."
},
"teamWorkspace": "Espace de travail déquipe",
"tierNameYearly": "{name} Annuel",
"tiers": {
"creator": {
"feature1": "Importez vos propres modèles",
"name": "Créateur"
},
"founder": {
@@ -3893,12 +3820,9 @@
"name": "Gratuit"
},
"pro": {
"feature1": "Durée dexécution du workflow prolongée (jusquà 1h)",
"name": "Pro"
},
"standard": {
"feature1": "Durée maximale dexécution du workflow : 30 minutes",
"feature2": "Ajoutez des crédits à tout moment",
"name": "Standard"
}
},
@@ -3911,7 +3835,6 @@
"upgradeToAddCredits": "Mettre à niveau pour ajouter des crédits",
"usdPerMonth": "USD / mois",
"usdPerMonthPerMember": "USD / mois / membre",
"videoEstimate": "Génère environ {count} vidéos de 5s*",
"videoEstimateExplanation": "Ces estimations sont basées sur le modèle Wan 2.2 Image-to-Video avec les paramètres par défaut (5 secondes, 640x640, 16fps, échantillonnage en 4 étapes).",
"videoEstimateHelp": "Plus de détails sur ce modèle",
"videoEstimateLabel": "Nombre approx. de vidéos de 5s générées avec le modèle Wan 2.2 Image-to-Video",
@@ -3921,7 +3844,6 @@
"viewMoreDetails": "Voir plus de détails",
"viewMoreDetailsPlans": "Voir plus de détails sur les forfaits et tarifs",
"viewUsageHistory": "Voir l'historique d'utilisation",
"whatsIncluded": "Inclus :",
"workspaceNotSubscribed": "Cet espace de travail na pas dabonnement",
"yearly": "Annuel",
"yearlyCreditsLabel": "Crédits annuels totaux",

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "お使いのAPIトークンにはこのリソースへのアクセス権がありません。トークンの権限を確認してください。",
"errorUserTokenInvalid": "保存されているAPIトークンが無効または期限切れです。設定でトークンを更新してください。",
"failedToCreateNode": "ノードの作成に失敗しました。再試行するか、詳細はコンソールをご確認ください。",
"failedToSetModelValue": "ノードが追加されましたが、そのモデルを自動的に設定できませんでした。詳細はコンソールをご確認ください。",
"fileFormats": "ファイル形式",
"fileName": "ファイル名",
"fileSize": "ファイルサイズ",
@@ -242,8 +241,7 @@
"auth/user-not-found": "このメールアドレスに紐づくアカウントが見つかりません。新しいアカウントを作成しますか?",
"auth/weak-password": "パスワードが弱すぎます。6文字以上のより強力なパスワードを使用してください。",
"auth/wrong-password": "入力されたパスワードが正しくありません。もう一度お試しください。",
"generic": "サインイン中に問題が発生しました。もう一度お試しください。",
"signupBlocked": "現在アカウントを作成できません。しばらくしてから再度お試しください。問題が解決しない場合は、support@comfy.org までご連絡ください。"
"generic": "サインイン中に問題が発生しました。もう一度お試しください。"
},
"login": {
"andText": "および",
@@ -757,13 +755,6 @@
"creditsAvailable": "利用可能なクレジット",
"details": "詳細",
"eventType": "イベントタイプ",
"eventTypes": {
"accountCreated": "アカウント作成",
"apiNodeUsage": "パートナーノード利用",
"apiUsage": "API利用",
"creditAdded": "クレジット追加",
"gpuUsage": "GPU利用"
},
"faqs": "よくある質問",
"invoiceHistory": "請求履歴",
"lastUpdated": "最終更新",
@@ -3711,27 +3702,6 @@
"customLoRAsLabel": "独自のLoRAをインポート",
"description": "あなたに最適なプランを選択してください",
"descriptionWorkspace": "ワークスペースに最適なプランを選択してください",
"downgrade": {
"body": "このワークスペースの他のメンバーはすぐに削除されます。",
"confirm": "プランを変更",
"confirmationPhrase": "理解しました",
"confirmationPrompt": "確認のため「{phrase}」と入力してください。",
"failed": "プランの変更に失敗しました",
"failedAfterMemberRemoval": "チームメンバーは削除されましたが、プランの変更が完了しませんでした — 再度お試しいただくか、サポートまでご連絡ください",
"memberRemovalFailed": "{email} をチームから削除できませんでした — 一部のメンバーはすでに削除されている可能性があり、プランは変更されませんでした",
"notAllowed": "このプラン変更は利用できません",
"paymentMethodRequired": "プランを変更するにはお支払い方法が必要です",
"paymentPageBlocked": "お支払いページを開けませんでした — もう一度お試しください",
"title": "{plan}プランに変更しますか?"
},
"enterprise": {
"cta": "詳細を見る",
"flexibility": "より柔軟な対応やカスタム機能をご希望ですか?",
"name": "エンタープライズ",
"needMoreMembers": "さらに多くのメンバーが必要ですか?",
"reachOut": "ぜひご連絡ください。お打ち合わせを調整いたします。"
},
"everythingInPlus": "{plan}のすべてに加えて:",
"expiresDate": "{date} に期限切れ",
"freeTier": {
"description": "無料プランには、Comfy Cloudをお試しいただける毎月{credits}クレジットが含まれています。",
@@ -3778,7 +3748,6 @@
"messageSupport": "サポートに連絡",
"monthly": "月額",
"monthlyBonusDescription": "月間クレジットボーナス",
"monthlyCredits": "月間クレジット",
"monthlyCreditsInfo": "これらのクレジットは毎月リフレッシュされ、繰り越しはできません",
"monthlyCreditsLabel": "月間クレジット",
"monthlyCreditsPerMemberLabel": "月間クレジット / メンバー",
@@ -3791,13 +3760,7 @@
"partnerNodesCredits": "パートナーノードクレジット",
"partnerNodesDescription": "商用/独自モデルの実行用",
"perMonth": "USD / 月",
"personalHeader": "個人プランは個人利用専用です。{action}",
"personalHeaderAction": "チームメンバーを追加するには、チームプランにご加入ください。",
"personalWorkspace": "個人ワークスペース",
"planScope": {
"personal": "個人向け",
"team": "チーム向け"
},
"plansAndPricing": "プランと価格",
"plansForWorkspace": "{workspace} のプラン",
"prepaidCreditsInfo": "別途購入した有効期限のないクレジット",
@@ -3808,10 +3771,8 @@
"confirm": "確認",
"confirmPayment": "お支払いを確認",
"confirmPlanChange": "プラン変更を確認",
"creditsRefillTo": "クレジットが補充されます:",
"eachMonthCreditsRefill": "毎月クレジットが補充されます",
"ends": "{date}に終了",
"everyMonthStarting": "毎月{date}から開始",
"hideFeatures": "機能を隠す",
"nextPaymentDue": "次回のお支払いは{date}です。いつでもキャンセルできます。",
"perMember": "/ メンバー",
@@ -3821,18 +3782,10 @@
"showMoreFeatures": "さらに機能を表示",
"starting": "{date}から開始",
"startingToday": "本日から開始",
"subscribeToPlan": "{plan}に加入",
"switchToPlan": "{plan}に切り替え",
"terms": "利用規約",
"termsAgreement": "続行することで、Comfy Orgの{terms}および{privacy}に同意したものとみなされます。",
"totalDueToday": "本日のお支払い合計",
"youllBeCharged": "ご請求額:"
"totalDueToday": "本日のお支払い合計"
},
"pricingBlurb": "*このテンプレートに基づきます。{seeDetails}。{questions}や{enterpriseDiscussions}についてはお問い合わせください。詳細な料金については{clickHere}。",
"pricingBlurbClickHere": "こちらをクリック",
"pricingBlurbEnterprise": "エンタープライズのご相談",
"pricingBlurbQuestions": "ご質問",
"pricingBlurbSeeDetails": "詳細を見る",
"refreshCredits": "クレジットを更新",
"renewsDate": "{date} に更新",
"required": {
@@ -3846,8 +3799,6 @@
"resubscribe": "再購読する",
"resubscribeSuccess": "サブスクリプションが再開されました",
"resubscribeTo": "{plan}を再購読する",
"saveYearly": "年間契約で20%お得",
"saveYearlyUpTo": "最大20%お得",
"soloUseOnly": "個人利用のみ",
"subscribeForMore": "アップグレード",
"subscribeNow": "今すぐ購読",
@@ -3856,34 +3807,10 @@
"subscribeToRun": "購読する",
"subscribeToRunFull": "実行を購読",
"subscriptionRequiredMessage": "クラウドでワークフローを実行するにはメンバーにサブスクリプションが必要です",
"success": {
"allSet": "準備が整いました",
"planUpdated": "プランが正常に更新されました。",
"receiptEmailed": "領収書をメールで送信しました。"
},
"teamHeader": "チームでのコラボレーションを希望する方へ。さらに多くのメンバーが必要ですか?{learnMore}(エンタープライズについて)",
"teamHeaderLearnMore": "詳細はこちら",
"teamPlan": {
"changePlan": "プランを変更",
"checkoutComingSoon": "チームプランのチェックアウトは近日公開予定です。",
"comingSoonLabel": "近日公開:",
"cta": "チーム年間プランに加入",
"ctaMonthly": "チーム月間プランに加入",
"currentPlan": "現在のプラン",
"detailsTitle": "詳細",
"name": "チームプラン",
"perkConcurrentRuns": "メンバーはワークフローを同時に実行可能",
"perkInviteMembers": "チームメンバーを招待",
"perkProjectAssets": "プロジェクト・アセット管理",
"perkRolePermissions": "ロールベースの権限管理",
"perkSharedPool": "全メンバーで共有するクレジットプール",
"tagline": "毎月のクレジットサブスクリプションを自由に選択。多くのクレジットでさらに割引。"
},
"teamWorkspace": "チームワークスペース",
"tierNameYearly": "{name} 年間",
"tiers": {
"creator": {
"feature1": "独自モデルのインポート",
"name": "クリエイター"
},
"founder": {
@@ -3893,12 +3820,9 @@
"name": "無料"
},
"pro": {
"feature1": "より長いワークフロー実行時間最大1時間",
"name": "プロ"
},
"standard": {
"feature1": "最大30分のワークフロー実行時間",
"feature2": "いつでもクレジット追加可能",
"name": "スタンダード"
}
},
@@ -3911,7 +3835,6 @@
"upgradeToAddCredits": "クレジット追加のためアップグレード",
"usdPerMonth": "USD / 月",
"usdPerMonthPerMember": "USD / 月 / メンバー",
"videoEstimate": "約{count}本の5秒動画を生成*",
"videoEstimateExplanation": "これらの見積もりは、Wan 2.2 画像から動画テンプレートのデフォルト設定5秒、640x640、16fps、4ステップサンプリングに基づいています。",
"videoEstimateHelp": "このテンプレートの詳細",
"videoEstimateLabel": "Wan 2.2 画像から動画テンプレートで生成される約5秒動画数",
@@ -3921,7 +3844,6 @@
"viewMoreDetails": "詳細を表示",
"viewMoreDetailsPlans": "プランと価格の詳細を見る",
"viewUsageHistory": "利用履歴を表示",
"whatsIncluded": "含まれる内容:",
"workspaceNotSubscribed": "このワークスペースはサブスクリプションに加入していません",
"yearly": "年額",
"yearlyCreditsLabel": "年間合計クレジット",

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "API 토큰에 이 리소스에 대한 접근 권한이 없습니다. 토큰 권한을 확인해 주세요.",
"errorUserTokenInvalid": "저장된 API 토큰이 유효하지 않거나 만료되었습니다. 설정에서 토큰을 업데이트해 주세요.",
"failedToCreateNode": "노드 생성에 실패했습니다. 다시 시도하거나 콘솔에서 세부 정보를 확인하세요.",
"failedToSetModelValue": "노드는 추가되었지만, 모델을 자동으로 설정할 수 없습니다. 자세한 내용은 콘솔을 확인하세요.",
"fileFormats": "파일 형식",
"fileName": "파일 이름",
"fileSize": "파일 크기",
@@ -242,8 +241,7 @@
"auth/user-not-found": "이 이메일로 등록된 계정이 없습니다. 새 계정을 생성하시겠습니까?",
"auth/weak-password": "비밀번호가 너무 약합니다. 최소 6자 이상의 강력한 비밀번호를 사용해 주세요.",
"auth/wrong-password": "입력하신 비밀번호가 올바르지 않습니다. 다시 시도해 주세요.",
"generic": "로그인 중 문제가 발생했습니다. 다시 시도해 주세요.",
"signupBlocked": "현재 계정을 생성할 수 없습니다. 나중에 다시 시도해 주세요. 계속 문제가 발생하면 support@comfy.org로 이메일을 보내 주세요."
"generic": "로그인 중 문제가 발생했습니다. 다시 시도해 주세요."
},
"login": {
"andText": "및",
@@ -757,13 +755,6 @@
"creditsAvailable": "사용 가능한 크레딧",
"details": "세부 정보",
"eventType": "이벤트 유형",
"eventTypes": {
"accountCreated": "계정 생성됨",
"apiNodeUsage": "파트너 노드 사용",
"apiUsage": "API 사용",
"creditAdded": "크레딧 추가됨",
"gpuUsage": "GPU 사용"
},
"faqs": "자주 묻는 질문",
"invoiceHistory": "청구서 내역",
"lastUpdated": "마지막 업데이트",
@@ -3711,27 +3702,6 @@
"customLoRAsLabel": "나만의 LoRA 가져오기",
"description": "가장 적합한 플랜을 선택하세요",
"descriptionWorkspace": "워크스페이스에 가장 적합한 플랜을 선택하세요",
"downgrade": {
"body": "이 워크스페이스의 다른 모든 멤버가 즉시 제거됩니다.",
"confirm": "플랜 변경",
"confirmationPhrase": "이해했습니다",
"confirmationPrompt": "확인을 위해 \"{phrase}\"를 입력하세요.",
"failed": "플랜 변경에 실패했습니다",
"failedAfterMemberRemoval": "팀 멤버는 제거되었지만 플랜 변경이 완료되지 않았습니다 — 다시 시도하거나 지원팀에 문의해 주세요",
"memberRemovalFailed": "{email}을(를) 팀에서 제거할 수 없습니다 — 일부 멤버는 이미 제거되었으며 플랜이 변경되지 않았습니다",
"notAllowed": "이 플랜 변경은 사용할 수 없습니다",
"paymentMethodRequired": "플랜을 변경하려면 결제 수단이 필요합니다",
"paymentPageBlocked": "결제 페이지를 열 수 없습니다 — 다시 시도해 주세요",
"title": "{plan} 플랜으로 변경하시겠습니까?"
},
"enterprise": {
"cta": "자세히 알아보기",
"flexibility": "더 많은 유연성이나 맞춤 기능이 필요하신가요?",
"name": "엔터프라이즈",
"needMoreMembers": "더 많은 멤버가 필요하신가요?",
"reachOut": "저희에게 연락해 주시면 상담 일정을 잡아드리겠습니다."
},
"everythingInPlus": "{plan}의 모든 기능, 그리고 추가로:",
"expiresDate": "만료일 {date}",
"freeTier": {
"description": "무료 플랜에는 Comfy Cloud를 체험할 수 있도록 매월 {credits} 크레딧이 포함되어 있습니다.",
@@ -3778,7 +3748,6 @@
"messageSupport": "고객 지원 문의",
"monthly": "월간",
"monthlyBonusDescription": "월간 크레딧 보너스",
"monthlyCredits": "월간 크레딧",
"monthlyCreditsInfo": "이 크레딧은 매월 갱신되며 이월되지 않습니다",
"monthlyCreditsLabel": "월간 크레딧",
"monthlyCreditsPerMemberLabel": "월별 크레딧 / 멤버",
@@ -3791,13 +3760,7 @@
"partnerNodesCredits": "파트너 노드 크레딧",
"partnerNodesDescription": "상용/독점 모델 실행용",
"perMonth": "USD / 월",
"personalHeader": "개인 플랜은 개인 사용만을 위한 것입니다. {action}",
"personalHeaderAction": "팀원을 추가하려면 팀 플랜을 구독하세요.",
"personalWorkspace": "개인 워크스페이스",
"planScope": {
"personal": "개인용",
"team": "팀용"
},
"plansAndPricing": "플랜 및 가격",
"plansForWorkspace": "{workspace}의 플랜",
"prepaidCreditsInfo": "별도 구매하여 만료되지 않는 크레딧",
@@ -3808,10 +3771,8 @@
"confirm": "확인",
"confirmPayment": "결제 확인",
"confirmPlanChange": "플랜 변경 확인",
"creditsRefillTo": "크레딧이 다음으로 충전됨",
"eachMonthCreditsRefill": "매월 크레딧이 다시 충전됩니다",
"ends": "{date}에 종료",
"everyMonthStarting": "매월 {date}부터",
"hideFeatures": "기능 숨기기",
"nextPaymentDue": "다음 결제일: {date}. 언제든지 취소할 수 있습니다.",
"perMember": "/ 멤버",
@@ -3821,18 +3782,10 @@
"showMoreFeatures": "더 많은 기능 보기",
"starting": "{date}부터 시작",
"startingToday": "오늘부터 시작",
"subscribeToPlan": "{plan} 구독하기",
"switchToPlan": "{plan}으로 전환",
"terms": "이용약관",
"termsAgreement": "계속 진행하면 Comfy Org의 {terms} 및 {privacy}에 동의하게 됩니다.",
"totalDueToday": "오늘 결제 금액 합계",
"youllBeCharged": "다음 금액이 청구됩니다"
"totalDueToday": "오늘 결제 금액 합계"
},
"pricingBlurb": "*이 템플릿 기준, {seeDetails}. {questions} 또는 {enterpriseDiscussions}에 대해 문의해 주세요. 자세한 가격 정보는 {clickHere}.",
"pricingBlurbClickHere": "여기를 클릭하세요",
"pricingBlurbEnterprise": "엔터프라이즈 상담",
"pricingBlurbQuestions": "문의",
"pricingBlurbSeeDetails": "자세히 보기",
"refreshCredits": "크레딧 새로고침",
"renewsDate": "{date}에 갱신됨",
"required": {
@@ -3846,8 +3799,6 @@
"resubscribe": "다시 구독하기",
"resubscribeSuccess": "구독이 성공적으로 재활성화되었습니다",
"resubscribeTo": "{plan} 다시 구독하기",
"saveYearly": "연간 20% 절약",
"saveYearlyUpTo": "최대 20% 절약",
"soloUseOnly": "개인용 전용",
"subscribeForMore": "업그레이드",
"subscribeNow": "지금 구독하기",
@@ -3856,34 +3807,10 @@
"subscribeToRun": "구독",
"subscribeToRunFull": "실행 구독",
"subscriptionRequiredMessage": "클라우드에서 워크플로우를 실행하려면 멤버가 구독해야 합니다",
"success": {
"allSet": "모든 준비가 완료되었습니다",
"planUpdated": "플랜이 성공적으로 업데이트되었습니다.",
"receiptEmailed": "영수증이 이메일로 발송되었습니다."
},
"teamHeader": "협업을 원하는 팀을 위한 플랜입니다. 더 많은 멤버가 필요하신가요? 엔터프라이즈에 대해 {learnMore}.",
"teamHeaderLearnMore": "자세히 알아보기",
"teamPlan": {
"changePlan": "플랜 변경",
"checkoutComingSoon": "팀 플랜 결제는 곧 제공됩니다.",
"comingSoonLabel": "곧 제공:",
"cta": "팀 연간 구독하기",
"ctaMonthly": "팀 월간 구독하기",
"currentPlan": "현재 플랜",
"detailsTitle": "세부 정보",
"name": "팀 플랜",
"perkConcurrentRuns": "멤버별 동시 워크플로우 실행 가능",
"perkInviteMembers": "팀 멤버 초대",
"perkProjectAssets": "프로젝트 및 에셋 관리",
"perkRolePermissions": "역할 기반 권한",
"perkSharedPool": "모든 멤버를 위한 공유 크레딧 풀",
"tagline": "월간 크레딧 구독을 직접 선택하세요. 더 많은 크레딧 구독 시 더 큰 할인 혜택을 받으세요."
},
"teamWorkspace": "팀 워크스페이스",
"tierNameYearly": "{name} 연간",
"tiers": {
"creator": {
"feature1": "나만의 모델 가져오기",
"name": "Creator"
},
"founder": {
@@ -3893,12 +3820,9 @@
"name": "무료"
},
"pro": {
"feature1": "더 긴 워크플로우 실행 시간(최대 1시간)",
"name": "Pro"
},
"standard": {
"feature1": "최대 30분 워크플로우 실행 시간",
"feature2": "언제든지 크레딧 추가 가능",
"name": "Standard"
}
},
@@ -3911,7 +3835,6 @@
"upgradeToAddCredits": "크레딧 추가를 위해 업그레이드하세요",
"usdPerMonth": "USD / 월",
"usdPerMonthPerMember": "USD / 월 / 멤버",
"videoEstimate": "약 {count}개의 5초 영상 생성*",
"videoEstimateExplanation": "이 추정치는 기본 설정(5초, 640x640, 16fps, 4단계 샘플링)을 사용한 Wan 2.2 이미지-투-비디오 템플릿을 기준으로 합니다.",
"videoEstimateHelp": "이 템플릿에 대한 자세한 정보",
"videoEstimateLabel": "Wan 2.2 이미지-투-비디오 템플릿으로 생성 가능한 5초 비디오 수",
@@ -3921,7 +3844,6 @@
"viewMoreDetails": "자세히 보기",
"viewMoreDetailsPlans": "플랜 및 가격에 대한 자세한 정보 보기",
"viewUsageHistory": "사용 기록 보기",
"whatsIncluded": "포함 내용:",
"workspaceNotSubscribed": "이 워크스페이스는 구독 중이 아닙니다",
"yearly": "연간",
"yearlyCreditsLabel": "연간 총 크레딧",

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "Seu token de API não tem acesso a este recurso. Verifique as permissões do seu token.",
"errorUserTokenInvalid": "Seu token de API armazenado é inválido ou expirou. Atualize seu token nas configurações.",
"failedToCreateNode": "Falha ao criar o nó. Por favor, tente novamente ou verifique o console para mais detalhes.",
"failedToSetModelValue": "Nó adicionado, mas seu modelo não pôde ser definido automaticamente. Verifique o console para mais detalhes.",
"fileFormats": "Formatos de arquivo",
"fileName": "Nome do arquivo",
"fileSize": "Tamanho do arquivo",
@@ -242,8 +241,7 @@
"auth/user-not-found": "Nenhuma conta encontrada com este e-mail. Gostaria de criar uma nova conta?",
"auth/weak-password": "A senha é muito fraca. Use uma senha mais forte com pelo menos 6 caracteres.",
"auth/wrong-password": "A senha que você digitou está incorreta. Por favor, tente novamente.",
"generic": "Algo deu errado ao fazer login. Por favor, tente novamente.",
"signupBlocked": "Não foi possível criar sua conta no momento. Por favor, tente novamente mais tarde. Se o problema persistir, envie um e-mail para support@comfy.org."
"generic": "Algo deu errado ao fazer login. Por favor, tente novamente."
},
"login": {
"andText": "e",
@@ -757,13 +755,6 @@
"creditsAvailable": "Créditos disponíveis",
"details": "Detalhes",
"eventType": "Tipo de evento",
"eventTypes": {
"accountCreated": "Conta criada",
"apiNodeUsage": "Uso de Node Parceiro",
"apiUsage": "Uso da API",
"creditAdded": "Créditos adicionados",
"gpuUsage": "Uso de GPU"
},
"faqs": "Perguntas frequentes",
"invoiceHistory": "Histórico de faturas",
"lastUpdated": "Última atualização",
@@ -3723,27 +3714,6 @@
"customLoRAsLabel": "Importe seus próprios LoRAs",
"description": "Escolha o melhor plano para você",
"descriptionWorkspace": "Escolha o melhor plano para seu workspace",
"downgrade": {
"body": "Todos os outros membros deste workspace serão removidos imediatamente.",
"confirm": "Mudar plano",
"confirmationPhrase": "Eu entendo",
"confirmationPrompt": "Digite \"{phrase}\" para confirmar.",
"failed": "Falha ao mudar o plano",
"failedAfterMemberRemoval": "Os membros da equipe foram removidos, mas a alteração do plano não foi concluída — por favor, tente novamente ou entre em contato com o suporte",
"memberRemovalFailed": "Não foi possível remover {email} da equipe — alguns membros podem já ter sido removidos e seu plano não foi alterado",
"notAllowed": "Esta alteração de plano não está disponível",
"paymentMethodRequired": "É necessário um método de pagamento para mudar de plano",
"paymentPageBlocked": "Não foi possível abrir a página de pagamento — por favor, tente novamente",
"title": "Mudar para o plano {plan}?"
},
"enterprise": {
"cta": "Saiba mais",
"flexibility": "Procurando mais flexibilidade ou recursos personalizados?",
"name": "Enterprise",
"needMoreMembers": "Precisa de mais membros?",
"reachOut": "Entre em contato conosco e vamos agendar uma conversa."
},
"everythingInPlus": "Tudo do {plan}, além de:",
"expiresDate": "Expira em {date}",
"freeTier": {
"description": "Seu plano gratuito inclui {credits} créditos por mês para testar o Comfy Cloud.",
@@ -3790,7 +3760,6 @@
"messageSupport": "Falar com o suporte",
"monthly": "Mensal",
"monthlyBonusDescription": "Bônus mensal de créditos",
"monthlyCredits": "créditos mensais",
"monthlyCreditsInfo": "Esses créditos são renovados mensalmente e não acumulam",
"monthlyCreditsLabel": "Créditos mensais",
"monthlyCreditsPerMemberLabel": "Créditos mensais / membro",
@@ -3803,13 +3772,7 @@
"partnerNodesCredits": "Preços dos Partner Nodes",
"partnerNodesDescription": "Para executar modelos comerciais/proprietários",
"perMonth": "/ mês",
"personalHeader": "Planos pessoais são para uso individual apenas. {action}",
"personalHeaderAction": "Para adicionar colegas de equipe, assine o plano para equipes.",
"personalWorkspace": "Espaço de Trabalho Pessoal",
"planScope": {
"personal": "Para uso pessoal",
"team": "Para equipes"
},
"plansAndPricing": "Planos e preços",
"plansForWorkspace": "Planos para {workspace}",
"prepaidCreditsInfo": "Créditos pré-pagos expiram após 1 ano da data de compra.",
@@ -3820,10 +3783,8 @@
"confirm": "Confirmar",
"confirmPayment": "Confirme seu pagamento",
"confirmPlanChange": "Confirme a alteração do plano",
"creditsRefillTo": "Créditos recarregados para",
"eachMonthCreditsRefill": "A cada mês os créditos são recarregados para",
"ends": "Termina em {date}",
"everyMonthStarting": "Todo mês a partir de {date}",
"hideFeatures": "Ocultar recursos",
"nextPaymentDue": "Próximo pagamento em {date}. Cancele a qualquer momento.",
"perMember": "/ membro",
@@ -3833,18 +3794,10 @@
"showMoreFeatures": "Mostrar mais recursos",
"starting": "Começando em {date}",
"startingToday": "Começando hoje",
"subscribeToPlan": "Assinar o plano {plan}",
"switchToPlan": "Mudar para o plano {plan}",
"terms": "Termos",
"termsAgreement": "Ao continuar, você concorda com os {terms} e {privacy} da Comfy Org.",
"totalDueToday": "Total devido hoje",
"youllBeCharged": "Você será cobrado"
"totalDueToday": "Total devido hoje"
},
"pricingBlurb": "*Baseado neste template, {seeDetails}. Entre em contato para {questions} ou {enterpriseDiscussions}. Para mais detalhes sobre preços, {clickHere}.",
"pricingBlurbClickHere": "clique aqui",
"pricingBlurbEnterprise": "discussões enterprise",
"pricingBlurbQuestions": "dúvidas",
"pricingBlurbSeeDetails": "ver detalhes",
"refreshCredits": "Atualizar créditos",
"renewsDate": "Renova em {date}",
"required": {
@@ -3858,8 +3811,6 @@
"resubscribe": "Reassinar",
"resubscribeSuccess": "Assinatura reativada com sucesso",
"resubscribeTo": "Reassinar {plan}",
"saveYearly": "Economize 20%",
"saveYearlyUpTo": "Economize até 20%",
"soloUseOnly": "Apenas para uso individual",
"subscribeForMore": "Fazer upgrade",
"subscribeNow": "Assine Agora",
@@ -3868,34 +3819,10 @@
"subscribeToRun": "Assinar",
"subscribeToRunFull": "Assine para Executar",
"subscriptionRequiredMessage": "Uma assinatura é necessária para que os membros executem fluxos de trabalho na Nuvem",
"success": {
"allSet": "Tudo pronto",
"planUpdated": "Seu plano foi atualizado com sucesso.",
"receiptEmailed": "Um recibo foi enviado para seu e-mail."
},
"teamHeader": "Para equipes que desejam colaborar. Precisa de mais membros? {learnMore} sobre enterprise.",
"teamHeaderLearnMore": "Saiba mais",
"teamPlan": {
"changePlan": "Mudar plano",
"checkoutComingSoon": "Checkout do plano para equipes em breve.",
"comingSoonLabel": "Em breve:",
"cta": "Assinar Equipe Anual",
"ctaMonthly": "Assinar Equipe Mensal",
"currentPlan": "Plano atual",
"detailsTitle": "Detalhes",
"name": "Plano para Equipes",
"perkConcurrentRuns": "Membros podem executar workflows simultaneamente",
"perkInviteMembers": "Convide membros da equipe",
"perkProjectAssets": "Gestão de projetos e ativos",
"perkRolePermissions": "Permissões baseadas em função",
"perkSharedPool": "Pool de créditos compartilhado para todos os membros",
"tagline": "Escolha sua própria assinatura mensal de créditos. Obtenha um desconto maior com uma assinatura de créditos maior."
},
"teamWorkspace": "Espaço de Trabalho em Equipe",
"tierNameYearly": "{name} Anual",
"tiers": {
"creator": {
"feature1": "Importe seus próprios modelos",
"name": "Criador"
},
"founder": {
@@ -3905,12 +3832,9 @@
"name": "Gratuito"
},
"pro": {
"feature1": "Tempo de execução do workflow mais longo (até 1 hora)",
"name": "Pro"
},
"standard": {
"feature1": "Tempo máximo de execução do workflow: 30 minutos",
"feature2": "Adicione mais créditos a qualquer momento",
"name": "Padrão"
}
},
@@ -3923,7 +3847,6 @@
"upgradeToAddCredits": "Faça upgrade para adicionar créditos",
"usdPerMonth": "USD / mês",
"usdPerMonthPerMember": "USD / mês / membro",
"videoEstimate": "Gera aproximadamente {count} vídeos de 5s*",
"videoEstimateExplanation": "Essas estimativas são baseadas no template Wan 2.2 Image-to-Video usando as configurações padrão (5 segundos, 640x640, 16fps, amostragem de 4 etapas).",
"videoEstimateHelp": "Mais detalhes sobre este template",
"videoEstimateLabel": "Quantidade aprox. de vídeos de 5s gerados com o template Wan 2.2 Image-to-Video",
@@ -3933,7 +3856,6 @@
"viewMoreDetails": "Ver mais detalhes",
"viewMoreDetailsPlans": "Veja mais detalhes sobre planos e preços",
"viewUsageHistory": "Ver histórico de uso",
"whatsIncluded": "O que está incluído:",
"workspaceNotSubscribed": "Este espaço de trabalho não possui uma assinatura",
"yearly": "Anual",
"yearlyCreditsLabel": "Total de créditos anuais",

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "Ваш API-токен не имеет доступа к этому ресурсу. Проверьте права доступа токена.",
"errorUserTokenInvalid": "Ваш сохранённый API-токен недействителен или истёк. Обновите токен в настройках.",
"failedToCreateNode": "Не удалось создать узел. Попробуйте ещё раз или проверьте консоль для подробностей.",
"failedToSetModelValue": "Узел добавлен, но его модель не удалось установить автоматически. Проверьте консоль для получения подробностей.",
"fileFormats": "Форматы файлов",
"fileName": "Имя файла",
"fileSize": "Размер файла",
@@ -242,8 +241,7 @@
"auth/user-not-found": "Учетная запись с этим email не найдена. Хотите создать новую учетную запись?",
"auth/weak-password": "Пароль слишком слабый. Пожалуйста, используйте более надежный пароль длиной не менее 6 символов.",
"auth/wrong-password": "Введенный пароль неверен. Пожалуйста, попробуйте еще раз.",
"generic": "Произошла ошибка при входе в систему. Пожалуйста, попробуйте еще раз.",
"signupBlocked": "Сейчас не удалось создать вашу учетную запись. Пожалуйста, попробуйте позже. Если проблема сохраняется, напишите на support@comfy.org."
"generic": "Произошла ошибка при входе в систему. Пожалуйста, попробуйте еще раз."
},
"login": {
"andText": "и",
@@ -757,13 +755,6 @@
"creditsAvailable": "Доступно кредитов",
"details": "Детали",
"eventType": "Тип события",
"eventTypes": {
"accountCreated": "Учетная запись создана",
"apiNodeUsage": "Использование партнерского узла",
"apiUsage": "Использование API",
"creditAdded": "Кредиты добавлены",
"gpuUsage": "Использование GPU"
},
"faqs": "Часто задаваемые вопросы",
"invoiceHistory": "История счетов",
"lastUpdated": "Последнее обновление",
@@ -3711,27 +3702,6 @@
"customLoRAsLabel": "Импортируйте свои LoRA",
"description": "Выберите лучший план для себя",
"descriptionWorkspace": "Выберите лучший тариф для вашего рабочего пространства",
"downgrade": {
"body": "Все остальные участники этой рабочей области будут немедленно удалены.",
"confirm": "Сменить тариф",
"confirmationPhrase": "Я понимаю",
"confirmationPrompt": "Введите «{phrase}» для подтверждения.",
"failed": "Не удалось сменить тариф",
"failedAfterMemberRemoval": "Участники команды были удалены, но смена тарифа не завершена — попробуйте еще раз или обратитесь в поддержку",
"memberRemovalFailed": "Не удалось удалить {email} из команды — некоторые участники могли быть уже удалены, и ваш тариф не был изменен",
"notAllowed": "Этот переход на тариф недоступен",
"paymentMethodRequired": "Для смены тарифа требуется способ оплаты",
"paymentPageBlocked": "Не удалось открыть страницу оплаты — попробуйте еще раз",
"title": "Перейти на тариф {plan}?"
},
"enterprise": {
"cta": "Узнать больше",
"flexibility": "Нужна большая гибкость или индивидуальные функции?",
"name": "Корпоративный тариф",
"needMoreMembers": "Нужно больше участников?",
"reachOut": "Свяжитесь с нами, чтобы назначить встречу."
},
"everythingInPlus": "Все из {plan}, плюс:",
"expiresDate": "Истекает {date}",
"freeTier": {
"description": "Ваш бесплатный тариф включает {credits} кредитов каждый месяц для использования Comfy Cloud.",
@@ -3778,7 +3748,6 @@
"messageSupport": "Написать в поддержку",
"monthly": "Ежемесячно",
"monthlyBonusDescription": "Ежемесячный бонус кредитов",
"monthlyCredits": "кредиты в месяц",
"monthlyCreditsInfo": "Эти кредиты обновляются ежемесячно и не переносятся",
"monthlyCreditsLabel": "Ежемесячные кредиты",
"monthlyCreditsPerMemberLabel": "Ежемесячные кредиты / участник",
@@ -3791,13 +3760,7 @@
"partnerNodesCredits": "Кредиты партнёрских узлов",
"partnerNodesDescription": "Для запуска коммерческих/проприетарных моделей",
"perMonth": "USD / месяц",
"personalHeader": "Личные тарифы предназначены только для индивидуального использования. {action}",
"personalHeaderAction": "Чтобы добавить участников, оформите командный тариф.",
"personalWorkspace": "Личное рабочее пространство",
"planScope": {
"personal": "Для личного использования",
"team": "Для команд"
},
"plansAndPricing": "Планы и цены",
"plansForWorkspace": "Тарифы для {workspace}",
"prepaidCreditsInfo": "Кредиты, приобретённые отдельно и не имеющие срока действия",
@@ -3808,10 +3771,8 @@
"confirm": "Подтвердить",
"confirmPayment": "Подтвердите оплату",
"confirmPlanChange": "Подтвердите смену тарифа",
"creditsRefillTo": "Кредиты пополняются до",
"eachMonthCreditsRefill": "Ежемесячное пополнение кредитов до",
"ends": "Заканчивается {date}",
"everyMonthStarting": "Каждый месяц начиная с {date}",
"hideFeatures": "Скрыть функции",
"nextPaymentDue": "Следующий платеж {date}. Отменить можно в любое время.",
"perMember": "/ участник",
@@ -3821,18 +3782,10 @@
"showMoreFeatures": "Показать больше функций",
"starting": "Начинается {date}",
"startingToday": "Начинается сегодня",
"subscribeToPlan": "Подписаться на {plan}",
"switchToPlan": "Перейти на {plan}",
"terms": "Условия",
"termsAgreement": "Продолжая, вы соглашаетесь с {terms} и {privacy} Comfy Org.",
"totalDueToday": "Итого к оплате сегодня",
"youllBeCharged": "С вас будет списано"
"totalDueToday": "Итого к оплате сегодня"
},
"pricingBlurb": "*Основано на этом шаблоне, {seeDetails}. Свяжитесь с нами по {questions} или для {enterpriseDiscussions}. Для подробной информации о ценах {clickHere}.",
"pricingBlurbClickHere": "нажмите здесь",
"pricingBlurbEnterprise": "корпоративным обсуждениям",
"pricingBlurbQuestions": "вопросам",
"pricingBlurbSeeDetails": "подробнее",
"refreshCredits": "Обновить кредиты",
"renewsDate": "Обновляется {date}",
"required": {
@@ -3846,8 +3799,6 @@
"resubscribe": "Возобновить подписку",
"resubscribeSuccess": "Подписка успешно возобновлена",
"resubscribeTo": "Возобновить подписку на {plan}",
"saveYearly": "Экономьте 20%",
"saveYearlyUpTo": "Экономьте до 20%",
"soloUseOnly": "Только для индивидуального использования",
"subscribeForMore": "Обновить",
"subscribeNow": "Подписаться сейчас",
@@ -3856,34 +3807,10 @@
"subscribeToRun": "Подписаться",
"subscribeToRunFull": "Подписаться для запуска",
"subscriptionRequiredMessage": "Для запуска рабочих процессов в облаке участникам требуется подписка",
"success": {
"allSet": "Все готово",
"planUpdated": "Ваш тариф успешно обновлен.",
"receiptEmailed": "Квитанция отправлена на вашу электронную почту."
},
"teamHeader": "Для команд, желающих сотрудничать. Нужно больше участников? {learnMore} о корпоративных возможностях.",
"teamHeaderLearnMore": "Узнать больше",
"teamPlan": {
"changePlan": "Сменить тариф",
"checkoutComingSoon": "Оформление командного тарифа скоро появится.",
"comingSoonLabel": "Скоро появится:",
"cta": "Оформить командную годовую подписку",
"ctaMonthly": "Оформить командную месячную подписку",
"currentPlan": "Текущий тариф",
"detailsTitle": "Детали",
"name": "Командный тариф",
"perkConcurrentRuns": "Участники могут запускать workflow одновременно",
"perkInviteMembers": "Приглашайте участников команды",
"perkProjectAssets": "Управление проектами и активами",
"perkRolePermissions": "Права доступа по ролям",
"perkSharedPool": "Общий пул кредитов для всех участников",
"tagline": "Выберите свою ежемесячную подписку на кредиты. Чем больше подписка — тем больше скидка."
},
"teamWorkspace": "Командное рабочее пространство",
"tierNameYearly": "{name} Ежегодно",
"tiers": {
"creator": {
"feature1": "Импортируйте свои собственные модели",
"name": "Creator"
},
"founder": {
@@ -3893,12 +3820,9 @@
"name": "Бесплатно"
},
"pro": {
"feature1": "Более длительное выполнение workflow (до 1 часа)",
"name": "Pro"
},
"standard": {
"feature1": "Максимальное время выполнения workflow — 30 минут",
"feature2": "Добавляйте кредиты в любое время",
"name": "Standard"
}
},
@@ -3911,7 +3835,6 @@
"upgradeToAddCredits": "Обновите, чтобы добавить кредиты",
"usdPerMonth": "USD / мес",
"usdPerMonthPerMember": "USD / мес / участник",
"videoEstimate": "Генерирует ~{count} видео по 5 сек.*",
"videoEstimateExplanation": "Эти оценки основаны на шаблоне Wan 2.2 Image-to-Video с настройками по умолчанию (5 секунд, 640x640, 16 кадров/с, 4 шага семплирования).",
"videoEstimateHelp": "Подробнее об этом шаблоне",
"videoEstimateLabel": "Примерное количество 5-секундных видео, созданных с помощью шаблона Wan 2.2 Image-to-Video",
@@ -3921,7 +3844,6 @@
"viewMoreDetails": "Подробнее",
"viewMoreDetailsPlans": "Подробнее о планах и ценах",
"viewUsageHistory": "История использования",
"whatsIncluded": "Что входит:",
"workspaceNotSubscribed": "Это рабочее пространство не имеет подписки",
"yearly": "Ежегодно",
"yearlyCreditsLabel": "Годовые кредиты",

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "API anahtarınızın bu kaynağa erişimi yok. Lütfen anahtar izinlerinizi kontrol edin.",
"errorUserTokenInvalid": "Kayıtlı API anahtarınız geçersiz veya süresi dolmuş. Lütfen ayarlardan anahtarınızı güncelleyin.",
"failedToCreateNode": "Düğüm oluşturulamadı. Lütfen tekrar deneyin veya ayrıntılar için konsolu kontrol edin.",
"failedToSetModelValue": "Düğüm eklendi, ancak modeli otomatik olarak ayarlanamadı. Ayrıntılar için konsolu kontrol edin.",
"fileFormats": "Dosya formatları",
"fileName": "Dosya Adı",
"fileSize": "Dosya Boyutu",
@@ -242,8 +241,7 @@
"auth/user-not-found": "Bu e-posta ile ilişkili bir hesap bulunamadı. Yeni bir hesap oluşturmak ister misiniz?",
"auth/weak-password": "Parola çok zayıf. Lütfen en az 6 karakterden oluşan daha güçlü bir parola kullanın.",
"auth/wrong-password": "Girdiğiniz parola yanlış. Lütfen tekrar deneyin.",
"generic": "Oturum açarken bir hata oluştu. Lütfen tekrar deneyin.",
"signupBlocked": "Şu anda hesabınızı oluşturamıyoruz. Lütfen daha sonra tekrar deneyin. Sorun devam ederse, support@comfy.org adresine e-posta gönderin."
"generic": "Oturum açarken bir hata oluştu. Lütfen tekrar deneyin."
},
"login": {
"andText": "ve",
@@ -757,13 +755,6 @@
"creditsAvailable": "Mevcut kredi",
"details": "Detaylar",
"eventType": "Etkinlik Türü",
"eventTypes": {
"accountCreated": "Hesap Oluşturuldu",
"apiNodeUsage": "Partner Node Kullanımı",
"apiUsage": "API Kullanımı",
"creditAdded": "Kredi Eklendi",
"gpuUsage": "GPU Kullanımı"
},
"faqs": "SSS",
"invoiceHistory": "Fatura Geçmişi",
"lastUpdated": "Son güncellenme",
@@ -3711,27 +3702,6 @@
"customLoRAsLabel": "Kendi LoRA'larınızı içe aktarın",
"description": "Sizin için en iyi planı seçin",
"descriptionWorkspace": "Çalışma alanınız için en iyi planı seçin",
"downgrade": {
"body": "Bu çalışma alanındaki diğer tüm üyeler hemen kaldırılacaktır.",
"confirm": "Planı değiştir",
"confirmationPhrase": "Anladım",
"confirmationPrompt": "Onaylamak için \"{phrase}\" yazın.",
"failed": "Plan değiştirilemedi",
"failedAfterMemberRemoval": "Ekip üyeleri kaldırıldı, ancak plan değişikliği tamamlanmadı — lütfen tekrar deneyin veya destek ile iletişime geçin",
"memberRemovalFailed": "{email} ekipten kaldırılamadı — bazı üyeler zaten kaldırılmış olabilir ve planınız değişmedi",
"notAllowed": "Bu plan değişikliği mevcut değil",
"paymentMethodRequired": "Planı değiştirmek için bir ödeme yöntemi gereklidir",
"paymentPageBlocked": "Ödeme sayfasıılamadı — lütfen tekrar deneyin",
"title": "{plan} planına geçilsin mi?"
},
"enterprise": {
"cta": "Daha fazla bilgi",
"flexibility": "Daha fazla esneklik veya özel özellikler mi arıyorsunuz?",
"name": "Kurumsal",
"needMoreMembers": "Daha fazla üyeye mi ihtiyacınız var?",
"reachOut": "Bizimle iletişime geçin ve bir görüşme planlayalım."
},
"everythingInPlus": "{plan} içeriğinin tamamı ve ayrıca:",
"expiresDate": "{date} tarihinde sona erer",
"freeTier": {
"description": "Ücretsiz planınız, Comfy Cloud'u denemek için her ay {credits} kredi içerir.",
@@ -3778,7 +3748,6 @@
"messageSupport": "Destek ekibine mesaj gönder",
"monthly": "Aylık",
"monthlyBonusDescription": "Aylık kredi bonusu",
"monthlyCredits": "aylık kredi",
"monthlyCreditsInfo": "Bu krediler her ay yenilenir ve devretmez",
"monthlyCreditsLabel": "Aylık krediler",
"monthlyCreditsPerMemberLabel": "Aylık kredi / üye",
@@ -3791,13 +3760,7 @@
"partnerNodesCredits": "Partner Düğümleri kredileri",
"partnerNodesDescription": "Ticari/özel modelleri çalıştırmak için",
"perMonth": "USD / ay",
"personalHeader": "Bireysel planlar yalnızca kişisel kullanım içindir. {action}",
"personalHeaderAction": "Ekip arkadaşları eklemek için ekip planına abone olun.",
"personalWorkspace": "Kişisel Çalışma Alanı",
"planScope": {
"personal": "Bireysel Kullanım İçin",
"team": "Ekipler İçin"
},
"plansAndPricing": "Planlar ve fiyatlandırma",
"plansForWorkspace": "{workspace} için planlar",
"prepaidCreditsInfo": "Ayrıca satın alınan ve son kullanma tarihi olmayan krediler",
@@ -3808,10 +3771,8 @@
"confirm": "Onayla",
"confirmPayment": "Ödemenizi onaylayın",
"confirmPlanChange": "Plan değişikliğini onaylayın",
"creditsRefillTo": "Krediler şu seviyeye doldurulur:",
"eachMonthCreditsRefill": "Her ay krediler yenilenir",
"ends": "{date} tarihinde bitiyor",
"everyMonthStarting": "Her ay {date} tarihinde başlar",
"hideFeatures": "Özellikleri gizle",
"nextPaymentDue": "Sonraki ödeme {date} tarihinde. İstediğiniz zaman iptal edebilirsiniz.",
"perMember": "/ üye",
@@ -3821,18 +3782,10 @@
"showMoreFeatures": "Daha fazla özelliği göster",
"starting": "{date} tarihinde başlıyor",
"startingToday": "Bugünden itibaren başlıyor",
"subscribeToPlan": "{plan} planına abone ol",
"switchToPlan": "{plan} planına geç",
"terms": "Şartlar",
"termsAgreement": "Devam ederek Comfy Org'un {terms} ve {privacy} politikasını kabul etmiş olursunuz.",
"totalDueToday": "Bugün ödenecek toplam tutar",
"youllBeCharged": "Şu kadar ücretlendirileceksiniz:"
"totalDueToday": "Bugün ödenecek toplam tutar"
},
"pricingBlurb": "*Bu şablona göre, {seeDetails}. {questions} veya {enterpriseDiscussions} için bizimle iletişime geçin. Daha fazla fiyatlandırma detayı için {clickHere}.",
"pricingBlurbClickHere": "buraya tıklayın",
"pricingBlurbEnterprise": "kurumsal görüşmeler",
"pricingBlurbQuestions": "sorular",
"pricingBlurbSeeDetails": "detayları gör",
"refreshCredits": "Kredileri yenile",
"renewsDate": "{date} tarihinde yenilenir",
"required": {
@@ -3846,8 +3799,6 @@
"resubscribe": "Yeniden abone ol",
"resubscribeSuccess": "Abonelik başarıyla yeniden etkinleştirildi",
"resubscribeTo": "{plan} planına yeniden abone ol",
"saveYearly": "%20 tasarruf edin",
"saveYearlyUpTo": "Yıllık %20'ye varan tasarruf",
"soloUseOnly": "Sadece bireysel kullanım",
"subscribeForMore": "Yükselt",
"subscribeNow": "Hemen Abone Ol",
@@ -3856,34 +3807,10 @@
"subscribeToRun": "Abone Ol",
"subscribeToRunFull": "Çalıştırmaya Abone Ol",
"subscriptionRequiredMessage": "Üyelerin Bulut'ta iş akışlarını çalıştırabilmesi için abonelik gereklidir",
"success": {
"allSet": "Her şey hazır",
"planUpdated": "Planınız başarıyla güncellendi.",
"receiptEmailed": "Makbuz e-posta ile gönderildi."
},
"teamHeader": "Birlikte çalışmak isteyen ekipler için. Daha fazla üyeye mi ihtiyacınız var? {learnMore} kurumsal hakkında.",
"teamHeaderLearnMore": "Daha fazla bilgi",
"teamPlan": {
"changePlan": "Planı değiştir",
"checkoutComingSoon": "Ekip planı ödeme sayfası yakında geliyor.",
"comingSoonLabel": "Yakında:",
"cta": "Ekip Yıllık Aboneliğe Katıl",
"ctaMonthly": "Ekip Aylık Aboneliğe Katıl",
"currentPlan": "Mevcut plan",
"detailsTitle": "Detaylar",
"name": "Ekip Planı",
"perkConcurrentRuns": "Üyeler iş akışlarını aynı anda çalıştırabilir",
"perkInviteMembers": "Ekip üyelerini davet edin",
"perkProjectAssets": "Proje & varlık yönetimi",
"perkRolePermissions": "Rol tabanlı izinler",
"perkSharedPool": "Tüm üyeler için ortak kredi havuzu",
"tagline": "Kendi aylık kredi aboneliğinizi seçin. Daha fazla krediyle daha büyük indirim elde edin."
},
"teamWorkspace": "Takım Çalışma Alanı",
"tierNameYearly": "{name} Yıllık",
"tiers": {
"creator": {
"feature1": "Kendi modellerinizi içe aktarın",
"name": "Yaratıcı"
},
"founder": {
@@ -3893,12 +3820,9 @@
"name": "Ücretsiz"
},
"pro": {
"feature1": "Daha uzun iş akışı çalışma süresi (1 saate kadar)",
"name": "Pro"
},
"standard": {
"feature1": "Maksimum 30 dakika iş akışı çalışma süresi",
"feature2": "İstediğiniz zaman daha fazla kredi ekleyin",
"name": "Standart"
}
},
@@ -3911,7 +3835,6 @@
"upgradeToAddCredits": "Kredi eklemek için yükselt",
"usdPerMonth": "USD / ay",
"usdPerMonthPerMember": "USD / ay / üye",
"videoEstimate": "Yaklaşık ~{count} adet 5 sn'lik video üretir*",
"videoEstimateExplanation": "Bu tahminler, varsayılan ayarlarla (5 saniye, 640x640, 16fps, 4 adım örnekleme) Wan 2.2 Görselden Videoya şablonuna dayanmaktadır.",
"videoEstimateHelp": "Bu şablon hakkında daha fazla bilgi",
"videoEstimateLabel": "Wan 2.2 Görselden Videoya şablonu ile yaklaşık 5 sn'lik video sayısı",
@@ -3921,7 +3844,6 @@
"viewMoreDetails": "Daha fazla detay görüntüle",
"viewMoreDetailsPlans": "Planlar ve fiyatlandırma hakkında daha fazla detay",
"viewUsageHistory": "Kullanım geçmişini görüntüle",
"whatsIncluded": "Dahil olanlar:",
"workspaceNotSubscribed": "Bu çalışma alanı bir aboneliğe sahip değil",
"yearly": "Yıllık",
"yearlyCreditsLabel": "Toplam yıllık krediler",

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "您的 API 金鑰無法存取此資源,請檢查權限。",
"errorUserTokenInvalid": "儲存的 API 金鑰無效或已過期,請在設定中更新。",
"failedToCreateNode": "無法建立節點。請重試或查看主控台以取得詳細資訊。",
"failedToSetModelValue": "已新增節點,但無法自動設定其模型。請檢查主控台以獲取詳細資訊。",
"fileFormats": "檔案格式",
"fileName": "檔案名稱",
"fileSize": "檔案大小",
@@ -242,8 +241,7 @@
"auth/user-not-found": "找不到使用此電子郵件的帳戶。您要建立新帳戶嗎?",
"auth/weak-password": "密碼強度不足。請使用至少 6 個字元的更強密碼。",
"auth/wrong-password": "您輸入的密碼不正確。請再試一次。",
"generic": "登入時發生錯誤,請再試一次。",
"signupBlocked": "我們目前無法建立您的帳戶。請稍後再試。如果問題持續發生,請聯絡 support@comfy.org。"
"generic": "登入時發生錯誤,請再試一次。"
},
"login": {
"andText": "以及",
@@ -757,13 +755,6 @@
"creditsAvailable": "可用點數",
"details": "詳細資料",
"eventType": "事件類型",
"eventTypes": {
"accountCreated": "已建立帳戶",
"apiNodeUsage": "合作夥伴節點使用量",
"apiUsage": "API 使用量",
"creditAdded": "已新增點數",
"gpuUsage": "GPU 使用量"
},
"faqs": "常見問題",
"invoiceHistory": "發票紀錄",
"lastUpdated": "最後更新",
@@ -3711,27 +3702,6 @@
"customLoRAsLabel": "匯入您自己的 LoRAs",
"description": "選擇最適合您的方案",
"descriptionWorkspace": "為您的工作區選擇最佳方案",
"downgrade": {
"body": "此工作區的所有其他成員將立即被移除。",
"confirm": "變更方案",
"confirmationPhrase": "我已了解",
"confirmationPrompt": "請輸入「{phrase}」以確認。",
"failed": "變更方案失敗",
"failedAfterMemberRemoval": "團隊成員已移除,但方案變更未完成 — 請再試一次或聯絡客服",
"memberRemovalFailed": "無法將 {email} 從團隊移除 — 有些成員可能已被移除,您的方案未變更",
"notAllowed": "無法進行此方案變更",
"paymentMethodRequired": "變更方案需先新增付款方式",
"paymentPageBlocked": "無法開啟付款頁面 — 請再試一次",
"title": "切換至 {plan} 方案?"
},
"enterprise": {
"cta": "了解更多",
"flexibility": "需要更多彈性或客製化功能?",
"name": "企業方案",
"needMoreMembers": "需要更多成員嗎?",
"reachOut": "歡迎聯絡我們,安排諮詢。"
},
"everythingInPlus": "包含 {plan} 的所有內容,並加上:",
"expiresDate": "將於 {date} 到期",
"freeTier": {
"description": "您的免費方案每月包含 {credits} 點數,可體驗 Comfy Cloud。",
@@ -3778,7 +3748,6 @@
"messageSupport": "聯繫客服",
"monthly": "每月",
"monthlyBonusDescription": "每月點數獎勵",
"monthlyCredits": "每月點數",
"monthlyCreditsInfo": "這些點數每月重置,不可累積",
"monthlyCreditsLabel": "每月點數",
"monthlyCreditsPerMemberLabel": "每月點數/成員",
@@ -3791,13 +3760,7 @@
"partnerNodesCredits": "合作節點點數",
"partnerNodesDescription": "用於執行商業/專有模型",
"perMonth": "美元 / 月",
"personalHeader": "個人方案僅限個人使用。{action}",
"personalHeaderAction": "如需新增團隊成員,請訂閱團隊方案。",
"personalWorkspace": "個人工作區",
"planScope": {
"personal": "個人使用",
"team": "團隊使用"
},
"plansAndPricing": "方案與價格",
"plansForWorkspace": "{workspace} 的方案",
"prepaidCreditsInfo": "單獨購買且不會過期的點數",
@@ -3808,10 +3771,8 @@
"confirm": "確認",
"confirmPayment": "確認您的付款",
"confirmPlanChange": "確認您的方案變更",
"creditsRefillTo": "點數補充至",
"eachMonthCreditsRefill": "每月點數補充至",
"ends": "結束於 {date}",
"everyMonthStarting": "每月自 {date} 起",
"hideFeatures": "隱藏功能",
"nextPaymentDue": "下次付款日為 {date}。可隨時取消。",
"perMember": "/成員",
@@ -3821,18 +3782,10 @@
"showMoreFeatures": "顯示更多功能",
"starting": "自 {date} 開始",
"startingToday": "今日開始",
"subscribeToPlan": "訂閱 {plan}",
"switchToPlan": "切換至 {plan}",
"terms": "服務條款",
"termsAgreement": "繼續即表示您同意 Comfy Org 的{terms}與{privacy}。",
"totalDueToday": "今日應付總額",
"youllBeCharged": "您將被收取"
"totalDueToday": "今日應付總額"
},
"pricingBlurb": "*根據此範本,{seeDetails}。如有{questions}或{enterpriseDiscussions},請聯絡我們。更多價格資訊,{clickHere}。",
"pricingBlurbClickHere": "點此查看",
"pricingBlurbEnterprise": "企業洽談",
"pricingBlurbQuestions": "問題諮詢",
"pricingBlurbSeeDetails": "查看詳情",
"refreshCredits": "刷新點數",
"renewsDate": "將於 {date} 續訂",
"required": {
@@ -3846,8 +3799,6 @@
"resubscribe": "重新訂閱",
"resubscribeSuccess": "訂閱已重新啟用",
"resubscribeTo": "重新訂閱 {plan}",
"saveYearly": "年繳省 20%",
"saveYearlyUpTo": "最高可省 20%",
"soloUseOnly": "僅限個人使用",
"subscribeForMore": "升級",
"subscribeNow": "立即訂閱",
@@ -3856,34 +3807,10 @@
"subscribeToRun": "訂閱",
"subscribeToRunFull": "訂閱運行方案",
"subscriptionRequiredMessage": "會員需訂閱才能在雲端執行工作流程",
"success": {
"allSet": "設定完成",
"planUpdated": "您的方案已成功更新。",
"receiptEmailed": "收據已寄送至您的電子郵件。"
},
"teamHeader": "適合需要協作的團隊。需要更多成員嗎?{learnMore} 企業方案。",
"teamHeaderLearnMore": "了解更多",
"teamPlan": {
"changePlan": "變更方案",
"checkoutComingSoon": "團隊方案結帳功能即將推出。",
"comingSoonLabel": "即將推出:",
"cta": "訂閱團隊年繳方案",
"ctaMonthly": "訂閱團隊月繳方案",
"currentPlan": "目前方案",
"detailsTitle": "方案詳情",
"name": "團隊方案",
"perkConcurrentRuns": "成員可同時執行多個工作流程",
"perkInviteMembers": "邀請團隊成員",
"perkProjectAssets": "專案與資產管理",
"perkRolePermissions": "依角色分配權限",
"perkSharedPool": "全體成員共享點數池",
"tagline": "自選每月點數訂閱。訂閱越多,折扣越大。"
},
"teamWorkspace": "團隊工作區",
"tierNameYearly": "{name} 年度方案",
"tiers": {
"creator": {
"feature1": "可匯入自有模型",
"name": "創作者版"
},
"founder": {
@@ -3893,12 +3820,9 @@
"name": "免費"
},
"pro": {
"feature1": "更長的工作流程執行時間(最長 1 小時)",
"name": "專業版"
},
"standard": {
"feature1": "單次工作流程最長 30 分鐘",
"feature2": "可隨時加購點數",
"name": "標準版"
}
},
@@ -3911,7 +3835,6 @@
"upgradeToAddCredits": "升級以增加點數",
"usdPerMonth": "美元/月",
"usdPerMonthPerMember": "美元/月/成員",
"videoEstimate": "約可產生 ~{count} 個 5 秒影片*",
"videoEstimateExplanation": "此估算以 Wan 2.2 圖轉影範本的預設設定5 秒、640x640、16fps、4 步採樣)為基礎。",
"videoEstimateHelp": "查看更多此範本細節",
"videoEstimateLabel": "以 Wan 2.2 圖轉影範本約可產生的 5 秒影片數量",
@@ -3921,7 +3844,6 @@
"viewMoreDetails": "查看更多詳情",
"viewMoreDetailsPlans": "查看更多方案與價格細節",
"viewUsageHistory": "檢視使用記錄",
"whatsIncluded": "包含內容:",
"workspaceNotSubscribed": "此工作區尚未訂閱",
"yearly": "每年",
"yearlyCreditsLabel": "年度總點數",

View File

@@ -92,7 +92,6 @@
"errorUserTokenAccessDenied": "您的 API 密钥无权访问此资源。请检查密钥权限。",
"errorUserTokenInvalid": "您保存的 API 密钥无效或已过期。请在设置中更新密钥。",
"failedToCreateNode": "创建节点失败。请重试或查看控制台获取详细信息。",
"failedToSetModelValue": "节点已添加,但无法自动设置其模型。请查看控制台获取详细信息。",
"fileFormats": "文件格式",
"fileName": "文件名",
"fileSize": "文件大小",
@@ -242,8 +241,7 @@
"auth/user-not-found": "未找到使用此电子邮件的账户。您想要创建一个新账户吗?",
"auth/weak-password": "密码强度太弱。请使用至少6个字符的更强密码。",
"auth/wrong-password": "您输入的密码不正确,请重试。",
"generic": "登录时出现问题,请重试。",
"signupBlocked": "我们目前无法创建您的账户。请稍后再试。如果问题持续发生,请发送邮件至 support@comfy.org。"
"generic": "登录时出现问题,请重试。"
},
"login": {
"andText": "和",
@@ -757,13 +755,6 @@
"creditsAvailable": "积分可用",
"details": "详情",
"eventType": "事件类型",
"eventTypes": {
"accountCreated": "账户已创建",
"apiNodeUsage": "合作节点使用",
"apiUsage": "API 使用",
"creditAdded": "已添加积分",
"gpuUsage": "GPU 使用"
},
"faqs": "常见问题",
"invoiceHistory": "发票历史",
"lastUpdated": "最近更新",
@@ -3723,27 +3714,6 @@
"customLoRAsLabel": "导入您的 Lora",
"description": "选择最适合您的订阅计划",
"descriptionWorkspace": "为您的工作区选择最佳方案",
"downgrade": {
"body": "该工作区的所有其他成员将被立即移除。",
"confirm": "更改套餐",
"confirmationPhrase": "我已知晓",
"confirmationPrompt": "请输入“{phrase}”以确认。",
"failed": "更改套餐失败",
"failedAfterMemberRemoval": "团队成员已被移除,但套餐更改未完成 — 请重试或联系客服",
"memberRemovalFailed": "无法将 {email} 从团队移除 — 部分成员可能已被移除,您的套餐未更改",
"notAllowed": "无法进行此套餐更改",
"paymentMethodRequired": "更改套餐需要添加支付方式",
"paymentPageBlocked": "无法打开支付页面 — 请重试",
"title": "切换到 {plan} 套餐?"
},
"enterprise": {
"cta": "了解更多",
"flexibility": "需要更灵活或定制的功能?",
"name": "企业版",
"needMoreMembers": "需要更多成员?",
"reachOut": "联系我们,安排沟通。"
},
"everythingInPlus": "包含 {plan} 的所有内容,并额外提供:",
"expiresDate": "于 {date} 过期",
"freeTier": {
"description": "您的免费套餐每月包含 {credits} 积分,可体验 Comfy Cloud。",
@@ -3790,7 +3760,6 @@
"messageSupport": "消息支持",
"monthly": "月度",
"monthlyBonusDescription": "每月积分奖励",
"monthlyCredits": "每月积分",
"monthlyCreditsInfo": "积分每月刷新,不会保留",
"monthlyCreditsLabel": "每月积分",
"monthlyCreditsPerMemberLabel": "每成员每月积分",
@@ -3803,13 +3772,7 @@
"partnerNodesCredits": "合作伙伴节点积分",
"partnerNodesDescription": "用于运行商业/专有模型",
"perMonth": "美元 / 月",
"personalHeader": "个人套餐仅限个人使用。{action}",
"personalHeaderAction": "如需添加团队成员,请订阅团队套餐。",
"personalWorkspace": "个人工作区",
"planScope": {
"personal": "个人使用",
"team": "团队使用"
},
"plansAndPricing": "订阅和定价",
"plansForWorkspace": "{workspace} 的套餐",
"prepaidCreditsInfo": "单独购买且不会过期的积分",
@@ -3820,10 +3783,8 @@
"confirm": "确认",
"confirmPayment": "确认付款",
"confirmPlanChange": "确认更改方案",
"creditsRefillTo": "积分补充至",
"eachMonthCreditsRefill": "每月积分补充至",
"ends": "{date} 结束",
"everyMonthStarting": "每月从 {date} 开始",
"hideFeatures": "隐藏功能",
"nextPaymentDue": "下次付款截止日期 {date}。可随时取消。",
"perMember": "/ 每成员",
@@ -3833,18 +3794,10 @@
"showMoreFeatures": "显示更多功能",
"starting": "{date} 开始",
"startingToday": "今日开始",
"subscribeToPlan": "订阅 {plan}",
"switchToPlan": "切换到 {plan}",
"terms": "条款",
"termsAgreement": "继续操作即表示您同意 Comfy Org 的{terms}和{privacy}。",
"totalDueToday": "今日应付总额",
"youllBeCharged": "您将被收取"
"totalDueToday": "今日应付总额"
},
"pricingBlurb": "*基于此模板,{seeDetails}。如有{questions}或{enterpriseDiscussions},请联系我们。更多价格详情,{clickHere}。",
"pricingBlurbClickHere": "点击这里",
"pricingBlurbEnterprise": "企业合作",
"pricingBlurbQuestions": "问题咨询",
"pricingBlurbSeeDetails": "查看详情",
"refreshCredits": "刷新额度",
"renewsDate": "将于 {date} 续订",
"required": {
@@ -3858,8 +3811,6 @@
"resubscribe": "重新订阅",
"resubscribeSuccess": "订阅已成功重新激活",
"resubscribeTo": "重新订阅 {plan}",
"saveYearly": "年付节省 20%",
"saveYearlyUpTo": "最高可节省 20%",
"soloUseOnly": "仅限个人使用",
"subscribeForMore": "升级",
"subscribeNow": "立即订阅",
@@ -3868,34 +3819,10 @@
"subscribeToRun": "订阅",
"subscribeToRunFull": "订阅 Run",
"subscriptionRequiredMessage": "成员在云端运行工作流需要订阅",
"success": {
"allSet": "全部设置完成",
"planUpdated": "您的套餐已成功更新。",
"receiptEmailed": "收据已通过邮件发送给您。"
},
"teamHeader": "适合希望协作的团队。需要更多成员?{learnMore} 关于企业版。",
"teamHeaderLearnMore": "了解更多",
"teamPlan": {
"changePlan": "更改套餐",
"checkoutComingSoon": "团队套餐结算即将上线。",
"comingSoonLabel": "即将上线:",
"cta": "订阅团队年付套餐",
"ctaMonthly": "订阅团队月付套餐",
"currentPlan": "当前套餐",
"detailsTitle": "详情",
"name": "团队套餐",
"perkConcurrentRuns": "成员可同时运行多个工作流",
"perkInviteMembers": "邀请团队成员",
"perkProjectAssets": "项目与资产管理",
"perkRolePermissions": "基于角色的权限管理",
"perkSharedPool": "所有成员共享积分池",
"tagline": "自定义每月积分订阅。订阅更多积分享受更大折扣。"
},
"teamWorkspace": "团队工作区",
"tierNameYearly": "{name} 年度",
"tiers": {
"creator": {
"feature1": "导入您自己的模型",
"name": "Creator"
},
"founder": {
@@ -3905,12 +3832,9 @@
"name": "免费"
},
"pro": {
"feature1": "更长的工作流运行时间(最长 1 小时)",
"name": "Pro"
},
"standard": {
"feature1": "最长 30 分钟工作流运行时间",
"feature2": "可随时增加积分",
"name": "Standard"
}
},
@@ -3923,7 +3847,6 @@
"upgradeToAddCredits": "升级以添加积分",
"usdPerMonth": "USD / mo",
"usdPerMonthPerMember": "美元 / 月 / 每成员",
"videoEstimate": "可生成约 {count} 个 5 秒视频*",
"videoEstimateExplanation": "这些预估基于 Wan Fun Control 生成 5 秒视频。",
"videoEstimateHelp": "这是什么?",
"videoEstimateLabel": "可使用 Wan Fun Control 模板生成 5 秒视频的数量",
@@ -3933,7 +3856,6 @@
"viewMoreDetails": "查看更多详情",
"viewMoreDetailsPlans": "查看有关订阅和定价的更多信息",
"viewUsageHistory": "查看使用历史",
"whatsIncluded": "包含内容:",
"workspaceNotSubscribed": "此工作区未订阅",
"yearly": "年度",
"yearlyCreditsLabel": "总共年度积分",

View File

@@ -10,6 +10,7 @@ import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { MediaAssetKey } from '@/platform/assets/schemas/mediaAssetSchema'
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
import type { AssetMeta } from '@/platform/assets/schemas/mediaAssetSchema'
import type * as outputAssetUtilModule from '../utils/outputAssetUtil'
import { useMediaAssetActions } from './useMediaAssetActions'
// Use vi.hoisted to create a mutable reference for isCloud
@@ -145,6 +146,17 @@ vi.mock('../schemas/assetMetadataSchema', () => ({
getOutputAssetMetadata: mockGetOutputAssetMetadata
}))
const mockResolveOutputAssetItems = vi.hoisted(() =>
vi.fn().mockResolvedValue([])
)
vi.mock('../utils/outputAssetUtil', async (importOriginal) => {
const actual = await importOriginal<typeof outputAssetUtilModule>()
return {
...actual,
resolveOutputAssetItems: mockResolveOutputAssetItems
}
})
const mockDeleteAsset = vi.hoisted(() => vi.fn())
const mockCreateAssetExport = vi.hoisted(() =>
vi.fn().mockResolvedValue({ task_id: 'test-task-id', status: 'pending' })
@@ -283,6 +295,8 @@ describe('useMediaAssetActions', () => {
mockGetOutputAssetMetadata.mockReset()
mockGetOutputAssetMetadata.mockReturnValue(null)
mockGetAssetType.mockReset()
mockResolveOutputAssetItems.mockReset()
mockResolveOutputAssetItems.mockResolvedValue([])
})
describe('addWorkflow', () => {
@@ -582,6 +596,236 @@ describe('useMediaAssetActions', () => {
})
})
describe('downloadAssets - OSS multi-output expansion', () => {
beforeEach(() => {
mockIsCloud.value = false
mockGetAssetType.mockReturnValue('output')
mockGetOutputAssetMetadata.mockImplementation(
(meta: Record<string, unknown> | undefined) =>
meta && 'jobId' in meta ? meta : null
)
})
function createOutputAsset(
id: string,
name: string,
jobId: string,
outputCount?: number,
previewUrl?: string
): AssetItem {
return createMockAsset({
id,
name,
tags: ['output'],
preview_url: previewUrl ?? `https://example.com/${name}`,
user_metadata: { jobId, nodeId: '1', subfolder: '', outputCount }
})
}
it('expands a grouped asset into individual downloads', async () => {
const grouped = createOutputAsset(
'g1',
'cover.png',
'job1',
3,
'https://example.com/cover.png'
)
mockResolveOutputAssetItems.mockResolvedValueOnce([
createOutputAsset('g1-out1', 'out1.png', 'job1'),
createOutputAsset('g1-out2', 'out2.png', 'job1'),
createOutputAsset('g1-out3', 'out3.png', 'job1')
])
const actions = useMediaAssetActions()
actions.downloadAssets([grouped])
await vi.waitFor(() => {
expect(mockDownloadFile).toHaveBeenCalledTimes(3)
})
expect(mockResolveOutputAssetItems).toHaveBeenCalledTimes(1)
expect(mockResolveOutputAssetItems).toHaveBeenCalledWith(
expect.objectContaining({ jobId: 'job1', outputCount: 3 }),
expect.objectContaining({ createdAt: expect.any(String) })
)
expect(mockDownloadFile).toHaveBeenNthCalledWith(
1,
'https://example.com/out1.png',
'out1.png'
)
expect(mockDownloadFile).toHaveBeenNthCalledWith(
2,
'https://example.com/out2.png',
'out2.png'
)
expect(mockDownloadFile).toHaveBeenNthCalledWith(
3,
'https://example.com/out3.png',
'out3.png'
)
expect(mockCreateAssetExport).not.toHaveBeenCalled()
})
it('mixes grouped and single-output assets in one selection', async () => {
const grouped = createOutputAsset('g1', 'cover.png', 'job1', 2)
const single = createOutputAsset('s1', 'solo.png', 'job2')
mockResolveOutputAssetItems.mockResolvedValueOnce([
createOutputAsset('g1-a', 'a.png', 'job1'),
createOutputAsset('g1-b', 'b.png', 'job1')
])
const actions = useMediaAssetActions()
actions.downloadAssets([grouped, single])
await vi.waitFor(() => {
expect(mockDownloadFile).toHaveBeenCalledTimes(3)
})
expect(mockResolveOutputAssetItems).toHaveBeenCalledTimes(1)
const filenames = mockDownloadFile.mock.calls.map((call) => call[1])
expect(filenames).toEqual(['a.png', 'b.png', 'solo.png'])
})
it('falls back to the original asset when resolveOutputAssetItems returns empty', async () => {
const grouped = createOutputAsset(
'g1',
'cover.png',
'job1',
3,
'https://example.com/cover.png'
)
mockResolveOutputAssetItems.mockResolvedValueOnce([])
const actions = useMediaAssetActions()
actions.downloadAssets([grouped])
await vi.waitFor(() => {
expect(mockDownloadFile).toHaveBeenCalledTimes(1)
})
expect(mockDownloadFile).toHaveBeenCalledWith(
'https://example.com/cover.png',
'cover.png'
)
})
it('does not call resolveOutputAssetItems when no grouped assets are selected', () => {
const single1 = createOutputAsset(
's1',
'a.png',
'job1',
undefined,
'https://example.com/a.png'
)
const single2 = createOutputAsset(
's2',
'b.png',
'job2',
1,
'https://example.com/b.png'
)
const actions = useMediaAssetActions()
actions.downloadAssets([single1, single2])
expect(mockResolveOutputAssetItems).not.toHaveBeenCalled()
expect(mockDownloadFile).toHaveBeenCalledTimes(2)
})
it('deduplicates downloads when an expanded child is also selected alongside its parent', async () => {
const grouped = createOutputAsset('job1-cover', 'cover.png', 'job1', 3)
const child = createMockAsset({
id: 'job1-child-a',
name: 'out1.png',
tags: ['output'],
preview_url: 'https://example.com/out1.png',
user_metadata: { jobId: 'job1', nodeId: '1', subfolder: '' }
})
mockResolveOutputAssetItems.mockResolvedValueOnce([
createMockAsset({
id: 'job1-child-a',
name: 'out1.png',
tags: ['output'],
preview_url: 'https://example.com/out1.png',
user_metadata: { jobId: 'job1', nodeId: '1', subfolder: '' }
}),
createMockAsset({
id: 'job1-child-b',
name: 'out2.png',
tags: ['output'],
preview_url: 'https://example.com/out2.png',
user_metadata: { jobId: 'job1', nodeId: '1', subfolder: '' }
})
])
const actions = useMediaAssetActions()
actions.downloadAssets([grouped, child])
await vi.waitFor(() => {
expect(mockDownloadFile).toHaveBeenCalledTimes(2)
})
const filenames = mockDownloadFile.mock.calls.map((call) => call[1])
expect(filenames).toEqual(['out1.png', 'out2.png'])
})
it('falls back to the preview download when resolveOutputAssetItems rejects', async () => {
const grouped = createOutputAsset(
'g1',
'cover.png',
'job1',
3,
'https://example.com/cover.png'
)
mockResolveOutputAssetItems.mockRejectedValueOnce(new Error('boom'))
const actions = useMediaAssetActions()
actions.downloadAssets([grouped])
await vi.waitFor(() => {
expect(mockDownloadFile).toHaveBeenCalledTimes(1)
})
expect(mockDownloadFile).toHaveBeenCalledWith(
'https://example.com/cover.png',
'cover.png'
)
})
it('still downloads resolvable assets when one grouped asset fails to expand', async () => {
const failingGrouped = createOutputAsset(
'g1',
'cover1.png',
'job1',
3,
'https://example.com/cover1.png'
)
const okGrouped = createOutputAsset('g2', 'cover2.png', 'job2', 2)
mockResolveOutputAssetItems.mockImplementation(
(metadata: { jobId: string }) => {
if (metadata.jobId === 'job1') {
return Promise.reject(new Error('job1 lookup failed'))
}
return Promise.resolve([
createOutputAsset('g2-a', 'out2a.png', 'job2'),
createOutputAsset('g2-b', 'out2b.png', 'job2')
])
}
)
const actions = useMediaAssetActions()
actions.downloadAssets([failingGrouped, okGrouped])
await vi.waitFor(() => {
expect(mockDownloadFile).toHaveBeenCalledTimes(3)
})
const filenames = mockDownloadFile.mock.calls.map((call) => call[1])
expect(filenames).toEqual(['cover1.png', 'out2a.png', 'out2b.png'])
})
})
describe('downloadAssets - cloud zip filters', () => {
beforeEach(() => {
mockIsCloud.value = true

View File

@@ -27,7 +27,10 @@ import { getAssetUrl } from '../utils/assetUrlUtil'
import { clearDeletedAssetWidgetValues } from '../utils/clearDeletedAssetWidgetValues'
import { clearNodePreviewCacheForValues } from '../utils/clearNodePreviewCacheForValues'
import { markDeletedAssetsAsMissingMedia } from '../utils/markDeletedAssetsAsMissingMedia'
import { getAssetOutputCount } from '../utils/outputAssetUtil'
import {
getAssetOutputCount,
resolveOutputAssetItems
} from '../utils/outputAssetUtil'
import { createAnnotatedPath } from '@/utils/createAnnotatedPath'
import { detectNodeTypeFromFilename } from '@/utils/loaderNodeUtil'
import { isResultItemType } from '@/utils/typeGuardUtil'
@@ -109,8 +112,9 @@ export function useMediaAssetActions() {
* Download one or more assets.
* In cloud mode, creates a ZIP export via the backend when called with
* 2+ assets or with any asset whose job has `outputCount > 1`.
* Falls back to direct downloads in OSS mode and for single single-output
* assets. With no argument, uses the asset from `MediaAssetKey` context.
* In OSS mode, downloads each file directly, expanding grouped assets
* (`outputCount > 1`) into their individual outputs.
* With no argument, uses the asset from `MediaAssetKey` context.
*/
const downloadAssets = (assets?: AssetItem[]) => {
const targetAssets =
@@ -127,13 +131,13 @@ export function useMediaAssetActions() {
return
}
try {
targetAssets.forEach((asset) => {
const filename = getAssetDisplayName(asset)
const downloadUrl = asset.preview_url || getAssetUrl(asset)
downloadFile(downloadUrl, filename)
})
if (hasMultiOutputJobs) {
void downloadAssetsIndividually(targetAssets)
return
}
try {
targetAssets.forEach((asset) => downloadSingleAsset(asset))
toast.add({
severity: 'success',
summary: t('g.success'),
@@ -150,6 +154,66 @@ export function useMediaAssetActions() {
}
}
function downloadSingleAsset(asset: AssetItem) {
const filename = getAssetDisplayName(asset)
const downloadUrl = asset.preview_url || getAssetUrl(asset)
downloadFile(downloadUrl, filename)
}
async function expandAssetForDownload(
asset: AssetItem
): Promise<AssetItem[]> {
const metadata = getOutputAssetMetadata(asset.user_metadata)
if (
!metadata ||
typeof metadata.outputCount !== 'number' ||
metadata.outputCount <= 1
) {
return [asset]
}
try {
const resolved = await resolveOutputAssetItems(metadata, {
createdAt: asset.created_at
})
return resolved.length > 0 ? resolved : [asset]
} catch (error) {
console.error('Failed to expand grouped asset for download:', error)
return [asset]
}
}
async function downloadAssetsIndividually(assets: AssetItem[]) {
try {
const expanded = await Promise.all(assets.map(expandAssetForDownload))
const seenAssetIds = new Set<string>()
const filesToDownload = expanded.flat().filter((asset) => {
if (seenAssetIds.has(asset.id)) return false
seenAssetIds.add(asset.id)
return true
})
filesToDownload.forEach((asset) => downloadSingleAsset(asset))
toast.add({
severity: 'success',
summary: t('g.success'),
detail: t(
'mediaAsset.selection.downloadsStarted',
filesToDownload.length
),
life: 2000
})
} catch (error) {
console.error('Failed to download assets:', error)
toast.add({
severity: 'error',
summary: t('g.error'),
detail: t('g.failedToDownloadImage')
})
}
}
async function downloadAssetsAsZip(assets: AssetItem[]) {
const assetExportStore = useAssetExportStore()

147
src/utils/fuseUtil.test.ts Normal file
View File

@@ -0,0 +1,147 @@
import { describe, expect, it, vi } from 'vitest'
import type { FuseSearchable } from '@/utils/fuseUtil'
import { FuseFilter, FuseSearch } from '@/utils/fuseUtil'
interface SearchItem extends Partial<FuseSearchable> {
name: string
}
interface FilterItem {
options: string[]
}
const makeSearch = <T>(data: T[] = []) =>
new FuseSearch<T>(data, {
fuseOptions: {
keys: ['name'],
includeScore: true,
threshold: 0.6,
shouldSort: false
},
advancedScoring: true
})
describe('FuseSearch', () => {
it('assigns stable ranking tiers for exact, prefix, word, substring, and multi-part matches', () => {
const search = new FuseSearch<string>([], {})
const cases = [
{ query: 'load image', item: 'load image', tier: 0 },
{ query: 'load', item: 'Load Image', tier: 1 },
{ query: 'image', item: 'LoadImage', tier: 2 },
{ query: 'cast', item: 'broadcast', tier: 3 },
{ query: 'batch latent', item: 'LatentBatch', tier: 4 },
{ query: 'ten bat', item: 'LatentBatch', tier: 5 },
{ query: 'vae', item: 'KSampler', tier: 9 }
]
for (const { query, item, tier } of cases) {
expect(search.calcAuxSingle(query, item, 0)[0]).toBe(tier)
}
})
it('penalizes deprecated non-exact matches without penalizing exact matches', () => {
const search = makeSearch<SearchItem>()
expect(
search.calcAuxScores('image', { name: 'Image Deprecated' }, 0)[0]
).toBe(6)
expect(
search.calcAuxScores('deprecated node', { name: 'Deprecated Node' }, 0)[0]
).toBe(0)
})
it('lets searchable entries post-process their auxiliary scores', () => {
const search = makeSearch<SearchItem>()
const entry: SearchItem = {
name: 'Image Loader',
postProcessSearchScores: (scores) => [scores[0] + 2, ...scores.slice(1)]
}
expect(search.calcAuxScores('image', entry, 0)[0]).toBe(3)
})
it('sorts advanced search results by auxiliary ranking instead of Fuse order', () => {
const exact = { name: 'Image' }
const prefix = { name: 'Image Loader' }
const camelCaseWord = { name: 'LoadImage' }
const substring = { name: 'PreimageNode' }
const deprecated = { name: 'Image Deprecated' }
const search = makeSearch([
substring,
deprecated,
camelCaseWord,
prefix,
exact
])
expect(search.search('image')).toEqual([
exact,
prefix,
camelCaseWord,
substring,
deprecated
])
})
it('returns data in original order for an empty query without calling Fuse', () => {
const data = [{ name: 'B' }, { name: 'A' }]
const search = makeSearch(data)
const fuseSearchSpy = vi.spyOn(search.fuse, 'search')
expect(search.search('')).toEqual(data)
expect(fuseSearchSpy).not.toHaveBeenCalled()
})
it('compares auxiliary scores by the first differing value and then length', () => {
const search = new FuseSearch<string>([], {})
expect(
[
[1, 4],
[1, 2],
[0, 99]
].sort(search.compareAux)
).toEqual([
[0, 99],
[1, 2],
[1, 4]
])
expect(
[
[1, 2, 0],
[1, 2]
].sort(search.compareAux)
).toEqual([
[1, 2],
[1, 2, 0]
])
})
})
describe('FuseFilter', () => {
it('matches single values, comma-separated values, and wildcard fallbacks', () => {
const imageItem = { options: ['IMAGE', 'LATENT'] }
const modelItem = { options: ['MODEL'] }
const filter = new FuseFilter<FilterItem, string>([imageItem, modelItem], {
id: 'type',
name: 'Type',
invokeSequence: 't',
getItemOptions: (item) => item.options
})
expect(filter.getAllNodeOptions([imageItem, modelItem, imageItem])).toEqual(
['IMAGE', 'LATENT', 'MODEL']
)
expect(filter.matches(imageItem, 'IMAGE')).toBe(true)
expect(filter.matches(imageItem, 'MODEL')).toBe(false)
expect(filter.matches(imageItem, 'MODEL,IMAGE')).toBe(true)
expect(filter.matches(modelItem, '*', { wildcard: '*' })).toBe(true)
expect(filter.matches(imageItem, 'MODEL', { wildcard: 'IMAGE' })).toBe(true)
expect(filter.matches(modelItem, 'MODEL', { wildcard: 'IMAGE' })).toBe(
false
)
})
})

View File

@@ -0,0 +1,227 @@
import { describe, expect, it } from 'vitest'
import type { JobListItem } from '@/platform/remote/comfyui/jobs/jobTypes'
import type { JobState } from '@/types/queue'
import type { BuildJobDisplayCtx } from '@/utils/queueDisplay'
import { buildJobDisplay, iconForJobState } from '@/utils/queueDisplay'
type QueueDisplayTask = Parameters<typeof buildJobDisplay>[0]
type PreviewOutput = NonNullable<QueueDisplayTask['previewOutput']>
function createJob(
status: JobListItem['status'],
overrides: Partial<JobListItem> = {}
): JobListItem {
return {
id: 'job-123456',
status,
create_time: 1_710_000_000_000,
priority: 12,
...overrides
}
}
function createTask({
job,
jobId = 'job-123456',
createTime = 1_710_000_000_000,
executionTime,
executionTimeInSeconds,
previewOutput
}: {
job?: Partial<JobListItem>
jobId?: string
createTime?: number
executionTime?: number
executionTimeInSeconds?: number
previewOutput?: PreviewOutput
} = {}): QueueDisplayTask {
return {
job: createJob(job?.status ?? 'pending', job),
jobId,
createTime,
executionTime,
executionTimeInSeconds,
previewOutput
} as QueueDisplayTask
}
function createCtx(
overrides: Partial<BuildJobDisplayCtx> = {}
): BuildJobDisplayCtx {
return {
t: (key, values) => {
const entries = Object.entries(values ?? {})
if (!entries.length) return key
return `${key}(${entries
.map(([name, value]) => `${name}=${String(value)}`)
.join(',')})`
},
locale: 'en-US',
formatClockTimeFn: (ts, locale) => `${locale}:${ts}`,
isActive: false,
...overrides
}
}
describe('iconForJobState', () => {
it.for<[JobState, string]>([
['pending', 'icon-[lucide--loader-circle]'],
['initialization', 'icon-[lucide--server-crash]'],
['running', 'icon-[lucide--zap]'],
['completed', 'icon-[lucide--check-check]'],
['failed', 'icon-[lucide--alert-circle]']
])('maps %s to its icon', ([state, icon]) => {
expect(iconForJobState(state)).toBe(icon)
})
})
describe('buildJobDisplay', () => {
it('shows the added hint for pending jobs when requested', () => {
expect(
buildJobDisplay(
createTask(),
'pending',
createCtx({ showAddedHint: true })
)
).toEqual({
iconName: 'icon-[lucide--check]',
primary: 'queue.jobAddedToQueue',
secondary: 'en-US:1710000000000',
showClear: true
})
})
it('shows queued time for pending and initializing jobs', () => {
expect(buildJobDisplay(createTask(), 'pending', createCtx())).toMatchObject(
{
iconName: 'icon-[lucide--loader-circle]',
primary: 'queue.inQueue',
secondary: 'en-US:1710000000000',
showClear: true
}
)
expect(
buildJobDisplay(createTask(), 'initialization', createCtx())
).toMatchObject({
iconName: 'icon-[lucide--server-crash]',
primary: 'queue.initializingAlmostReady',
secondary: 'en-US:1710000000000',
showClear: true
})
})
it('formats active running progress from the injected context', () => {
expect(
buildJobDisplay(
createTask({ job: { status: 'in_progress' } }),
'running',
createCtx({
isActive: true,
totalPercent: 42.7,
currentNodePercent: -10,
currentNodeName: 'KSampler'
})
)
).toEqual({
iconName: 'icon-[lucide--zap]',
primary: 'sideToolbar.queueProgressOverlay.total(percent=43%)',
secondary:
'KSampler sideToolbar.queueProgressOverlay.colonPercent(percent=0%)',
showClear: true
})
})
it('uses a compact running label when the job is not active', () => {
expect(
buildJobDisplay(
createTask({ job: { status: 'in_progress' } }),
'running',
createCtx()
)
).toEqual({
iconName: 'icon-[lucide--zap]',
primary: 'g.running',
secondary: '',
showClear: true
})
})
it('shows local completed jobs as the preview filename', () => {
expect(
buildJobDisplay(
createTask({
job: {
status: 'completed'
},
executionTimeInSeconds: 3.51,
previewOutput: {
filename: 'preview.png',
isImage: true,
url: '/api/view?filename=preview.png&type=output&subfolder='
} as PreviewOutput
}),
'completed',
createCtx()
)
).toEqual({
iconName: 'icon-[lucide--check-check]',
iconImageUrl: '/api/view?filename=preview.png&type=output&subfolder=',
primary: 'preview.png',
secondary: '3.51s',
showClear: false
})
})
it('shows cloud completed jobs as elapsed time', () => {
expect(
buildJobDisplay(
createTask({
job: {
status: 'completed'
},
executionTime: 64_000,
executionTimeInSeconds: 64
}),
'completed',
createCtx({ isCloud: true })
)
).toMatchObject({
iconName: 'icon-[lucide--check-check]',
primary: 'queue.completedIn(duration=1m 4s)',
secondary: '64.00s',
showClear: false
})
})
it('falls back to job title for completed jobs without a preview filename', () => {
expect(
buildJobDisplay(
createTask({
job: {
status: 'completed',
priority: 42
}
}),
'completed',
createCtx()
)
).toMatchObject({
iconName: 'icon-[lucide--check-check]',
primary: 'g.job #42',
secondary: '',
showClear: false
})
})
it('shows failed jobs as clearable failures', () => {
expect(buildJobDisplay(createTask(), 'failed', createCtx())).toEqual({
iconName: 'icon-[lucide--alert-circle]',
primary: 'g.failed',
secondary: 'g.failed',
showClear: true
})
})
})