diff --git a/api/controllers/console/app/message.py b/api/controllers/console/app/message.py index 5e79e8dece..d4ce5921c2 100644 --- a/api/controllers/console/app/message.py +++ b/api/controllers/console/app/message.py @@ -5,7 +5,6 @@ from flask_restful import Resource, fields, marshal_with, reqparse from flask_restful.inputs import int_range from werkzeug.exceptions import Forbidden, InternalServerError, NotFound -import services from controllers.console import api from controllers.console.app.error import ( CompletionRequestError, @@ -133,7 +132,7 @@ class MessageFeedbackApi(Resource): rating=args.get("rating"), content=None, ) - except services.errors.message.MessageNotExistsError: + except MessageNotExistsError: raise NotFound("Message Not Exists.") return {"result": "success"} diff --git a/api/controllers/console/auth/error.py b/api/controllers/console/auth/error.py index 1984339add..8c5e23de58 100644 --- a/api/controllers/console/auth/error.py +++ b/api/controllers/console/auth/error.py @@ -113,9 +113,3 @@ class MemberNotInTenantError(BaseHTTPException): error_code = "member_not_in_tenant" description = "The member is not in the workspace." code = 400 - - -class AccountInFreezeError(BaseHTTPException): - error_code = "account_in_freeze" - description = "This email is temporarily unavailable." - code = 400 diff --git a/api/controllers/console/explore/message.py b/api/controllers/console/explore/message.py index 822777604a..de95a9e7b0 100644 --- a/api/controllers/console/explore/message.py +++ b/api/controllers/console/explore/message.py @@ -5,7 +5,6 @@ from flask_restful import marshal_with, reqparse from flask_restful.inputs import int_range from werkzeug.exceptions import InternalServerError, NotFound -import services from controllers.console.app.error import ( AppMoreLikeThisDisabledError, CompletionRequestError, @@ -29,7 +28,11 @@ from models.model import AppMode from services.app_generate_service import AppGenerateService from services.errors.app import MoreLikeThisDisabledError from services.errors.conversation import ConversationNotExistsError -from services.errors.message import MessageNotExistsError, SuggestedQuestionsAfterAnswerDisabledError +from services.errors.message import ( + FirstMessageNotExistsError, + MessageNotExistsError, + SuggestedQuestionsAfterAnswerDisabledError, +) from services.message_service import MessageService @@ -52,9 +55,9 @@ class MessageListApi(InstalledAppResource): return MessageService.pagination_by_first_id( app_model, current_user, args["conversation_id"], args["first_id"], args["limit"] ) - except services.errors.conversation.ConversationNotExistsError: + except ConversationNotExistsError: raise NotFound("Conversation Not Exists.") - except services.errors.message.FirstMessageNotExistsError: + except FirstMessageNotExistsError: raise NotFound("First Message Not Exists.") @@ -77,7 +80,7 @@ class MessageFeedbackApi(InstalledAppResource): rating=args.get("rating"), content=args.get("content"), ) - except services.errors.message.MessageNotExistsError: + except MessageNotExistsError: raise NotFound("Message Not Exists.") return {"result": "success"} diff --git a/api/controllers/console/workspace/account.py b/api/controllers/console/workspace/account.py index 45513c368d..4d5357cd18 100644 --- a/api/controllers/console/workspace/account.py +++ b/api/controllers/console/workspace/account.py @@ -9,14 +9,13 @@ from configs import dify_config from constants.languages import supported_language from controllers.console import api from controllers.console.auth.error import ( - AccountInFreezeError, EmailAlreadyInUseError, EmailChangeLimitError, EmailCodeError, InvalidEmailError, InvalidTokenError, ) -from controllers.console.error import AccountNotFound, EmailSendIpLimitError +from controllers.console.error import AccountInFreezeError, AccountNotFound, EmailSendIpLimitError from controllers.console.workspace.error import ( AccountAlreadyInitedError, CurrentPasswordIncorrectError, @@ -496,7 +495,7 @@ class ChangeEmailResetApi(Resource): if current_user.email != old_email: raise AccountNotFound() - updated_account = AccountService.update_account(current_user, email=args["new_email"]) + updated_account = AccountService.update_account_email(current_user, email=args["new_email"]) AccountService.send_change_email_completed_notify_email( email=args["new_email"], diff --git a/api/controllers/service_api/app/message.py b/api/controllers/service_api/app/message.py index d90fa2081f..a4f95cb1cb 100644 --- a/api/controllers/service_api/app/message.py +++ b/api/controllers/service_api/app/message.py @@ -15,7 +15,11 @@ from fields.message_fields import agent_thought_fields, feedback_fields from fields.raws import FilesContainedField from libs.helper import TimestampField, uuid_value from models.model import App, AppMode, EndUser -from services.errors.message import SuggestedQuestionsAfterAnswerDisabledError +from services.errors.message import ( + FirstMessageNotExistsError, + MessageNotExistsError, + SuggestedQuestionsAfterAnswerDisabledError, +) from services.message_service import MessageService @@ -65,7 +69,7 @@ class MessageListApi(Resource): ) except services.errors.conversation.ConversationNotExistsError: raise NotFound("Conversation Not Exists.") - except services.errors.message.FirstMessageNotExistsError: + except FirstMessageNotExistsError: raise NotFound("First Message Not Exists.") @@ -87,7 +91,7 @@ class MessageFeedbackApi(Resource): rating=args.get("rating"), content=args.get("content"), ) - except services.errors.message.MessageNotExistsError: + except MessageNotExistsError: raise NotFound("Message Not Exists.") return {"result": "success"} @@ -117,7 +121,7 @@ class MessageSuggestedApi(Resource): questions = MessageService.get_suggested_questions_after_answer( app_model=app_model, user=end_user, message_id=message_id, invoke_from=InvokeFrom.SERVICE_API ) - except services.errors.message.MessageNotExistsError: + except MessageNotExistsError: raise NotFound("Message Not Exists.") except SuggestedQuestionsAfterAnswerDisabledError: raise BadRequest("Suggested Questions Is Disabled.") diff --git a/api/controllers/web/message.py b/api/controllers/web/message.py index f2e1873601..7bb81cd0d3 100644 --- a/api/controllers/web/message.py +++ b/api/controllers/web/message.py @@ -4,7 +4,6 @@ from flask_restful import fields, marshal_with, reqparse from flask_restful.inputs import int_range from werkzeug.exceptions import InternalServerError, NotFound -import services from controllers.web import api from controllers.web.error import ( AppMoreLikeThisDisabledError, @@ -29,7 +28,11 @@ from models.model import AppMode from services.app_generate_service import AppGenerateService from services.errors.app import MoreLikeThisDisabledError from services.errors.conversation import ConversationNotExistsError -from services.errors.message import MessageNotExistsError, SuggestedQuestionsAfterAnswerDisabledError +from services.errors.message import ( + FirstMessageNotExistsError, + MessageNotExistsError, + SuggestedQuestionsAfterAnswerDisabledError, +) from services.message_service import MessageService @@ -73,9 +76,9 @@ class MessageListApi(WebApiResource): return MessageService.pagination_by_first_id( app_model, end_user, args["conversation_id"], args["first_id"], args["limit"] ) - except services.errors.conversation.ConversationNotExistsError: + except ConversationNotExistsError: raise NotFound("Conversation Not Exists.") - except services.errors.message.FirstMessageNotExistsError: + except FirstMessageNotExistsError: raise NotFound("First Message Not Exists.") @@ -96,7 +99,7 @@ class MessageFeedbackApi(WebApiResource): rating=args.get("rating"), content=args.get("content"), ) - except services.errors.message.MessageNotExistsError: + except MessageNotExistsError: raise NotFound("Message Not Exists.") return {"result": "success"} diff --git a/api/core/ops/entities/config_entity.py b/api/core/ops/entities/config_entity.py index 89ff0cfded..626782cee5 100644 --- a/api/core/ops/entities/config_entity.py +++ b/api/core/ops/entities/config_entity.py @@ -102,7 +102,7 @@ class LangfuseConfig(BaseTracingConfig): @field_validator("host") @classmethod def host_validator(cls, v, info: ValidationInfo): - return cls.validate_endpoint_url(v, "https://api.langfuse.com") + return validate_url_with_path(v, "https://api.langfuse.com") class LangSmithConfig(BaseTracingConfig): diff --git a/api/core/ops/utils.py b/api/core/ops/utils.py index 573e8cac88..2c0afb1600 100644 --- a/api/core/ops/utils.py +++ b/api/core/ops/utils.py @@ -67,7 +67,13 @@ def generate_dotted_order( def validate_url(url: str, default_url: str, allowed_schemes: tuple = ("https", "http")) -> str: """ - Validate and normalize URL with proper error handling + Validate and normalize URL with proper error handling. + + NOTE: This function does not retain the `path` component of the provided URL. + In most cases, it is recommended to use `validate_url_with_path` instead. + + This function is deprecated and retained only for compatibility purposes. + New implementations should use `validate_url_with_path`. Args: url: The URL to validate diff --git a/api/core/workflow/nodes/tool/entities.py b/api/core/workflow/nodes/tool/entities.py index 4f47fb1efc..c1cfbb1edc 100644 --- a/api/core/workflow/nodes/tool/entities.py +++ b/api/core/workflow/nodes/tool/entities.py @@ -55,7 +55,7 @@ class ToolNodeData(BaseNodeData, ToolEntity): if not isinstance(val, str): raise ValueError("value must be a list of strings") elif typ == "constant" and not isinstance(value, str | int | float | bool | dict): - raise ValueError("value must be a string, int, float, or bool") + raise ValueError("value must be a string, int, float, bool or dict") return typ tool_parameters: dict[str, ToolInput] diff --git a/api/pyproject.toml b/api/pyproject.toml index 7ec8a91198..be42b509ed 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dify-api" -version = "1.7.0" +version = "1.7.1" requires-python = ">=3.11,<3.13" dependencies = [ diff --git a/api/services/account_service.py b/api/services/account_service.py index 34b3ce0543..1cce8e67a4 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -355,6 +355,17 @@ class AccountService: db.session.commit() return account + @staticmethod + def update_account_email(account: Account, email: str) -> Account: + """Update account email""" + account.email = email + account_integrate = db.session.query(AccountIntegrate).filter_by(account_id=account.id).first() + if account_integrate: + db.session.delete(account_integrate) + db.session.add(account) + db.session.commit() + return account + @staticmethod def update_login_info(account: Account, *, ip_address: str) -> None: """Update last login time and ip""" diff --git a/api/services/app_service.py b/api/services/app_service.py index 3557f13337..0f22666d5a 100644 --- a/api/services/app_service.py +++ b/api/services/app_service.py @@ -53,9 +53,10 @@ class AppService: if args.get("name"): name = args["name"][:30] filters.append(App.name.ilike(f"%{name}%")) - if args.get("tag_ids"): + # Check if tag_ids is not empty to avoid WHERE false condition + if args.get("tag_ids") and len(args["tag_ids"]) > 0: target_ids = TagService.get_target_ids_by_tag_ids("app", tenant_id, args["tag_ids"]) - if target_ids: + if target_ids and len(target_ids) > 0: filters.append(App.id.in_(target_ids)) else: return None diff --git a/api/services/conversation_service.py b/api/services/conversation_service.py index 525c87fe4a..206c832a20 100644 --- a/api/services/conversation_service.py +++ b/api/services/conversation_service.py @@ -46,9 +46,11 @@ class ConversationService: Conversation.from_account_id == (user.id if isinstance(user, Account) else None), or_(Conversation.invoke_from.is_(None), Conversation.invoke_from == invoke_from.value), ) - if include_ids is not None: + # Check if include_ids is not None and not empty to avoid WHERE false condition + if include_ids is not None and len(include_ids) > 0: stmt = stmt.where(Conversation.id.in_(include_ids)) - if exclude_ids is not None: + # Check if exclude_ids is not None and not empty to avoid WHERE false condition + if exclude_ids is not None and len(exclude_ids) > 0: stmt = stmt.where(~Conversation.id.in_(exclude_ids)) # define sort fields and directions diff --git a/api/services/dataset_service.py b/api/services/dataset_service.py index 209d153b0c..1280399990 100644 --- a/api/services/dataset_service.py +++ b/api/services/dataset_service.py @@ -91,14 +91,16 @@ class DatasetService: if user.current_role == TenantAccountRole.DATASET_OPERATOR: # only show datasets that the user has permission to access - if permitted_dataset_ids: + # Check if permitted_dataset_ids is not empty to avoid WHERE false condition + if permitted_dataset_ids and len(permitted_dataset_ids) > 0: query = query.where(Dataset.id.in_(permitted_dataset_ids)) else: return [], 0 else: if user.current_role != TenantAccountRole.OWNER or not include_all: # show all datasets that the user has permission to access - if permitted_dataset_ids: + # Check if permitted_dataset_ids is not empty to avoid WHERE false condition + if permitted_dataset_ids and len(permitted_dataset_ids) > 0: query = query.where( db.or_( Dataset.permission == DatasetPermissionEnum.ALL_TEAM, @@ -127,9 +129,10 @@ class DatasetService: if search: query = query.where(Dataset.name.ilike(f"%{search}%")) - if tag_ids: + # Check if tag_ids is not empty to avoid WHERE false condition + if tag_ids and len(tag_ids) > 0: target_ids = TagService.get_target_ids_by_tag_ids("knowledge", tenant_id, tag_ids) - if target_ids: + if target_ids and len(target_ids) > 0: query = query.where(Dataset.id.in_(target_ids)) else: return [], 0 @@ -158,6 +161,9 @@ class DatasetService: @staticmethod def get_datasets_by_ids(ids, tenant_id): + # Check if ids is not empty to avoid WHERE false condition + if not ids or len(ids) == 0: + return [], 0 stmt = select(Dataset).where(Dataset.id.in_(ids), Dataset.tenant_id == tenant_id) datasets = db.paginate(select=stmt, page=1, per_page=len(ids), max_per_page=len(ids), error_out=False) @@ -951,6 +957,9 @@ class DocumentService: @staticmethod def delete_documents(dataset: Dataset, document_ids: list[str]): + # Check if document_ids is not empty to avoid WHERE false condition + if not document_ids or len(document_ids) == 0: + return documents = db.session.query(Document).where(Document.id.in_(document_ids)).all() file_ids = [ document.data_source_info_dict["upload_file_id"] @@ -2320,6 +2329,9 @@ class SegmentService: @classmethod def delete_segments(cls, segment_ids: list, document: Document, dataset: Dataset): + # Check if segment_ids is not empty to avoid WHERE false condition + if not segment_ids or len(segment_ids) == 0: + return index_node_ids = ( db.session.query(DocumentSegment) .with_entities(DocumentSegment.index_node_id) @@ -2339,6 +2351,9 @@ class SegmentService: @classmethod def update_segments_status(cls, segment_ids: list, action: str, dataset: Dataset, document: Document): + # Check if segment_ids is not empty to avoid WHERE false condition + if not segment_ids or len(segment_ids) == 0: + return if action == "enable": segments = ( db.session.query(DocumentSegment) @@ -2600,7 +2615,8 @@ class SegmentService: DocumentSegment.document_id == document_id, DocumentSegment.tenant_id == tenant_id ) - if status_list: + # Check if status_list is not empty to avoid WHERE false condition + if status_list and len(status_list) > 0: query = query.where(DocumentSegment.status.in_(status_list)) if keyword: diff --git a/api/services/message_service.py b/api/services/message_service.py index 283b7b9b4b..a19d6ee157 100644 --- a/api/services/message_service.py +++ b/api/services/message_service.py @@ -111,7 +111,8 @@ class MessageService: base_query = base_query.where(Message.conversation_id == conversation.id) - if include_ids is not None: + # Check if include_ids is not None and not empty to avoid WHERE false condition + if include_ids is not None and len(include_ids) > 0: base_query = base_query.where(Message.id.in_(include_ids)) if last_id: diff --git a/api/services/tag_service.py b/api/services/tag_service.py index 75fa52a75c..2e5e96214b 100644 --- a/api/services/tag_service.py +++ b/api/services/tag_service.py @@ -26,6 +26,9 @@ class TagService: @staticmethod def get_target_ids_by_tag_ids(tag_type: str, current_tenant_id: str, tag_ids: list) -> list: + # Check if tag_ids is not empty to avoid WHERE false condition + if not tag_ids or len(tag_ids) == 0: + return [] tags = ( db.session.query(Tag) .where(Tag.id.in_(tag_ids), Tag.tenant_id == current_tenant_id, Tag.type == tag_type) @@ -34,6 +37,9 @@ class TagService: if not tags: return [] tag_ids = [tag.id for tag in tags] + # Check if tag_ids is not empty to avoid WHERE false condition + if not tag_ids or len(tag_ids) == 0: + return [] tag_bindings = ( db.session.query(TagBinding.target_id) .where(TagBinding.tag_id.in_(tag_ids), TagBinding.tenant_id == current_tenant_id) diff --git a/api/tasks/retry_document_indexing_task.py b/api/tasks/retry_document_indexing_task.py index a868cb500b..2576d7b051 100644 --- a/api/tasks/retry_document_indexing_task.py +++ b/api/tasks/retry_document_indexing_task.py @@ -24,79 +24,83 @@ def retry_document_indexing_task(dataset_id: str, document_ids: list[str]): """ documents: list[Document] = [] start_at = time.perf_counter() + try: + dataset = db.session.query(Dataset).where(Dataset.id == dataset_id).first() + if not dataset: + logging.info(click.style(f"Dataset not found: {dataset_id}", fg="red")) + return + tenant_id = dataset.tenant_id + for document_id in document_ids: + retry_indexing_cache_key = f"document_{document_id}_is_retried" + # check document limit + features = FeatureService.get_features(tenant_id) + try: + if features.billing.enabled: + vector_space = features.vector_space + if 0 < vector_space.limit <= vector_space.size: + raise ValueError( + "Your total number of documents plus the number of uploads have over the limit of " + "your subscription." + ) + except Exception as e: + document = ( + db.session.query(Document) + .where(Document.id == document_id, Document.dataset_id == dataset_id) + .first() + ) + if document: + document.indexing_status = "error" + document.error = str(e) + document.stopped_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None) + db.session.add(document) + db.session.commit() + redis_client.delete(retry_indexing_cache_key) + return - dataset = db.session.query(Dataset).where(Dataset.id == dataset_id).first() - if not dataset: - logging.info(click.style(f"Dataset not found: {dataset_id}", fg="red")) - db.session.close() - return - tenant_id = dataset.tenant_id - for document_id in document_ids: - retry_indexing_cache_key = f"document_{document_id}_is_retried" - # check document limit - features = FeatureService.get_features(tenant_id) - try: - if features.billing.enabled: - vector_space = features.vector_space - if 0 < vector_space.limit <= vector_space.size: - raise ValueError( - "Your total number of documents plus the number of uploads have over the limit of " - "your subscription." - ) - except Exception as e: + logging.info(click.style(f"Start retry document: {document_id}", fg="green")) document = ( db.session.query(Document).where(Document.id == document_id, Document.dataset_id == dataset_id).first() ) - if document: + if not document: + logging.info(click.style(f"Document not found: {document_id}", fg="yellow")) + return + try: + # clean old data + index_processor = IndexProcessorFactory(document.doc_form).init_index_processor() + + segments = db.session.query(DocumentSegment).where(DocumentSegment.document_id == document_id).all() + if segments: + index_node_ids = [segment.index_node_id for segment in segments] + # delete from vector index + index_processor.clean(dataset, index_node_ids, with_keywords=True, delete_child_chunks=True) + + for segment in segments: + db.session.delete(segment) + db.session.commit() + + document.indexing_status = "parsing" + document.processing_started_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None) + db.session.add(document) + db.session.commit() + + indexing_runner = IndexingRunner() + indexing_runner.run([document]) + redis_client.delete(retry_indexing_cache_key) + except Exception as ex: document.indexing_status = "error" - document.error = str(e) + document.error = str(ex) document.stopped_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None) db.session.add(document) db.session.commit() - redis_client.delete(retry_indexing_cache_key) - db.session.close() - return - - logging.info(click.style(f"Start retry document: {document_id}", fg="green")) - document = ( - db.session.query(Document).where(Document.id == document_id, Document.dataset_id == dataset_id).first() + logging.info(click.style(str(ex), fg="yellow")) + redis_client.delete(retry_indexing_cache_key) + logging.exception("retry_document_indexing_task failed, document_id: %s", document_id) + end_at = time.perf_counter() + logging.info(click.style(f"Retry dataset: {dataset_id} latency: {end_at - start_at}", fg="green")) + except Exception as e: + logging.exception( + "retry_document_indexing_task failed, dataset_id: %s, document_ids: %s", dataset_id, document_ids ) - if not document: - logging.info(click.style(f"Document not found: {document_id}", fg="yellow")) - db.session.close() - return - try: - # clean old data - index_processor = IndexProcessorFactory(document.doc_form).init_index_processor() - - segments = db.session.query(DocumentSegment).where(DocumentSegment.document_id == document_id).all() - if segments: - index_node_ids = [segment.index_node_id for segment in segments] - # delete from vector index - index_processor.clean(dataset, index_node_ids, with_keywords=True, delete_child_chunks=True) - - for segment in segments: - db.session.delete(segment) - db.session.commit() - - document.indexing_status = "parsing" - document.processing_started_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None) - db.session.add(document) - db.session.commit() - - indexing_runner = IndexingRunner() - indexing_runner.run([document]) - redis_client.delete(retry_indexing_cache_key) - except Exception as ex: - document.indexing_status = "error" - document.error = str(ex) - document.stopped_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None) - db.session.add(document) - db.session.commit() - logging.info(click.style(str(ex), fg="yellow")) - redis_client.delete(retry_indexing_cache_key) - logging.exception("retry_document_indexing_task failed, document_id: %s", document_id) - finally: - db.session.close() - end_at = time.perf_counter() - logging.info(click.style(f"Retry dataset: {dataset_id} latency: {end_at - start_at}", fg="green")) + raise e + finally: + db.session.close() diff --git a/api/tests/unit_tests/core/ops/test_config_entity.py b/api/tests/unit_tests/core/ops/test_config_entity.py index 81cb04548d..4bcc6cb605 100644 --- a/api/tests/unit_tests/core/ops/test_config_entity.py +++ b/api/tests/unit_tests/core/ops/test_config_entity.py @@ -117,6 +117,13 @@ class TestLangfuseConfig: assert config.secret_key == "secret_key" assert config.host == "https://custom.langfuse.com" + def test_valid_config_with_path(self): + host = host = "https://custom.langfuse.com/api/v1" + config = LangfuseConfig(public_key="public_key", secret_key="secret_key", host=host) + assert config.public_key == "public_key" + assert config.secret_key == "secret_key" + assert config.host == host + def test_default_values(self): """Test default values are set correctly""" config = LangfuseConfig(public_key="public", secret_key="secret") diff --git a/api/uv.lock b/api/uv.lock index 623b125ab3..0bce38812e 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -1217,7 +1217,7 @@ wheels = [ [[package]] name = "dify-api" -version = "1.7.0" +version = "1.7.1" source = { virtual = "." } dependencies = [ { name = "arize-phoenix-otel" }, diff --git a/docker/docker-compose-template.yaml b/docker/docker-compose-template.yaml index 394a068200..fe8e4602b7 100644 --- a/docker/docker-compose-template.yaml +++ b/docker/docker-compose-template.yaml @@ -2,7 +2,7 @@ x-shared-env: &shared-api-worker-env services: # API service api: - image: langgenius/dify-api:1.7.0 + image: langgenius/dify-api:1.7.1 restart: always environment: # Use the shared environment variables. @@ -31,7 +31,7 @@ services: # worker service # The Celery worker for processing the queue. worker: - image: langgenius/dify-api:1.7.0 + image: langgenius/dify-api:1.7.1 restart: always environment: # Use the shared environment variables. @@ -58,7 +58,7 @@ services: # worker_beat service # Celery beat for scheduling periodic tasks. worker_beat: - image: langgenius/dify-api:1.7.0 + image: langgenius/dify-api:1.7.1 restart: always environment: # Use the shared environment variables. @@ -76,7 +76,7 @@ services: # Frontend web application. web: - image: langgenius/dify-web:1.7.0 + image: langgenius/dify-web:1.7.1 restart: always environment: CONSOLE_API_URL: ${CONSOLE_API_URL:-} diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 2c1429b5da..9e0f78eb07 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -542,7 +542,7 @@ x-shared-env: &shared-api-worker-env services: # API service api: - image: langgenius/dify-api:1.7.0 + image: langgenius/dify-api:1.7.1 restart: always environment: # Use the shared environment variables. @@ -571,7 +571,7 @@ services: # worker service # The Celery worker for processing the queue. worker: - image: langgenius/dify-api:1.7.0 + image: langgenius/dify-api:1.7.1 restart: always environment: # Use the shared environment variables. @@ -598,7 +598,7 @@ services: # worker_beat service # Celery beat for scheduling periodic tasks. worker_beat: - image: langgenius/dify-api:1.7.0 + image: langgenius/dify-api:1.7.1 restart: always environment: # Use the shared environment variables. @@ -616,7 +616,7 @@ services: # Frontend web application. web: - image: langgenius/dify-web:1.7.0 + image: langgenius/dify-web:1.7.1 restart: always environment: CONSOLE_API_URL: ${CONSOLE_API_URL:-} diff --git a/web/__tests__/i18n-upload-features.test.ts b/web/__tests__/i18n-upload-features.test.ts new file mode 100644 index 0000000000..37aefcbef4 --- /dev/null +++ b/web/__tests__/i18n-upload-features.test.ts @@ -0,0 +1,119 @@ +/** + * Test suite for verifying upload feature translations across all locales + * Specifically tests for issue #23062: Missing Upload feature translations (esp. audioUpload) across most locales + */ + +import fs from 'node:fs' +import path from 'node:path' + +// Get all supported locales from the i18n directory +const I18N_DIR = path.join(__dirname, '../i18n') +const getSupportedLocales = (): string[] => { + return fs.readdirSync(I18N_DIR) + .filter(item => fs.statSync(path.join(I18N_DIR, item)).isDirectory()) + .sort() +} + +// Helper function to load translation file content +const loadTranslationContent = (locale: string): string => { + const filePath = path.join(I18N_DIR, locale, 'app-debug.ts') + + if (!fs.existsSync(filePath)) + throw new Error(`Translation file not found: ${filePath}`) + + return fs.readFileSync(filePath, 'utf-8') +} + +// Helper function to check if upload features exist +const hasUploadFeatures = (content: string): { [key: string]: boolean } => { + return { + fileUpload: /fileUpload\s*:\s*{/.test(content), + imageUpload: /imageUpload\s*:\s*{/.test(content), + documentUpload: /documentUpload\s*:\s*{/.test(content), + audioUpload: /audioUpload\s*:\s*{/.test(content), + featureBar: /bar\s*:\s*{/.test(content), + } +} + +describe('Upload Features i18n Translations - Issue #23062', () => { + let supportedLocales: string[] + + beforeAll(() => { + supportedLocales = getSupportedLocales() + console.log(`Testing ${supportedLocales.length} locales for upload features`) + }) + + test('all locales should have translation files', () => { + supportedLocales.forEach((locale) => { + const filePath = path.join(I18N_DIR, locale, 'app-debug.ts') + expect(fs.existsSync(filePath)).toBe(true) + }) + }) + + test('all locales should have required upload features', () => { + const results: { [locale: string]: { [feature: string]: boolean } } = {} + + supportedLocales.forEach((locale) => { + const content = loadTranslationContent(locale) + const features = hasUploadFeatures(content) + results[locale] = features + + // Check that all upload features exist + expect(features.fileUpload).toBe(true) + expect(features.imageUpload).toBe(true) + expect(features.documentUpload).toBe(true) + expect(features.audioUpload).toBe(true) + expect(features.featureBar).toBe(true) + }) + + console.log('✅ All locales have complete upload features') + }) + + test('previously missing locales should now have audioUpload - Issue #23062', () => { + // These locales were specifically missing audioUpload + const previouslyMissingLocales = ['fa-IR', 'hi-IN', 'ro-RO', 'sl-SI', 'th-TH', 'uk-UA', 'vi-VN'] + + previouslyMissingLocales.forEach((locale) => { + const content = loadTranslationContent(locale) + + // Verify audioUpload exists + expect(/audioUpload\s*:\s*{/.test(content)).toBe(true) + + // Verify it has title and description + expect(/audioUpload[^}]*title\s*:/.test(content)).toBe(true) + expect(/audioUpload[^}]*description\s*:/.test(content)).toBe(true) + + console.log(`✅ ${locale} - Issue #23062 resolved: audioUpload feature present`) + }) + }) + + test('upload features should have required properties', () => { + supportedLocales.forEach((locale) => { + const content = loadTranslationContent(locale) + + // Check fileUpload has required properties + if (/fileUpload\s*:\s*{/.test(content)) { + expect(/fileUpload[^}]*title\s*:/.test(content)).toBe(true) + expect(/fileUpload[^}]*description\s*:/.test(content)).toBe(true) + } + + // Check imageUpload has required properties + if (/imageUpload\s*:\s*{/.test(content)) { + expect(/imageUpload[^}]*title\s*:/.test(content)).toBe(true) + expect(/imageUpload[^}]*description\s*:/.test(content)).toBe(true) + } + + // Check documentUpload has required properties + if (/documentUpload\s*:\s*{/.test(content)) { + expect(/documentUpload[^}]*title\s*:/.test(content)).toBe(true) + expect(/documentUpload[^}]*description\s*:/.test(content)).toBe(true) + } + + // Check audioUpload has required properties + if (/audioUpload\s*:\s*{/.test(content)) { + expect(/audioUpload[^}]*title\s*:/.test(content)).toBe(true) + expect(/audioUpload[^}]*description\s*:/.test(content)).toBe(true) + } + }) + }) +}) diff --git a/web/app/components/app/annotation/header-opts/index.tsx b/web/app/components/app/annotation/header-opts/index.tsx index dcf6176cc0..463ae58ac2 100644 --- a/web/app/components/app/annotation/header-opts/index.tsx +++ b/web/app/components/app/annotation/header-opts/index.tsx @@ -88,10 +88,10 @@ const HeaderOptions: FC = ({ await clearAllAnnotations(appId) onAdded() } -catch (_) { + catch (_) { } - finally { - setShowClearConfirm(false) + finally { + setShowClearConfirm(false) } } const Operations = () => { @@ -146,7 +146,7 @@ catch (_) { onClick={handleClearAll} className='mx-1 flex h-9 w-[calc(100%_-_8px)] cursor-pointer items-center space-x-2 rounded-lg px-3 py-2 text-red-600 hover:bg-red-50 disabled:opacity-50' > - + {t('appAnnotation.table.header.clearAll')} @@ -168,11 +168,9 @@ catch (_) { position="br" trigger="click" btnElement={ - + } - btnClassName='p-0 border-0' + btnClassName='btn btn-secondary btn-medium w-8 p-0' className={'!z-20 h-fit !w-[155px]'} popupClassName='!w-full !overflow-visible' manualClose @@ -196,12 +194,12 @@ catch (_) { ) } { - showClearConfirm && ( - setShowClearConfirm(false)} - onConfirm={handleConfirmed} - /> + showClearConfirm && ( + setShowClearConfirm(false)} + onConfirm={handleConfirmed} + /> ) } diff --git a/web/app/components/app/configuration/base/warning-mask/index.tsx b/web/app/components/app/configuration/base/warning-mask/index.tsx index fbe58bee11..8bd7ea12aa 100644 --- a/web/app/components/app/configuration/base/warning-mask/index.tsx +++ b/web/app/components/app/configuration/base/warning-mask/index.tsx @@ -22,14 +22,14 @@ const WarningMask: FC = ({ footer, }) => { return ( -
-
{warningIcon}
-
+
{warningIcon}
+
{title}
-
+
{description}
diff --git a/web/app/components/app/configuration/base/warning-mask/style.module.css b/web/app/components/app/configuration/base/warning-mask/style.module.css index 87f226fd96..a2c394de2a 100644 --- a/web/app/components/app/configuration/base/warning-mask/style.module.css +++ b/web/app/components/app/configuration/base/warning-mask/style.module.css @@ -1,5 +1,4 @@ .mask { - background-color: rgba(239, 244, 255, 0.9); backdrop-filter: blur(2px); } diff --git a/web/app/components/app/configuration/config-var/config-modal/index.tsx b/web/app/components/app/configuration/config-var/config-modal/index.tsx index 8fcc0f4c08..27072f5208 100644 --- a/web/app/components/app/configuration/config-var/config-modal/index.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/index.tsx @@ -20,6 +20,7 @@ import FileUploadSetting from '@/app/components/workflow/nodes/_base/components/ import Checkbox from '@/app/components/base/checkbox' import { DEFAULT_FILE_UPLOAD_SETTING } from '@/app/components/workflow/constants' import { DEFAULT_VALUE_MAX_LEN } from '@/config' +import { SimpleSelect } from '@/app/components/base/select' const TEXT_MAX_LENGTH = 256 @@ -234,9 +235,31 @@ const ConfigModal: FC = ({ )} {type === InputVarType.select && ( - - - + <> + + + + {options && options.length > 0 && ( + + opt.trim() !== '').map(option => ({ + value: option, + name: option, + })), + ]} + defaultValue={tempPayload.default || ''} + onSelect={item => handlePayloadChange('default')(item.value === '' ? undefined : item.value)} + placeholder={t('appDebug.variableConfig.selectDefaultValue')} + allowSearch={false} + /> + + )} + )} {[InputVarType.singleFile, InputVarType.multiFiles].includes(type) && ( diff --git a/web/app/components/base/chat/chat-with-history/hooks.tsx b/web/app/components/base/chat/chat-with-history/hooks.tsx index 76eb89164e..382ded3201 100644 --- a/web/app/components/base/chat/chat-with-history/hooks.tsx +++ b/web/app/components/base/chat/chat-with-history/hooks.tsx @@ -211,7 +211,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { const isInputInOptions = item.select.options.includes(initInputs[item.select.variable]) return { ...item.select, - default: (isInputInOptions ? initInputs[item.select.variable] : undefined) || item.default, + default: (isInputInOptions ? initInputs[item.select.variable] : undefined) || item.select.default, type: 'select', } } diff --git a/web/app/components/base/chat/chat-with-history/inputs-form/content.tsx b/web/app/components/base/chat/chat-with-history/inputs-form/content.tsx index 73a1f07b69..3304d50a50 100644 --- a/web/app/components/base/chat/chat-with-history/inputs-form/content.tsx +++ b/web/app/components/base/chat/chat-with-history/inputs-form/content.tsx @@ -73,7 +73,7 @@ const InputsFormContent = ({ showTip }: Props) => { {form.type === InputVarType.select && ( ({ value: option, name: option }))} onSelect={item => handleFormChange(form.variable, item.value as string)} placeholder={form.label} diff --git a/web/app/components/base/chat/embedded-chatbot/hooks.tsx b/web/app/components/base/chat/embedded-chatbot/hooks.tsx index 8ae86bda84..4e86ad50e4 100644 --- a/web/app/components/base/chat/embedded-chatbot/hooks.tsx +++ b/web/app/components/base/chat/embedded-chatbot/hooks.tsx @@ -199,7 +199,7 @@ export const useEmbeddedChatbot = () => { const isInputInOptions = item.select.options.includes(initInputs[item.select.variable]) return { ...item.select, - default: (isInputInOptions ? initInputs[item.select.variable] : undefined) || item.default, + default: (isInputInOptions ? initInputs[item.select.variable] : undefined) || item.select.default, type: 'select', } } diff --git a/web/app/components/base/chat/embedded-chatbot/inputs-form/content.tsx b/web/app/components/base/chat/embedded-chatbot/inputs-form/content.tsx index c5f39718f1..29fa5394ef 100644 --- a/web/app/components/base/chat/embedded-chatbot/inputs-form/content.tsx +++ b/web/app/components/base/chat/embedded-chatbot/inputs-form/content.tsx @@ -73,7 +73,7 @@ const InputsFormContent = ({ showTip }: Props) => { {form.type === InputVarType.select && ( ({ value: option, name: option }))} onSelect={item => handleFormChange(form.variable, item.value as string)} placeholder={form.label} diff --git a/web/app/components/base/select/index.tsx b/web/app/components/base/select/index.tsx index 77d229672f..d9285c1061 100644 --- a/web/app/components/base/select/index.tsx +++ b/web/app/components/base/select/index.tsx @@ -77,7 +77,6 @@ const Select: FC = ({ defaultSelect = existed setSelectedItem(defaultSelect) - // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultValue]) const filteredItems: Item[] @@ -201,7 +200,6 @@ const SimpleSelect: FC = ({ defaultSelect = existed setSelectedItem(defaultSelect) - // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultValue]) const listboxRef = useRef(null) @@ -344,7 +342,7 @@ const PortalSelect: FC = ({ > diff --git a/web/app/components/base/textarea/index.tsx b/web/app/components/base/textarea/index.tsx index 1e274515f8..43cc33d62e 100644 --- a/web/app/components/base/textarea/index.tsx +++ b/web/app/components/base/textarea/index.tsx @@ -39,7 +39,7 @@ const Textarea = React.forwardRef( destructive && 'border-components-input-border-destructive bg-components-input-bg-destructive text-components-input-text-filled hover:border-components-input-border-destructive hover:bg-components-input-bg-destructive focus:border-components-input-border-destructive focus:bg-components-input-bg-destructive', className, )} - value={value} + value={value ?? ''} onChange={onChange} disabled={disabled} {...props} diff --git a/web/app/components/share/text-generation/run-once/index.tsx b/web/app/components/share/text-generation/run-once/index.tsx index 546b21d2b0..cfafe73bf2 100644 --- a/web/app/components/share/text-generation/run-once/index.tsx +++ b/web/app/components/share/text-generation/run-once/index.tsx @@ -66,7 +66,9 @@ const RunOnce: FC = ({ useEffect(() => { const newInputs: Record = {} promptConfig.prompt_variables.forEach((item) => { - if (item.type === 'string' || item.type === 'paragraph') + if (item.type === 'select') + newInputs[item.key] = item.default + else if (item.type === 'string' || item.type === 'paragraph') newInputs[item.key] = '' else newInputs[item.key] = undefined diff --git a/web/app/components/tools/workflow-tool/configure-button.tsx b/web/app/components/tools/workflow-tool/configure-button.tsx index 0c72f82f39..bf418750b1 100644 --- a/web/app/components/tools/workflow-tool/configure-button.tsx +++ b/web/app/components/tools/workflow-tool/configure-button.tsx @@ -179,8 +179,8 @@ const WorkflowToolConfigureButton = ({ {(!published || !isLoading) && (
{isCurrentWorkspaceManager ? ( diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx index 269f5e0a96..430359b845 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx @@ -158,7 +158,7 @@ const FormItem: FC = ({ type === InputVarType.select && ( handleValueChange(e.target.value)} placeholder={placeholder?.[language] || placeholder?.en_US} /> diff --git a/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx b/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx index d0297cfd74..8758490bee 100644 --- a/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx +++ b/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx @@ -64,55 +64,56 @@ const ClassList: FC = ({ const handleSideWidth = 3 // Todo Remove; edit topic name return ( - ({ ...item }))} - setList={handleSortTopic} - handle='.handle' - ghostClass='bg-components-panel-bg' - animation={150} - disabled={readonly} - className='space-y-2' - > - { - list.map((item, index) => { - const canDrag = (() => { - if (readonly) - return false + <> + ({ ...item }))} + setList={handleSortTopic} + handle='.handle' + ghostClass='bg-components-panel-bg' + animation={150} + disabled={readonly} + className='space-y-2' + > + { + list.map((item, index) => { + const canDrag = (() => { + if (readonly) + return false - return topicCount >= 2 - })() - return ( -
-
- + return topicCount >= 2 + })() + return ( +
+
+ +
-
- ) - }) - } + ) + }) + } + {!readonly && ( )} - - + ) } export default React.memo(ClassList) diff --git a/web/app/components/workflow/nodes/tool/node.tsx b/web/app/components/workflow/nodes/tool/node.tsx index e15ddcaaaa..8cc3ec580d 100644 --- a/web/app/components/workflow/nodes/tool/node.tsx +++ b/web/app/components/workflow/nodes/tool/node.tsx @@ -22,13 +22,13 @@ const Node: FC> = ({ {key}
{typeof tool_configurations[key].value === 'string' && ( -
+
{paramSchemas?.find(i => i.name === key)?.type === FormTypeEnum.secretInput ? '********' : tool_configurations[key].value}
)} {typeof tool_configurations[key].value === 'number' && ( -
- {tool_configurations[key].value} +
+ {Number.isNaN(tool_configurations[key].value) ? '' : tool_configurations[key].value}
)} {typeof tool_configurations[key] !== 'string' && tool_configurations[key]?.type === FormTypeEnum.modelSelector && ( diff --git a/web/app/components/workflow/operator/tip-popup.tsx b/web/app/components/workflow/operator/tip-popup.tsx index 7946e1ddaa..3721ed8118 100644 --- a/web/app/components/workflow/operator/tip-popup.tsx +++ b/web/app/components/workflow/operator/tip-popup.tsx @@ -14,6 +14,7 @@ const TipPopup = ({ }: TipPopupProps) => { return ( s.appDetail) const workflowStore = useWorkflowStore() - const inputs = useStore(s => s.inputs) + const { inputs, setInputs } = useStore(s => ({ + inputs: s.inputs, + setInputs: s.setInputs, + })) + + const initialInputs = useMemo(() => { + const initInputs: Record = {} + if (startVariables) { + startVariables.forEach((variable) => { + if (variable.default) + initInputs[variable.variable] = variable.default + }) + } + return initInputs + }, [startVariables]) + const features = useFeatures(s => s.features) const config = useMemo(() => { return { @@ -82,6 +97,11 @@ const ChatWrapper = ( taskId => stopChatMessageResponding(appDetail!.id, taskId), ) + const handleRestartChat = useCallback(() => { + handleRestart() + setInputs(initialInputs) + }, [handleRestart, setInputs, initialInputs]) + const doSend: OnSend = useCallback((message, files, isRegenerate = false, parentAnswer: ChatItem | null = null) => { handleSend( { @@ -115,9 +135,18 @@ const ChatWrapper = ( useImperativeHandle(ref, () => { return { - handleRestart, + handleRestart: handleRestartChat, } - }, [handleRestart]) + }, [handleRestartChat]) + + useEffect(() => { + if (Object.keys(initialInputs).length > 0) { + setInputs({ + ...initialInputs, + ...inputs, + }) + } + }, [initialInputs]) useEffect(() => { if (isResponding) diff --git a/web/app/components/workflow/panel/inputs-panel.tsx b/web/app/components/workflow/panel/inputs-panel.tsx index 8be8d810f0..64ac6d8686 100644 --- a/web/app/components/workflow/panel/inputs-panel.tsx +++ b/web/app/components/workflow/panel/inputs-panel.tsx @@ -1,6 +1,7 @@ import { memo, useCallback, + useEffect, useMemo, } from 'react' import { useTranslation } from 'react-i18next' @@ -32,9 +33,12 @@ type Props = { const InputsPanel = ({ onRun }: Props) => { const { t } = useTranslation() const workflowStore = useWorkflowStore() + const { inputs, setInputs } = useStore(s => ({ + inputs: s.inputs, + setInputs: s.setInputs, + })) const fileSettings = useFeatures(s => s.features.file) const nodes = useNodes() - const inputs = useStore(s => s.inputs) const files = useStore(s => s.files) const workflowRunningData = useStore(s => s.workflowRunningData) const { @@ -44,6 +48,24 @@ const InputsPanel = ({ onRun }: Props) => { const startVariables = startNode?.data.variables const { checkInputsForm } = useCheckInputsForms() + const initialInputs = useMemo(() => { + const initInputs: Record = {} + if (startVariables) { + startVariables.forEach((variable) => { + if (variable.default) + initInputs[variable.variable] = variable.default + }) + } + return initInputs + }, [startVariables]) + + useEffect(() => { + setInputs({ + ...initialInputs, + ...inputs, + }) + }, [initialInputs]) + const variables = useMemo(() => { const data = startVariables || [] if (fileSettings?.image?.enabled) { diff --git a/web/app/dev-only/i18n-checker/page.tsx b/web/app/dev-only/i18n-checker/page.tsx deleted file mode 100644 index 7341c153c7..0000000000 --- a/web/app/dev-only/i18n-checker/page.tsx +++ /dev/null @@ -1,175 +0,0 @@ -'use client' -import { loadLangResources } from '@/i18n-config/i18next-config' -import { useCallback, useEffect, useState } from 'react' -import cn from '@/utils/classnames' -import { LanguagesSupported } from '@/i18n-config/language' - -export default function I18nTest() { - const [langs, setLangs] = useState([]) - - const getLangs = useCallback(async () => { - const langs = await genLangs() - setLangs(langs) - }, []) - - useEffect(() => { - getLangs() - }, []) - - return ( -
- -
-

Summary

- - - - - - - - - - - - - {langs.map(({ locale, count, missing, extra }, idx) => - - - - - - )} - -
- # - - lang - - count - - missing - - extra -
{idx}{locale}{count}{missing.length}{extra.length}
-
- -

Details

- - - - - - - - - - - - - {langs.map(({ locale, missing, extra }, idx) => { - return ( - - - - - - ) - })} - -
- # - - lang - - missing - - extra -
{idx}{locale} -
    - {missing.map(key => ( -
  • {key}
  • - ))} -
-
-
    - {extra.map(key => ( -
  • {key}
  • - ))} -
-
- -
- ) -} - -async function genLangs() { - const langs_: Lang[] = [] - let en!: Lang - - const resources: Record = {} - // Initialize empty resource object - for (const lang of LanguagesSupported) - resources[lang] = await loadLangResources(lang) - - for (const [key, value] of Object.entries(resources)) { - const keys = getNestedKeys(value.translation) - const lang: Lang = { - locale: key, - keys: new Set(keys), - count: keys.length, - missing: [], - extra: [], - } - - langs_.push(lang) - if (key === 'en-US') en = lang - } - - for (const lang of langs_) { - const missing: string[] = [] - const extra: string[] = [] - - for (const key of lang.keys) - if (!en.keys.has(key)) extra.push(key) - - for (const key of en.keys) - if (!lang.keys.has(key)) missing.push(key) - - lang.missing = missing - lang.extra = extra - } - return langs_ -} - -function getNestedKeys(translation: Record): string[] { - const nestedKeys: string[] = [] - const iterateKeys = (obj: Record, prefix = '') => { - for (const key in obj) { - const nestedKey = prefix ? `${prefix}.${key}` : key - // nestedKeys.push(nestedKey); - if (typeof obj[key] === 'object') iterateKeys(obj[key], nestedKey) - else if (typeof obj[key] === 'string') nestedKeys.push(nestedKey) - } - } - iterateKeys(translation) - return nestedKeys -} - -type Lang = { - locale: string; - keys: Set; - count: number; - missing: string[]; - extra: string[]; -} diff --git a/web/app/dev-only/layout.tsx b/web/app/dev-only/layout.tsx deleted file mode 100644 index d8bcc5e679..0000000000 --- a/web/app/dev-only/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import type React from 'react' -import { notFound } from 'next/navigation' - -export default async function Layout({ children }: React.PropsWithChildren) { - if (process.env.NODE_ENV !== 'development') - notFound() - - return children -} diff --git a/web/app/dev-preview/page.tsx b/web/app/dev-preview/page.tsx deleted file mode 100644 index 69464d612a..0000000000 --- a/web/app/dev-preview/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client' - -import DemoForm from '../components/base/form/form-scenarios/demo' - -export default function Page() { - return ( -
- -
- ) -} diff --git a/web/i18n-config/auto-gen-i18n.js b/web/i18n-config/auto-gen-i18n.js index fa7500e879..45f5606393 100644 --- a/web/i18n-config/auto-gen-i18n.js +++ b/web/i18n-config/auto-gen-i18n.js @@ -1,5 +1,6 @@ const fs = require('node:fs') const path = require('node:path') +const vm = require('node:vm') const transpile = require('typescript').transpile const magicast = require('magicast') const { parseModule, generateCode, loadFile } = magicast @@ -8,6 +9,7 @@ const { translate } = bingTranslate const data = require('./languages.json') const targetLanguage = 'en-US' +const i18nFolder = '../i18n' // Path to i18n folder relative to this script // https://github.com/plainheart/bing-translate-api/blob/master/src/met/lang.json const languageKeyMap = data.languages.reduce((map, language) => { if (language.supported) { @@ -21,11 +23,16 @@ const languageKeyMap = data.languages.reduce((map, language) => { }, {}) async function translateMissingKeyDeeply(sourceObj, targetObject, toLanguage) { + const skippedKeys = [] + const translatedKeys = [] + await Promise.all(Object.keys(sourceObj).map(async (key) => { if (targetObject[key] === undefined) { if (typeof sourceObj[key] === 'object') { targetObject[key] = {} - await translateMissingKeyDeeply(sourceObj[key], targetObject[key], toLanguage) + const result = await translateMissingKeyDeeply(sourceObj[key], targetObject[key], toLanguage) + skippedKeys.push(...result.skipped) + translatedKeys.push(...result.translated) } else { try { @@ -34,75 +41,198 @@ async function translateMissingKeyDeeply(sourceObj, targetObject, toLanguage) { targetObject[key] = '' return } - // not support translate with '(' or ')' - if (source.includes('(') || source.includes(')')) - return + // Only skip obvious code patterns, not normal text with parentheses + const codePatterns = [ + /\{\{.*\}\}/, // Template variables like {{key}} + /\$\{.*\}/, // Template literals ${...} + /<[^>]+>/, // HTML/XML tags + /function\s*\(/, // Function definitions + /=\s*\(/, // Assignment with function calls + ] + + const isCodeLike = codePatterns.some(pattern => pattern.test(source)) + if (isCodeLike) { + console.log(`⏭️ Skipping code-like content: "${source.substring(0, 50)}..."`) + skippedKeys.push(`${key}: ${source}`) + return + } + + console.log(`🔄 Translating: "${source}" to ${toLanguage}`) const { translation } = await translate(sourceObj[key], null, languageKeyMap[toLanguage]) targetObject[key] = translation + translatedKeys.push(`${key}: ${translation}`) + console.log(`✅ Translated: "${translation}"`) } - catch { - console.error(`Error translating "${sourceObj[key]}" to ${toLanguage}. Key: ${key}`) + catch (error) { + console.error(`❌ Error translating "${sourceObj[key]}" to ${toLanguage}. Key: ${key}`, error.message) + skippedKeys.push(`${key}: ${sourceObj[key]} (Error: ${error.message})`) + + // Add retry mechanism for network errors + if (error.message.includes('network') || error.message.includes('timeout')) { + console.log(`🔄 Retrying translation for key: ${key}`) + try { + await new Promise(resolve => setTimeout(resolve, 1000)) // Wait 1 second + const { translation } = await translate(sourceObj[key], null, languageKeyMap[toLanguage]) + targetObject[key] = translation + translatedKeys.push(`${key}: ${translation}`) + console.log(`✅ Retry successful: "${translation}"`) + } + catch (retryError) { + console.error(`❌ Retry failed for key ${key}:`, retryError.message) + } + } } } } else if (typeof sourceObj[key] === 'object') { targetObject[key] = targetObject[key] || {} - await translateMissingKeyDeeply(sourceObj[key], targetObject[key], toLanguage) + const result = await translateMissingKeyDeeply(sourceObj[key], targetObject[key], toLanguage) + skippedKeys.push(...result.skipped) + translatedKeys.push(...result.translated) } })) -} -async function autoGenTrans(fileName, toGenLanguage) { - const fullKeyFilePath = path.join(__dirname, targetLanguage, `${fileName}.ts`) - const toGenLanguageFilePath = path.join(__dirname, toGenLanguage, `${fileName}.ts`) - // eslint-disable-next-line sonarjs/code-eval - const fullKeyContent = eval(transpile(fs.readFileSync(fullKeyFilePath, 'utf8'))) - // if toGenLanguageFilePath is not exist, create it - if (!fs.existsSync(toGenLanguageFilePath)) { - fs.writeFileSync(toGenLanguageFilePath, `const translation = { + return { skipped: skippedKeys, translated: translatedKeys } +} +async function autoGenTrans(fileName, toGenLanguage, isDryRun = false) { + const fullKeyFilePath = path.resolve(__dirname, i18nFolder, targetLanguage, `${fileName}.ts`) + const toGenLanguageFilePath = path.resolve(__dirname, i18nFolder, toGenLanguage, `${fileName}.ts`) + + try { + const content = fs.readFileSync(fullKeyFilePath, 'utf8') + + // Create a safer module environment for vm + const moduleExports = {} + const context = { + exports: moduleExports, + module: { exports: moduleExports }, + require, + console, + __filename: fullKeyFilePath, + __dirname: path.dirname(fullKeyFilePath), + } + + // Use vm.runInNewContext instead of eval for better security + vm.runInNewContext(transpile(content), context) + + const fullKeyContent = moduleExports.default || moduleExports + + if (!fullKeyContent || typeof fullKeyContent !== 'object') + throw new Error(`Failed to extract translation object from ${fullKeyFilePath}`) + + // if toGenLanguageFilePath is not exist, create it + if (!fs.existsSync(toGenLanguageFilePath)) { + fs.writeFileSync(toGenLanguageFilePath, `const translation = { } export default translation `) - } - // To keep object format and format it for magicast to work: const translation = { ... } => export default {...} - const readContent = await loadFile(toGenLanguageFilePath) - const { code: toGenContent } = generateCode(readContent) - const mod = await parseModule(`export default ${toGenContent.replace('export default translation', '').replace('const translation = ', '')}`) - const toGenOutPut = mod.exports.default + } + // To keep object format and format it for magicast to work: const translation = { ... } => export default {...} + const readContent = await loadFile(toGenLanguageFilePath) + const { code: toGenContent } = generateCode(readContent) + const mod = await parseModule(`export default ${toGenContent.replace('export default translation', '').replace('const translation = ', '')}`) + const toGenOutPut = mod.exports.default - await translateMissingKeyDeeply(fullKeyContent, toGenOutPut, toGenLanguage) - const { code } = generateCode(mod) - const res = `const translation =${code.replace('export default', '')} + console.log(`\n🌍 Processing ${fileName} for ${toGenLanguage}...`) + const result = await translateMissingKeyDeeply(fullKeyContent, toGenOutPut, toGenLanguage) + + // Generate summary report + console.log(`\n📊 Translation Summary for ${fileName} -> ${toGenLanguage}:`) + console.log(` ✅ Translated: ${result.translated.length} keys`) + console.log(` ⏭️ Skipped: ${result.skipped.length} keys`) + + if (result.skipped.length > 0) { + console.log(`\n⚠️ Skipped keys in ${fileName} (${toGenLanguage}):`) + result.skipped.slice(0, 5).forEach(item => console.log(` - ${item}`)) + if (result.skipped.length > 5) + console.log(` ... and ${result.skipped.length - 5} more`) + } + + const { code } = generateCode(mod) + const res = `const translation =${code.replace('export default', '')} export default translation `.replace(/,\n\n/g, ',\n').replace('};', '}') - fs.writeFileSync(toGenLanguageFilePath, res) + if (!isDryRun) { + fs.writeFileSync(toGenLanguageFilePath, res) + console.log(`💾 Saved translations to ${toGenLanguageFilePath}`) + } + else { + console.log(`🔍 [DRY RUN] Would save translations to ${toGenLanguageFilePath}`) + } + + return result + } + catch (error) { + console.error(`Error processing file ${fullKeyFilePath}:`, error.message) + throw error + } +} + +// Add command line argument support +const isDryRun = process.argv.includes('--dry-run') +const targetFile = process.argv.find(arg => arg.startsWith('--file='))?.split('=')[1] +const targetLang = process.argv.find(arg => arg.startsWith('--lang='))?.split('=')[1] + +// Rate limiting helper +function delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)) } async function main() { - // const fileName = 'workflow' - // Promise.all(Object.keys(languageKeyMap).map(async (toLanguage) => { - // await autoGenTrans(fileName, toLanguage) - // })) + console.log('🚀 Starting auto-gen-i18n script...') + console.log(`📋 Mode: ${isDryRun ? 'DRY RUN (no files will be modified)' : 'LIVE MODE'}`) const files = fs - .readdirSync(path.join(__dirname, targetLanguage)) - .map(file => file.replace(/\.ts/, '')) + .readdirSync(path.resolve(__dirname, i18nFolder, targetLanguage)) + .filter(file => /\.ts$/.test(file)) // Only process .ts files + .map(file => file.replace(/\.ts$/, '')) .filter(f => f !== 'app-debug') // ast parse error in app-debug - await Promise.all(files.map(async (file) => { - await Promise.all(Object.keys(languageKeyMap).map(async (language) => { + // Filter by target file if specified + const filesToProcess = targetFile ? files.filter(f => f === targetFile) : files + const languagesToProcess = targetLang ? [targetLang] : Object.keys(languageKeyMap) + + console.log(`📁 Files to process: ${filesToProcess.join(', ')}`) + console.log(`🌍 Languages to process: ${languagesToProcess.join(', ')}`) + + let totalTranslated = 0 + let totalSkipped = 0 + let totalErrors = 0 + + // Process files sequentially to avoid API rate limits + for (const file of filesToProcess) { + console.log(`\n📄 Processing file: ${file}`) + + // Process languages with rate limiting + for (const language of languagesToProcess) { try { - await autoGenTrans(file, language) + const result = await autoGenTrans(file, language, isDryRun) + totalTranslated += result.translated.length + totalSkipped += result.skipped.length + + // Rate limiting: wait 500ms between language processing + await delay(500) } catch (e) { - console.error(`Error translating ${file} to ${language}`, e) + console.error(`❌ Error translating ${file} to ${language}:`, e.message) + totalErrors++ } - })) - })) + } + } + + // Final summary + console.log('\n🎉 Auto-translation completed!') + console.log('📊 Final Summary:') + console.log(` ✅ Total keys translated: ${totalTranslated}`) + console.log(` ⏭️ Total keys skipped: ${totalSkipped}`) + console.log(` ❌ Total errors: ${totalErrors}`) + + if (isDryRun) + console.log('\n💡 This was a dry run. To actually translate, run without --dry-run flag.') } main() diff --git a/web/i18n-config/check-i18n.js b/web/i18n-config/check-i18n.js index 5f4414c7ac..7e3b725c9e 100644 --- a/web/i18n-config/check-i18n.js +++ b/web/i18n-config/check-i18n.js @@ -1,15 +1,16 @@ const fs = require('node:fs') const path = require('node:path') +const vm = require('node:vm') const transpile = require('typescript').transpile const targetLanguage = 'en-US' const data = require('./languages.json') const languages = data.languages.filter(language => language.supported).map(language => language.value) -async function getKeysFromLanuage(language) { +async function getKeysFromLanguage(language) { return new Promise((resolve, reject) => { - const folderPath = path.join(__dirname, language) - let allKeys = [] + const folderPath = path.resolve(__dirname, '../i18n', language) + const allKeys = [] fs.readdir(folderPath, (err, files) => { if (err) { console.error('Error reading folder:', err) @@ -17,37 +18,61 @@ async function getKeysFromLanuage(language) { return } - files.forEach((file) => { + // Filter only .ts and .js files + const translationFiles = files.filter(file => /\.(ts|js)$/.test(file)) + + translationFiles.forEach((file) => { const filePath = path.join(folderPath, file) const fileName = file.replace(/\.[^/.]+$/, '') // Remove file extension const camelCaseFileName = fileName.replace(/[-_](.)/g, (_, c) => c.toUpperCase(), ) // Convert to camel case - // console.log(camelCaseFileName) - const content = fs.readFileSync(filePath, 'utf8') - // eslint-disable-next-line sonarjs/code-eval - const translationObj = eval(transpile(content)) - // console.log(translation) - if(!translationObj || typeof translationObj !== 'object') { - console.error(`Error parsing file: ${filePath}`) - reject(new Error(`Error parsing file: ${filePath}`)) - return - } - const keys = Object.keys(translationObj) - const nestedKeys = [] - const iterateKeys = (obj, prefix = '') => { - for (const key in obj) { - const nestedKey = prefix ? `${prefix}.${key}` : key - nestedKeys.push(nestedKey) - if (typeof obj[key] === 'object') - iterateKeys(obj[key], nestedKey) - } - } - iterateKeys(translationObj) - allKeys = [...keys, ...nestedKeys].map( - key => `${camelCaseFileName}.${key}`, - ) + try { + const content = fs.readFileSync(filePath, 'utf8') + + // Create a safer module environment for vm + const moduleExports = {} + const context = { + exports: moduleExports, + module: { exports: moduleExports }, + require, + console, + __filename: filePath, + __dirname: folderPath, + } + + // Use vm.runInNewContext instead of eval for better security + vm.runInNewContext(transpile(content), context) + + // Extract the translation object + const translationObj = moduleExports.default || moduleExports + + if(!translationObj || typeof translationObj !== 'object') { + console.error(`Error parsing file: ${filePath}`) + reject(new Error(`Error parsing file: ${filePath}`)) + return + } + + const nestedKeys = [] + const iterateKeys = (obj, prefix = '') => { + for (const key in obj) { + const nestedKey = prefix ? `${prefix}.${key}` : key + nestedKeys.push(nestedKey) + if (typeof obj[key] === 'object' && obj[key] !== null) + iterateKeys(obj[key], nestedKey) + } + } + iterateKeys(translationObj) + + // Fixed: accumulate keys instead of overwriting + const fileKeys = nestedKeys.map(key => `${camelCaseFileName}.${key}`) + allKeys.push(...fileKeys) + } + catch (error) { + console.error(`Error processing file ${filePath}:`, error.message) + reject(error) + } }) resolve(allKeys) }) @@ -56,8 +81,8 @@ async function getKeysFromLanuage(language) { async function main() { const compareKeysCount = async () => { - const targetKeys = await getKeysFromLanuage(targetLanguage) - const languagesKeys = await Promise.all(languages.map(language => getKeysFromLanuage(language))) + const targetKeys = await getKeysFromLanguage(targetLanguage) + const languagesKeys = await Promise.all(languages.map(language => getKeysFromLanguage(language))) const keysCount = languagesKeys.map(keys => keys.length) const targetKeysCount = targetKeys.length diff --git a/web/i18n/de-DE/app-annotation.ts b/web/i18n/de-DE/app-annotation.ts index ef2fa1f236..2e141ed380 100644 --- a/web/i18n/de-DE/app-annotation.ts +++ b/web/i18n/de-DE/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'Massenimport', bulkExport: 'Massenexport', clearAll: 'Alle Anmerkungen löschen', + clearAllConfirm: 'Alle Anmerkungen löschen?', }, }, editModal: { diff --git a/web/i18n/de-DE/app-debug.ts b/web/i18n/de-DE/app-debug.ts index 1adaf6f05d..1d7ebc3854 100644 --- a/web/i18n/de-DE/app-debug.ts +++ b/web/i18n/de-DE/app-debug.ts @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Datei-Upload', + description: 'Das Chat-Eingabefeld unterstützt das Hochladen von Bildern, Dokumenten und anderen Dateien.', + supportedTypes: 'Unterstützte Dateitypen', + numberLimit: 'Max. Uploads', + modalTitle: 'Datei-Upload-Einstellung', + }, + imageUpload: { + title: 'Bild-Upload', + description: 'Ermöglicht das Hochladen von Bildern.', + supportedTypes: 'Unterstützte Dateitypen', + numberLimit: 'Max. Uploads', + modalTitle: 'Bild-Upload-Einstellung', + }, + bar: { + empty: 'Funktionen aktivieren, um die Web-App-Benutzererfahrung zu verbessern', + enableText: 'Funktionen aktiviert', + manage: 'Verwalten', + }, + documentUpload: { + title: 'Dokument', + description: 'Das Aktivieren von Dokumenten ermöglicht es dem Modell, Dokumente aufzunehmen und Fragen zu ihnen zu beantworten.', + }, + audioUpload: { + title: 'Audio', + description: 'Das Aktivieren von Audio ermöglicht es dem Modell, Audiodateien für Transkription und Analyse zu verarbeiten.', + }, }, resetConfig: { title: 'Zurücksetzen bestätigen?', @@ -261,6 +288,9 @@ const translation = { options: 'Optionen', addOption: 'Option hinzufügen', apiBasedVar: 'API-basierte Variable', + defaultValue: 'Standardwert', + noDefaultValue: 'Kein Standardwert', + selectDefaultValue: 'Standardwert auswählen', }, vision: { name: 'Vision', diff --git a/web/i18n/de-DE/app.ts b/web/i18n/de-DE/app.ts index c28fcb2be5..31221e8f0b 100644 --- a/web/i18n/de-DE/app.ts +++ b/web/i18n/de-DE/app.ts @@ -268,6 +268,7 @@ const translation = { noAccessPermission: 'Keine Berechtigung zum Zugriff auf die Webanwendung', maxActiveRequests: 'Maximale gleichzeitige Anfragen', maxActiveRequestsPlaceholder: 'Geben Sie 0 für unbegrenzt ein', + maxActiveRequestsTip: 'Maximale Anzahl gleichzeitiger aktiver Anfragen pro App (0 für unbegrenzt)', } export default translation diff --git a/web/i18n/de-DE/plugin.ts b/web/i18n/de-DE/plugin.ts index 6fa6999ae5..aa136528e2 100644 --- a/web/i18n/de-DE/plugin.ts +++ b/web/i18n/de-DE/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: 'Nur fixieren', selectedDescription: 'Auto-Update nur für Patch-Versionen', + description: 'Automatische Aktualisierung nur für Patchversionen (z. B. 1.0.1 → 1.0.2). Kleinere Versionsänderungen lösen keine Aktualisierungen aus.', }, latest: { description: 'Immer auf die neueste Version aktualisieren', diff --git a/web/i18n/de-DE/workflow.ts b/web/i18n/de-DE/workflow.ts index de2c3ce38d..121f5da1a2 100644 --- a/web/i18n/de-DE/workflow.ts +++ b/web/i18n/de-DE/workflow.ts @@ -497,6 +497,7 @@ const translation = { search: 'Suchmetadaten', }, title: 'Metadatenfilterung', + tip: 'Metadatenfilterung ist der Prozess, Metadatenattribute (wie Tags, Kategorien oder Zugriffsberechtigungen) zu verwenden, um die Abfrage und Kontrolle der relevanten Informationen innerhalb eines Systems zu verfeinern.', }, }, http: { diff --git a/web/i18n/en-US/app-debug.ts b/web/i18n/en-US/app-debug.ts index 938cb27c12..589d91fb20 100644 --- a/web/i18n/en-US/app-debug.ts +++ b/web/i18n/en-US/app-debug.ts @@ -404,6 +404,9 @@ const translation = { atLeastOneOption: 'At least one option is required', optionRepeat: 'Has repeat options', }, + 'defaultValue': 'Default value', + 'noDefaultValue': 'No default value', + 'selectDefaultValue': 'Select default value', }, vision: { name: 'Vision', diff --git a/web/i18n/es-ES/app-annotation.ts b/web/i18n/es-ES/app-annotation.ts index e090c46122..2a797edcc3 100644 --- a/web/i18n/es-ES/app-annotation.ts +++ b/web/i18n/es-ES/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'Importar en Masa', bulkExport: 'Exportar en Masa', clearAll: 'Borrar Todas las Anotaciones', + clearAllConfirm: '¿Eliminar todas las anotaciones?', }, }, editModal: { diff --git a/web/i18n/es-ES/app-debug.ts b/web/i18n/es-ES/app-debug.ts index afdea66338..78b3329403 100644 --- a/web/i18n/es-ES/app-debug.ts +++ b/web/i18n/es-ES/app-debug.ts @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Subida de archivos', + description: 'La caja de entrada del chat permite subir imágenes, documentos y otros archivos.', + supportedTypes: 'Tipos de archivo soportados', + numberLimit: 'Máximo de subidas', + modalTitle: 'Configuración de subida de archivos', + }, + imageUpload: { + title: 'Subida de imágenes', + description: 'Permite subir imágenes.', + supportedTypes: 'Tipos de archivo soportados', + numberLimit: 'Máximo de subidas', + modalTitle: 'Configuración de subida de imágenes', + }, + bar: { + empty: 'Habilitar funciones para mejorar la experiencia del usuario de la aplicación web', + enableText: 'Funciones habilitadas', + manage: 'Gestionar', + }, + documentUpload: { + title: 'Documento', + description: 'Habilitar Documento permitirá al modelo aceptar documentos y responder preguntas sobre ellos.', + }, + audioUpload: { + title: 'Audio', + description: 'Habilitar Audio permitirá al modelo procesar archivos de audio para transcripción y análisis.', + }, }, automatic: { title: 'Orquestación automatizada de aplicaciones', @@ -282,12 +309,14 @@ const translation = { 'required': 'Requerido', 'hide': 'Ocultar', 'errorMsg': { - varNameRequired: 'Nombre de la variable es requerido', labelNameRequired: 'Nombre de la etiqueta es requerido', varNameCanBeRepeat: 'El nombre de la variable no puede repetirse', atLeastOneOption: 'Se requiere al menos una opción', optionRepeat: 'Hay opciones repetidas', }, + 'defaultValue': 'Valor predeterminado', + 'noDefaultValue': 'Sin valor predeterminado', + 'selectDefaultValue': 'Seleccionar valor predeterminado', }, vision: { name: 'Visión', diff --git a/web/i18n/es-ES/app.ts b/web/i18n/es-ES/app.ts index add9a4318d..55e14df838 100644 --- a/web/i18n/es-ES/app.ts +++ b/web/i18n/es-ES/app.ts @@ -261,6 +261,7 @@ const translation = { noAccessPermission: 'No se permite el acceso a la aplicación web', maxActiveRequestsPlaceholder: 'Introduce 0 para ilimitado', maxActiveRequests: 'Máximas solicitudes concurrentes', + maxActiveRequestsTip: 'Número máximo de solicitudes activas concurrentes por aplicación (0 para ilimitado)', } export default translation diff --git a/web/i18n/es-ES/plugin.ts b/web/i18n/es-ES/plugin.ts index 6299684851..e937db7a02 100644 --- a/web/i18n/es-ES/plugin.ts +++ b/web/i18n/es-ES/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: 'Arreglar Solo', selectedDescription: 'Actualización automática solo para versiones de parches', + description: 'Actualización automática solo para versiones de parche (por ejemplo, 1.0.1 → 1.0.2). Los cambios de versión menor no activarán actualizaciones.', }, latest: { selectedDescription: 'Siempre actualiza a la última versión', diff --git a/web/i18n/es-ES/workflow.ts b/web/i18n/es-ES/workflow.ts index 3c509934df..d57a0a40f2 100644 --- a/web/i18n/es-ES/workflow.ts +++ b/web/i18n/es-ES/workflow.ts @@ -497,6 +497,7 @@ const translation = { search: 'Buscar metadatos', }, title: 'Filtrado de Metadatos', + tip: 'El filtrado de metadatos es el proceso de utilizar atributos de metadatos (como etiquetas, categorías o permisos de acceso) para refinar y controlar la recuperación de información relevante dentro de un sistema.', }, }, http: { diff --git a/web/i18n/fa-IR/app-annotation.ts b/web/i18n/fa-IR/app-annotation.ts index e78fc8cd7e..d66c2eb0e5 100644 --- a/web/i18n/fa-IR/app-annotation.ts +++ b/web/i18n/fa-IR/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'واردات انبوه', bulkExport: 'صادرات انبوه', clearAll: 'پاک کردن همه یادداشت‌ها', + clearAllConfirm: 'آیا همه حاشیه‌نویسی‌ها را حذف کنیم؟', }, }, editModal: { diff --git a/web/i18n/fa-IR/app-debug.ts b/web/i18n/fa-IR/app-debug.ts index 75085ef30e..5efbb9421b 100644 --- a/web/i18n/fa-IR/app-debug.ts +++ b/web/i18n/fa-IR/app-debug.ts @@ -317,7 +317,6 @@ const translation = { 'required': 'مورد نیاز', 'hide': 'مخفی کردن', 'errorMsg': { - varNameRequired: 'نام متغیر مورد نیاز است', labelNameRequired: 'نام برچسب مورد نیاز است', varNameCanBeRepeat: 'نام متغیر نمی‌تواند تکراری باشد', atLeastOneOption: 'حداقل یک گزینه مورد نیاز است', @@ -451,6 +450,33 @@ const translation = { enabled: 'فعال', }, }, + fileUpload: { + title: 'آپلود فایل', + description: 'جعبه ورودی چت امکان آپلود تصاویر، اسناد و سایر فایل‌ها را فراهم می‌کند.', + supportedTypes: 'انواع فایل‌های پشتیبانی شده', + numberLimit: 'حداکثر آپلود', + modalTitle: 'تنظیمات آپلود فایل', + }, + imageUpload: { + title: 'آپلود تصویر', + description: 'امکان آپلود تصاویر را فراهم می‌کند.', + supportedTypes: 'انواع فایل‌های پشتیبانی شده', + numberLimit: 'حداکثر آپلود', + modalTitle: 'تنظیمات آپلود تصویر', + }, + bar: { + empty: 'فعال‌سازی ویژگی برای بهبود تجربه کاربری اپلیکیشن وب', + enableText: 'ویژگی‌های فعال', + manage: 'مدیریت', + }, + documentUpload: { + title: 'سند', + description: 'فعال‌سازی سند به مدل اجازه می‌دهد اسناد را دریافت کرده و درباره آن‌ها پاسخ دهد.', + }, + audioUpload: { + title: 'صوتی', + description: 'فعال‌سازی صوت به مدل اجازه می‌دهد فایل‌های صوتی را برای رونویسی و تجزیه و تحلیل پردازش کند.', + }, }, } diff --git a/web/i18n/fa-IR/app.ts b/web/i18n/fa-IR/app.ts index d8dfba3d81..b2cde413d9 100644 --- a/web/i18n/fa-IR/app.ts +++ b/web/i18n/fa-IR/app.ts @@ -261,6 +261,7 @@ const translation = { noAccessPermission: 'دسترسی به برنامه وب مجاز نیست', maxActiveRequests: 'بیشترین درخواست‌های همزمان', maxActiveRequestsPlaceholder: 'برای نامحدود، 0 را وارد کنید', + maxActiveRequestsTip: 'حداکثر تعداد درخواست‌های فعال همزمان در هر برنامه (0 برای نامحدود)', } export default translation diff --git a/web/i18n/fa-IR/plugin.ts b/web/i18n/fa-IR/plugin.ts index 5e1cbe02bf..1ba3a714a3 100644 --- a/web/i18n/fa-IR/plugin.ts +++ b/web/i18n/fa-IR/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: 'فقط تعمیر کنید', selectedDescription: 'به‌روزرسانی خودکار تنها برای نسخه‌های وصله', + description: 'به‌روزرسانی خودکار فقط برای نسخه‌های پچ (مانند ۱.۰.۱ → ۱.۰.۲). تغییرات جزئی نسخه باعث راه‌اندازی به‌روزرسانی‌ها نمی‌شود.', }, latest: { name: 'جدیدترین', diff --git a/web/i18n/fa-IR/workflow.ts b/web/i18n/fa-IR/workflow.ts index b1aa11d3bf..f95253e73d 100644 --- a/web/i18n/fa-IR/workflow.ts +++ b/web/i18n/fa-IR/workflow.ts @@ -497,6 +497,7 @@ const translation = { conditions: 'شرایط', }, title: 'فیلتر کردن فراداده', + tip: 'فیلتر کردن متاداده فرایند استفاده از ویژگی‌های متاداده (مانند برچسب‌ها، دسته‌ها یا مجوزهای دسترسی) برای تصفیه و کنترل بازیابی اطلاعات مرتبط در یک سیستم است.', }, }, http: { diff --git a/web/i18n/fr-FR/app-annotation.ts b/web/i18n/fr-FR/app-annotation.ts index 3926fe5e26..3a34e326f4 100644 --- a/web/i18n/fr-FR/app-annotation.ts +++ b/web/i18n/fr-FR/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'Importation en Vrac', bulkExport: 'Exportation en Vrac', clearAll: 'Effacer toutes les annotations', + clearAllConfirm: 'Supprimer toutes les annotations ?', }, }, editModal: { diff --git a/web/i18n/fr-FR/app-debug.ts b/web/i18n/fr-FR/app-debug.ts index f3984c0435..78294fbd8b 100644 --- a/web/i18n/fr-FR/app-debug.ts +++ b/web/i18n/fr-FR/app-debug.ts @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Téléchargement de fichier', + description: 'La boîte de saisie de chat permet de télécharger des images, des documents et d\'autres fichiers.', + supportedTypes: 'Types de fichiers supportés', + numberLimit: 'Nombre max de téléchargements', + modalTitle: 'Paramètres de téléchargement de fichier', + }, + imageUpload: { + title: 'Téléchargement d\'image', + description: 'Permet de télécharger des images.', + supportedTypes: 'Types de fichiers supportés', + numberLimit: 'Nombre max de téléchargements', + modalTitle: 'Paramètres de téléchargement d\'image', + }, + bar: { + empty: 'Activer la fonctionnalité pour améliorer l\'expérience utilisateur de l\'application web', + enableText: 'Fonctionnalités activées', + manage: 'Gérer', + }, + documentUpload: { + title: 'Document', + description: 'Activer Document permettra au modèle de prendre des documents et de répondre aux questions à leur sujet.', + }, + audioUpload: { + title: 'Audio', + description: 'Activer Audio permettra au modèle de traiter les fichiers audio pour la transcription et l\'analyse.', + }, }, resetConfig: { title: 'Confirmer la réinitialisation ?', @@ -270,12 +297,14 @@ const translation = { 'required': 'Required', 'hide': 'Caché', 'errorMsg': { - varNameRequired: 'Variable name is required', labelNameRequired: 'Label name is required', varNameCanBeRepeat: 'Variable name can not be repeated', atLeastOneOption: 'At least one option is required', optionRepeat: 'Has repeat options', }, + 'defaultValue': 'Valeur par défaut', + 'noDefaultValue': 'Aucune valeur par défaut', + 'selectDefaultValue': 'Sélectionner la valeur par défaut', }, vision: { name: 'Vision', diff --git a/web/i18n/fr-FR/app.ts b/web/i18n/fr-FR/app.ts index 523934152f..f572658d12 100644 --- a/web/i18n/fr-FR/app.ts +++ b/web/i18n/fr-FR/app.ts @@ -261,6 +261,7 @@ const translation = { noAccessPermission: 'Pas de permission d\'accéder à l\'application web', maxActiveRequestsPlaceholder: 'Entrez 0 pour illimité', maxActiveRequests: 'Nombre maximal de requêtes simultanées', + maxActiveRequestsTip: 'Nombre maximum de requêtes actives concurrentes par application (0 pour illimité)', } export default translation diff --git a/web/i18n/fr-FR/plugin.ts b/web/i18n/fr-FR/plugin.ts index 255171058a..ae6e8c068b 100644 --- a/web/i18n/fr-FR/plugin.ts +++ b/web/i18n/fr-FR/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { selectedDescription: 'Mise à jour automatique uniquement pour les versions de correctif', name: 'Réparer seulement', + description: 'Mise à jour automatique uniquement pour les versions de correctif (par exemple, 1.0.1 → 1.0.2). Les changements de version mineure ne déclencheront pas de mises à jour.', }, latest: { name: 'Dernier', diff --git a/web/i18n/fr-FR/workflow.ts b/web/i18n/fr-FR/workflow.ts index 96bead7ff2..884e3e9772 100644 --- a/web/i18n/fr-FR/workflow.ts +++ b/web/i18n/fr-FR/workflow.ts @@ -497,6 +497,7 @@ const translation = { title: 'Conditions de filtrage des métadonnées', }, title: 'Filtrage des métadonnées', + tip: 'Le filtrage des métadonnées est le processus d\'utilisation des attributs de métadonnées (tels que les étiquettes, les catégories ou les autorisations d\'accès) pour affiner et contrôler la récupération d\'informations pertinentes au sein d\'un système.', }, }, http: { diff --git a/web/i18n/hi-IN/app-annotation.ts b/web/i18n/hi-IN/app-annotation.ts index 0249ebf7d4..b89f33c438 100644 --- a/web/i18n/hi-IN/app-annotation.ts +++ b/web/i18n/hi-IN/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'बल्क आयात', bulkExport: 'बल्क निर्यात', clearAll: 'सभी एनोटेशन साफ करें', + clearAllConfirm: 'क्या सभी टिप्पणियाँ हटानी हैं?', }, }, editModal: { diff --git a/web/i18n/hi-IN/app-debug.ts b/web/i18n/hi-IN/app-debug.ts index ded2af4132..ea9b20c500 100644 --- a/web/i18n/hi-IN/app-debug.ts +++ b/web/i18n/hi-IN/app-debug.ts @@ -314,12 +314,14 @@ const translation = { 'required': 'आवश्यक', 'hide': 'छुपाएँ', 'errorMsg': { - varNameRequired: 'वेरिएबल नाम आवश्यक है', labelNameRequired: 'लेबल नाम आवश्यक है', varNameCanBeRepeat: 'वेरिएबल नाम दोहराया नहीं जा सकता', atLeastOneOption: 'कम से कम एक विकल्प आवश्यक है', optionRepeat: 'विकल्प दोहराए गए हैं', }, + 'defaultValue': 'डिफ़ॉल्ट मान', + 'noDefaultValue': 'कोई डिफ़ॉल्ट मान नहीं', + 'selectDefaultValue': 'डिफ़ॉल्ट मान चुनें', }, vision: { name: 'विजन', @@ -465,6 +467,33 @@ const translation = { 'उपकरणों का उपयोग करके एलएलएम की क्षमताओं का विस्तार किया जा सकता है, जैसे इंटरनेट पर खोज करना या वैज्ञानिक गणनाएँ करना', enabled: 'सक्षम', }, + fileUpload: { + title: 'फ़ाइल अपलोड', + description: 'चैट इनपुट बॉक्स छवियों, दस्तावेज़ों और अन्य फ़ाइलों को अपलोड करने की अनुमति देता है।', + supportedTypes: 'समर्थित फ़ाइल प्रकार', + numberLimit: 'अधिकतम अपलोड', + modalTitle: 'फ़ाइल अपलोड सेटिंग', + }, + imageUpload: { + title: 'छवि अपलोड', + description: 'छवियों को अपलोड करने की अनुमति दें।', + supportedTypes: 'समर्थित फ़ाइल प्रकार', + numberLimit: 'अधिकतम अपलोड', + modalTitle: 'छवि अपलोड सेटिंग', + }, + bar: { + empty: 'वेब ऐप उपयोगकर्ता अनुभव को बेहतर बनाने के लिए फीचर सक्षम करें', + enableText: 'फीचर सक्षम', + manage: 'प्रबंधित करें', + }, + documentUpload: { + title: 'दस्तावेज़', + description: 'दस्तावेज़ सक्षम करने से मॉडल दस्तावेज़ों को स्वीकार कर सकेगा और उनके बारे में प्रश्नों का उत्तर दे सकेगा।', + }, + audioUpload: { + title: 'ऑडियो', + description: 'ऑडियो सक्षम करने से मॉडल ट्रांसक्रिप्शन और विश्लेषण के लिए ऑडियो फ़ाइलों को प्रोसेस कर सकेगा।', + }, }, } diff --git a/web/i18n/hi-IN/app.ts b/web/i18n/hi-IN/app.ts index dcd5e54bdc..4c6bc7a8f8 100644 --- a/web/i18n/hi-IN/app.ts +++ b/web/i18n/hi-IN/app.ts @@ -261,6 +261,7 @@ const translation = { noAccessPermission: 'वेब एप्लिकेशन तक पहुँचने की अनुमति नहीं है', maxActiveRequests: 'अधिकतम समवर्ती अनुरोध', maxActiveRequestsPlaceholder: 'असीमित के लिए 0 दर्ज करें', + maxActiveRequestsTip: 'प्रति ऐप अधिकतम सक्रिय अनुरोधों की अधिकतम संख्या (असीमित के लिए 0)', } export default translation diff --git a/web/i18n/hi-IN/plugin.ts b/web/i18n/hi-IN/plugin.ts index ae4547421c..e15b6a85a7 100644 --- a/web/i18n/hi-IN/plugin.ts +++ b/web/i18n/hi-IN/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: 'केवल ठीक करें', selectedDescription: 'केवल पैच संस्करणों के लिए स्वचालित अपडेट', + description: 'केवल पैच संस्करणों के लिए स्वचालित अद्यतन (जैसे, 1.0.1 → 1.0.2)। छोटा संस्करण परिवर्तन अद्यतन को ट्रिगर नहीं करेगा।', }, latest: { name: 'नवीनतम', diff --git a/web/i18n/hi-IN/workflow.ts b/web/i18n/hi-IN/workflow.ts index 68937e5155..d613c87f6a 100644 --- a/web/i18n/hi-IN/workflow.ts +++ b/web/i18n/hi-IN/workflow.ts @@ -510,6 +510,7 @@ const translation = { search: 'खोज मेटाडेटा', }, title: 'मेटाडेटा फ़िल्टरिंग', + tip: 'मेटाडेटा छानने की प्रक्रिया है जिसमें मेटाडेटा विशेषताओं (जैसे टैग, श्रेणियाँ, या पहुंच अनुमतियाँ) का उपयोग करके एक प्रणाली के भीतर प्रासंगिक जानकारी की पुनर्प्राप्ति को सुधारने और नियंत्रित करने के लिए किया जाता है।', }, }, http: { diff --git a/web/i18n/it-IT/app-annotation.ts b/web/i18n/it-IT/app-annotation.ts index a7f615860c..bba10ba84e 100644 --- a/web/i18n/it-IT/app-annotation.ts +++ b/web/i18n/it-IT/app-annotation.ts @@ -18,6 +18,7 @@ const translation = { bulkImport: 'Importazione Bulk', bulkExport: 'Esportazione Bulk', clearAll: 'Cancella Tutte le Annotazioni', + clearAllConfirm: 'Eliminare tutte le annotazioni?', }, }, editModal: { diff --git a/web/i18n/it-IT/app-debug.ts b/web/i18n/it-IT/app-debug.ts index bfa75b282b..f79cccf6e7 100644 --- a/web/i18n/it-IT/app-debug.ts +++ b/web/i18n/it-IT/app-debug.ts @@ -216,6 +216,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Caricamento File', + description: 'La casella di input della chat consente di caricare immagini, documenti e altri file.', + supportedTypes: 'Tipi di File Supportati', + numberLimit: 'Caricamenti massimi', + modalTitle: 'Impostazione Caricamento File', + }, + imageUpload: { + title: 'Caricamento Immagine', + description: 'Consente di caricare immagini.', + supportedTypes: 'Tipi di File Supportati', + numberLimit: 'Caricamenti massimi', + modalTitle: 'Impostazione Caricamento Immagine', + }, + bar: { + empty: 'Abilita funzionalità per migliorare l\'esperienza utente dell\'app web', + enableText: 'Funzionalità Abilitate', + manage: 'Gestisci', + }, + documentUpload: { + title: 'Documento', + description: 'Abilitare Documento consentirà al modello di accettare documenti e rispondere a domande su di essi.', + }, + audioUpload: { + title: 'Audio', + description: 'Abilitare Audio consentirà al modello di elaborare file audio per trascrizione e analisi.', + }, }, automatic: { title: 'Orchestrazione automatizzata delle applicazioni', @@ -316,12 +343,14 @@ const translation = { 'required': 'Richiesto', 'hide': 'Nascondi', 'errorMsg': { - varNameRequired: 'Il nome della variabile è richiesto', labelNameRequired: 'Il nome dell\'etichetta è richiesto', varNameCanBeRepeat: 'Il nome della variabile non può essere ripetuto', atLeastOneOption: 'È richiesta almeno un\'opzione', optionRepeat: 'Ci sono opzioni ripetute', }, + 'defaultValue': 'Valore predefinito', + 'noDefaultValue': 'Nessun valore predefinito', + 'selectDefaultValue': 'Seleziona valore predefinito', }, vision: { name: 'Visione', diff --git a/web/i18n/it-IT/app.ts b/web/i18n/it-IT/app.ts index 63a25dccc6..66cb50b2a0 100644 --- a/web/i18n/it-IT/app.ts +++ b/web/i18n/it-IT/app.ts @@ -273,6 +273,7 @@ const translation = { noAccessPermission: 'Nessun permesso per accedere all\'app web', maxActiveRequestsPlaceholder: 'Inserisci 0 per illimitato', maxActiveRequests: 'Massimo numero di richieste concorrenti', + maxActiveRequestsTip: 'Numero massimo di richieste attive concorrenti per app (0 per illimitato)', } export default translation diff --git a/web/i18n/it-IT/plugin.ts b/web/i18n/it-IT/plugin.ts index e7b6b147fa..616e199906 100644 --- a/web/i18n/it-IT/plugin.ts +++ b/web/i18n/it-IT/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: 'Ripara solo', selectedDescription: 'Aggiornamento automatico solo per versioni patch', + description: 'Aggiornamento automatico solo per le versioni patch (ad es., 1.0.1 → 1.0.2). Le modifiche delle versioni minori non attiveranno aggiornamenti.', }, latest: { selectedDescription: 'Aggiorna sempre all\'ultima versione', diff --git a/web/i18n/it-IT/workflow.ts b/web/i18n/it-IT/workflow.ts index 97e4bc14f2..196e6f761a 100644 --- a/web/i18n/it-IT/workflow.ts +++ b/web/i18n/it-IT/workflow.ts @@ -514,6 +514,7 @@ const translation = { search: 'Cerca metadati', }, title: 'Filtraggio dei metadati', + tip: 'Il filtraggio dei metadati è il processo di utilizzo degli attributi dei metadati (come tag, categorie o permessi di accesso) per affinare e controllare il recupero di informazioni pertinenti all\'interno di un sistema.', }, }, http: { diff --git a/web/i18n/ja-JP/app-debug.ts b/web/i18n/ja-JP/app-debug.ts index decbe4863e..d13a64213a 100644 --- a/web/i18n/ja-JP/app-debug.ts +++ b/web/i18n/ja-JP/app-debug.ts @@ -222,6 +222,10 @@ const translation = { title: 'ドキュメント', description: 'ドキュメント機能を有効にすると、AI モデルがファイルを処理し、その内容に基づいて質問に回答できるようになります。', }, + audioUpload: { + title: '音声', + description: '音声機能を有効にすると、モデルが音声ファイルの転写と分析を処理できるようになります。', + }, }, codegen: { title: 'コードジェネレーター', @@ -307,6 +311,9 @@ const translation = { waitForImgUpload: '画像のアップロードが完了するまでお待ちください', waitForFileUpload: 'ファイルのアップロードが完了するまでお待ちください', }, + warningMessage: { + timeoutExceeded: 'タイムアウトのため結果が表示されません。完全な結果を取得するにはログを参照してください。', + }, chatSubTitle: 'プロンプト', completionSubTitle: '接頭辞プロンプト', promptTip: 'プロンプトは、AI の応答を指示と制約で誘導します。 {{input}} のような変数を挿入します。このプロンプトはユーザーには表示されません。', @@ -386,12 +393,14 @@ const translation = { 'maxNumberOfUploads': 'アップロードの最大数', 'maxNumberTip': 'ドキュメント < {{docLimit}}, 画像 < {{imgLimit}}, 音声 < {{audioLimit}}, 映像 < {{videoLimit}}', 'errorMsg': { - varNameRequired: '変数名は必須です', labelNameRequired: 'ラベル名は必須です', varNameCanBeRepeat: '変数名は繰り返すことができません', atLeastOneOption: '少なくとも 1 つのオプションが必要です', optionRepeat: '繰り返しオプションがあります', }, + 'defaultValue': 'デフォルト値', + 'noDefaultValue': 'デフォルト値なし', + 'selectDefaultValue': 'デフォルト値を選択', }, vision: { name: 'ビジョン', diff --git a/web/i18n/ja-JP/app.ts b/web/i18n/ja-JP/app.ts index e03e9e1177..f68835c7e7 100644 --- a/web/i18n/ja-JP/app.ts +++ b/web/i18n/ja-JP/app.ts @@ -260,6 +260,7 @@ const translation = { noAccessPermission: 'Web アプリにアクセス権限がありません', maxActiveRequestsPlaceholder: '無制限のために0を入力してください', maxActiveRequests: '最大同時リクエスト数', + maxActiveRequestsTip: 'アプリごとの同時アクティブリクエストの最大数(無制限の場合は0)', } export default translation diff --git a/web/i18n/ja-JP/plugin.ts b/web/i18n/ja-JP/plugin.ts index 38b73a847e..b202b404b3 100644 --- a/web/i18n/ja-JP/plugin.ts +++ b/web/i18n/ja-JP/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: '修正のみ', selectedDescription: 'パッチバージョンのみの自動更新', + description: 'パッチバージョンのみ自動更新 (例: 1.0.1 → 1.0.2)。マイナーバージョンの変更は更新をトリガーしません。', }, latest: { name: '最新', diff --git a/web/i18n/ko-KR/app-annotation.ts b/web/i18n/ko-KR/app-annotation.ts index 7a93d17821..662dc3f083 100644 --- a/web/i18n/ko-KR/app-annotation.ts +++ b/web/i18n/ko-KR/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: '일괄 가져오기', bulkExport: '일괄 내보내기', clearAll: '모든 어노테이션 지우기', + clearAllConfirm: '모든 주석을 삭제하시겠습니까?', }, }, editModal: { diff --git a/web/i18n/ko-KR/app-debug.ts b/web/i18n/ko-KR/app-debug.ts index b84946841f..f9bc9978d8 100644 --- a/web/i18n/ko-KR/app-debug.ts +++ b/web/i18n/ko-KR/app-debug.ts @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: '파일 업로드', + description: '채팅 입력 상자에서 이미지, 문서 및 기타 파일 업로드를 지원합니다.', + supportedTypes: '지원 파일 유형', + numberLimit: '최대 업로드 수', + modalTitle: '파일 업로드 설정', + }, + imageUpload: { + title: '이미지 업로드', + description: '이미지 업로드를 지원합니다.', + supportedTypes: '지원 파일 유형', + numberLimit: '최대 업로드 수', + modalTitle: '이미지 업로드 설정', + }, + bar: { + empty: '웹 앱 사용자 경험을 향상시키는 기능 활성화', + enableText: '기능 활성화됨', + manage: '관리', + }, + documentUpload: { + title: '문서', + description: '문서를 활성화하면 모델이 문서를 받아들이고 문서에 대한 질문에 답할 수 있습니다.', + }, + audioUpload: { + title: '오디오', + description: '오디오를 활성화하면 모델이 전사 및 분석을 위해 오디오 파일을 처리할 수 있습니다.', + }, }, automatic: { title: '자동 어플리케이션 오케스트레이션', @@ -281,12 +308,14 @@ const translation = { 'required': '필수', 'hide': '숨기기', 'errorMsg': { - varNameRequired: '변수명은 필수입니다', labelNameRequired: '레이블명은 필수입니다', varNameCanBeRepeat: '변수명은 중복될 수 없습니다', atLeastOneOption: '적어도 하나의 옵션이 필요합니다', optionRepeat: '옵션이 중복되어 있습니다', }, + 'defaultValue': '기본값', + 'noDefaultValue': '기본값 없음', + 'selectDefaultValue': '기본값 선택', }, vision: { name: '비전', diff --git a/web/i18n/ko-KR/app.ts b/web/i18n/ko-KR/app.ts index bcc18e70f0..c113947961 100644 --- a/web/i18n/ko-KR/app.ts +++ b/web/i18n/ko-KR/app.ts @@ -286,6 +286,7 @@ const translation = { noAccessPermission: '웹 앱에 대한 접근 권한이 없습니다.', maxActiveRequests: '동시 최대 요청 수', maxActiveRequestsPlaceholder: '무제한 사용을 원하시면 0을 입력하세요.', + maxActiveRequestsTip: '앱당 최대 동시 활성 요청 수(무제한은 0)', } export default translation diff --git a/web/i18n/ko-KR/plugin.ts b/web/i18n/ko-KR/plugin.ts index 1f60f1365b..815a30d3bb 100644 --- a/web/i18n/ko-KR/plugin.ts +++ b/web/i18n/ko-KR/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: '수정만 하기', selectedDescription: '패치 버전만 자동 업데이트', + description: '패치 버전만 자동 업데이트 (예: 1.0.1 → 1.0.2). 마이너 버전 변경은 업데이트를 유발하지 않습니다.', }, latest: { name: '최신', diff --git a/web/i18n/ko-KR/workflow.ts b/web/i18n/ko-KR/workflow.ts index be6c78f3ef..a65925f254 100644 --- a/web/i18n/ko-KR/workflow.ts +++ b/web/i18n/ko-KR/workflow.ts @@ -525,6 +525,7 @@ const translation = { conditions: '조건', }, title: '메타데이터 필터링', + tip: '메타데이터 필터링은 시스템 내에서 관련 정보를 검색하는 과정을 정제하고 제어하기 위해 메타데이터 속성(예: 태그, 카테고리 또는 접근 권한)을 사용하는 과정입니다.', }, }, http: { diff --git a/web/i18n/pl-PL/app-annotation.ts b/web/i18n/pl-PL/app-annotation.ts index 81a525935e..32efc76e66 100644 --- a/web/i18n/pl-PL/app-annotation.ts +++ b/web/i18n/pl-PL/app-annotation.ts @@ -18,6 +18,7 @@ const translation = { bulkImport: 'Masowy import', bulkExport: 'Masowy eksport', clearAll: 'Wyczyść wszystkie adnotacje', + clearAllConfirm: 'Usunąć wszystkie adnotacje?', }, }, editModal: { diff --git a/web/i18n/pl-PL/app-debug.ts b/web/i18n/pl-PL/app-debug.ts index dcd286d351..06e271fbbb 100644 --- a/web/i18n/pl-PL/app-debug.ts +++ b/web/i18n/pl-PL/app-debug.ts @@ -214,6 +214,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Przesyłanie plików', + description: 'Pole wprowadzania czatu umożliwia przesyłanie obrazów, dokumentów i innych plików.', + supportedTypes: 'Obsługiwane typy plików', + numberLimit: 'Maksymalna liczba przesłanych plików', + modalTitle: 'Ustawienia przesyłania plików', + }, + imageUpload: { + title: 'Przesyłanie obrazów', + description: 'Umożliwia przesyłanie obrazów.', + supportedTypes: 'Obsługiwane typy plików', + numberLimit: 'Maksymalna liczba przesłanych plików', + modalTitle: 'Ustawienia przesyłania obrazów', + }, + bar: { + empty: 'Włącz funkcje aby poprawić doświadczenie użytkownika aplikacji webowej', + enableText: 'Funkcje włączone', + manage: 'Zarządzaj', + }, + documentUpload: { + title: 'Dokument', + description: 'Włączenie Dokumentu pozwoli modelowi na przyjmowanie dokumentów i odpowiadanie na pytania o nich.', + }, + audioUpload: { + title: 'Dźwięk', + description: 'Włączenie Dźwięku pozwoli modelowi na przetwarzanie plików audio do transkrypcji i analizy.', + }, }, automatic: { title: 'Zautomatyzowana orkiestracja aplikacji', @@ -311,12 +338,14 @@ const translation = { 'required': 'Wymagane', 'hide': 'Ukryj', 'errorMsg': { - varNameRequired: 'Wymagana nazwa zmiennej', labelNameRequired: 'Wymagana nazwa etykiety', varNameCanBeRepeat: 'Nazwa zmiennej nie może się powtarzać', atLeastOneOption: 'Wymagana jest co najmniej jedna opcja', optionRepeat: 'Powtarzają się opcje', }, + 'defaultValue': 'Wartość domyślna', + 'noDefaultValue': 'Brak wartości domyślnej', + 'selectDefaultValue': 'Wybierz wartość domyślną', }, vision: { name: 'Wizja', diff --git a/web/i18n/pl-PL/app.ts b/web/i18n/pl-PL/app.ts index 040789424c..9a42b702e7 100644 --- a/web/i18n/pl-PL/app.ts +++ b/web/i18n/pl-PL/app.ts @@ -268,6 +268,7 @@ const translation = { noAccessPermission: 'Brak uprawnień do dostępu do aplikacji internetowej', maxActiveRequests: 'Maksymalne równoczesne żądania', maxActiveRequestsPlaceholder: 'Wprowadź 0, aby uzyskać nielimitowane', + maxActiveRequestsTip: 'Maksymalna liczba jednoczesnych aktywnych żądań na aplikację (0 dla nieograniczonej)', } export default translation diff --git a/web/i18n/pl-PL/plugin.ts b/web/i18n/pl-PL/plugin.ts index 10944a339b..5badeafe27 100644 --- a/web/i18n/pl-PL/plugin.ts +++ b/web/i18n/pl-PL/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { selectedDescription: 'Automatyczna aktualizacja tylko dla wersji poprawek', name: 'Napraw tylko', + description: 'Automatyczna aktualizacja tylko dla wersji łatkowych (np. 1.0.1 → 1.0.2). Zmiany w wersjach mniejszych nie będą wywoływać aktualizacji.', }, latest: { name: 'Najświeższy', diff --git a/web/i18n/pl-PL/workflow.ts b/web/i18n/pl-PL/workflow.ts index bd47328a65..a29ec9b8f2 100644 --- a/web/i18n/pl-PL/workflow.ts +++ b/web/i18n/pl-PL/workflow.ts @@ -497,6 +497,7 @@ const translation = { select: 'Wybierz zmienną...', }, title: 'Filtrowanie metadanych', + tip: 'Filtracja metadanych to proces wykorzystania atrybutów metadanych (takich jak tagi, kategorie lub uprawnienia dostępu) do precyzowania i kontrolowania pozyskiwania istotnych informacji w systemie.', }, }, http: { diff --git a/web/i18n/pt-BR/app-annotation.ts b/web/i18n/pt-BR/app-annotation.ts index 3ae53ca696..9e2760bf24 100644 --- a/web/i18n/pt-BR/app-annotation.ts +++ b/web/i18n/pt-BR/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'Importação em Massa', bulkExport: 'Exportação em Massa', clearAll: 'Limpar Todas as Anotações', + clearAllConfirm: 'Excluir todas as anotações?', }, }, editModal: { diff --git a/web/i18n/pt-BR/app-debug.ts b/web/i18n/pt-BR/app-debug.ts index 96d78dc9a3..5f8aabec65 100644 --- a/web/i18n/pt-BR/app-debug.ts +++ b/web/i18n/pt-BR/app-debug.ts @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Upload de Arquivo', + description: 'A caixa de entrada do chat permite fazer upload de imagens, documentos e outros arquivos.', + supportedTypes: 'Tipos de Arquivo Suportados', + numberLimit: 'Máximo de uploads', + modalTitle: 'Configuração de Upload de Arquivo', + }, + imageUpload: { + title: 'Upload de Imagem', + description: 'Permite fazer upload de imagens.', + supportedTypes: 'Tipos de Arquivo Suportados', + numberLimit: 'Máximo de uploads', + modalTitle: 'Configuração de Upload de Imagem', + }, + bar: { + empty: 'Habilitar recursos para melhorar a experiência do usuário do aplicativo web', + enableText: 'Recursos Habilitados', + manage: 'Gerenciar', + }, + documentUpload: { + title: 'Documento', + description: 'Habilitar Documento permitirá que o modelo aceite documentos e responda perguntas sobre eles.', + }, + audioUpload: { + title: 'Áudio', + description: 'Habilitar Áudio permitirá que o modelo processe arquivos de áudio para transcrição e análise.', + }, }, automatic: { title: 'Orquestração Automatizada de Aplicativos', @@ -287,12 +314,14 @@ const translation = { 'required': 'Obrigatório', 'hide': 'Ocultar', 'errorMsg': { - varNameRequired: 'O nome da variável é obrigatório', labelNameRequired: 'O nome do rótulo é obrigatório', varNameCanBeRepeat: 'O nome da variável não pode ser repetido', atLeastOneOption: 'Pelo menos uma opção é obrigatória', optionRepeat: 'Tem opções repetidas', }, + 'defaultValue': 'Valor padrão', + 'noDefaultValue': 'Nenhum valor padrão', + 'selectDefaultValue': 'Selecionar valor padrão', }, vision: { name: 'Visão', diff --git a/web/i18n/pt-BR/app.ts b/web/i18n/pt-BR/app.ts index 980767316f..6122a75a97 100644 --- a/web/i18n/pt-BR/app.ts +++ b/web/i18n/pt-BR/app.ts @@ -261,6 +261,7 @@ const translation = { noAccessPermission: 'Sem permissão para acessar o aplicativo web', maxActiveRequestsPlaceholder: 'Digite 0 para ilimitado', maxActiveRequests: 'Máximo de solicitações simultâneas', + maxActiveRequestsTip: 'Número máximo de solicitações ativas simultâneas por aplicativo (0 para ilimitado)', } export default translation diff --git a/web/i18n/pt-BR/plugin.ts b/web/i18n/pt-BR/plugin.ts index 47490d218c..9b31f5e190 100644 --- a/web/i18n/pt-BR/plugin.ts +++ b/web/i18n/pt-BR/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { selectedDescription: 'Atualização automática apenas para versões de patch', name: 'Reparar Apenas', + description: 'Atualização automática apenas para versões de patch (por exemplo, 1.0.1 → 1.0.2). Mudanças de versão menor não ativarão atualizações.', }, latest: { description: 'Sempre atualize para a versão mais recente', diff --git a/web/i18n/pt-BR/workflow.ts b/web/i18n/pt-BR/workflow.ts index f36e3b8499..ec870d0e17 100644 --- a/web/i18n/pt-BR/workflow.ts +++ b/web/i18n/pt-BR/workflow.ts @@ -497,6 +497,7 @@ const translation = { placeholder: 'Insira o valor', }, title: 'Filtragem de Metadados', + tip: 'A filtragem de metadados é o processo de usar atributos de metadados (como etiquetas, categorias ou permissões de acesso) para refinar e controlar a recuperação de informações relevantes dentro de um sistema.', }, }, http: { diff --git a/web/i18n/ro-RO/app-annotation.ts b/web/i18n/ro-RO/app-annotation.ts index 42fd17c12b..67feb9db1f 100644 --- a/web/i18n/ro-RO/app-annotation.ts +++ b/web/i18n/ro-RO/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'Import în Masă', bulkExport: 'Export în Masă', clearAll: 'Șterge Toate Anotațiile', + clearAllConfirm: 'Șterge toate anotările?', }, }, editModal: { diff --git a/web/i18n/ro-RO/app-debug.ts b/web/i18n/ro-RO/app-debug.ts index f7240055e3..f6a10df1d2 100644 --- a/web/i18n/ro-RO/app-debug.ts +++ b/web/i18n/ro-RO/app-debug.ts @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Încărcare fișier', + description: 'Caseta de intrare chat permite încărcarea de imagini, documente și alte fișiere.', + supportedTypes: 'Tipuri de fișiere suportate', + numberLimit: 'Numărul maxim de încărcări', + modalTitle: 'Setări încărcare fișier', + }, + imageUpload: { + title: 'Încărcare imagine', + description: 'Permite încărcarea imaginilor.', + supportedTypes: 'Tipuri de fișiere suportate', + numberLimit: 'Numărul maxim de încărcări', + modalTitle: 'Setări încărcare imagine', + }, + bar: { + empty: 'Activează funcții pentru a îmbunătăți experiența utilizatorilor aplicației web', + enableText: 'Funcții activate', + manage: 'Gestionează', + }, + documentUpload: { + title: 'Document', + description: 'Activarea Documentului va permite modelului să primească documente și să răspundă la întrebări despre ele.', + }, + audioUpload: { + title: 'Audio', + description: 'Activarea Audio va permite modelului să proceseze fișiere audio pentru transcriere și analiză.', + }, }, automatic: { title: 'Orchestrarea automată a aplicațiilor', @@ -287,12 +314,14 @@ const translation = { 'required': 'Obligatoriu', 'hide': 'Ascundeți', 'errorMsg': { - varNameRequired: 'Numele variabilei este obligatoriu', labelNameRequired: 'Numele etichetei este obligatoriu', varNameCanBeRepeat: 'Numele variabilei nu poate fi repetat', atLeastOneOption: 'Este necesară cel puțin o opțiune', optionRepeat: 'Există opțiuni repetate', }, + 'defaultValue': 'Valoare implicită', + 'noDefaultValue': 'Fără valoare implicită', + 'selectDefaultValue': 'Selectați valoarea implicită', }, vision: { name: 'Viziune', diff --git a/web/i18n/ro-RO/app.ts b/web/i18n/ro-RO/app.ts index 791bbcbc7e..d674b4ca82 100644 --- a/web/i18n/ro-RO/app.ts +++ b/web/i18n/ro-RO/app.ts @@ -261,6 +261,7 @@ const translation = { noAccessPermission: 'Nici o permisiune pentru a accesa aplicația web', maxActiveRequestsPlaceholder: 'Introduceți 0 pentru nelimitat', maxActiveRequests: 'Maxime cereri simultane', + maxActiveRequestsTip: 'Numărul maxim de cereri active concurente pe aplicație (0 pentru nelimitat)', } export default translation diff --git a/web/i18n/ro-RO/plugin.ts b/web/i18n/ro-RO/plugin.ts index 8c3ba06bbc..d65dc829f8 100644 --- a/web/i18n/ro-RO/plugin.ts +++ b/web/i18n/ro-RO/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { selectedDescription: 'Actualizare automată doar pentru versiuni patch', name: 'Fix doar', + description: 'Actualizare automată doar pentru versiunile de patch (de exemplu, 1.0.1 → 1.0.2). Schimbările de versiune minore nu vor declanșa actualizări.', }, latest: { name: 'Ultimul', diff --git a/web/i18n/ro-RO/workflow.ts b/web/i18n/ro-RO/workflow.ts index 2569d5339c..5612f5d1fc 100644 --- a/web/i18n/ro-RO/workflow.ts +++ b/web/i18n/ro-RO/workflow.ts @@ -497,6 +497,7 @@ const translation = { search: 'Căutare metadate', }, title: 'Filtrarea metadatelor', + tip: 'Filtrarea metadatelor este procesul de utilizare a atributelor metadatelor (cum ar fi etichetele, categoriile sau permisiunile de acces) pentru a rafina și controla recuperarea informațiilor relevante într-un sistem.', }, }, http: { diff --git a/web/i18n/ru-RU/app-annotation.ts b/web/i18n/ru-RU/app-annotation.ts index 18f2ae4a11..e189c9ca93 100644 --- a/web/i18n/ru-RU/app-annotation.ts +++ b/web/i18n/ru-RU/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'Массовый импорт', bulkExport: 'Массовый экспорт', clearAll: 'Очистить все аннотации', + clearAllConfirm: 'Удалить все аннотации?', }, }, editModal: { diff --git a/web/i18n/ru-RU/app-debug.ts b/web/i18n/ru-RU/app-debug.ts index 5d4dbb53d3..1d45c90a43 100644 --- a/web/i18n/ru-RU/app-debug.ts +++ b/web/i18n/ru-RU/app-debug.ts @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Загрузка файлов', + description: 'Поле ввода чата позволяет загружать изображения, документы и другие файлы.', + supportedTypes: 'Поддерживаемые типы файлов', + numberLimit: 'Максимум загрузок', + modalTitle: 'Настройка загрузки файлов', + }, + imageUpload: { + title: 'Загрузка изображений', + description: 'Позволяет загружать изображения.', + supportedTypes: 'Поддерживаемые типы файлов', + numberLimit: 'Максимум загрузок', + modalTitle: 'Настройка загрузки изображений', + }, + bar: { + empty: 'Включить функции для улучшения пользовательского опыта веб-приложения', + enableText: 'Функции включены', + manage: 'Управлять', + }, + documentUpload: { + title: 'Документ', + description: 'Включение Документа позволит модели принимать документы и отвечать на вопросы о них.', + }, + audioUpload: { + title: 'Аудио', + description: 'Включение Аудио позволит модели обрабатывать аудиофайлы для транскрипции и анализа.', + }, }, generate: { title: 'Генератор промпта', @@ -329,6 +356,9 @@ const translation = { atLeastOneOption: 'Требуется хотя бы один вариант', optionRepeat: 'Есть повторяющиеся варианты', }, + 'defaultValue': 'Значение по умолчанию', + 'noDefaultValue': 'Без значения по умолчанию', + 'selectDefaultValue': 'Выберите значение по умолчанию', }, vision: { name: 'Зрение', diff --git a/web/i18n/ru-RU/app.ts b/web/i18n/ru-RU/app.ts index d12f25ed57..b02d01b263 100644 --- a/web/i18n/ru-RU/app.ts +++ b/web/i18n/ru-RU/app.ts @@ -261,6 +261,7 @@ const translation = { noAccessPermission: 'Нет разрешения на доступ к веб-приложению', maxActiveRequests: 'Максимальное количество параллельных запросов', maxActiveRequestsPlaceholder: 'Введите 0 для неограниченного количества', + maxActiveRequestsTip: 'Максимальное количество одновременно активных запросов на одно приложение (0 для неограниченного количества)', } export default translation diff --git a/web/i18n/ru-RU/plugin.ts b/web/i18n/ru-RU/plugin.ts index f39139aa05..9bbb3c4852 100644 --- a/web/i18n/ru-RU/plugin.ts +++ b/web/i18n/ru-RU/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: 'Только исправить', selectedDescription: 'Автообновление только для версий патчей', + description: 'Автообновление только для патч-версий (например, 1.0.1 → 1.0.2). Изменения в минорных версиях не вызовут обновления.', }, latest: { name: 'Новости', diff --git a/web/i18n/ru-RU/workflow.ts b/web/i18n/ru-RU/workflow.ts index d8452122ad..8ab0f04c8e 100644 --- a/web/i18n/ru-RU/workflow.ts +++ b/web/i18n/ru-RU/workflow.ts @@ -497,6 +497,7 @@ const translation = { search: 'Поиск метаданных', }, title: 'Фильтрация метаданных', + tip: 'Фильтрация метаданных — это процесс использования атрибутов метаданных (таких как теги, категории или права доступа) для уточнения и контроля извлечения соответствующей информации внутри системы.', }, }, http: { diff --git a/web/i18n/sl-SI/app-annotation.ts b/web/i18n/sl-SI/app-annotation.ts index 07b175a8e7..6cd88a47ee 100644 --- a/web/i18n/sl-SI/app-annotation.ts +++ b/web/i18n/sl-SI/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'Množični uvoz', bulkExport: 'Množični izvoz', clearAll: 'Počisti vse opombe', + clearAllConfirm: 'Izbrišite vse opombe?', }, }, editModal: { diff --git a/web/i18n/sl-SI/app-debug.ts b/web/i18n/sl-SI/app-debug.ts index 7e5c7dd6b0..597a8afa06 100644 --- a/web/i18n/sl-SI/app-debug.ts +++ b/web/i18n/sl-SI/app-debug.ts @@ -236,6 +236,33 @@ const translation = { ok: 'V redu', }, }, + fileUpload: { + title: 'Nalaganje datoteke', + description: 'Pogovorno polje omogoča nalaganje slik, dokumentov in drugih datotek.', + supportedTypes: 'Podprte vrste datotek', + numberLimit: 'Največje število nalaganj', + modalTitle: 'Nastavitve nalaganja datoteke', + }, + imageUpload: { + title: 'Nalaganje slike', + description: 'Omogoči nalaganje slik.', + supportedTypes: 'Podprte vrste datotek', + numberLimit: 'Največje število nalaganj', + modalTitle: 'Nastavitve nalaganja slike', + }, + bar: { + empty: 'Omogoči funkcije za izboljšanje uporabniške izkušnje spletne aplikacije', + enableText: 'Funkcije omogočene', + manage: 'Upravljaj', + }, + documentUpload: { + title: 'Dokument', + description: 'Omogočitev dokumenta bo omogočila modelu, da sprejme dokumente in odgovori na vprašanja o njih.', + }, + audioUpload: { + title: 'Zvok', + description: 'Omogočitev zvoka bo omogočila modelu, da obdela zvočne datoteke za prepisovanje in analizo.', + }, }, } diff --git a/web/i18n/sl-SI/app.ts b/web/i18n/sl-SI/app.ts index cd6d1169a4..337bd10359 100644 --- a/web/i18n/sl-SI/app.ts +++ b/web/i18n/sl-SI/app.ts @@ -261,6 +261,7 @@ const translation = { noAccessPermission: 'Brez dovoljenja za dostop do spletne aplikacije', maxActiveRequestsPlaceholder: 'Vnesite 0 za neomejeno', maxActiveRequests: 'Maksimalno število hkratnih zahtevkov', + maxActiveRequestsTip: 'Največje število hkrati aktivnih zahtevkov na aplikacijo (0 za neomejeno)', } export default translation diff --git a/web/i18n/sl-SI/plugin.ts b/web/i18n/sl-SI/plugin.ts index 049a80f859..dc435f2302 100644 --- a/web/i18n/sl-SI/plugin.ts +++ b/web/i18n/sl-SI/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: 'Popravi samo', selectedDescription: 'Samodejno posodabljanje samo za različice popravkov', + description: 'Samodejno posodabljanje samo za različice popravkov (npr. 1.0.1 → 1.0.2). Spremembe manjših različic ne bodo povzročile posodobitev.', }, latest: { selectedDescription: 'Vedno posodobite na najnovejšo različico', diff --git a/web/i18n/sl-SI/workflow.ts b/web/i18n/sl-SI/workflow.ts index 125d82e78f..72150701de 100644 --- a/web/i18n/sl-SI/workflow.ts +++ b/web/i18n/sl-SI/workflow.ts @@ -279,6 +279,7 @@ const translation = { 'start': 'Določite začetne parametre za zagon delovnega toka', 'variable-assigner': 'Združite večpodružinske spremenljivke v eno samo spremenljivko za enotno konfiguracijo spodnjih vozlišč.', 'variable-aggregator': 'Združite večpodružnične spremenljivke v eno samo spremenljivko za enotno konfiguracijo spodnjih vozlišč.', + 'assigner': 'Vožnji vozlišča za dodelitev spremenljivk se uporablja za dodeljevanje vrednosti spremenljivkam, ki jih je mogoče zapisati (kot so spremenljivke za pogovor).', }, operator: { zoomOut: 'Zoomirati ven', @@ -312,6 +313,7 @@ const translation = { organizeBlocks: 'Organizirajte vozlišča', minimize: 'Izhod iz celotnega zaslona', maximize: 'Maksimiziraj platno', + optional: '(neobvezno)', }, nodes: { common: { @@ -497,6 +499,7 @@ const translation = { add: 'Dodaj pogoj', }, title: 'Filtriranje metapodatkov', + tip: 'Filtriranje metapodatkov je postopek uporabe metapodatkovnih atributov (kot so oznake, kategorije ali dovoljenja za dostop) za natančnejše določanje in nadzorovanje pridobivanja relevantnih informacij znotraj sistema.', }, queryVariable: 'Vprašanje spremenljivka', knowledge: 'Znanje', diff --git a/web/i18n/th-TH/app-annotation.ts b/web/i18n/th-TH/app-annotation.ts index 5fba357d04..f038f5ef8c 100644 --- a/web/i18n/th-TH/app-annotation.ts +++ b/web/i18n/th-TH/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'นําเข้าจํานวนมาก', bulkExport: 'ส่งออกจํานวนมาก', clearAll: 'ล้างคําอธิบายประกอบทั้งหมด', + clearAllConfirm: 'ลบหมายเหตุต่างๆ ทั้งหมดหรือไม่?', }, }, editModal: { diff --git a/web/i18n/th-TH/app-debug.ts b/web/i18n/th-TH/app-debug.ts index 928649474b..a2e939b5fe 100644 --- a/web/i18n/th-TH/app-debug.ts +++ b/web/i18n/th-TH/app-debug.ts @@ -1,4 +1,33 @@ const translation = { + feature: { + fileUpload: { + title: 'การอัปโหลดไฟล์', + description: 'กล่องข้อความแชทช่วยให้สามารถอัปโหลดรูปภาพ เอกสาร และไฟล์อื่นๆ ได้', + supportedTypes: 'ประเภทไฟล์ที่รองรับ', + numberLimit: 'จำนวนสูงสุดที่อัปโหลดได้', + modalTitle: 'การตั้งค่าการอัปโหลดไฟล์', + }, + imageUpload: { + title: 'การอัปโหลดรูปภาพ', + description: 'อนุญาตให้อัปโหลดรูปภาพได้', + supportedTypes: 'ประเภทไฟล์ที่รองรับ', + numberLimit: 'จำนวนสูงสุดที่อัปโหลดได้', + modalTitle: 'การตั้งค่าการอัปโหลดรูปภาพ', + }, + bar: { + empty: 'เปิดใช้งานคุณสมบัติเพื่อเพิ่มประสบการณ์ผู้ใช้ของเว็บแอป', + enableText: 'เปิดใช้งานคุณสมบัติแล้ว', + manage: 'จัดการ', + }, + documentUpload: { + title: 'เอกสาร', + description: 'การเปิดใช้งานเอกสารจะทำให้โมเดลสามารถรับเอกสารและตอบคำถามเกี่ยวกับเอกสารเหล่านั้นได้', + }, + audioUpload: { + title: 'เสียง', + description: 'การเปิดใช้งานเสียงจะทำให้โมเดลสามารถประมวลผลไฟล์เสียงเพื่อการถอดข้อความและการวิเคราะห์ได้', + }, + }, } export default translation diff --git a/web/i18n/th-TH/app.ts b/web/i18n/th-TH/app.ts index af2f67bcc1..8c8c0e02a2 100644 --- a/web/i18n/th-TH/app.ts +++ b/web/i18n/th-TH/app.ts @@ -257,6 +257,7 @@ const translation = { noAccessPermission: 'ไม่มีสิทธิ์เข้าถึงเว็บแอป', maxActiveRequestsPlaceholder: 'ใส่ 0 สำหรับไม่จำกัด', maxActiveRequests: 'จำนวนคำขอพร้อมกันสูงสุด', + maxActiveRequestsTip: 'จำนวนการร้องขอที่ใช้งานพร้อมกันสูงสุดต่อแอป (0 หมายถึงไม่จำกัด)', } export default translation diff --git a/web/i18n/th-TH/plugin.ts b/web/i18n/th-TH/plugin.ts index 6a53350cad..a967280dbd 100644 --- a/web/i18n/th-TH/plugin.ts +++ b/web/i18n/th-TH/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: 'ซ่อมเฉพาะ', selectedDescription: 'อัปเดตอัตโนมัติเฉพาะเวอร์ชันแพตช์เท่านั้น', + description: 'การอัปเดตอัตโนมัติสำหรับเฉพาะเวอร์ชันแพทช์ (เช่น 1.0.1 → 1.0.2) การเปลี่ยนแปลงเวอร์ชันย่อยจะไม่ทำให้เกิดการอัปเดต', }, latest: { name: 'ล่าสุด', diff --git a/web/i18n/th-TH/workflow.ts b/web/i18n/th-TH/workflow.ts index f03b021fd1..875f347cbb 100644 --- a/web/i18n/th-TH/workflow.ts +++ b/web/i18n/th-TH/workflow.ts @@ -497,6 +497,7 @@ const translation = { placeholder: 'ใส่ค่า', }, title: 'การกรองข้อมูลเมตา', + tip: 'การกรองข้อมูลเมตาดาต้าเป็นกระบวนการที่ใช้คุณลักษณะของเมตาดาต้า (เช่น แท็ก หมวดหมู่ หรือสิทธิการเข้าถึง) เพื่อปรับแต่งและควบคุมการดึงข้อมูลที่เกี่ยวข้องภายในระบบ.', }, }, http: { diff --git a/web/i18n/tr-TR/app-annotation.ts b/web/i18n/tr-TR/app-annotation.ts index bcd6db1fb2..f9b29bb711 100644 --- a/web/i18n/tr-TR/app-annotation.ts +++ b/web/i18n/tr-TR/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'Toplu İçe Aktarma', bulkExport: 'Toplu Dışa Aktarma', clearAll: 'Tüm Ek Açıklamaları Temizle', + clearAllConfirm: 'Tüm açıklamaları silinsin mi?', }, }, editModal: { diff --git a/web/i18n/tr-TR/app-debug.ts b/web/i18n/tr-TR/app-debug.ts index 6ed0e0a5eb..631974edb5 100644 --- a/web/i18n/tr-TR/app-debug.ts +++ b/web/i18n/tr-TR/app-debug.ts @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Dosya Yükleme', + description: 'Sohbet giriş kutusu görüntü, belge ve diğer dosyaların yüklenmesine izin verir.', + supportedTypes: 'Desteklenen Dosya Türleri', + numberLimit: 'Maksimum yükleme sayısı', + modalTitle: 'Dosya Yükleme Ayarları', + }, + imageUpload: { + title: 'Görüntü Yükleme', + description: 'Görüntü yüklemeye izin verir.', + supportedTypes: 'Desteklenen Dosya Türleri', + numberLimit: 'Maksimum yükleme sayısı', + modalTitle: 'Görüntü Yükleme Ayarları', + }, + bar: { + empty: 'Web uygulaması kullanıcı deneyimini geliştirmek için özellikleri etkinleştirin', + enableText: 'Özellikler Etkinleştirildi', + manage: 'Yönet', + }, + documentUpload: { + title: 'Belge', + description: 'Belgeyi etkinleştirmek modelin belgeleri almasına ve bunlar hakkında sorulara cevap vermesine izin verir.', + }, + audioUpload: { + title: 'Ses', + description: 'Sesi etkinleştirmek modelin transkripsiyon ve analiz için ses dosyalarını işlemesine izin verir.', + }, }, generate: { title: 'Prompt Oluşturucu', @@ -323,12 +350,14 @@ const translation = { content: 'İçerik', required: 'Gerekli', errorMsg: { - varNameRequired: 'Değişken adı gereklidir', labelNameRequired: 'Etiket adı gereklidir', varNameCanBeRepeat: 'Değişken adı tekrar edemez', atLeastOneOption: 'En az bir seçenek gereklidir', optionRepeat: 'Yinelenen seçenekler var', }, + defaultValue: 'Varsayılan değer', + noDefaultValue: 'Varsayılan değer yok', + selectDefaultValue: 'Varsayılan değer seç', }, vision: { name: 'Görüş', diff --git a/web/i18n/tr-TR/app.ts b/web/i18n/tr-TR/app.ts index 1847af9cf4..05ad7c1378 100644 --- a/web/i18n/tr-TR/app.ts +++ b/web/i18n/tr-TR/app.ts @@ -257,6 +257,7 @@ const translation = { noAccessPermission: 'Web uygulamasına erişim izni yok', maxActiveRequestsPlaceholder: 'Sınırsız için 0 girin', maxActiveRequests: 'Maksimum eş zamanlı istekler', + maxActiveRequestsTip: 'Her uygulama için maksimum eşzamanlı aktif istek sayısı (sınırsız için 0)', } export default translation diff --git a/web/i18n/tr-TR/plugin.ts b/web/i18n/tr-TR/plugin.ts index 4c2b5510d2..1856a34c7e 100644 --- a/web/i18n/tr-TR/plugin.ts +++ b/web/i18n/tr-TR/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { selectedDescription: 'Sadece yamanın versiyonları için otomatik güncelleme', name: 'Sadece Düzelt', + description: 'Yalnızca yamanın sürüm güncellemeleri için otomatik güncelleme (örneğin, 1.0.1 → 1.0.2). Küçük sürüm değişiklikleri güncellemeleri tetiklemez.', }, latest: { name: 'Son', diff --git a/web/i18n/tr-TR/workflow.ts b/web/i18n/tr-TR/workflow.ts index b30442023a..9572217062 100644 --- a/web/i18n/tr-TR/workflow.ts +++ b/web/i18n/tr-TR/workflow.ts @@ -497,6 +497,7 @@ const translation = { datePlaceholder: 'Bir zaman seçin...', }, title: 'Meta Verileri Filtreleme', + tip: 'Metadata filtreleme, bir sistem içinde ilgili bilgilerin alınmasını ince ayar ve kontrol etmek için metadata özniteliklerini (etiketler, kategoriler veya erişim izinleri gibi) kullanma sürecidir.', }, }, http: { diff --git a/web/i18n/uk-UA/app-annotation.ts b/web/i18n/uk-UA/app-annotation.ts index d34be76d7f..918cea529a 100644 --- a/web/i18n/uk-UA/app-annotation.ts +++ b/web/i18n/uk-UA/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'Масовий імпорт', bulkExport: 'Масовий експорт', clearAll: 'Очистити всі анотації', + clearAllConfirm: 'Видалити всі анотації?', }, }, editModal: { diff --git a/web/i18n/uk-UA/app-debug.ts b/web/i18n/uk-UA/app-debug.ts index 70bbebe37e..fe6fefa801 100644 --- a/web/i18n/uk-UA/app-debug.ts +++ b/web/i18n/uk-UA/app-debug.ts @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Завантаження файлу', + description: 'Поле вводу чату дозволяє завантажувати зображення, документи та інші файли.', + supportedTypes: 'Підтримувані типи файлів', + numberLimit: 'Максимальна кількість завантажень', + modalTitle: 'Налаштування завантаження файлів', + }, + imageUpload: { + title: 'Завантаження зображення', + description: 'Дозволити завантаження зображень.', + supportedTypes: 'Підтримувані типи файлів', + numberLimit: 'Максимальна кількість завантажень', + modalTitle: 'Налаштування завантаження зображень', + }, + bar: { + empty: 'Увімкніть функції для покращення користувацького досвіду веб-додатка', + enableText: 'Функції увімкнено', + manage: 'Керувати', + }, + documentUpload: { + title: 'Документ', + description: 'Увімкнення документа дозволить моделі приймати документи та відповідати на запитання про них.', + }, + audioUpload: { + title: 'Аудіо', + description: 'Увімкнення аудіо дозволить моделі обробляти аудіофайли для транскрипції та аналізу.', + }, }, automatic: { title: 'Автоматизована оркестрація застосунків', @@ -281,12 +308,14 @@ const translation = { 'required': 'Обов\'язково', 'hide': 'Приховати', 'errorMsg': { - varNameRequired: 'Потрібно вказати назву змінної', labelNameRequired: 'Потрібно вказати назву мітки', varNameCanBeRepeat: 'Назва змінної не може повторюватися', atLeastOneOption: 'Потрібно щонайменше одну опцію', optionRepeat: 'Є повторні опції', }, + 'defaultValue': 'Значення за замовчуванням', + 'noDefaultValue': 'Без значення за замовчуванням', + 'selectDefaultValue': 'Обрати значення за замовчуванням', }, vision: { name: 'Зображення', // Vision diff --git a/web/i18n/uk-UA/app.ts b/web/i18n/uk-UA/app.ts index 0d41e1e631..26c059f727 100644 --- a/web/i18n/uk-UA/app.ts +++ b/web/i18n/uk-UA/app.ts @@ -261,6 +261,7 @@ const translation = { noAccessPermission: 'Немає дозволу на доступ до веб-додатку', maxActiveRequestsPlaceholder: 'Введіть 0 для необмеженого', maxActiveRequests: 'Максимальна кількість одночасних запитів', + maxActiveRequestsTip: 'Максимальна кількість одночасних активних запитів на додаток (0 для необмеженої кількості)', } export default translation diff --git a/web/i18n/uk-UA/plugin.ts b/web/i18n/uk-UA/plugin.ts index 877d7843ff..22b98fbd41 100644 --- a/web/i18n/uk-UA/plugin.ts +++ b/web/i18n/uk-UA/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: 'Виправити тільки', selectedDescription: 'Автоматичне оновлення лише для версій патчів', + description: 'Автооновлення лише для патч-версій (наприклад, 1.0.1 → 1.0.2). Зміни в малих версіях не активують оновлення.', }, latest: { name: 'Останні', diff --git a/web/i18n/uk-UA/workflow.ts b/web/i18n/uk-UA/workflow.ts index 5b62ef83ee..65dfab68ad 100644 --- a/web/i18n/uk-UA/workflow.ts +++ b/web/i18n/uk-UA/workflow.ts @@ -497,6 +497,7 @@ const translation = { add: 'Додати умову', }, title: 'Фільтрація метаданих', + tip: 'Фільтрація метаданих — це процес використання атрибутів метаданих (таких як теги, категорії або права доступу) для уточнення та контролю отримання відповідної інформації в системі.', }, }, http: { diff --git a/web/i18n/vi-VN/app-annotation.ts b/web/i18n/vi-VN/app-annotation.ts index 6a9457f3d7..5b9f3b35a5 100644 --- a/web/i18n/vi-VN/app-annotation.ts +++ b/web/i18n/vi-VN/app-annotation.ts @@ -17,6 +17,7 @@ const translation = { bulkImport: 'Nhập hàng loạt', bulkExport: 'Xuất hàng loạt', clearAll: 'Xóa tất cả chú thích', + clearAllConfirm: 'Xóa tất cả các chú thích?', }, }, editModal: { diff --git a/web/i18n/vi-VN/app-debug.ts b/web/i18n/vi-VN/app-debug.ts index cd57f78e79..381b766306 100644 --- a/web/i18n/vi-VN/app-debug.ts +++ b/web/i18n/vi-VN/app-debug.ts @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: 'Tải lên tệp', + description: 'Hộp nhập chat cho phép tải lên hình ảnh, tài liệu và các tệp khác.', + supportedTypes: 'Các loại tệp được hỗ trợ', + numberLimit: 'Số lượng tối đa có thể tải lên', + modalTitle: 'Cài đặt tải lên tệp', + }, + imageUpload: { + title: 'Tải lên hình ảnh', + description: 'Cho phép tải lên hình ảnh.', + supportedTypes: 'Các loại tệp được hỗ trợ', + numberLimit: 'Số lượng tối đa có thể tải lên', + modalTitle: 'Cài đặt tải lên hình ảnh', + }, + bar: { + empty: 'Bật tính năng để cải thiện trải nghiệm người dùng ứng dụng web', + enableText: 'Tính năng đã được bật', + manage: 'Quản lý', + }, + documentUpload: { + title: 'Tài liệu', + description: 'Bật tài liệu sẽ cho phép mô hình nhận tài liệu và trả lời các câu hỏi về chúng.', + }, + audioUpload: { + title: 'Âm thanh', + description: 'Bật âm thanh sẽ cho phép mô hình xử lý các tệp âm thanh để phiên âm và phân tích.', + }, }, automatic: { title: 'Tự động hóa triển khai ứng dụng', @@ -281,12 +308,14 @@ const translation = { 'required': 'Bắt buộc', 'hide': 'Ẩn', 'errorMsg': { - varNameRequired: 'Tên biến là bắt buộc', labelNameRequired: 'Tên nhãn là bắt buộc', varNameCanBeRepeat: 'Tên biến không được trùng lặp', atLeastOneOption: 'Cần ít nhất một tùy chọn', optionRepeat: 'Có các tùy chọn trùng lặp', }, + 'defaultValue': 'Giá trị mặc định', + 'noDefaultValue': 'Không có giá trị mặc định', + 'selectDefaultValue': 'Chọn giá trị mặc định', }, vision: { name: 'Thị giác', diff --git a/web/i18n/vi-VN/app.ts b/web/i18n/vi-VN/app.ts index 4100b52b36..9ad2058330 100644 --- a/web/i18n/vi-VN/app.ts +++ b/web/i18n/vi-VN/app.ts @@ -261,6 +261,7 @@ const translation = { accessControl: 'Kiểm soát truy cập ứng dụng web', maxActiveRequestsPlaceholder: 'Nhập 0 để không giới hạn', maxActiveRequests: 'Số yêu cầu đồng thời tối đa', + maxActiveRequestsTip: 'Số yêu cầu hoạt động đồng thời tối đa cho mỗi ứng dụng (0 để không giới hạn)', } export default translation diff --git a/web/i18n/vi-VN/plugin.ts b/web/i18n/vi-VN/plugin.ts index 677d90e6a7..c0f3dfac5f 100644 --- a/web/i18n/vi-VN/plugin.ts +++ b/web/i18n/vi-VN/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: 'Chỉ sửa chữa', selectedDescription: 'Tự động cập nhật chỉ cho các phiên bản bản vá', + description: 'Tự động cập nhật chỉ cho các phiên bản vá (ví dụ: 1.0.1 → 1.0.2). Thay đổi phiên bản nhỏ sẽ không kích hoạt cập nhật.', }, latest: { name: 'Mới nhất', diff --git a/web/i18n/vi-VN/workflow.ts b/web/i18n/vi-VN/workflow.ts index a4525a3ffa..ebe06807b1 100644 --- a/web/i18n/vi-VN/workflow.ts +++ b/web/i18n/vi-VN/workflow.ts @@ -497,6 +497,7 @@ const translation = { search: 'Tìm kiếm siêu dữ liệu', }, title: 'Lọc siêu dữ liệu', + tip: 'Lọc siêu dữ liệu là quá trình sử dụng các thuộc tính siêu dữ liệu (chẳng hạn như thẻ, danh mục hoặc quyền truy cập) để tinh chỉnh và kiểm soát việc truy xuất thông tin liên quan trong một hệ thống.', }, }, http: { diff --git a/web/i18n/zh-Hans/app-debug.ts b/web/i18n/zh-Hans/app-debug.ts index 3728d86e58..8bdb56ac64 100644 --- a/web/i18n/zh-Hans/app-debug.ts +++ b/web/i18n/zh-Hans/app-debug.ts @@ -222,6 +222,10 @@ const translation = { title: '文档', description: '启用文档后,模型可以接收文档并回答关于它们的问题。', }, + audioUpload: { + title: '音频', + description: '启用音频后,模型可以处理音频文件进行转录和分析。', + }, }, codegen: { title: '代码生成器', @@ -307,6 +311,9 @@ const translation = { waitForImgUpload: '请等待图片上传完成', waitForFileUpload: '请等待文件上传完成', }, + warningMessage: { + timeoutExceeded: '由于超时,结果未显示。请参考日志获取完整结果。', + }, chatSubTitle: '提示词', completionSubTitle: '前缀提示词', promptTip: @@ -394,6 +401,9 @@ const translation = { atLeastOneOption: '至少需要一个选项', optionRepeat: '选项不能重复', }, + 'defaultValue': '默认值', + 'noDefaultValue': '无默认值', + 'selectDefaultValue': '选择默认值', }, vision: { name: '视觉', diff --git a/web/i18n/zh-Hant/app-annotation.ts b/web/i18n/zh-Hant/app-annotation.ts index e1fee4626d..02eb98f5d4 100644 --- a/web/i18n/zh-Hant/app-annotation.ts +++ b/web/i18n/zh-Hant/app-annotation.ts @@ -19,6 +19,7 @@ const translation = { bulkImport: '批次匯入', bulkExport: '批次匯出', clearAll: '刪除所有標註', + clearAllConfirm: '要刪除所有標註嗎?', }, }, editModal: { diff --git a/web/i18n/zh-Hant/app-debug.ts b/web/i18n/zh-Hant/app-debug.ts index b31b9a9d66..434bc830a5 100644 --- a/web/i18n/zh-Hant/app-debug.ts +++ b/web/i18n/zh-Hant/app-debug.ts @@ -25,7 +25,7 @@ const translation = { debugConfig: '除錯', addFeature: '新增功能', automatic: '產生', - stopResponding: '停止響應', + stopResponding: '停止回應', agree: '贊同', disagree: '反對', cancelAgree: '取消贊同', @@ -198,6 +198,33 @@ const translation = { }, }, }, + fileUpload: { + title: '檔案上傳', + description: '聊天輸入框支援上傳檔案。類型包括圖片、文件以及其它類型', + supportedTypes: '支援的檔案類型', + numberLimit: '最大上傳數', + modalTitle: '檔案上傳設定', + }, + imageUpload: { + title: '圖片上傳', + description: '支援上傳圖片', + supportedTypes: '支援的檔案類型', + numberLimit: '最大上傳數', + modalTitle: '圖片上傳設定', + }, + bar: { + empty: '開啟功能增強 web app 使用者體驗', + enableText: '功能已開啟', + manage: '管理', + }, + documentUpload: { + title: '文件', + description: '啟用文件後,模型可以接收文件並回答關於它們的問題。', + }, + audioUpload: { + title: '音訊', + description: '啟用音訊後,模型可以處理音訊檔案進行轉錄和分析。', + }, }, resetConfig: { title: '確認重置?', @@ -272,6 +299,9 @@ const translation = { atLeastOneOption: '至少需要一個選項', optionRepeat: '選項不能重複', }, + 'defaultValue': '預設值', + 'noDefaultValue': '無預設值', + 'selectDefaultValue': '選擇預設值', }, vision: { name: '視覺', diff --git a/web/i18n/zh-Hant/app.ts b/web/i18n/zh-Hant/app.ts index 07b6c85453..e6a3a0b570 100644 --- a/web/i18n/zh-Hant/app.ts +++ b/web/i18n/zh-Hant/app.ts @@ -260,6 +260,7 @@ const translation = { noAccessPermission: '沒有權限訪問網絡應用程式', maxActiveRequestsPlaceholder: '輸入 0 以表示無限', maxActiveRequests: '同時最大請求數', + maxActiveRequestsTip: '每個應用程式可同時活躍請求的最大數量(0為無限制)', } export default translation diff --git a/web/i18n/zh-Hant/plugin.ts b/web/i18n/zh-Hant/plugin.ts index 0d0e1f8782..117491fe05 100644 --- a/web/i18n/zh-Hant/plugin.ts +++ b/web/i18n/zh-Hant/plugin.ts @@ -257,6 +257,7 @@ const translation = { fixOnly: { name: '僅修理', selectedDescription: '僅限於修補版本的自動更新', + description: '僅為補丁版本自動更新(例如:1.0.1 → 1.0.2)。次要版本變更不會觸發更新。', }, latest: { description: '始終更新至最新版本', diff --git a/web/i18n/zh-Hant/workflow.ts b/web/i18n/zh-Hant/workflow.ts index 0ffdde7713..935d042fa7 100644 --- a/web/i18n/zh-Hant/workflow.ts +++ b/web/i18n/zh-Hant/workflow.ts @@ -497,6 +497,7 @@ const translation = { placeholder: '輸入數值', }, title: '元數據過濾', + tip: '元數據過濾是使用元數據屬性(如標籤、類別或訪問權限)來精煉和控制在系統內檢索相關信息的過程。', }, }, http: { diff --git a/web/package.json b/web/package.json index 6915620312..d93788a368 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "dify-web", - "version": "1.7.0", + "version": "1.7.1", "private": true, "engines": { "node": ">=v22.11.0" @@ -30,8 +30,8 @@ "prepare": "cd ../ && node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky ./web/.husky", "gen-icons": "node ./app/components/base/icons/script.mjs", "uglify-embed": "node ./bin/uglify-embed", - "check-i18n": "node ./i18n/check-i18n.js", - "auto-gen-i18n": "node ./i18n/auto-gen-i18n.js", + "check-i18n": "node ./i18n-config/check-i18n.js", + "auto-gen-i18n": "node ./i18n-config/auto-gen-i18n.js", "test": "jest", "test:watch": "jest --watch", "storybook": "storybook dev -p 6006", @@ -270,4 +270,4 @@ "which-typed-array": "npm:@nolyfill/which-typed-array@^1" } } -} \ No newline at end of file +} diff --git a/web/service/base.ts b/web/service/base.ts index 8ffacaa0f1..8081899837 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -115,7 +115,7 @@ function requiredWebSSOLogin(message?: string, code?: number) { params.append('message', message) if (code) params.append('code', String(code)) - globalThis.location.href = `/webapp-signin?${params.toString()}` + globalThis.location.href = `${globalThis.location.origin}${basePath}/webapp-signin?${params.toString()}` } export function format(text: string) { diff --git a/web/utils/model-config.ts b/web/utils/model-config.ts index 330d8f9b52..3a500f22bc 100644 --- a/web/utils/model-config.ts +++ b/web/utils/model-config.ts @@ -62,6 +62,7 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | options: content.options, is_context_var, hide: content.hide, + default: content.default, }) } else if (type === 'file') { @@ -148,7 +149,7 @@ export const promptVariablesToUserInputsForm = (promptVariables: PromptVariable[ variable: item.key, required: item.required !== false, // default true options: item.options, - default: '', + default: item.default ?? '', hide: item.hide, }, } as any)