diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/secrets/DBSecretsManager.java b/openmetadata-service/src/main/java/org/openmetadata/service/secrets/DBSecretsManager.java index f89cf32b5f8e..410f7c07cacb 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/secrets/DBSecretsManager.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/secrets/DBSecretsManager.java @@ -13,6 +13,7 @@ package org.openmetadata.service.secrets; +import java.util.Objects; import org.openmetadata.schema.security.secrets.SecretsManagerProvider; public class DBSecretsManager extends SecretsManager { @@ -33,6 +34,9 @@ public static DBSecretsManager getInstance( @Override protected String storeValue(String fieldName, String value, String secretId, boolean store) { + if (Objects.isNull(value) || value.isEmpty()) { + return null; + } return value; } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/secrets/ExternalSecretsManager.java b/openmetadata-service/src/main/java/org/openmetadata/service/secrets/ExternalSecretsManager.java index 8819de5dd835..e22a6e474428 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/secrets/ExternalSecretsManager.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/secrets/ExternalSecretsManager.java @@ -33,6 +33,15 @@ protected ExternalSecretsManager( @Override protected String storeValue(String fieldName, String value, String secretId, boolean store) { String fieldSecretId = buildSecretId(false, secretId, fieldName.toLowerCase(Locale.ROOT)); + if (Objects.isNull(value) || value.isEmpty()) { + if (store) { + try { + deleteSecretInternal(fieldSecretId); + } catch (Exception e) { + } + } + return null; + } // check if value does not start with 'config:' only String can have password annotation if (Boolean.FALSE.equals(isSecret(value))) { if (store) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/secrets/SecretsManager.java b/openmetadata-service/src/main/java/org/openmetadata/service/secrets/SecretsManager.java index 2732aa8bd1c8..6bd7d473d2d7 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/secrets/SecretsManager.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/secrets/SecretsManager.java @@ -414,9 +414,11 @@ private Object encryptPasswordFields(Object toEncryptObject, String secretId, bo // set new value ReflectionUtil.setValueInMethod( toEncryptObject, - Fernet.isTokenized(newFieldValue) - ? newFieldValue - : store ? fernet.encrypt(newFieldValue) : newFieldValue, + isNull(newFieldValue) + ? null + : Fernet.isTokenized(newFieldValue) + ? newFieldValue + : store ? fernet.encrypt(newFieldValue) : newFieldValue, toSet); } }); diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/secrets/DBSecretsManagerTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/secrets/DBSecretsManagerTest.java index f74f521b61d6..e5b97a617bdb 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/secrets/DBSecretsManagerTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/secrets/DBSecretsManagerTest.java @@ -51,7 +51,9 @@ static void setUp() { "openmetadata", "prefix", List.of("key:value", "key2:value2"), null)); Fernet fernet = Mockito.mock(Fernet.class); lenient().when(fernet.decrypt(anyString())).thenReturn(DECRYPTED_VALUE); - lenient().when(fernet.decryptIfApplies(anyString())).thenReturn(DECRYPTED_VALUE); + lenient() + .when(fernet.decryptIfApplies(anyString())) + .thenAnswer(invocation -> invocation.getArgument(0)); lenient().when(fernet.encrypt(anyString())).thenReturn(ENCRYPTED_VALUE); secretsManager.setFernet(fernet); } @@ -89,6 +91,21 @@ void testDecryptServiceConnectionWithoutPassword() { assertNotSame(connection, actualConfig); } + @Test + void testEncryptDatabaseConnectionWithEmptyPasswordRemovesValue() { + MysqlConnection connection = + new MysqlConnection().withAuthType(new basicAuth().withPassword("")); + Object actualConfig = + secretsManager.encryptServiceConnectionConfig( + connection, Mysql.value(), "test-empty-password", ServiceType.DATABASE); + + assertEquals( + null, + JsonUtils.convertValue(((MysqlConnection) actualConfig).getAuthType(), basicAuth.class) + .getPassword()); + assertNotSame(connection, actualConfig); + } + @Test void testReturnsExpectedSecretManagerProvider() { assertEquals(SecretsManagerProvider.DB, secretsManager.getSecretsManagerProvider()); diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/secrets/ExternalSecretsManagerTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/secrets/ExternalSecretsManagerTest.java index 275c85dbb2c0..db7b7c12254b 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/secrets/ExternalSecretsManagerTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/secrets/ExternalSecretsManagerTest.java @@ -1,7 +1,6 @@ package org.openmetadata.service.secrets; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.openmetadata.schema.api.services.CreateDatabaseService.DatabaseServiceType.Mysql; @@ -236,10 +235,9 @@ void testEncryptDatabaseConnectionWithEmptyPassword() { mysqlConnection, Mysql.value(), "test-empty-password", ServiceType.DATABASE); assertNotNull(actualConnection, "Encryption should succeed even with empty password"); - assertFalse( - JsonUtils.convertValue(actualConnection.getAuthType(), basicAuth.class) - .getPassword() - .isEmpty(), - "Empty password should be converted to non-empty encrypted value"); + assertEquals( + null, + JsonUtils.convertValue(actualConnection.getAuthType(), basicAuth.class).getPassword(), + "Empty password should be treated as explicit secret removal"); } } diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/secrets/KubernetesSecretsManagerTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/secrets/KubernetesSecretsManagerTest.java index 7325e16acd0c..4247d692eeb5 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/secrets/KubernetesSecretsManagerTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/secrets/KubernetesSecretsManagerTest.java @@ -261,27 +261,17 @@ void testBuildSecretIdHandlesSpecialCharacters() throws ApiException { } @Test - void testEmptySecretValueShouldBeStoredAsNullString() throws ApiException { - ArgumentCaptor secretCaptor = ArgumentCaptor.forClass(V1Secret.class); - when(mockApiClient.readNamespacedSecret(anyString(), eq(NAMESPACE))).thenReturn(readRequest); - when(readRequest.execute()).thenThrow(new ApiException(404, "Not Found")); - - when(mockApiClient.createNamespacedSecret(eq(NAMESPACE), any(V1Secret.class))) - .thenReturn(createRequest); - when(createRequest.execute()).thenReturn(new V1Secret()); - - secretsManager.storeValue("field", "", SECRET_ID, true); + void testEmptySecretValueShouldDeleteSecret() throws ApiException { + when(mockApiClient.deleteNamespacedSecret("openmetadata-database-myservice-field", NAMESPACE)) + .thenReturn(deleteRequest); + when(deleteRequest.execute()).thenReturn(new V1Status()); - verify(mockApiClient).createNamespacedSecret(eq(NAMESPACE), secretCaptor.capture()); - verify(createRequest).execute(); + String result = secretsManager.storeValue("field", "", SECRET_ID, true); - V1Secret createdSecret = secretCaptor.getValue(); - Map data = createdSecret.getData(); - assert data != null; - assertEquals( - ExternalSecretsManager.NULL_SECRET_STRING, - new String(data.get("value"), StandardCharsets.UTF_8), - "Empty string should be stored as 'null' to prevent secrets manager rejection"); + assertNull(result); + verify(mockApiClient) + .deleteNamespacedSecret("openmetadata-database-myservice-field", NAMESPACE); + verify(deleteRequest).execute(); } @Test diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceForm.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceForm.spec.ts index 3a3140fc9aa6..16742fd6d333 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceForm.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceForm.spec.ts @@ -553,6 +553,70 @@ test.describe( page.locator(String.raw`#root\/schemaRegistryTopicSuffixName`) ).toHaveValue(''); }); + + test('should clear optional saslPassword via remove button and persist the empty value', async ({ + page, + }) => { + await visitServiceDetailsPage( + page, + { + name: kafkaService.entity.name, + type: SERVICE_TYPE.Messaging, + }, + false, + false + ); + + await page.getByRole('tab', { name: 'Connection' }).click(); + await page.getByTestId('edit-connection-button').click(); + await waitForAllLoadersToDisappear(page); + + const removePasswordButton = page.getByTestId( + 'password-remove-btn-root/saslPassword' + ); + + await expect(removePasswordButton).toBeVisible(); + await removePasswordButton.click(); + + const passwordInput = page.getByTestId( + 'password-input-widget-root/saslPassword' + ); + + await expect(passwordInput).toBeVisible(); + await expect(passwordInput).toHaveValue(''); + + const patchResponse = page.waitForResponse( + (response) => + response.url().includes('/api/v1/services/messagingServices') && + response.request().method() === 'PATCH' + ); + + await page.getByTestId('submit-btn').click(); + await page.getByTestId('submit-btn').click(); + + const patch = await patchResponse; + const patchBody = patch.request().postDataJSON() as Array<{ + op: string; + path: string; + value?: unknown; + }>; + + const passwordOp = patchBody.find( + (op) => op.path === '/connection/config/saslPassword' + ); + + expect(passwordOp).toBeDefined(); + expect(passwordOp?.value).toBe(''); + + await waitForAllLoadersToDisappear(page); + + await page.getByTestId('edit-connection-button').click(); + await waitForAllLoadersToDisappear(page); + + await expect( + page.getByTestId('password-input-widget-root/saslPassword') + ).toHaveValue(''); + }); }); } ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.test.tsx index 4c2d3ab41fcf..3f0aed283b67 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.test.tsx @@ -39,6 +39,15 @@ const mockProps: WidgetProps = { ...MOCK_PASSWORD_WIDGET, }; +const mockPropsUnmasked: WidgetProps = { + onFocus: mockOnFocus, + onBlur: mockOnBlur, + onChange: mockOnChange, + registry: {} as Registry, + ...MOCK_PASSWORD_WIDGET, + value: 'my-plain-password', +}; + const mockProps2: WidgetProps = { onFocus: mockOnFocus, onBlur: mockOnBlur, @@ -48,84 +57,132 @@ const mockProps2: WidgetProps = { }; describe('Test PasswordWidget Component', () => { - it('Should render select component', async () => { + it('Should show saved indicator when value is masked', async () => { render(); - const passwordInput = screen.getByTestId( - 'password-input-widget-root/password' - ); - const FileUploadWidget = screen.queryByText('FileUploadWidget'); + expect( + screen.getByTestId('password-update-btn-root/password') + ).toBeInTheDocument(); + expect( + screen.queryByTestId('password-input-widget-root/password') + ).not.toBeInTheDocument(); + }); - expect(passwordInput).toBeInTheDocument(); - expect(FileUploadWidget).not.toBeInTheDocument(); + it('Should show remove button when field is not required and value is masked', async () => { + render(); + + expect( + screen.getByTestId('password-remove-btn-root/password') + ).toBeInTheDocument(); }); - it('Should be disabled', async () => { - render(); + it('Should not show remove button when field is required', async () => { + render(); - const passwordInput = screen.getByTestId( - 'password-input-widget-root/password' - ); + expect( + screen.queryByTestId('password-remove-btn-root/password') + ).not.toBeInTheDocument(); + }); - expect(passwordInput).toBeDisabled(); + it('Should show password input after clicking Update', async () => { + render(); + + fireEvent.click(screen.getByTestId('password-update-btn-root/password')); + + expect( + screen.getByTestId('password-input-widget-root/password') + ).toBeInTheDocument(); + expect( + screen.getByTestId('password-cancel-edit-btn-root/password') + ).toBeInTheDocument(); }); - it('Should call onFocus', async () => { + it('Should return to saved indicator after clicking Cancel', async () => { render(); - const passwordInput = screen.getByTestId( - 'password-input-widget-root/password' + fireEvent.click(screen.getByTestId('password-update-btn-root/password')); + fireEvent.click( + screen.getByTestId('password-cancel-edit-btn-root/password') ); - fireEvent.focus(passwordInput); + expect( + screen.getByTestId('password-update-btn-root/password') + ).toBeInTheDocument(); + expect( + screen.queryByTestId('password-input-widget-root/password') + ).not.toBeInTheDocument(); + }); - expect(mockOnFocus).toHaveBeenCalled(); + it('Should call onChange with empty string when Remove is clicked', async () => { + render(); + + fireEvent.click(screen.getByTestId('password-remove-btn-root/password')); + + expect(mockOnChange).toHaveBeenCalledWith(''); }); - it('Should call onBlur', async () => { - render(); + it('Should be disabled after clicking Update and field is disabled', async () => { + render(); + + fireEvent.click(screen.getByTestId('password-update-btn-root/password')); const passwordInput = screen.getByTestId( 'password-input-widget-root/password' ); - fireEvent.blur(passwordInput); + expect(passwordInput).toBeDisabled(); + }); - expect(mockOnBlur).toHaveBeenCalled(); + it('Should render password input directly when value is not masked', async () => { + render(); + + const passwordInput = screen.getByTestId( + 'password-input-widget-root/password' + ); + + expect(passwordInput).toBeInTheDocument(); + expect( + screen.queryByTestId('password-update-btn-root/password') + ).not.toBeInTheDocument(); }); - it('Should call onChange', async () => { + it('Should call onChange after clicking Update and typing', async () => { render(); + fireEvent.click(screen.getByTestId('password-update-btn-root/password')); + const passwordInput = screen.getByTestId( 'password-input-widget-root/password' ); + fireEvent.change(passwordInput, { target: { value: 'newpassword' } }); - fireEvent.change(passwordInput, { target: { value: 'password' } }); - - expect(mockOnChange).toHaveBeenCalledWith('password'); + expect(mockOnChange).toHaveBeenCalledWith('newpassword'); }); - it('Should call onChange with asterisk', async () => { + it('Should call onFocus after clicking Update', async () => { render(); + fireEvent.click(screen.getByTestId('password-update-btn-root/password')); + const passwordInput = screen.getByTestId( 'password-input-widget-root/password' ); + fireEvent.focus(passwordInput); - fireEvent.change(passwordInput, { target: { value: '*******' } }); - - expect(mockOnChange).toHaveBeenCalledWith('*******'); + expect(mockOnFocus).toHaveBeenCalled(); }); - it('Should not show password if the value is masked', async () => { + it('Should call onBlur after clicking Update', async () => { render(); + fireEvent.click(screen.getByTestId('password-update-btn-root/password')); + const passwordInput = screen.getByTestId( 'password-input-widget-root/password' ); + fireEvent.blur(passwordInput); - expect(passwordInput).toHaveValue(''); + expect(mockOnBlur).toHaveBeenCalled(); }); it('Should render FileWidget and password input if uiFieldType is fileOrInput', async () => { @@ -139,17 +196,12 @@ describe('Test PasswordWidget Component', () => { expect(fileUploadWidget).toBeInTheDocument(); expect(passwordInput).toBeInTheDocument(); - // Check if the password input is disabled expect(passwordInput).toBeDisabled(); - // Click on the Enter file content radio button const enterFileContentRadioButton = screen.getByTestId('radio-file-path'); fireEvent.click(enterFileContentRadioButton); - // Check if the password input is enabled expect(passwordInput).toBeEnabled(); - - // Check if the file upload widget is disabled expect(fileUploadWidget).toBeDisabled(); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.tsx index d43ce33e6c92..a5642f4d9f7a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.tsx @@ -10,9 +10,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { CloseCircleOutlined } from '@ant-design/icons'; import { WidgetProps } from '@rjsf/utils'; -import { Col, Input, Radio, RadioChangeEvent, Row, Typography } from 'antd'; -import { FC, useCallback, useMemo, useState } from 'react'; +import { + Button, + Col, + Input, + Radio, + RadioChangeEvent, + Row, + Space, + Typography, +} from 'antd'; +import { FC, useCallback, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ALL_ASTERISKS_REGEX } from '../../../../../constants/regex.constants'; import { CertificationInputType } from '../../../../../enums/PasswordWidget.enum'; @@ -21,6 +31,15 @@ import './password-widget.less'; const PasswordWidget: FC = (props) => { const { t } = useTranslation(); + + const isMaskedValue = useMemo( + () => ALL_ASTERISKS_REGEX.test(props.value), + [props.value] + ); + + const wasOriginallyMasked = useRef(isMaskedValue); + + const [isEditing, setIsEditing] = useState(!isMaskedValue); const [inputType, setInputType] = useState( props.schema.uiFieldType === 'fileOrInput' ? CertificationInputType.FILE_UPLOAD @@ -31,12 +50,28 @@ const PasswordWidget: FC = (props) => { const isInputTypeFileOrInput = props.schema.uiFieldType === 'fileOrInput'; const passwordWidgetValue = useMemo(() => { - if (ALL_ASTERISKS_REGEX.test(props.value)) { - return undefined; // Do not show the password if it is masked - } else { - return props.value; + if (isMaskedValue) { + return undefined; } - }, [props.value]); + + return props.value; + }, [props.value, isMaskedValue]); + + const handleRemove = useCallback(() => { + props.onChange(''); + setIsEditing(true); + }, [props.onChange]); + + const handleUpdate = useCallback(() => { + setIsEditing(true); + }, []); + + const originalValue = useRef(props.value); + + const handleCancelEdit = useCallback(() => { + setIsEditing(false); + props.onChange(originalValue.current); + }, [props.onChange]); const getPasswordInput = useCallback( (disabled?: boolean) => ( @@ -56,7 +91,58 @@ const PasswordWidget: FC = (props) => { onFocus={() => props.onFocus(props.id, props.value)} /> ), - [props] + [props, passwordWidgetValue] + ); + + const getSavedPasswordIndicator = useCallback( + () => ( + + + + {t('message.password-saved')} + + + + + {!props.required && ( + + )} + + + ), + [props.id, props.required, handleUpdate, handleRemove, t] + ); + + const getEditingPasswordInput = useCallback( + (disabled?: boolean) => ( + + {getPasswordInput(disabled)} + {wasOriginallyMasked.current && ( + + )} + + ), + [getPasswordInput, props.id, handleCancelEdit, t] ); const onRadioChange = (e: RadioChangeEvent) => { @@ -93,9 +179,11 @@ const PasswordWidget: FC = (props) => { data-testid={`radio-${CertificationInputType.FILE_PATH}`} value={CertificationInputType.FILE_PATH}> {t('label.enter-file-content')} - {getPasswordInput( - inputType === CertificationInputType.FILE_UPLOAD - )} + {wasOriginallyMasked.current && !isEditing + ? getSavedPasswordIndicator() + : getEditingPasswordInput( + inputType === CertificationInputType.FILE_UPLOAD + )} @@ -103,7 +191,11 @@ const PasswordWidget: FC = (props) => { ); } - return getPasswordInput(); + if (wasOriginallyMasked.current && !isEditing) { + return getSavedPasswordIndicator(); + } + + return getEditingPasswordInput(); }; export default PasswordWidget; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/password-widget.less b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/password-widget.less index 58e07660c8ad..476130d6a923 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/password-widget.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/password-widget.less @@ -10,6 +10,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +@import "../../../../../styles/variables.less"; + .password-widget { display: block; width: 100%; @@ -25,3 +28,15 @@ } } } + +.password-saved-indicator { + width: 100%; + + .password-saved-icon { + color: @success-color; + } + + .password-saved-text { + font-size: 12px; + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json index 5e30920532c2..1a4dae3f6644 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "اجتاز {{count}} من عمليات التحقق", "password-error-message": "يجب أن تتراوح كلمة المرور بين 8 و 56 حرفًا كحد أدنى وأقصى وأن تحتوي على حرف كبير واحد على الأقل (A-Z)، وحرف صغير واحد (a-z)، ورقم واحد، وحرف خاص واحد (مثل !, %، @، أو #)", "password-pattern-error": "يجب أن تكون كلمة المرور بحد أدنى 8 وأقصى 56 حرفًا، مع حرف خاص واحد، وحرف كبير واحد، وحرف صغير واحد", + "password-saved": "تم حفظ كلمة المرور. أدخل قيمة جديدة للتحديث أو الإزالة.", "path-of-the-dbt-files-stored": "مسار المجلد الذي يتم فيه تخزين ملفات dbt", "permanently-delete-ingestion-pipeline": "سيؤدي الحذف الدائم لـ <0>{{entityName}} إلى فقدان تكوين المسار (pipeline)، ولا يمكن استعادته.", "permanently-delete-metadata": "سيؤدي الحذف الدائم لـ <0>{{entityName}} إلى إزالة بياناته الوصفية من OpenMetadata، ولا يمكن استعادته.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index 3216325bd896..e09cbeec23ad 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "{{count}} Prüfungen bestanden", "password-error-message": "Das Passwort muss mindestens 8 und maximal 56 Zeichen lang sein und mindestens einen Großbuchstaben (A-Z), einen Kleinbuchstaben (a-z), eine Zahl und ein Sonderzeichen (z. B. !, %, @ oder #) enthalten.", "password-pattern-error": "Das Passwort muss mindestens 8 und maximal 56 Zeichen lang sein und mindestens einen Sonderbuchstaben, einen Großbuchstaben und einen Kleinbuchstaben enthalten.", + "password-saved": "Das Passwort wurde gespeichert. Geben Sie einen neuen Wert ein, um es zu aktualisieren oder zu entfernen.", "path-of-the-dbt-files-stored": "Pfad zum Ordner, in dem die dbt-Dateien gespeichert sind", "permanently-delete-ingestion-pipeline": "Das dauerhafte Löschen von <0>{{entityName}} führt zum Verlust der Pipeline-Konfiguration und kann nicht wiederhergestellt werden.", "permanently-delete-metadata": "Das dauerhafte Löschen dieses <0>{{entityName}} entfernt seine Metadaten dauerhaft aus {{brandName}}.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 412decf4af00..04a5eca162b6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "Passed {{count}} checks", "password-error-message": "Password must be a minimum of 8 and a maximum of 56 characters long and contain at least one uppercase character (A-Z), one lowercase character (a-z), one number, and one special character (such as !, %, @, or #)", "password-pattern-error": "Password must be of minimum 8 and maximum 56 characters, with one special , one upper, one lower case character", + "password-saved": "Password is saved. Enter a new value to update or remove it.", "path-of-the-dbt-files-stored": "Path of the folder where the dbt files are stored", "permanently-delete-ingestion-pipeline": "Permanently deleting this <0>{{entityName}} will result in loss of pipeline configuration, and it cannot be recovered.", "permanently-delete-metadata": "Permanently deleting this <0>{{entityName}} will result in the removal of its metadata from {{brandName}}, and it cannot be recovered.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index 75a6ff1d1809..db8eeab9615b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "Pasaron {{count}} verificaciones", "password-error-message": "La contraseña debe tener como mínimo 8 y como máximo 56 caracteres, con al menos una letra mayúscula, una letra minúscula, un número y un carácter especial (como !, %, @ o #)", "password-pattern-error": "La contraseña debe tener como mínimo 8 y como máximo 56 caracteres, con al menos una letra mayúscula, una letra minúscula, un número y un carácter especial (como !, %, @ o #)", + "password-saved": "La contraseña se ha guardado. Introduce un nuevo valor para actualizarla o eliminarla.", "path-of-the-dbt-files-stored": "Ruta de la carpeta donde se almacenan los archivos dbt", "permanently-delete-ingestion-pipeline": "Al eliminar permanentemente este <0>{{entityName}}, se perderá la configuración de la pipeline, y no podrá ser recuperada.", "permanently-delete-metadata": "Al eliminar permanentemente este <0>{{entityName}}, se eliminarán sus metadatos de {{brandName}} permanentemente.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index 6b3863af98f8..fb4dbe0271a7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "{{count}} vérifications réussies", "password-error-message": "Le mot de passe doit comporter au moins 8 caractères et au plus 56 caractères et doit contenir au moins une lettre majuscule (A-Z), une lettre minuscule (a-z), un chiffre et un caractère spécial (tel que !, %, @ ou #).", "password-pattern-error": "Le mot de passe doit comporter au moins 8 caractères et au plus 56 caractères, avec un caractère spécial, une lettre majuscule et une lettre minuscule.", + "password-saved": "Le mot de passe est enregistré. Saisissez une nouvelle valeur pour le mettre à jour ou le supprimer.", "path-of-the-dbt-files-stored": "Chemin du dossier où sont situés les fichiers dbt.", "permanently-delete-ingestion-pipeline": "La suppression définitive de <0>{{entityName}} entraînera la perte de la configuration du pipeline et ne pourra pas être récupérée.", "permanently-delete-metadata": "La suppression permanente de cette <0>{{entityName}} supprimera ses métadonnées de façon permanente d'{{brandName}}.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json index a886c032c103..fe5ecdb05422 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "Pasaron {{count}} verificacións", "password-error-message": "O contrasinal debe ter un mínimo de 8 e un máximo de 56 caracteres e conter polo menos unha maiúscula (A-Z), unha minúscula (a-z), un número e un carácter especial (como !, %, @ ou #)", "password-pattern-error": "O contrasinal debe ter un mínimo de 8 e un máximo de 56 caracteres, cun carácter especial, unha maiúscula e unha minúscula", + "password-saved": "O contrasinal gardouse. Introduce un novo valor para actualizalo ou eliminalo.", "path-of-the-dbt-files-stored": "Ruta do cartafol onde se almacenan os ficheiros dbt", "permanently-delete-ingestion-pipeline": "A eliminación permanente deste <0>{{entityName}} provocará a perda da configuración da canalización e non se poderá recuperar.", "permanently-delete-metadata": "Eliminar permanentemente este <0>{{entityName}} eliminará os seus metadatos de {{brandName}} e non poderá recuperarse.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json index 5fff7d7f2f72..ead1934ce07a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "עבר {{count}} בדיקות", "password-error-message": "יש להזין סיסמה באורך של 8 עד 15 תווים ולכלול לפחות אות גדולה אחת (A-Z), אות קטנה אחת (a-z), מספר אחד ותו מיוחד (כמו !, %, @, או #)", "password-pattern-error": "יש להזין סיסמה באורך של 8 עד 56 תווים, עם תו מיוחד אחד, אות גדולה אחת ואות קטנה אחת", + "password-saved": "הסיסמה נשמרה. הזן ערך חדש כדי לעדכן או להסיר אותה.", "path-of-the-dbt-files-stored": "נתיב לתיקייה בה נשמרים קבצי ה-dbt", "permanently-delete-ingestion-pipeline": "מחיקה קבועה של <0>{{entityName}} תגרום לאובדן תצורת הצינור, ולא ניתן יהיה לשחזר אותה.", "permanently-delete-metadata": "מחיקה של {{entityName}} תסיר את המטה-דאטה שלו מ-{{brandName}} לצמיתות.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index 6a9b6a20597b..0e2622f9893d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "{{count}} チェックに合格しました", "password-error-message": "パスワードは8文字以上66文字以下で、大文字 (A-Z)、小文字 (a-z)、数字、特殊文字 (!、%、@、# など) をそれぞれ少なくとも1文字含める必要があります。", "password-pattern-error": "パスワードは8~56文字で、1つの特殊文字、大文字1つ、小文字1つが含まれている必要があります。", + "password-saved": "パスワードは保存されました。更新または削除するには新しい値を入力してください。", "path-of-the-dbt-files-stored": "dbtファイルが保存されているフォルダのパス", "permanently-delete-ingestion-pipeline": "この <0>{{entityName}} を完全に削除すると、パイプライン設定が失われ、元に戻すことはできません。", "permanently-delete-metadata": "この <0>{{entityName}} を完全に削除すると、{{brandName}} からメタデータが完全に削除されます。", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json index 4248bc8f0325..e372839f502f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "{{count}}개 검사 통과", "password-error-message": "비밀번호는 최소 8자에서 최대 56자 길이여야 하며, 최소 하나의 대문자(A-Z), 하나의 소문자(a-z), 하나의 숫자, 그리고 하나의 특수 문자(!, %, @, # 등)를 포함해야 합니다", "password-pattern-error": "비밀번호는 최소 8자에서 최대 56자 길이여야 하며, 하나의 특수 문자, 하나의 대문자, 하나의 소문자를 포함해야 합니다", + "password-saved": "비밀번호가 저장되었습니다. 업데이트하거나 삭제하려면 새 값을 입력하세요.", "path-of-the-dbt-files-stored": "dbt 파일이 저장된 폴더의 경로", "permanently-delete-ingestion-pipeline": "이 <0>{{entityName}}을(를) 영구적으로 삭제하면 파이프라인 구성이 손실되며 복구할 수 없습니다.", "permanently-delete-metadata": "이 <0>{{entityName}}을(를) 영구적으로 삭제하면 {{brandName}}에서 해당 메타데이터가 제거되며 복구할 수 없습니다.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json index 2cdc55e2b5f3..d3c3f3cc9b37 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "{{count}} तपासण्या उत्तीर्ण", "password-error-message": "पासवर्ड किमान 8 आणि कमाल 56 वर्णांचा असावा आणि त्यात किमान एक मोठा अक्षर (A-Z), एक लहान अक्षर (a-z), एक संख्या आणि एक विशेष वर्ण (जसे की !, %, @, किंवा #) असावा", "password-pattern-error": "पासवर्ड किमान 8 आणि कमाल 56 वर्णांचा असावा, ज्यात एक विशेष, एक मोठा, एक लहान अक्षर असावा", + "password-saved": "पासवर्ड जतन केला आहे. तो अद्यतनित किंवा काढण्यासाठी नवीन मूल्य प्रविष्ट करा.", "path-of-the-dbt-files-stored": "dbt फाइल्स जिथे संग्रहित केल्या आहेत त्या फोल्डरचा पथ", "permanently-delete-ingestion-pipeline": "हे <0>{{entityName}} कायमचे मिटवल्याने पाइपलाइन संरचना गमावली जाईल आणि ती पुनर्प्राप्त केली जाऊ शकत नाही.", "permanently-delete-metadata": "हे <0>{{entityName}} कायमचे मिटवल्याने त्याचे मेटाडेटा {{brandName}} मधून काढले जाईल आणि ते पुनर्प्राप्त केले जाऊ शकत नाही.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json index 9c050bb5ae94..37f3394502c9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "{{count}} controles geslaagd", "password-error-message": "Het wachtwoord moet minimaal 8 en maximaal 56 tekens lang zijn en ten minste één hoofdletter (A-Z), één kleine letter (a-z), één cijfer en één speciaal teken (zoals !, %, @ of #) bevatten", "password-pattern-error": "Wachtwoord moet minimaal 8 en maximaal 56 tekens lang zijn, met één speciaal teken, één hoofdletter, één kleine letter", + "password-saved": "Het wachtwoord is opgeslagen. Voer een nieuwe waarde in om het bij te werken of te verwijderen.", "path-of-the-dbt-files-stored": "Pad naar de map waarin de dbt-bestanden zijn opgeslagen", "permanently-delete-ingestion-pipeline": "Als u deze <0>{{entityName}} definitief verwijdert, gaat de pijplijnconfiguratie verloren en kan deze niet worden hersteld.", "permanently-delete-metadata": "Het permanent verwijderen van deze <0>{{entityName}} verwijdert de metadata ervan permanent uit {{brandName}}.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json index daaddfa0bce9..3a614e22db0f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "{{count}} بررسی موفق", "password-error-message": "رمز عبور باید حداقل 8 و حداکثر 56 کاراکتر باشد و حداقل یک کاراکتر بزرگ (A-Z)، یک کاراکتر کوچک (a-z)، یک عدد و یک کاراکتر خاص (مثل !، %، @، یا #) داشته باشد.", "password-pattern-error": "رمز عبور باید حداقل 8 و حداکثر 56 کاراکتر، با یک کاراکتر خاص، یک حرف بزرگ و یک حرف کوچک باشد.", + "password-saved": "Password is saved. Enter a new value to update or remove it.", "path-of-the-dbt-files-stored": "مسیر پوشه‌ای که فایل‌های dbt در آن ذخیره شده‌اند.", "permanently-delete-ingestion-pipeline": "Permanently deleting this <0>{{entityName}} will result in loss of pipeline configuration, and it cannot be recovered.", "permanently-delete-metadata": "حذف دائم این <0>{{entityName}} منجر به حذف متادیتای آن از {{brandName}} می‌شود و قابل بازیابی نخواهد بود.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index 7ba5339defad..4fc71ca4b940 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "{{count}} verificações aprovadas", "password-error-message": "A senha deve ter no mínimo 8 e no máximo 56 caracteres e conter pelo menos uma letra maiúscula (A-Z), uma letra minúscula (a-z), um número e um caractere especial (como !, %, @ ou #)", "password-pattern-error": "A senha deve ter no mínimo 8 e no máximo 56 caracteres, com pelo menos um caractere especial, uma letra maiúscula e uma letra minúscula", + "password-saved": "A senha foi salva. Insira um novo valor para atualizá-la ou removê-la.", "path-of-the-dbt-files-stored": "Caminho da pasta onde os arquivos dbt são armazenados", "permanently-delete-ingestion-pipeline": "A exclusão permanente de <0>{{entityName}} resultará na perda da configuração do pipeline e não poderá ser recuperada.", "permanently-delete-metadata": "Excluir permanentemente este(a) <0>{{entityName}} removerá seus metadados do {{brandName}} permanentemente.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json index 499a2b29368f..d4f07f73238a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "{{count}} verificações aprovadas", "password-error-message": "A senha deve ter no mínimo 8 e no máximo 56 caracteres e conter pelo menos uma letra maiúscula (A-Z), uma letra minúscula (a-z), um número e um caracter especial (como !, %, @ ou #)", "password-pattern-error": "A senha deve ter no mínimo 8 e no máximo 56 caracteres, com pelo menos um caracter especial, uma letra maiúscula e uma letra minúscula", + "password-saved": "A palavra-passe foi guardada. Introduza um novo valor para a atualizar ou remover.", "path-of-the-dbt-files-stored": "Caminho da pasta onde os arquivos dbt são armazenados", "permanently-delete-ingestion-pipeline": "A exclusão permanente deste <0>{{entityName}} resultará na perda da configuração do pipeline e não poderá ser recuperada.", "permanently-delete-metadata": "Excluir permanentemente este(a) <0>{{entityName}} removerá seus metadados do {{brandName}} permanentemente.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index e40ae66e45ff..686321ca8621 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "Пройдено {{count}} проверок", "password-error-message": "Пароль должен содержать не менее 8 и не более 56 символов, включая как минимум один символ верхнего регистра (A-Z), один символ нижнего регистра (az), одну цифру и один специальный символ (например, !, %, @ или #). )", "password-pattern-error": "Пароль должен состоять минимум из 8 и максимум из 56 символов, включая один специальный, один верхний и один нижний регистр.", + "password-saved": "Пароль сохранён. Введите новое значение, чтобы обновить или удалить его.", "path-of-the-dbt-files-stored": "Путь к папке, в которой хранятся файлы dbt", "permanently-delete-ingestion-pipeline": "Безвозвратное удаление <0>{{entityName}} приведет к потере конфигурации конвейера, и его нельзя будет восстановить.", "permanently-delete-metadata": "Внимание! Удаление объекта <0>{{entityName}} невозможно отменить — все метаданные будут утеряны.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json index bc455cd4ece5..ea1621045f46 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "Passed {{count}} checks", "password-error-message": "รหัสผ่านต้องมีความยาวขั้นต่ำ 8 ตัวและสูงสุด 56 ตัว และต้องมีอย่างน้อยหนึ่งตัวอักษรพิมพ์ใหญ่ (A-Z), หนึ่งตัวอักษรพิมพ์เล็ก (a-z), หนึ่งตัวเลข, และหนึ่งอักขระพิเศษ (เช่น !, %, @, หรือ #)", "password-pattern-error": "รหัสผ่านต้องมีความยาวขั้นต่ำ 8 ตัวและสูงสุด 56 ตัว มีอักขระพิเศษหนึ่งตัว, ตัวอักษรพิมพ์ใหญ่หนึ่งตัว, ตัวอักษรพิมพ์เล็กหนึ่งตัว", + "password-saved": "บันทึกรหัสผ่านแล้ว ป้อนค่าใหม่เพื่ออัปเดตหรือลบออก", "path-of-the-dbt-files-stored": "เส้นทางของโฟลเดอร์ที่จัดเก็บไฟล์ dbt", "permanently-delete-ingestion-pipeline": "การลบ <0>{{entityName}} นี้อย่างถาวรจะทำให้การตั้งค่าท่อสูญหายและไม่สามารถกู้คืนได้", "permanently-delete-metadata": "การลบ <0>{{entityName}} นี้อย่างถาวรจะทำให้ข้อมูลเมตาของมันถูกลบออกจาก {{brandName}} และไม่สามารถกู้คืนได้", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json index 1d03830ba0a8..b7f45eb1b53a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "{{count}} kontrol geçildi", "password-error-message": "Şifre en az 8 ve en fazla 56 karakter uzunluğunda olmalı ve en az bir büyük harf (A-Z), bir küçük harf (a-z), bir rakam ve bir özel karakter ( !, %, @ veya # gibi) içermelidir.", "password-pattern-error": "Şifre en az 8 ve en fazla 56 karakterden oluşmalı, bir özel karakter, bir büyük harf, bir küçük harf içermelidir", + "password-saved": "Parola kaydedildi. Güncellemek veya kaldırmak için yeni bir değer girin.", "path-of-the-dbt-files-stored": "dbt dosyalarının saklandığı klasörün yolu", "permanently-delete-ingestion-pipeline": "Bu <0>{{entityName}} kalıcı olarak silinmesi, iş akışı yapılandırmasının kaybolmasına neden olacak ve kurtarılamayacaktır.", "permanently-delete-metadata": "Bu <0>{{entityName}} kalıcı olarak silinmesi, metadatasının {{brandName}}'dan kaldırılmasına neden olacak ve kurtarılamayacaktır.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index 8f2869c95e02..82e403568c82 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "通过 {{count}} 项检查", "password-error-message": "密码必须为8到56个字符, 至少包括一个大写字母(A-Z)、一个小写字母(a-z), 和一个特殊字符(例如:!, %, @, or #)", "password-pattern-error": "密码必须为8到56个字符, 至少包括一个特殊字符、一个大写字母、一个小写字母", + "password-saved": "密码已保存。请输入新值以更新或移除它。", "path-of-the-dbt-files-stored": "存储 dbt 文件的文件夹路径", "permanently-delete-ingestion-pipeline": "永久删除此 <0>{{entityName}} 将导致管道配置丢失,并且无法恢复。", "permanently-delete-metadata": "永久删除此<0>{{entityName}}将永久从 {{brandName}} 中删除其元数据", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json index da9fc64c0df9..488b03855efb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json @@ -3089,6 +3089,7 @@ "passed-x-checks": "通過 {{count}} 個檢查", "password-error-message": "密碼長度必須介於 8 到 56 個字元之間,並包含至少一個大寫字母 (A-Z)、一個小寫字母 (a-z)、一個數字和一個特殊字元(如 !、%、@ 或 #)", "password-pattern-error": "密碼長度必須介於 8 到 56 個字元之間,並包含一個特殊字元、一個大寫字母和一個小寫字母", + "password-saved": "密碼已儲存。請輸入新值以更新或移除它。", "path-of-the-dbt-files-stored": "儲存 dbt 檔案的資料夾路徑", "permanently-delete-ingestion-pipeline": "永久刪除 <0>{{entityName}} 將導致管線組態遺失,且無法復原。", "permanently-delete-metadata": "永久刪除 <0>{{entityName}} 將導致其元資料從 {{brandName}} 中移除,且無法復原。",