diff --git a/specifyweb/backend/trees/extras.py b/specifyweb/backend/trees/extras.py index 456a46f3ceb..f780e849027 100644 --- a/specifyweb/backend/trees/extras.py +++ b/specifyweb/backend/trees/extras.py @@ -1,6 +1,8 @@ +import json import re from contextlib import contextmanager import logging +from typing import Iterable from specifyweb.backend.trees.ranks import RankOperation, post_tree_rank_save, pre_tree_rank_deletion, \ verify_rank_parent_chain_integrity, pre_tree_rank_init, post_tree_rank_deletion @@ -16,7 +18,97 @@ from specifyweb.backend.businessrules.exceptions import TreeBusinessRuleException import specifyweb.specify.models as spmodels -from specifyweb.backend.workbench.upload.auditcodes import TREE_BULK_MOVE, TREE_MERGE, TREE_SYNONYMIZE, TREE_DESYNONYMIZE +from specifyweb.backend.workbench.upload.auditcodes import TREE_BULK_MOVE, TREE_MERGE, TREE_SYNONYMIZE, TREE_DESYNONYMIZE + +_SYNONYM_PREF_KEYS_BY_TABLE: dict[str, tuple[str, ...]] = { + 'GeologicTimePeriod': ( + 'sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod', + 'sp7.allow_adding_child_to_synonymized_parent.ChronosStrat', + 'sp7.allow_adding_child_to_synonymized_parent.ChronoStrat', + ), + 'ChronosStrat': ( + 'sp7.allow_adding_child_to_synonymized_parent.ChronosStrat', + 'sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod', + 'sp7.allow_adding_child_to_synonymized_parent.ChronoStrat', + ), + 'ChronoStrat': ( + 'sp7.allow_adding_child_to_synonymized_parent.ChronoStrat', + 'sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod', + 'sp7.allow_adding_child_to_synonymized_parent.ChronosStrat', + ), +} + + +def _synonym_pref_keys(node) -> tuple[str, ...]: + table_name = node.specify_model.name + base_key = f'sp7.allow_adding_child_to_synonymized_parent.{table_name}' + keys = _SYNONYM_PREF_KEYS_BY_TABLE.get(table_name) + if keys is None: + return (base_key,) + + if keys and keys[0] == base_key: + return keys + return (base_key, *keys) + + +def _collection_synonym_pref_enabled(keys: Iterable[str]) -> bool: + from specifyweb.specify.models import Spappresourcedata + + qs = Spappresourcedata.objects.filter( + spappresource__name='CollectionPreferences' + ).values_list('data', flat=True) + + for raw_data in qs: + if not raw_data: + continue + if isinstance(raw_data, memoryview): + raw_data = raw_data.tobytes() + if isinstance(raw_data, (bytes, bytearray)): + try: + raw_data = raw_data.decode('utf-8') + except UnicodeDecodeError: + continue + try: + prefs = json.loads(raw_data) + except (TypeError, ValueError): + continue + + if not isinstance(prefs, dict): + continue + + tree_management = prefs.get('treeManagement') + if not isinstance(tree_management, dict): + continue + + synonymized = tree_management.get('synonymized') + if not isinstance(synonymized, dict): + continue + + for key in keys: + value = synonymized.get(key) + if value is True: + return True + + return False + + +def _remote_synonym_pref_enabled(keys: Iterable[str]) -> bool: + from specifyweb.backend.context.remote_prefs import get_remote_prefs + + prefs_text = get_remote_prefs() + for key in keys: + pattern = r'^' + re.escape(key) + r'(?:_\d+)?=(.+)' + override = re.search(pattern, prefs_text, re.MULTILINE) + if override is not None and override.group(1).strip().lower() == "true": + return True + return False + + +def _synonym_override_enabled(node) -> bool: + """Return True when collection or remote prefs allow actions on synonymized parents.""" + + keys = _synonym_pref_keys(node) + return _collection_synonym_pref_enabled(keys) or _remote_synonym_pref_enabled(keys) @contextmanager def validate_node_numbers(table, revalidate_after=True): @@ -208,11 +300,10 @@ def adding_node(node): model = type(node) parent = model.objects.select_for_update().get(id=node.parent.id) if parent.accepted_id is not None: - from specifyweb.backend.context.remote_prefs import get_remote_prefs - # This business rule can be overriden by a remote pref. - pattern = r'^sp7\.allow_adding_child_to_synonymized_parent\.' + node.specify_model.name + '=(.+)' - override = re.search(pattern, get_remote_prefs(), re.MULTILINE) - if override is None or override.group(1).strip().lower() != "true": + if not _synonym_override_enabled(node): + node_children = [] if node.pk is None else list(node.children.values('id', 'fullname')) + parent_children = list(parent.children.values('id', 'fullname')) + parent_parent_id = parent.parent.id if parent.parent_id else None raise TreeBusinessRuleException( f'Adding node "{node.fullname}" to synonymized parent "{parent.fullname}"', {"tree" : "Taxon", @@ -223,14 +314,14 @@ def adding_node(node): "rankid" : node.rankid, "fullName" : node.fullname, "parentid": node.parent.id, - "children": list(node.children.values('id', 'fullname')) + "children": node_children }, "parent" : { "id" : parent.id, "rankid" : parent.rankid, "fullName" : parent.fullname, - "parentid": parent.parent.id, - "children": list(parent.children.values('id', 'fullname')) + "parentid": parent_parent_id, + "children": parent_children }}) insertion_point = open_interval(model, parent.nodenumber, 1) @@ -396,11 +487,8 @@ def synonymize(node, into, agent): node.isaccepted = False node.save() - # This check can be disabled by a remote pref - from specifyweb.backend.context.remote_prefs import get_remote_prefs - pattern = r'^sp7\.allow_adding_child_to_synonymized_parent\.' + node.specify_model.name + '=(.+)' - override = re.search(pattern, get_remote_prefs(), re.MULTILINE) - if node.children.count() > 0 and (override is None or override.group(1).strip().lower() != "true"): + # This check can be disabled by a remote or collection preference override + if node.children.count() > 0 and not _synonym_override_enabled(node): raise TreeBusinessRuleException( f'Synonymizing node "{node.fullname}" which has children', {"tree" : "Taxon", diff --git a/specifyweb/backend/trees/tests/test_tree_extras/test_synonymize.py b/specifyweb/backend/trees/tests/test_tree_extras/test_synonymize.py index ef724e0c7fe..107c2d2b012 100644 --- a/specifyweb/backend/trees/tests/test_tree_extras/test_synonymize.py +++ b/specifyweb/backend/trees/tests/test_tree_extras/test_synonymize.py @@ -1,11 +1,77 @@ +import json + from specifyweb.backend.businessrules.exceptions import TreeBusinessRuleException -from specifyweb.specify.models import Determination, Taxon, Taxontreedef +from specifyweb.specify.models import ( + Determination, + Spappresource, + Spappresourcedata, + Spappresourcedir, + Taxon, + Taxontreedef, +) from specifyweb.backend.trees.tests.test_trees import GeographyTree from specifyweb.backend.trees.extras import synonymize class TestSynonymize(GeographyTree): + def _set_synonym_pref(self, key: str, value: bool = True) -> None: + app_dir = Spappresourcedir.objects.filter( + ispersonal=False, + collection=self.collection, + discipline=self.discipline, + specifyuser=self.specifyuser, + ).first() + if app_dir is None: + app_dir = Spappresourcedir.objects.create( + ispersonal=False, + collection=self.collection, + discipline=self.discipline, + specifyuser=self.specifyuser, + ) + + app_resource = Spappresource.objects.filter( + spappresourcedir=app_dir, + name="CollectionPreferences", + specifyuser=self.specifyuser, + ).first() + if app_resource is None: + app_resource = Spappresource.objects.create( + spappresourcedir=app_dir, + name="CollectionPreferences", + level=0, + metadata="", + mimetype="application/json", + specifyuser=self.specifyuser, + ) + + app_data = Spappresourcedata.objects.filter( + spappresource=app_resource + ).first() + if app_data is None: + app_data = Spappresourcedata.objects.create( + spappresource=app_resource, + data=json.dumps({}), + ) + + raw_data = app_data.data + if isinstance(raw_data, memoryview): + raw_data = raw_data.tobytes() + if isinstance(raw_data, (bytes, bytearray)): + raw_data = raw_data.decode('utf-8') + + try: + prefs = json.loads(raw_data if raw_data else '{}') + except (TypeError, ValueError): + prefs = {} + + tree_management = prefs.setdefault('treeManagement', {}) + synonymized = tree_management.setdefault('synonymized', {}) + synonymized[key] = value + + app_data.data = json.dumps(prefs) + app_data.save() + def test_different_type(self): with self.assertRaises(AssertionError) as context: synonymize(self.na, self.collectionobjects[0], self.agent) @@ -69,6 +135,19 @@ def test_synonymize_geography_target_children(self): self.assertEqual(context.exception.args[1]['localizationKey'], "nodeSynonimizeWithChildren") + def test_synonymize_geography_target_children_with_collection_pref(self): + self._set_synonym_pref( + 'sp7.allow_adding_child_to_synonymized_parent.Geography', + True, + ) + + try: + synonymize(self.kansas, self.mo, self.agent) + except TreeBusinessRuleException: + self.fail( + 'synonymize raised TreeBusinessRuleException despite collection preference override' + ) + def test_synonymize_taxon_no_target_children(self): life = Taxon.objects.create( @@ -141,4 +220,4 @@ def test_synonymize_taxon_no_target_children(self): self.assertEqual(det_plantae_1.preferredtaxon_id, plantae.id) self.assertEqual(det_plantae_2.preferredtaxon_id, plantae.id) - \ No newline at end of file + diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/Create.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/Create.tsx index 1f4b82d129c..64e0c8e0f49 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/Create.tsx +++ b/specifyweb/frontend/js_src/lib/components/AppResources/Create.tsx @@ -31,6 +31,7 @@ import { formatUrl } from '../Router/queryString'; import type { AppResourcesTree } from './hooks'; import { useResourcesTree } from './hooks'; import type { AppResourcesOutlet } from './index'; +import { shouldShowCollectionPreferenceSubType } from './permissions'; import type { AppResourceType, ScopedAppResourceDir } from './types'; import { appResourceSubTypes, appResourceTypes } from './types'; @@ -60,6 +61,7 @@ export function CreateAppResource(): JSX.Element { const [templateFile, setTemplateFile] = React.useState< string | false | undefined >(undefined); + const canSeeCollectionPreferences = shouldShowCollectionPreferenceSubType(); return directory === undefined ? ( ) : type === undefined ? ( @@ -98,11 +100,16 @@ export function CreateAppResource(): JSX.Element { - {Object.entries(appResourceSubTypes).map( - ([ - key, - { icon, mimeType, name = '', documentationUrl, label, ...rest }, - ]) => + {Object.entries(appResourceSubTypes) + .filter( + ([key]) => + key !== 'collectionPreferences' || canSeeCollectionPreferences + ) + .map( + ([ + key, + { icon, mimeType, name = '', documentationUrl, label, ...rest }, + ]) => 'scope' in rest && !f.includes(rest.scope, directory.scope) ? undefined : ( diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/Filters.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/Filters.tsx index e5ddd5ded39..05ae7ac709d 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/Filters.tsx +++ b/specifyweb/frontend/js_src/lib/components/AppResources/Filters.tsx @@ -22,6 +22,7 @@ import { isAllAppResourceTypes, } from './filtersHelpers'; import type { AppResources } from './hooks'; +import { shouldShowCollectionPreferenceSubType } from './permissions'; import { appResourceSubTypes, appResourceTypes } from './types'; export function AppResourcesFilters({ @@ -33,12 +34,23 @@ export function AppResourcesFilters({ 'appResources', 'filters' ); + const canSeeCollectionPreferences = shouldShowCollectionPreferenceSubType(); + const visibleAppResources = React.useMemo( + () => + canSeeCollectionPreferences + ? allAppResources + : allAppResources.filter((type) => type !== 'collectionPreferences'), + [canSeeCollectionPreferences] + ); - const showAllResources = isAllAppResourceTypes(filters.appResources); + const showAllResources = isAllAppResourceTypes( + filters.appResources, + visibleAppResources + ); const handleToggleResources = (): void => setFilters({ ...filters, - appResources: showAllResources ? [] : allAppResources, + appResources: showAllResources ? [] : visibleAppResources, }); const [isOpen, handleOpen, handleClose] = useBooleanState(); @@ -63,7 +75,9 @@ export function AppResourcesFilters({ setFilters({ viewSets: false, appResources: - filters.viewSets || !showAllResources ? allAppResources : [], + filters.viewSets || !showAllResources + ? visibleAppResources + : [], }) } > @@ -116,48 +130,54 @@ export function AppResourcesFilters({ {commonText.countLine({ resource: resourcesText.appResources(), count: countAppResources(initialResources, { - appResources: allAppResources, + appResources: visibleAppResources, viewSets: false, }), })} diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/TabDefinitions.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/TabDefinitions.tsx index b44e76eea67..3cf1e712444 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/TabDefinitions.tsx +++ b/specifyweb/frontend/js_src/lib/components/AppResources/TabDefinitions.tsx @@ -28,6 +28,7 @@ import { formattersSpec } from '../Formatters/spec'; import { FormEditor } from '../FormEditor'; import { viewSetsSpec } from '../FormEditor/spec'; import { UserPreferencesEditor } from '../Preferences/Editor'; +import { CollectionPreferencesEditor } from '../Preferences/Editor'; import { useDarkMode } from '../Preferences/Hooks'; import type { BaseSpec } from '../Syncer'; import type { SimpleXmlNode } from '../Syncer/xmlToJson'; @@ -156,7 +157,7 @@ export const visualAppResourceEditors = f.store< json: AppResourceTextEditor, }, collectionPreferences: { - // FEATURE: add visual editor + visual: CollectionPreferencesEditor, json: AppResourceTextEditor, }, leafletLayers: undefined, diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/AppResourcesAside.test.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/AppResourcesAside.test.tsx index d661ae50a83..49fe636ffe4 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/AppResourcesAside.test.tsx +++ b/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/AppResourcesAside.test.tsx @@ -8,6 +8,16 @@ import type { AppResourcesConformation } from '../Aside'; import { AppResourcesAside } from '../Aside'; import { testAppResources } from './testAppResources'; +jest.mock('../permissions', () => { + const actual = jest.requireActual('../permissions'); + return { + ...actual, + filterCollectionPreferencesResources: (resources: readonly any[]) => + resources, + canAccessCollectionPreferencesResource: () => true, + }; +}); + requireContext(); describe('AppResourcesAside (simple no conformation case)', () => { diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/AppResourcesFilters.test.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/AppResourcesFilters.test.tsx index 9f67a920b45..4d8d7c42ad6 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/AppResourcesFilters.test.tsx +++ b/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/AppResourcesFilters.test.tsx @@ -6,7 +6,27 @@ import { UnloadProtectsContext } from '../../Router/UnloadProtect'; import { AppResourcesFilters } from '../Filters'; import { testAppResources } from './testAppResources'; +const mockCanSeeCollectionPreferences = jest.fn(() => false); + +jest.mock('../permissions', () => { + const actual = jest.requireActual('../permissions'); + return { + ...actual, + canAccessCollectionPreferencesResource: () => + mockCanSeeCollectionPreferences(), + shouldShowCollectionPreferenceSubType: () => + mockCanSeeCollectionPreferences(), + filterCollectionPreferencesResources: (resources: readonly any[]) => + mockCanSeeCollectionPreferences() + ? resources + : resources.filter( + (resource) => resource?.name !== 'CollectionPreferences' + ), + }; +}); + beforeEach(() => { + mockCanSeeCollectionPreferences.mockReturnValue(true); setCache( 'appResources', 'filters', @@ -99,4 +119,34 @@ describe('AppResourcesFilters', () => { viewSets: true, }); }); + + test('collection preferences option visible when permitted', async () => { + mockCanSeeCollectionPreferences.mockReturnValue(true); + + const { getAllByRole, user } = mount( + + ); + + const button = getAllByRole('button')[1]; + + await user.click(button); + + const filters = getCache('appResources', 'filters'); + expect(filters).toBeDefined(); + expect(filters?.appResources).toContain('collectionPreferences'); + }); + + test('collection preferences option hidden when not permitted', async () => { + mockCanSeeCollectionPreferences.mockReturnValue(false); + + const { getAllByRole, user } = mount( + + ); + + const button = getAllByRole('button')[1]; + await user.click(button); + + const filters = getCache('appResources', 'filters'); + expect(filters?.appResources).not.toContain('collectionPreferences'); + }); }); diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/filtersHelpers.ts b/specifyweb/frontend/js_src/lib/components/AppResources/filtersHelpers.ts index 446eb29de05..e252f484d36 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/filtersHelpers.ts +++ b/specifyweb/frontend/js_src/lib/components/AppResources/filtersHelpers.ts @@ -5,6 +5,7 @@ import { toResource } from '../DataModel/helpers'; import type { SerializedResource } from '../DataModel/helperTypes'; import type { SpAppResource, SpViewSetObj } from '../DataModel/types'; import type { AppResources } from './hooks'; +import { filterCollectionPreferencesResources } from './permissions'; import { appResourceSubTypes } from './types'; export const allAppResources = Array.from( @@ -25,10 +26,11 @@ export type AppResourceFilters = { * Determine if all app resource types are visible */ export const isAllAppResourceTypes = ( - appResources: RA + appResources: RA, + universe: RA = allAppResources ): boolean => JSON.stringify(Array.from(appResources).sort(sortFunction(f.id))) === - JSON.stringify(allAppResources); + JSON.stringify(universe); export function countAppResources( resources: AppResources, @@ -44,14 +46,15 @@ export const filterAppResources = ( ): AppResources => ({ ...resources, viewSets: filters.viewSets ? resources.viewSets : [], - appResources: + appResources: filterCollectionPreferencesResources( filters.appResources.length === 0 ? [] : isAllAppResourceTypes(filters.appResources) ? resources.appResources : resources.appResources.filter((resource) => filters.appResources.includes(getAppResourceType(resource)) - ), + ) + ), }); export const getResourceType = ( diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/permissions.ts b/specifyweb/frontend/js_src/lib/components/AppResources/permissions.ts new file mode 100644 index 00000000000..7105bbfa94e --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/AppResources/permissions.ts @@ -0,0 +1,78 @@ +import type { RA } from '../../utils/types'; +import type { SerializedResource } from '../DataModel/helperTypes'; +import { schema } from '../DataModel/schema'; +import type { SpAppResource, Tables } from '../DataModel/types'; +import { getOperationPermissions, getTablePermissions } from '../Permissions'; +import { toolDefinitions } from '../Security/registry'; +import { tableNameToResourceName } from '../Security/utils'; + +const COLLECTION_PREFERENCES_NAME = 'CollectionPreferences'; + +const getCollectionId = (): number => schema.domainLevelIds?.collection ?? -1; + +type OperationPermissionMap = Record>; +type TablePermissionMap = Record>; + +const getOperationPermissionMap = (): OperationPermissionMap => { + const collectionId = getCollectionId(); + return collectionId === -1 + ? ({} as OperationPermissionMap) + : ((getOperationPermissions()[collectionId] ?? + {}) as OperationPermissionMap); +}; + +const getTablePermissionMap = (): TablePermissionMap => { + const collectionId = getCollectionId(); + return collectionId === -1 + ? ({} as TablePermissionMap) + : ((getTablePermissions()[collectionId] ?? {}) as TablePermissionMap); +}; + +const hasOperationPermission = (resource: string, action: string): boolean => { + const permissions = getOperationPermissionMap(); + return Boolean(permissions[resource]?.[action]); +}; + +const hasToolTablePermission = ( + tool: keyof ReturnType, + action: 'create' | 'delete' | 'read' | 'update' +): boolean => { + const tablePermissions = getTablePermissionMap(); + const tables = toolDefinitions()[tool].tables as RA; + return tables.every((tableName) => { + const resourceName = tableNameToResourceName(tableName); + return Boolean(tablePermissions[resourceName]?.[action]); + }); +}; + +export const canAccessCollectionPreferencesResource = (): boolean => { + const collectionId = getCollectionId(); + if (collectionId === -1) return true; + + const tablePermissions = getTablePermissionMap(); + const operationPermissions = getOperationPermissionMap(); + if ( + Object.keys(tablePermissions).length === 0 || + Object.keys(operationPermissions).length === 0 + ) + return true; + + return ( + hasToolTablePermission('resources', 'update') && + hasOperationPermission('/preferences/collection', 'edit_collection') + ); +}; + +export const filterCollectionPreferencesResources = < + RESOURCE extends SerializedResource, +>( + resources: RA +): RA => + canAccessCollectionPreferencesResource() + ? resources + : resources.filter( + (resource) => resource.name !== COLLECTION_PREFERENCES_NAME + ); + +export const shouldShowCollectionPreferenceSubType = (): boolean => + canAccessCollectionPreferencesResource(); diff --git a/specifyweb/frontend/js_src/lib/components/Atoms/Icons.tsx b/specifyweb/frontend/js_src/lib/components/Atoms/Icons.tsx index fa59a2bcc77..9ee3b99a0a2 100644 --- a/specifyweb/frontend/js_src/lib/components/Atoms/Icons.tsx +++ b/specifyweb/frontend/js_src/lib/components/Atoms/Icons.tsx @@ -107,6 +107,7 @@ export const icons = { minus: , minusCircle: , nonStrict: , + office: , pencil: , pencilAt: , photos: , diff --git a/specifyweb/frontend/js_src/lib/components/Attachments/RecordSetAttachment.tsx b/specifyweb/frontend/js_src/lib/components/Attachments/RecordSetAttachment.tsx index 597e4ed3665..c41f2c2da3e 100644 --- a/specifyweb/frontend/js_src/lib/components/Attachments/RecordSetAttachment.tsx +++ b/specifyweb/frontend/js_src/lib/components/Attachments/RecordSetAttachment.tsx @@ -109,7 +109,8 @@ export function RecordSetAttachments({ const isComplete = fetchedCount.current === recordCount; - const [showCreateRecordSetDialog, setShowCreateRecordSetDialog] = React.useState(false); + const [showCreateRecordSetDialog, setShowCreateRecordSetDialog] = + React.useState(false); return ( <> @@ -133,16 +134,17 @@ export function RecordSetAttachments({ - (recordSetId === undefined && !isComplete) ? - setShowCreateRecordSetDialog(true) - : - loading( - downloadAllAttachments( - (recordSetId !== undefined && !isComplete) ? [] : attachmentsRef.current?.attachments ?? [], - name, - recordSetId, - ) - ) + recordSetId === undefined && !isComplete + ? setShowCreateRecordSetDialog(true) + : loading( + downloadAllAttachments( + recordSetId !== undefined && !isComplete + ? [] + : (attachmentsRef.current?.attachments ?? []), + name, + recordSetId + ) + ) } > {attachmentsText.downloadAll()} @@ -157,15 +159,15 @@ export function RecordSetAttachments({ header={ attachmentsRef.current?.attachments === undefined ? attachmentsText.attachments() - : (isComplete ? - commonText.countLine({ - resource: attachmentsText.attachments(), - count: attachmentsRef.current.attachments.length - }) : - commonText.countLineOrMore({ - resource: attachmentsText.attachments(), - count: attachmentsRef.current.attachments.length - })) + : isComplete + ? commonText.countLine({ + resource: attachmentsText.attachments(), + count: attachmentsRef.current.attachments.length, + }) + : commonText.countLineOrMore({ + resource: attachmentsText.attachments(), + count: attachmentsRef.current.attachments.length, + }) } onClose={handleHideAttachments} > @@ -215,13 +217,11 @@ function CreateRecordSetDialog({ }): JSX.Element { return ( {commonText.close()} - } + buttons={{commonText.close()}} header={attachmentsText.downloadAll()} onClose={onClose} > {attachmentsText.createRecordSetToDownloadAll()} ); -} \ No newline at end of file +} diff --git a/specifyweb/frontend/js_src/lib/components/Attachments/attachments.ts b/specifyweb/frontend/js_src/lib/components/Attachments/attachments.ts index fde546546b0..ac94e79001c 100644 --- a/specifyweb/frontend/js_src/lib/components/Attachments/attachments.ts +++ b/specifyweb/frontend/js_src/lib/components/Attachments/attachments.ts @@ -8,10 +8,15 @@ import type { UploadAttachmentSpec } from '../AttachmentsBulkImport/types'; import { getField } from '../DataModel/helpers'; import type { SerializedResource } from '../DataModel/helperTypes'; import type { SpecifyResource } from '../DataModel/legacyTypes'; +import { schema } from '../DataModel/schema'; import { tables } from '../DataModel/tables'; import type { Attachment } from '../DataModel/types'; import { load } from '../InitialContext'; -import { getPref } from '../InitialContext/remotePrefs'; +import { + ensureCollectionPreferencesLoaded, + getCollectionPref, + getPref, +} from '../InitialContext/remotePrefs'; import { downloadFile } from '../Molecules/FilePicker'; import { formatUrl } from '../Router/queryString'; // Import SVG icons, but better than in Icons.tsx @@ -284,15 +289,42 @@ export async function uploadFile( } }) ); + const isPublicDefault = await getAttachmentPublicDefault(); + return new tables.Attachment.Resource({ attachmentlocation: data.attachmentLocation, mimetype: fixMimeType(file.type), origfilename: file.name, title: file.name, - isPublic: getPref('attachment.is_public_default'), + isPublic: isPublicDefault, }); } +async function getAttachmentPublicDefault(): Promise { + const collectionPrefKey = + 'attachment.is_public_default' as const; + const collectionId = schema.domainLevelIds.collection; + try { + const collectionPreferences = await ensureCollectionPreferencesLoaded(); + const rawValue = + collectionPreferences + .getRaw() + ?.general?.attachments?.['attachment.is_public_default']; + if (typeof rawValue === 'boolean') return rawValue; + return collectionPreferences.get( + 'general', + 'attachments', + 'attachment.is_public_default' + ); + } catch { + try { + return getCollectionPref(collectionPrefKey, collectionId); + } catch { + return getPref(collectionPrefKey); + } + } +} + /** * A temporary workaround for mimeTypes for `.docx` and `.xlsx` files being * longer than the length limit on the `Attachment.mimeType` field. diff --git a/specifyweb/frontend/js_src/lib/components/BatchEdit/index.tsx b/specifyweb/frontend/js_src/lib/components/BatchEdit/index.tsx index 31b410f3be4..59cb682763b 100644 --- a/specifyweb/frontend/js_src/lib/components/BatchEdit/index.tsx +++ b/specifyweb/frontend/js_src/lib/components/BatchEdit/index.tsx @@ -247,7 +247,6 @@ const containsSystemTables = (queryFieldSpec: QueryFieldSpec) => { return Boolean(baseIsBlocked || pathHasBlockedSystem); }; - const hasHierarchyBaseTable = (queryFieldSpec: QueryFieldSpec) => Object.keys(schema.domainLevelIds).includes( queryFieldSpec.baseTable.name.toLowerCase() as 'collection' diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts index 4ec08b2b697..1c982de08d2 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts @@ -5,7 +5,6 @@ import { overrideAjax } from '../../../tests/ajax'; import { mockTime, requireContext } from '../../../tests/helpers'; import type { RA } from '../../../utils/types'; import { overwriteReadOnly } from '../../../utils/types'; -import { getPref } from '../../InitialContext/remotePrefs'; import { cogTypes } from '../helpers'; import type { SerializedResource } from '../helperTypes'; import { getResourceApiUrl } from '../resource'; @@ -13,6 +12,7 @@ import { useSaveBlockers } from '../saveBlockers'; import { schema } from '../schema'; import type { SpecifyTable } from '../specifyTable'; import { tables } from '../tables'; +import { getSynonymPreferenceForTree } from '../treeBusinessRules'; import type { CollectingEvent, CollectionObjectType, @@ -864,14 +864,20 @@ describe('treeBusinessRules', () => { expect(fieldChangeResult.current[0]).toStrictEqual(['Bad tree structure.']); }); test('saveBlocker not on synonymized parent w/preference', async () => { - const remotePrefs = await import('../../InitialContext/remotePrefs'); - jest - .spyOn(remotePrefs, 'getPref') - .mockImplementation((key) => - key === 'sp7.allow_adding_child_to_synonymized_parent.Taxon' - ? true - : getPref(key) - ); + const { collectionPreferences } = await import( + '../../Preferences/collectionPreferences' + ); + const originalRaw = collectionPreferences.getRaw(); + collectionPreferences.setRaw({ + ...originalRaw, + treeManagement: { + ...originalRaw.treeManagement, + synonymized: { + ...originalRaw.treeManagement?.synonymized, + 'sp7.allow_adding_child_to_synonymized_parent.Taxon': true, + }, + }, + } as typeof originalRaw); const taxon = new tables.Taxon.Resource({ name: 'dauricus', @@ -887,5 +893,53 @@ describe('treeBusinessRules', () => { useSaveBlockers(taxon, tables.Taxon.getField('parent')) ); expect(result.current[0]).toStrictEqual([]); + collectionPreferences.setRaw(originalRaw); + }); + + test('getSynonymPreferenceForTree respects geologic time pref', async () => { + const { collectionPreferences } = await import( + '../../Preferences/collectionPreferences' + ); + const originalRaw = collectionPreferences.getRaw(); + collectionPreferences.setRaw({ + ...originalRaw, + treeManagement: { + ...originalRaw.treeManagement, + synonymized: { + ...originalRaw.treeManagement?.synonymized, + 'sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod': + true, + }, + }, + } as typeof originalRaw); + + await expect( + getSynonymPreferenceForTree('GeologicTimePeriod') + ).resolves.toBe(true); + + collectionPreferences.setRaw(originalRaw); + }); + + test('getSynonymPreferenceForTree handles chronostrat legacy key', async () => { + const { collectionPreferences } = await import( + '../../Preferences/collectionPreferences' + ); + const originalRaw = collectionPreferences.getRaw(); + collectionPreferences.setRaw({ + ...originalRaw, + treeManagement: { + ...originalRaw.treeManagement, + synonymized: { + ...originalRaw.treeManagement?.synonymized, + 'sp7.allow_adding_child_to_synonymized_parent.ChronosStrat': true, + }, + }, + } as typeof originalRaw); + + await expect( + getSynonymPreferenceForTree('GeologicTimePeriod') + ).resolves.toBe(true); + + collectionPreferences.setRaw(originalRaw); }); }); diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/treeBusinessRules.ts b/specifyweb/frontend/js_src/lib/components/DataModel/treeBusinessRules.ts index f738a71b256..8657be7e748 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/treeBusinessRules.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/treeBusinessRules.ts @@ -1,15 +1,59 @@ import { treeText } from '../../localization/tree'; import { ajax } from '../../utils/ajax'; import { f } from '../../utils/functools'; -import { getPref } from '../InitialContext/remotePrefs'; +import { + ensureCollectionPreferencesLoaded, + getCollectionPref, + getPref, + remotePrefsDefinitions, +} from '../InitialContext/remotePrefs'; import { fetchPossibleRanks } from '../PickLists/TreeLevelPickList'; import { formatUrl } from '../Router/queryString'; import type { BusinessRuleResult } from './businessRules'; import type { AnyTree, TableFields } from './helperTypes'; import type { SpecifyResource } from './legacyTypes'; import { idFromUrl } from './resource'; +import { schema } from './schema'; import type { Tables } from './types'; +const remoteSynonymPrefKeysByTable = { + Taxon: ['sp7.allow_adding_child_to_synonymized_parent.Taxon'] as const, + Geography: [ + 'sp7.allow_adding_child_to_synonymized_parent.Geography', + ] as const, + Storage: ['sp7.allow_adding_child_to_synonymized_parent.Storage'] as const, + GeologicTimePeriod: [ + 'sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod', + 'sp7.allow_adding_child_to_synonymized_parent.ChronosStrat', + 'sp7.allow_adding_child_to_synonymized_parent.ChronoStrat', + ] as const, + LithoStrat: [ + 'sp7.allow_adding_child_to_synonymized_parent.LithoStrat', + ] as const, + TectonicUnit: [ + 'sp7.allow_adding_child_to_synonymized_parent.TectonicUnit', + ] as const, +} as const; + +type RemoteSynonymPrefKey = + (typeof remoteSynonymPrefKeysByTable)[keyof typeof remoteSynonymPrefKeysByTable][number]; + +type CollectionSynonymPrefKey = + (typeof remoteSynonymPrefKeysByTable)[keyof typeof remoteSynonymPrefKeysByTable][0]; + +export const expandSynonymPrefItemsByTable = remoteSynonymPrefKeysByTable; + +const collectionSynonymPrefKeyMap = Object.fromEntries( + ( + Object.values( + remoteSynonymPrefKeysByTable + ) as readonly (readonly RemoteSynonymPrefKey[])[] + ).flatMap((keys) => { + const [primary, ...aliases] = keys; + return [[primary, primary], ...aliases.map((alias) => [alias, primary])]; + }) +) as Record; + // eslint-disable-next-line unicorn/prevent-abbreviations export type TreeDefItem = Tables[`${TREE['tableName']}TreeDefItem`]; @@ -40,8 +84,8 @@ export const treeBusinessRules = async ( idFromUrl(parentDefItem.get('treeDef'))! ); - const doExpandSynonymActionsPref = getPref( - `sp7.allow_adding_child_to_synonymized_parent.${resource.specifyTable.name}` + const doExpandSynonymActionsPref = await getSynonymPreferenceForTree( + resource.specifyTable.name ); const isParentSynonym = !parent.get('isAccepted'); @@ -89,6 +133,61 @@ export const treeBusinessRules = async ( ); }); +export async function getSynonymPreferenceForTree( + tableName: AnyTree['tableName'] +): Promise { + const preferenceKeys = expandSynonymPrefItemsByTable[tableName] ?? []; + const [primaryKey] = preferenceKeys; + if (typeof primaryKey !== 'string') return false; + + const collectionId = schema.domainLevelIds.collection; + + try { + const collectionPreferences = await ensureCollectionPreferencesLoaded(); + const rawSynonymPrefs = + collectionPreferences.getRaw()?.treeManagement?.synonymized ?? {}; + + for (const key of preferenceKeys) + if (Object.hasOwn(rawSynonymPrefs, key)) { + const value = rawSynonymPrefs[key as keyof typeof rawSynonymPrefs]; + if (typeof value === 'boolean') return value; + } + + const defaultCollectionKey = preferenceKeys + .map((key) => collectionSynonymPrefKeyMap[key]) + .find((key): key is CollectionSynonymPrefKey => key !== undefined); + if (defaultCollectionKey !== undefined) + return collectionPreferences.get( + 'treeManagement', + 'synonymized', + defaultCollectionKey + ); + } catch { + /* Ignore and try fallbacks */ + } + + for (const key of preferenceKeys) { + const collectionKey = collectionSynonymPrefKeyMap[key]; + if (collectionKey !== undefined) + try { + return getCollectionPref(collectionKey, collectionId); + } catch { + /* Continue */ + } + } + + const remoteDefinitions = remotePrefsDefinitions(); + for (const key of preferenceKeys) + if (key in remoteDefinitions) + try { + return getPref(key as keyof ReturnType); + } catch { + /* Continue */ + } + + return false; +} + const getRelatedTreeTables = async < TREE extends AnyTree, TREE_DEF_ITEM extends TreeDefItem, diff --git a/specifyweb/frontend/js_src/lib/components/Header/userToolDefinitions.ts b/specifyweb/frontend/js_src/lib/components/Header/userToolDefinitions.ts index 6855811b3b4..d0c7ea8bf52 100644 --- a/specifyweb/frontend/js_src/lib/components/Header/userToolDefinitions.ts +++ b/specifyweb/frontend/js_src/lib/components/Header/userToolDefinitions.ts @@ -9,6 +9,7 @@ import { f } from '../../utils/functools'; import type { IR } from '../../utils/types'; import { ensure } from '../../utils/types'; import { toLowerCase } from '../../utils/utils'; +import { canAccessCollectionPreferencesResource } from '../AppResources/permissions'; import { icons } from '../Atoms/Icons'; import type { MenuItem } from '../Core/Main'; import { getDisciplineTrees } from '../InitialContext/treeRanks'; @@ -53,10 +54,16 @@ const rawUserTools = ensure>>>()({ }, [preferencesText.customization()]: { userPreferences: { - title: preferencesText.preferences(), + title: preferencesText.userPreferences(), url: '/specify/user-preferences/', icon: icons.cog, }, + collectionPreferences: { + title: preferencesText.collectionPreferences(), + url: '/specify/collection-preferences/', + icon: icons.office, + enabled: () => canAccessCollectionPreferencesResource(), + }, schemaConfig: { title: schemaText.schemaConfig(), url: '/specify/schema-config/', diff --git a/specifyweb/frontend/js_src/lib/components/InitialContext/__tests__/__snapshots__/remotePrefs.test.ts.snap b/specifyweb/frontend/js_src/lib/components/InitialContext/__tests__/__snapshots__/remotePrefs.test.ts.snap index 0ba6373e6e3..6b9a157028d 100644 --- a/specifyweb/frontend/js_src/lib/components/InitialContext/__tests__/__snapshots__/remotePrefs.test.ts.snap +++ b/specifyweb/frontend/js_src/lib/components/InitialContext/__tests__/__snapshots__/remotePrefs.test.ts.snap @@ -67,7 +67,7 @@ exports[`fetches and parses remotePrefs correctly 1`] = ` "Treeeditor.TreeColColor2.LithoStrat": "151, 221, 255", "Treeeditor.TreeColColor2.Storage": "128, 128, 0", "Treeeditor.TreeColColor2.Taxon": "151, 221, 255", - "attachment.is_public_default": "true", + "attachment.is_public_default_32768": "true", "attachment.key": "c3wNpDBTLMedXWSb8w2TeSwHWVFLvBwiYmtU0CdOzLQtelcibV9sTXW7NxZlX68", "attachment.path": "", "attachment.preview_size": "123.3", @@ -109,6 +109,8 @@ exports[`fetches and parses remotePrefs correctly 1`] = ` "settings.email.smtp": "authsmtp.ku.edu", "settings.email.testconnection": "", "settings.email.username": "abentley", + "sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod_32768": "true", + "sp7.allow_adding_child_to_synonymized_parent.Taxon_32768": "true", "specify.bg.image": "", "ui.formatting.disciplineicon.KUFishtissue": "colobj_backstop", "ui.formatting.disciplineicon.KUFishvoucher": "colobj_backstop", diff --git a/specifyweb/frontend/js_src/lib/components/InitialContext/__tests__/remotePrefs.test.ts b/specifyweb/frontend/js_src/lib/components/InitialContext/__tests__/remotePrefs.test.ts index d545c838da1..19c97490e65 100644 --- a/specifyweb/frontend/js_src/lib/components/InitialContext/__tests__/remotePrefs.test.ts +++ b/specifyweb/frontend/js_src/lib/components/InitialContext/__tests__/remotePrefs.test.ts @@ -24,3 +24,22 @@ describe('Parsing Remote Prefs', () => { test('can retrieve collection pref', () => expect(getCollectionPref('CO_CREATE_COA', 32_678)).toBe(false)); + +test('parses collection boolean pref', () => + expect(getCollectionPref('attachment.is_public_default', 32_768)).toBe(true)); + +test('parses collection tree synonym pref', () => + expect( + getCollectionPref( + 'sp7.allow_adding_child_to_synonymized_parent.Taxon', + 32_768 + ) + ).toBe(true)); + +test('parses collection chronostrat synonym pref', () => + expect( + getCollectionPref( + 'sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod', + 32_768 + ) + ).toBe(true)); diff --git a/specifyweb/frontend/js_src/lib/components/InitialContext/remotePrefs.ts b/specifyweb/frontend/js_src/lib/components/InitialContext/remotePrefs.ts index 0ab66f5601c..e09ca3d3a45 100644 --- a/specifyweb/frontend/js_src/lib/components/InitialContext/remotePrefs.ts +++ b/specifyweb/frontend/js_src/lib/components/InitialContext/remotePrefs.ts @@ -132,25 +132,6 @@ export const remotePrefsDefinitions = f.store( parser: 'java.lang.Boolean', isLegacy: true, }, - 'attachment.preview_size': { - description: 'The size in px of the generated attachment thumbnails', - defaultValue: 123, - parser: 'java.lang.Long', - isLegacy: true, - }, - // These are used on the back end only: - 'auditing.do_audits': { - description: 'Whether Audit Log is enabled', - defaultValue: true, - parser: 'java.lang.Boolean', - isLegacy: true, - }, - 'auditing.audit_field_updates': { - description: 'Whether Audit Log records field value changes', - defaultValue: true, - parser: 'java.lang.Boolean', - isLegacy: true, - }, 'sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod': { description: 'Allowed to add children to synopsized Geologic Time Period records', @@ -158,8 +139,16 @@ export const remotePrefsDefinitions = f.store( parser: 'java.lang.Boolean', isLegacy: false, }, - 'sp7.allow_adding_child_to_synonymized_parent.Taxon': { - description: 'Allowed to add children to synopsized Taxon records', + 'sp7.allow_adding_child_to_synonymized_parent.ChronosStrat': { + description: + 'Allowed to add children to synopsized Chronostratigraphy records', + defaultValue: false, + parser: 'java.lang.Boolean', + isLegacy: false, + }, + 'sp7.allow_adding_child_to_synonymized_parent.ChronoStrat': { + description: + 'Allowed to add children to synopsized Chronostratigraphy records', defaultValue: false, parser: 'java.lang.Boolean', isLegacy: false, @@ -182,6 +171,12 @@ export const remotePrefsDefinitions = f.store( parser: 'java.lang.Boolean', isLegacy: false, }, + 'sp7.allow_adding_child_to_synonymized_parent.Taxon': { + description: 'Allowed to add children to synopsized Taxon records', + defaultValue: false, + parser: 'java.lang.Boolean', + isLegacy: false, + }, 'sp7.allow_adding_child_to_synonymized_parent.TectonicUnit': { description: 'Allowed to add children to synopsized TectonicUnit records', @@ -189,6 +184,25 @@ export const remotePrefsDefinitions = f.store( parser: 'java.lang.Boolean', isLegacy: false, }, + 'attachment.preview_size': { + description: 'The size in px of the generated attachment thumbnails', + defaultValue: 123, + parser: 'java.lang.Long', + isLegacy: true, + }, + // These are used on the back end only: + 'auditing.do_audits': { + description: 'Whether Audit Log is enabled', + defaultValue: true, + parser: 'java.lang.Boolean', + isLegacy: true, + }, + 'auditing.audit_field_updates': { + description: 'Whether Audit Log records field value changes', + defaultValue: true, + parser: 'java.lang.Boolean', + isLegacy: true, + }, // This is actually stored in Global Prefs: /* * 'AUDIT_LIFESPAN_MONTHS': { @@ -227,6 +241,49 @@ export const collectionPrefsDefinitions = { defaultValue: false, parser: 'java.lang.Boolean', }, + 'attachment.is_public_default': { + separator: '_', + description: 'Whether new Attachments are public by default', + defaultValue: false, + parser: 'java.lang.Boolean', + }, + 'sp7.allow_adding_child_to_synonymized_parent.Taxon': { + separator: '_', + description: 'Allowed to add children to synopsized Taxon records', + defaultValue: false, + parser: 'java.lang.Boolean', + }, + 'sp7.allow_adding_child_to_synonymized_parent.Geography': { + separator: '_', + description: 'Allowed to add children to synopsized Geography records', + defaultValue: false, + parser: 'java.lang.Boolean', + }, + 'sp7.allow_adding_child_to_synonymized_parent.Storage': { + separator: '_', + description: 'Allowed to add children to synopsized Storage records', + defaultValue: false, + parser: 'java.lang.Boolean', + }, + 'sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod': { + separator: '_', + description: + 'Allowed to add children to synopsized Geologic Time Period records', + defaultValue: false, + parser: 'java.lang.Boolean', + }, + 'sp7.allow_adding_child_to_synonymized_parent.LithoStrat': { + separator: '_', + description: 'Allowed to add children to synopsized LithoStrat records', + defaultValue: false, + parser: 'java.lang.Boolean', + }, + 'sp7.allow_adding_child_to_synonymized_parent.TectonicUnit': { + separator: '_', + description: 'Allowed to add children to synopsized TectonicUnit records', + defaultValue: false, + parser: 'java.lang.Boolean', + }, sp7_scope_table_picklists: { separator: '_', description: @@ -235,3 +292,22 @@ export const collectionPrefsDefinitions = { parser: 'java.lang.Boolean', }, } as const; + +let collectionPrefsFetchPromise: Promise | undefined; + +export async function ensureCollectionPreferencesLoaded(): Promise< + (typeof import('../Preferences/collectionPreferences'))['collectionPreferences'] +> { + const { collectionPreferences } = await import( + '../Preferences/collectionPreferences' + ); + if (Object.keys(collectionPreferences.getRaw()).length === 0) { + if (collectionPrefsFetchPromise === undefined) + collectionPrefsFetchPromise = collectionPreferences + .fetch() + .catch(() => undefined) + .then(() => undefined); + await collectionPrefsFetchPromise; + } + return collectionPreferences; +} diff --git a/specifyweb/frontend/js_src/lib/components/Permissions/definitions.ts b/specifyweb/frontend/js_src/lib/components/Permissions/definitions.ts index a5ee87a2028..0045e7a884b 100644 --- a/specifyweb/frontend/js_src/lib/components/Permissions/definitions.ts +++ b/specifyweb/frontend/js_src/lib/components/Permissions/definitions.ts @@ -122,6 +122,7 @@ export const institutionPermissions = new Set([ export const frontEndPermissions = { '/preferences/user': ['edit_protected'], '/preferences/statistics': ['edit_shared'], + '/preferences/collection': ['edit_collection'], } as const; /** diff --git a/specifyweb/frontend/js_src/lib/components/PickLists/__tests__/fetch.test.ts b/specifyweb/frontend/js_src/lib/components/PickLists/__tests__/fetch.test.ts index 7889b2f32a5..25f200f6147 100644 --- a/specifyweb/frontend/js_src/lib/components/PickLists/__tests__/fetch.test.ts +++ b/specifyweb/frontend/js_src/lib/components/PickLists/__tests__/fetch.test.ts @@ -18,6 +18,10 @@ const { unsafeFetchPickList, fetchPickListItems } = exportsForTests; requireContext(); +afterEach(() => { + jest.restoreAllMocks(); +}); + describe('unsafeFetchPickList', () => { test('front-end pick list', async () => { const resource = await unsafeFetchPickList('_AgentTypeComboBox'); @@ -113,7 +117,22 @@ describe('fetchPickListItems', () => { }, objects: [{ id: 3, _tableName: 'Locality', localityname: 'abc' }], }); + overrideAjax('/api/specify/locality/?domainfilter=false&limit=0', { + meta: { + total_count: 2, + }, + objects: [ + { id: 3, _tableName: 'Locality', localityname: 'abc' }, + { id: 4, _tableName: 'Locality', localityname: 'def' }, + ], + }); test('pick list from entire table', async () => { + const remotePrefs = await import('../../InitialContext/remotePrefs'); + jest + .spyOn(remotePrefs, 'ensureCollectionPreferencesLoaded') + .mockRejectedValue(new Error('no prefs')); + jest.spyOn(remotePrefs, 'getCollectionPref').mockReturnValue(true); + const pickList = deserializeResource( addMissingFields('PickList', { type: PickListTypes.TABLE, @@ -130,6 +149,16 @@ describe('fetchPickListItems', () => { ]); }); + overrideAjax('/api/specify/locality/?limit=0', { + meta: { + total_count: 2, + }, + objects: [ + { id: 3, _tableName: 'Locality', localityname: 'abc' }, + { id: 4, _tableName: 'Locality', localityname: 'def' }, + ], + }); + overrideAjax('/api/specify/collection/?domainfilter=true&limit=0', { meta: { total_count: 1, @@ -137,6 +166,16 @@ describe('fetchPickListItems', () => { objects: [{ id: 1, _tableName: 'Collection', collectionname: 'abc' }], }); + overrideAjax('/api/specify/collection/?domainfilter=false&limit=0', { + meta: { + total_count: 2, + }, + objects: [ + { id: 1, _tableName: 'Collection', collectionname: 'abc' }, + { id: 2, _tableName: 'Collection', collectionname: 'cba' }, + ], + }); + overrideAjax('/api/specify/collection/?limit=0', { meta: { total_count: 2, @@ -148,6 +187,12 @@ describe('fetchPickListItems', () => { }); test('Picklistitems for Entire Table scoped by default', async () => { + const remotePrefs = await import('../../InitialContext/remotePrefs'); + jest + .spyOn(remotePrefs, 'ensureCollectionPreferencesLoaded') + .mockRejectedValue(new Error('no prefs')); + jest.spyOn(remotePrefs, 'getCollectionPref').mockReturnValue(true); + const picklist = deserializeResource( addMissingFields('PickList', { type: PickListTypes.TABLE, @@ -163,6 +208,9 @@ describe('fetchPickListItems', () => { test('Picklistitems unscoped for sp7_scope_table_picklists', async () => { const remotePrefs = await import('../../InitialContext/remotePrefs'); + jest + .spyOn(remotePrefs, 'ensureCollectionPreferencesLoaded') + .mockRejectedValue(new Error('no prefs')); jest .spyOn(remotePrefs, 'getCollectionPref') .mockImplementation(() => false); diff --git a/specifyweb/frontend/js_src/lib/components/PickLists/fetch.ts b/specifyweb/frontend/js_src/lib/components/PickLists/fetch.ts index 1b6bcf4b785..6a187a28675 100644 --- a/specifyweb/frontend/js_src/lib/components/PickLists/fetch.ts +++ b/specifyweb/frontend/js_src/lib/components/PickLists/fetch.ts @@ -24,7 +24,10 @@ import type { PickList, PickListItem, Tables } from '../DataModel/types'; import { softFail } from '../Errors/Crash'; import { format } from '../Formatters/formatters'; import type { PickListItemSimple } from '../FormFields/ComboBox'; -import { getCollectionPref } from '../InitialContext/remotePrefs'; +import { + ensureCollectionPreferencesLoaded, + getCollectionPref, +} from '../InitialContext/remotePrefs'; import { hasTablePermission, hasToolPermission } from '../Permissions/helpers'; import { createPickListItem, @@ -118,18 +121,39 @@ async function fetchFromTable( pickList: SpecifyResource, limit: number ): Promise> { - const tableName = strictGetTable(pickList.get('tableName')).name; + const specifyTable = strictGetTable(pickList.get('tableName')); + const tableName = specifyTable.name; if (!hasTablePermission(tableName, 'read')) return []; - const scopeTablePicklist = getCollectionPref( - 'sp7_scope_table_picklists', - schema.domainLevelIds.collection - ); + let scopeTablePicklist: boolean; + try { + const collectionPreferences = await ensureCollectionPreferencesLoaded(); + const rawValue = + collectionPreferences.getRaw()?.general?.pickLists + ?.sp7_scope_table_picklists; + scopeTablePicklist = + typeof rawValue === 'boolean' + ? rawValue + : collectionPreferences.get( + 'general', + 'pickLists', + 'sp7_scope_table_picklists' + ); + } catch { + scopeTablePicklist = getCollectionPref( + 'sp7_scope_table_picklists', + schema.domainLevelIds.collection + ); + } + const tableHasScope = specifyTable.getScope() !== undefined; + const tableSupportsDomainFilter = + tableHasScope || + !f.includes(Object.keys(schema.domainLevelIds), toLowerCase(tableName)); const { records } = await fetchCollection(tableName, { - domainFilter: scopeTablePicklist - ? true - : !f.includes(Object.keys(schema.domainLevelIds), toLowerCase(tableName)), + domainFilter: tableSupportsDomainFilter + ? Boolean(scopeTablePicklist) + : undefined, limit, }); return Promise.all( diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/Aside.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/Aside.tsx index 806575fafad..4a5463691d3 100644 --- a/specifyweb/frontend/js_src/lib/components/Preferences/Aside.tsx +++ b/specifyweb/frontend/js_src/lib/components/Preferences/Aside.tsx @@ -7,18 +7,21 @@ import type { GetSet, WritableArray } from '../../utils/types'; import { Link } from '../Atoms/Link'; import { pathIsOverlay } from '../Router/UnloadProtect'; import { scrollIntoView } from '../TreeView/helpers'; +import type { PreferenceType } from './index'; import { usePrefDefinitions } from './index'; export function PreferencesAside({ activeCategory, setActiveCategory, references, + prefType = 'user', }: { readonly activeCategory: number | undefined; readonly setActiveCategory: (activeCategory: number | undefined) => void; readonly references: React.RefObject>; + readonly prefType?: PreferenceType; }): JSX.Element { - const definitions = usePrefDefinitions(); + const definitions = usePrefDefinitions(prefType); const navigate = useNavigate(); const location = useLocation(); const isInOverlay = pathIsOverlay(location.pathname); @@ -38,6 +41,22 @@ export function PreferencesAside({ const [freezeCategory, setFreezeCategory] = useFrozenCategory(); const currentIndex = freezeCategory ?? activeCategory; + const visibleDefinitions = React.useMemo( + () => + definitions + .map( + (definition, index) => + [index, definition] as const + ) + .filter( + ([, [category]]) => + !( + prefType === 'collection' && + category === 'catalogNumberParentInheritance' + ) + ), + [definitions, prefType] + ); React.useEffect(() => { const active = location.hash.replace('#', '').toLowerCase(); @@ -58,12 +77,14 @@ export function PreferencesAside({ overflow-y-auto md:sticky md:flex-1 `} > - {definitions.map(([category, { title }], index) => ( + {visibleDefinitions.map(([definitionIndex, [category, { title }]]) => ( setFreezeCategory(index)} + onClick={(): void => setFreezeCategory(definitionIndex)} > {typeof title === 'function' ? title() : title} diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/CollectionDefinitions.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/CollectionDefinitions.tsx index ce8612a47fe..a8944be59fc 100644 --- a/specifyweb/frontend/js_src/lib/components/Preferences/CollectionDefinitions.tsx +++ b/specifyweb/frontend/js_src/lib/components/Preferences/CollectionDefinitions.tsx @@ -1,80 +1,151 @@ +/** + * Definitions for Collection preferences + */ + +import type { LocalizedString } from 'typesafe-i18n'; + +import { attachmentsText } from '../../localization/attachments'; import { preferencesText } from '../../localization/preferences'; import { queryText } from '../../localization/query'; import { specifyNetworkText } from '../../localization/specifyNetwork'; import { statsText } from '../../localization/stats'; +import { treeText } from '../../localization/tree'; import { f } from '../../utils/functools'; import type { RA } from '../../utils/types'; -import { ensure, localized } from '../../utils/types'; -import type { QueryView } from '../QueryBuilder/Header'; +import { ensure } from '../../utils/types'; +import { camelToHuman } from '../../utils/utils'; +import { getField } from '../DataModel/helpers'; +import { genericTables } from '../DataModel/tables'; +import { tables } from '../DataModel/tables'; +import type { Tables } from '../DataModel/types'; import type { StatLayout } from '../Statistics/types'; import type { GenericPreferences } from './types'; import { definePref } from './types'; +const tableLabel = (tableName: keyof Tables): LocalizedString => + genericTables[tableName]?.label ?? camelToHuman(tableName); + +const specifyNetworkItems = { + publishingOrganization: definePref({ + title: specifyNetworkText.publishingOrganizationKey(), + description: specifyNetworkText.publishingOrganizationKeyDescription(), + requiresReload: false, + visible: true, + defaultValue: undefined, + type: 'java.lang.String', + }), + collectionKey: definePref({ + title: specifyNetworkText.collectionKey(), + description: specifyNetworkText.collectionKeyDescription(), + requiresReload: false, + visible: true, + defaultValue: undefined, + type: 'java.lang.String', + }), +} as const; + export const collectionPreferenceDefinitions = { - statistics: { - title: statsText.statistics(), + general: { + title: preferencesText.general(), subCategories: { - appearance: { - title: preferencesText.appearance(), + pickLists: { + title: preferencesText.filterPickLists(), items: { - layout: definePref | undefined>({ - title: localized('_Defines the layout of the stats page'), + sp7_scope_table_picklists: definePref({ + title: preferencesText.scopeEntireTablePicklists(), + description: preferencesText.scopeEntireTablePicklistsDescription(), requiresReload: false, - visible: false, - defaultValue: undefined, - renderer: f.never, - container: 'label', - }), - showPreparationsTotal: definePref({ - title: localized('Defines if preparation stats include total'), - requiresReload: false, - visible: false, - defaultValue: true, - renderer: f.never, - container: 'label', + visible: true, + defaultValue: false, type: 'java.lang.Boolean', }), - refreshRate: definePref({ - title: localized('_Defines the rate of auto refresh in hours'), - requiresReload: false, - visible: false, - defaultValue: 24, - renderer: f.never, - container: 'label', - type: 'java.lang.Float', - }), }, }, - specifyNetwork: { - title: specifyNetworkText.specifyNetwork(), + attachments: { + title: attachmentsText.attachments(), items: { - publishingOrganization: definePref({ - title: localized('_Stores GBIF\'s "publishingOrgKey"'), + 'attachment.is_public_default': definePref({ + title: attachmentsText.publicDefault(), + description: attachmentsText.publicDefaultDescription(), requiresReload: false, - visible: false, - defaultValue: undefined, - renderer: f.never, - container: 'label', - }), - collectionKey: definePref({ - title: localized('_Stores GBIF\'s "dataSetKey"'), - requiresReload: false, - visible: false, - defaultValue: undefined, - renderer: f.never, - container: 'label', + visible: true, + defaultValue: false, + type: 'java.lang.Boolean', }), }, }, }, }, + + treeManagement: { + title: treeText.treeManagement(), + subCategories: { + synonymized: { + title: treeText.synonymizedNodes(), + description: treeText.synonymizedNodesDescription(), + items: { + 'sp7.allow_adding_child_to_synonymized_parent.Taxon': + definePref({ + title: () => tableLabel('Taxon'), + requiresReload: false, + visible: true, + defaultValue: false, + type: 'java.lang.Boolean', + }), + 'sp7.allow_adding_child_to_synonymized_parent.Geography': + definePref({ + title: () => tableLabel('Geography'), + requiresReload: false, + visible: true, + defaultValue: false, + type: 'java.lang.Boolean', + }), + 'sp7.allow_adding_child_to_synonymized_parent.Storage': + definePref({ + title: () => tableLabel('Storage'), + requiresReload: false, + visible: true, + defaultValue: false, + type: 'java.lang.Boolean', + }), + 'sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod': + definePref({ + title: () => tableLabel('GeologicTimePeriod'), + requiresReload: false, + visible: true, + defaultValue: false, + type: 'java.lang.Boolean', + }), + 'sp7.allow_adding_child_to_synonymized_parent.LithoStrat': + definePref({ + title: () => tableLabel('LithoStrat'), + requiresReload: false, + visible: true, + defaultValue: false, + type: 'java.lang.Boolean', + }), + 'sp7.allow_adding_child_to_synonymized_parent.TectonicUnit': + definePref({ + title: () => tableLabel('TectonicUnit'), + requiresReload: false, + visible: true, + defaultValue: false, + type: 'java.lang.Boolean', + }), + }, + }, + }, + }, queryBuilder: { title: queryText.queryBuilder(), subCategories: { appearance: { title: preferencesText.appearance(), items: { - display: definePref({ + display: definePref<{ + readonly basicView: RA; + readonly detailedView: RA; + }>({ title: preferencesText.displayBasicView(), requiresReload: false, visible: false, @@ -89,19 +160,72 @@ export const collectionPreferenceDefinitions = { }, }, }, + + statistics: { + title: statsText.statistics(), + subCategories: { + appearance: { + title: preferencesText.appearance(), + items: { + layout: definePref | undefined>({ + title: statsText.layoutPreference(), + requiresReload: false, + visible: false, + defaultValue: undefined, + renderer: f.never, + container: 'label', + }), + showPreparationsTotal: definePref({ + title: statsText.showPreparationsTotal(), + description: statsText.showPreparationsTotalDescription(), + requiresReload: false, + visible: true, + defaultValue: true, + type: 'java.lang.Boolean', + }), + refreshRate: definePref({ + title: statsText.autoRefreshRate(), + description: statsText.autoRefreshRateDescription(), + requiresReload: false, + visible: true, + defaultValue: 24, + type: 'java.lang.Integer', + }), + }, + }, + specifyNetwork: { + title: specifyNetworkText.specifyNetwork(), + items: specifyNetworkItems, + }, + }, + }, + catalogNumberInheritance: { title: queryText.catalogNumberInheritance(), subCategories: { behavior: { - title: preferencesText.behavior(), + title: () => tableLabel('CollectionObjectGroup'), items: { inheritance: definePref({ - title: preferencesText.inheritanceCatNumberPref(), + title: () => + preferencesText.inheritanceCatNumberPref({ + catalogNumber: getField( + tables.CollectionObject, + 'catalogNumber' + ).label, + collectionObject: tables.CollectionObject.label, + }), + description: () => + preferencesText.inheritanceCatNumberPrefDescription({ + catalogNumber: getField( + tables.CollectionObject, + 'catalogNumber' + ).label, + collectionObject: tables.CollectionObject.label, + }), requiresReload: false, - visible: false, + visible: true, defaultValue: false, - renderer: f.never, - container: 'label', type: 'java.lang.Boolean', }), }, @@ -109,24 +233,38 @@ export const collectionPreferenceDefinitions = { }, }, catalogNumberParentInheritance: { - title: queryText.catalogNumberParentCOInheritance(), + title: queryText.catalogNumberInheritance(), subCategories: { behavior: { - title: preferencesText.behavior(), + title: () => camelToHuman('Component'), items: { inheritance: definePref({ - title: preferencesText.inheritanceCatNumberParentCOPref(), + title: () => + preferencesText.inheritanceCatNumberParentCOPref({ + catalogNumber: getField( + tables.CollectionObject, + 'catalogNumber' + ).label, + collectionObject: tables.CollectionObject.label, + }), + description: () => + preferencesText.inheritanceCatNumberParentCOPrefDescription({ + catalogNumber: getField( + tables.CollectionObject, + 'catalogNumber' + ).label, + collectionObject: tables.CollectionObject.label, + }), requiresReload: false, - visible: false, + visible: true, defaultValue: false, - renderer: f.never, - container: 'label', type: 'java.lang.Boolean', }), }, }, }, }, + uniqueCatalogNumberAccrossComponentAndCO: { title: queryText.uniqueCatalogNumberAcrossComponentAndCo(), subCategories: { diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/Editor.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/Editor.tsx index bac46be2988..b7b53ff6ad1 100644 --- a/specifyweb/frontend/js_src/lib/components/Preferences/Editor.tsx +++ b/specifyweb/frontend/js_src/lib/components/Preferences/Editor.tsx @@ -2,41 +2,125 @@ import React from 'react'; import { useLiveState } from '../../hooks/useLiveState'; import type { AppResourceTabProps } from '../AppResources/TabDefinitions'; +import type { PreferenceType } from '../Preferences'; import { PreferencesContent } from '../Preferences'; +import { PreferencesAside } from '../Preferences/Aside'; import { BasePreferences } from '../Preferences/BasePreferences'; import { userPreferenceDefinitions } from '../Preferences/UserDefinitions'; import { userPreferences } from '../Preferences/userPreferences'; +import { useTopChild } from '../Preferences/useTopChild'; +import { collectionPreferenceDefinitions } from './CollectionDefinitions'; +import { collectionPreferences } from './collectionPreferences'; +import type { GenericPreferences } from './types'; -export function UserPreferencesEditor({ - data, - onChange: handleChange, -}: AppResourceTabProps): JSX.Element { - const [preferencesContext] = useLiveState( - React.useCallback(() => { - const userPreferences = new BasePreferences({ - definitions: userPreferenceDefinitions, - values: { - resourceName: 'UserPreferences', - fetchUrl: '/context/user_resource/', - }, - defaultValues: undefined, - developmentGlobal: '_editingUserPreferences', - syncChanges: false, - }); - userPreferences.setRaw( - JSON.parse(data === null || data.length === 0 ? '{}' : data) - ); - userPreferences.events.on('update', () => - handleChange(JSON.stringify(userPreferences.getRaw())) - ); - return userPreferences; - }, [handleChange]) - ); - - const Context = userPreferences.Context; - return ( - - - - ); +type EditorDependencies = Pick; + +type PreferencesEditorConfig = { + readonly definitions: DEFINITIONS; + readonly Context: BasePreferences['Context']; + readonly resourceName: string; + readonly fetchUrl: string; + readonly developmentGlobal: string; + readonly prefType?: PreferenceType; + readonly dependencyResolver?: ( + inputs: EditorDependencies + ) => React.DependencyList; +}; + +const defaultDependencyResolver = ({ onChange }: EditorDependencies) => [ + onChange, +]; + +function createPreferencesEditor( + config: PreferencesEditorConfig +) { + const { + definitions, + Context, + resourceName, + fetchUrl, + developmentGlobal, + prefType, + dependencyResolver = defaultDependencyResolver, + } = config; + + return function PreferencesEditor({ + data, + onChange, + }: AppResourceTabProps): JSX.Element { + const dependencies = dependencyResolver({ data, onChange }); + + const [preferencesInstance] = useLiveState>( + React.useCallback(() => { + const preferences = new BasePreferences({ + definitions, + values: { + resourceName, + fetchUrl, + }, + defaultValues: undefined, + developmentGlobal, + syncChanges: false, + }); + + preferences.setRaw( + JSON.parse(data === null || data.length === 0 ? '{}' : data) + ); + + preferences.events.on('update', () => + onChange(JSON.stringify(preferences.getRaw())) + ); + + return preferences; + }, dependencies) + ); + + const Provider = Context.Provider; + const contentProps = prefType === undefined ? {} : { prefType }; + const { + visibleChild, + setVisibleChild, + references, + forwardRefs, + scrollContainerRef, + } = useTopChild(); + const asidePrefType = prefType ?? 'user'; + + return ( + +
+ + + +
+
+ ); + }; } + +export const UserPreferencesEditor = createPreferencesEditor({ + definitions: userPreferenceDefinitions, + Context: userPreferences.Context, + resourceName: 'UserPreferences', + fetchUrl: '/context/user_resource/', + developmentGlobal: 'editingUserPreferences', + dependencyResolver: ({ onChange }) => [onChange], +}); + +export const CollectionPreferencesEditor = createPreferencesEditor({ + definitions: collectionPreferenceDefinitions, + Context: collectionPreferences.Context, + resourceName: 'CollectionPreferences', + fetchUrl: '/context/collection_resource/', + developmentGlobal: 'editingCollectionPreferences', + prefType: 'collection', + dependencyResolver: ({ data, onChange }) => [data, onChange], +}); diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/Renderers.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/Renderers.tsx index 0af5d46b5a2..a1d25e5ff3e 100644 --- a/specifyweb/frontend/js_src/lib/components/Preferences/Renderers.tsx +++ b/specifyweb/frontend/js_src/lib/components/Preferences/Renderers.tsx @@ -24,12 +24,16 @@ import type { RA } from '../../utils/types'; import { Input, Select, Textarea } from '../Atoms/Form'; import { iconClassName } from '../Atoms/Icons'; import { ReadOnlyContext } from '../Core/Contexts'; -import type { AnySchema } from '../DataModel/helperTypes'; +import type { AnySchema, AnyTree } from '../DataModel/helperTypes'; import type { SpecifyTable } from '../DataModel/specifyTable'; import { tables } from '../DataModel/tables'; import type { Collection } from '../DataModel/types'; import { rawMenuItemsPromise } from '../Header/menuItemDefinitions'; import { useMenuItems, useUserTools } from '../Header/menuItemProcessing'; +import { + getTreeDefinitions, + treeRanksPromise, +} from '../InitialContext/treeRanks'; import { AttachmentPicker } from '../Molecules/AttachmentPicker'; import { AutoComplete } from '../Molecules/AutoComplete'; import { ListEdit } from '../Toolbar/ListEdit'; @@ -378,3 +382,63 @@ export function DefaultPreferenceItemRender({ /> ); } + +type Rank = { readonly rankId: number; readonly name: string }; + +/* + * This grabs the ranks from the API and displays them in a dropdown + * The ranks are sorted in ascending order by `rankId` so they appear in the correct order for the user + */ +export function ThresholdRank({ + value, + onChange, + tableName, +}: PreferenceRendererProps & { + readonly tableName: AnyTree['tableName']; +}): JSX.Element { + const [items, setItems] = React.useState([]); + + React.useEffect(() => { + let isMounted = true; + treeRanksPromise + .then(() => { + const definitions = getTreeDefinitions(tableName); + const activeDefinition = definitions[0]; + /** + * Only expose ranks from the treedef tied to the current discipline. + * Otherwise ranks from unrelated trees leak into the dropdown. + */ + const ranks = activeDefinition?.ranks ?? []; + if (!isMounted) return; + setItems( + ranks + .map(({ rankId, name }) => ({ + rankId, + name, + })) + .sort((rankA, rankB) => rankA.rankId - rankB.rankId) + ); + }) + .catch((error: unknown) => { + console.error('Error fetching ThresholdRank items:', error); + if (isMounted) setItems([]); + }); + return () => { + isMounted = false; + }; + }, [tableName]); + + return ( + + ); +} diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/UserDefinitions.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/UserDefinitions.tsx index 722657bfe27..c40a1016154 100644 --- a/specifyweb/frontend/js_src/lib/components/Preferences/UserDefinitions.tsx +++ b/specifyweb/frontend/js_src/lib/components/Preferences/UserDefinitions.tsx @@ -50,6 +50,7 @@ import { defaultFont, FontFamilyPreferenceItem, HeaderItemsPreferenceItem, + ThresholdRank, WelcomePageModePreferenceItem, } from './Renderers'; import type { GenericPreferences, PreferencesVisibilityContext } from './types'; @@ -1395,7 +1396,7 @@ export const userPreferenceDefinitions = { title: preferencesText.sortByField(), requiresReload: false, visible: true, - defaultValue: 'rankId', + defaultValue: 'name', values: [ { value: 'name', @@ -1474,6 +1475,17 @@ export const userPreferenceDefinitions = { renderer: ColorPickerPreferenceItem, container: 'label', }), + rankThreshold: definePref({ + title: preferencesText.rankThreshold(), + description: preferencesText.rankThresholdDescription(), + requiresReload: true, + visible: true, + defaultValue: 0, + renderer: (props) => ( + + ), + container: 'label', + }), statsThreshold: definePref({ title: preferencesText.treeStatsThreshold(), description: preferencesText.treeStatsThresholdDescription(), @@ -1511,6 +1523,15 @@ export const userPreferenceDefinitions = { defaultValue: true, type: 'java.lang.Boolean', }), + rankThreshold: definePref({ + title: preferencesText.rankThreshold(), + description: preferencesText.rankThresholdDescription(), + requiresReload: true, + visible: true, + defaultValue: 0, + renderer: (props) => , + container: 'label', + }), statsThreshold: definePref({ title: preferencesText.treeStatsThreshold(), description: preferencesText.treeStatsThresholdDescription(), @@ -1541,6 +1562,17 @@ export const userPreferenceDefinitions = { renderer: ColorPickerPreferenceItem, container: 'label', }), + rankThreshold: definePref({ + title: preferencesText.rankThreshold(), + description: preferencesText.rankThresholdDescription(), + requiresReload: true, + visible: true, + defaultValue: 0, + renderer: (props) => ( + + ), + container: 'label', + }), statsThreshold: definePref({ title: preferencesText.treeStatsThreshold(), description: preferencesText.treeStatsThresholdDescription(), @@ -1571,6 +1603,17 @@ export const userPreferenceDefinitions = { renderer: ColorPickerPreferenceItem, container: 'label', }), + rankThreshold: definePref({ + title: preferencesText.rankThreshold(), + description: preferencesText.rankThresholdDescription(), + requiresReload: true, + visible: true, + defaultValue: 0, + renderer: (props) => ( + + ), + container: 'label', + }), statsThreshold: definePref({ title: preferencesText.treeStatsThreshold(), description: preferencesText.treeStatsThresholdDescription(), @@ -1601,6 +1644,17 @@ export const userPreferenceDefinitions = { renderer: ColorPickerPreferenceItem, container: 'label', }), + rankThreshold: definePref({ + title: preferencesText.rankThreshold(), + description: preferencesText.rankThresholdDescription(), + requiresReload: true, + visible: true, + defaultValue: 0, + renderer: (props) => ( + + ), + container: 'label', + }), statsThreshold: definePref({ title: preferencesText.treeStatsThreshold(), description: preferencesText.treeStatsThresholdDescription(), @@ -1631,6 +1685,17 @@ export const userPreferenceDefinitions = { renderer: ColorPickerPreferenceItem, container: 'label', }), + rankThreshold: definePref({ + title: preferencesText.rankThreshold(), + description: preferencesText.rankThresholdDescription(), + requiresReload: true, + visible: true, + defaultValue: 0, + renderer: (props) => ( + + ), + container: 'label', + }), statsThreshold: definePref({ title: preferencesText.treeStatsThreshold(), description: preferencesText.treeStatsThresholdDescription(), diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/__tests__/ThresholdRank.test.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/__tests__/ThresholdRank.test.tsx new file mode 100644 index 00000000000..5bc25699f33 --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/Preferences/__tests__/ThresholdRank.test.tsx @@ -0,0 +1,63 @@ +import { render, screen, waitFor } from '@testing-library/react'; +import React from 'react'; + +import { preferencesText } from '../../../localization/preferences'; +import { overrideAjax } from '../../../tests/ajax'; +import { requireContext } from '../../../tests/helpers'; +import * as treeRanks from '../../InitialContext/treeRanks'; +import { ThresholdRank } from '../Renderers'; +import type { PreferenceItem } from '../types'; + +overrideAjax('/context/schema_localization.json', {}); +requireContext(); + +const mockedGetTreeDefinitions = jest.spyOn(treeRanks, 'getTreeDefinitions'); + +describe('ThresholdRank', () => { + beforeEach(() => { + mockedGetTreeDefinitions.mockReset(); + }); + + test('only renders ranks from the active tree definition', async () => { + mockedGetTreeDefinitions.mockReturnValue([ + { + definition: { id: 1 } as any, + ranks: [ + { rankId: 50, name: 'Active Rank B' }, + { rankId: 10, name: 'Active Rank A' }, + ] as any, + }, + { + definition: { id: 2 } as any, + ranks: [{ rankId: 5, name: 'Inactive Rank' }] as any, + }, + ]); + + const definition: PreferenceItem = { + title: preferencesText.rankThreshold(), + requiresReload: false, + visible: true, + defaultValue: 0, + values: [], + }; + render( + + ); + + await waitFor(() => expect(screen.getAllByRole('option')).toHaveLength(3)); + + const labels = screen + .getAllByRole('option') + .map((option) => option.textContent); + expect(labels).toEqual(['None', 'Active Rank A', 'Active Rank B']); + expect(screen.queryByText('Inactive Rank')).not.toBeInTheDocument(); + }); +}); diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/index.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/index.tsx index 64faa2c0d8f..3d188776f0b 100644 --- a/specifyweb/frontend/js_src/lib/components/Preferences/index.tsx +++ b/specifyweb/frontend/js_src/lib/components/Preferences/index.tsx @@ -9,9 +9,11 @@ import type { LocalizedString } from 'typesafe-i18n'; import { usePromise } from '../../hooks/useAsyncState'; import { useBooleanState } from '../../hooks/useBooleanState'; import { commonText } from '../../localization/common'; +import { headerText } from '../../localization/header'; import { preferencesText } from '../../localization/preferences'; import { StringToJsx } from '../../localization/utils'; import { f } from '../../utils/functools'; +import type { IR } from '../../utils/types'; import { Container, H2, Key } from '../Atoms'; import { Button } from '../Atoms/Button'; import { className } from '../Atoms/className'; @@ -21,7 +23,13 @@ import { Submit } from '../Atoms/Submit'; import { LoadingContext, ReadOnlyContext } from '../Core/Contexts'; import { ErrorBoundary } from '../Errors/ErrorBoundary'; import { hasPermission } from '../Permissions/helpers'; +import { + ProtectedAction, + ProtectedTool, +} from '../Permissions/PermissionDenied'; import { PreferencesAside } from './Aside'; +import type { BasePreferences } from './BasePreferences'; +import { collectionPreferenceDefinitions } from './CollectionDefinitions'; import { collectionPreferences } from './collectionPreferences'; import { useDarkMode } from './Hooks'; import { DefaultPreferenceItemRender } from './Renderers'; @@ -30,6 +38,58 @@ import { userPreferenceDefinitions } from './UserDefinitions'; import { userPreferences } from './userPreferences'; import { useTopChild } from './useTopChild'; +export type PreferenceType = keyof typeof preferenceInstances; + +const preferenceInstances: IR> = { + user: userPreferences, + collection: collectionPreferences, +}; + +const preferenceDefinitions: IR = { + user: userPreferenceDefinitions, + collection: collectionPreferenceDefinitions, +}; + +type SubcategoryDocumentation = { + readonly href: string; + readonly label: LocalizedString | (() => LocalizedString); +}; + +const SUBCATEGORY_DOCS_MAP: Record< + string, + Record +> = { + treeManagement: { + synonymized: { + href: 'https://discourse.specifysoftware.org/t/enable-creating-children-for-synonymized-nodes/987', + label: headerText.documentation(), + }, + }, + statistics: { + appearance: { + href: 'https://discourse.specifysoftware.org/t/statistics-page/1135', + label: headerText.documentation(), + }, + }, +}; + +type DocumentHrefResolver = + | (( + category: string, + subcategory: string, + name: string + ) => string | undefined) + | undefined; + +const documentHrefResolvers: IR = { + user: undefined, + collection: undefined, +}; + +const collectionPreferencesPromise = Promise.all([ + collectionPreferences.fetch(), +]).then(f.true); + /** * Fetch app resource that stores current user preferences * @@ -42,20 +102,29 @@ const preferencesPromise = Promise.all([ collectionPreferences.fetch(), ]).then(f.true); -function Preferences(): JSX.Element { +function Preferences({ + prefType = 'user', +}: { + readonly prefType?: PreferenceType; +} = {}): JSX.Element { const [changesMade, handleChangesMade] = useBooleanState(); const [needsRestart, handleRestartNeeded] = useBooleanState(); const loading = React.useContext(LoadingContext); const navigate = useNavigate(); + const basePreferences = preferenceInstances[prefType]; + const heading = + prefType === 'collection' + ? preferencesText.collectionPreferences() + : preferencesText.preferences(); React.useEffect( () => - userPreferences.events.on('update', (payload) => { + basePreferences.events.on('update', (payload) => { if (payload?.definition?.requiresReload === true) handleRestartNeeded(); handleChangesMade(); }), - [handleChangesMade, handleRestartNeeded] + [basePreferences, handleChangesMade, handleRestartNeeded] ); const { @@ -68,12 +137,12 @@ function Preferences(): JSX.Element { return ( -

{preferencesText.preferences()}

+

{heading}

loading( - userPreferences + basePreferences .awaitSynced() .then(() => needsRestart @@ -89,10 +158,11 @@ function Preferences(): JSX.Element { > - +
@@ -110,20 +180,23 @@ function Preferences(): JSX.Element { } /** Hide invisible preferences. Remote empty categories and subCategories */ -export function usePrefDefinitions() { +export function usePrefDefinitions(prefType: PreferenceType = 'user') { const isDarkMode = useDarkMode(); const isRedirecting = React.useContext(userPreferences.Context) !== undefined; - const preferencesVisibilityContext = React.useMemo( - () => ({ - isDarkMode, - isRedirecting, - }), - [isDarkMode, isRedirecting] + + const visibilityContext = React.useMemo( + () => + prefType === 'user' + ? { isDarkMode, isRedirecting } + : { isDarkMode: false, isRedirecting: false }, + [prefType, isDarkMode, isRedirecting] ); + const definitions = preferenceDefinitions[prefType]; + return React.useMemo( () => - Object.entries(userPreferenceDefinitions as GenericPreferences) + Object.entries(definitions) .map( ([category, { subCategories, ...categoryData }]) => [ @@ -140,7 +213,7 @@ export function usePrefDefinitions() { items: Object.entries(items).filter( ([_name, { visible }]) => typeof visible === 'function' - ? visible(preferencesVisibilityContext) + ? visible(visibilityContext) : visible !== false ), }, @@ -151,157 +224,254 @@ export function usePrefDefinitions() { ] as const ) .filter(([_name, { subCategories }]) => subCategories.length > 0), - [preferencesVisibilityContext] + [definitions, visibilityContext] ); } export function PreferencesContent({ forwardRefs, + prefType = 'user', }: { readonly forwardRefs?: (index: number, element: HTMLElement | null) => void; + readonly prefType?: PreferenceType; }): JSX.Element { const isReadOnly = React.useContext(ReadOnlyContext); - const definitions = usePrefDefinitions(); + const definitions = usePrefDefinitions(prefType); + const basePreferences = preferenceInstances[prefType]; + const preferences = + React.useContext(basePreferences.Context) ?? basePreferences; + const resolveDocumentHref = documentHrefResolvers[prefType]; + const definitionsMap = React.useMemo( + () => new Map(definitions), + [definitions] + ); + + const renderSubCategory = React.useCallback( + ( + categoryKey: string, + subcategoryKey: string, + { + title, + description = undefined, + items, + }: { + readonly title: LocalizedString | (() => LocalizedString); + readonly description?: LocalizedString | (() => LocalizedString); + readonly items: readonly (readonly [string, PreferenceItem])[]; + }, + options: { readonly hideTitle?: boolean } = {} + ): JSX.Element => { + const subcategoryDocument = + SUBCATEGORY_DOCS_MAP[categoryKey]?.[subcategoryKey]; + const { hideTitle = false } = options; + + return ( +
+
+

+ {typeof title === 'function' ? title() : title} +

+
+ + items.forEach(([name]) => { + const definition = preferences.definition( + categoryKey as never, + subcategoryKey as never, + name as never + ); + preferences.set( + categoryKey as never, + subcategoryKey as never, + name as never, + definition.defaultValue as never + ); + }) + } + > + {commonText.reset()} + +
+
+ {subcategoryDocument !== undefined && ( +

+ + + +

+ )} + {description !== undefined && ( +

+ {typeof description === 'function' ? description() : description} +

+ )} + {items.map(([name, item]) => { + const canEdit = + !isReadOnly && + (item.visible !== 'protected' || + hasPermission('/preferences/user', 'edit_protected')); + const documentHref = resolveDocumentHref?.( + categoryKey, + subcategoryKey, + name + ); + const stackDocumentation = + prefType === 'collection' && documentHref !== undefined; + const props = { + className: ` + flex items-start gap-2 md:flex-row flex-col + ${canEdit ? '' : '!cursor-not-allowed'} + `, + key: name, + title: canEdit + ? undefined + : preferencesText.adminsOnlyPreference(), + }; + const children = ( + <> +
+

+ +

+ {(item.description !== undefined || + documentHref !== undefined) && ( +

+ {item.description !== undefined && ( + + )} + {documentHref !== undefined && ( + + {headerText.documentation()} + + )} +

+ )} +
+
+ + + +
+ + ); + return 'container' in item && item.container === 'div' ? ( +
{children}
+ ) : ( + + ); + })} +
+ ); + }, + [isReadOnly, prefType, preferences, resolveDocumentHref] + ); + return (
{definitions.map( ( [category, { title, description = undefined, subCategories }], index - ) => ( - - -

- {typeof title === 'function' ? title() : title} -

- {description !== undefined && ( -

- {typeof description === 'function' - ? description() - : description} -

- )} - {subCategories.map( - ([subcategory, { title, description = undefined, items }]) => ( -
-
-

- {typeof title === 'function' ? title() : title} -

-
- - items.forEach(([name]) => { - userPreferences.set( - category as 'general', - subcategory as 'ui', - name as 'theme', - /* - * Need to get default value via this - * function as defaults may be changed - */ - userPreferences.definition( - category as 'general', - subcategory as 'ui', - name as 'theme' - ).defaultValue - ); - }) - } - > - {commonText.reset()} - -
-
- {description !== undefined && ( -

- {typeof description === 'function' - ? description() - : description} -

- )} - {items.map(([name, item]) => { - const canEdit = - !isReadOnly && - (item.visible !== 'protected' || - hasPermission('/preferences/user', 'edit_protected')); - const props = { - className: ` - flex items-start gap-2 md:flex-row flex-col - ${canEdit ? '' : '!cursor-not-allowed'} - `, - key: name, - title: canEdit - ? undefined - : preferencesText.adminsOnlyPreference(), - }; - const children = ( - <> -
-

- -

- {item.description !== undefined && ( -

- -

- )} -
-
- - - -
- - ); - return 'container' in item && item.container === 'div' ? ( -
{children}
- ) : ( - - ); - })} -
- ) - )} -
-
- ) + ) => { + if ( + prefType === 'collection' && + category === 'catalogNumberParentInheritance' + ) + return null; + + const isCatalogInheritance = + prefType === 'collection' && + category === 'catalogNumberInheritance'; + const parentDefinition = isCatalogInheritance + ? (definitionsMap.get('catalogNumberParentInheritance') ?? + undefined) + : undefined; + + return ( + + +

+ {typeof title === 'function' ? title() : title} +

+ {description !== undefined && ( +

+ {typeof description === 'function' + ? description() + : description} +

+ )} + {subCategories.map(([subcategory, data]) => + renderSubCategory(category, subcategory, data) + )} + {isCatalogInheritance && + parentDefinition?.subCategories.map(([subcategory, data]) => + renderSubCategory( + 'catalogNumberParentInheritance', + subcategory, + data + ) + )} +
+
+ ); + } )}
); @@ -333,19 +503,20 @@ function Item({ category, subcategory, name, + preferences, }: { readonly item: PreferenceItem; readonly category: string; readonly subcategory: string; readonly name: string; + readonly preferences: BasePreferences; }): JSX.Element { const Renderer = 'renderer' in item ? item.renderer : DefaultPreferenceItemRender; - const [value, setValue] = userPreferences.use( - // Asserting types just to simplify typing - category as 'general', - subcategory as 'ui', - name as 'theme' + const [value, setValue] = preferences.use( + category as never, + subcategory as never, + name as never ); const children = ( + + + + + ); +} + +function FetchGate({ + promise, + children, +}: { + readonly promise: Promise; + readonly children?: React.ReactNode; +}): JSX.Element | null { + const [hasFetched] = usePromise(promise, true); + return hasFetched ? <>{children} : null; +} + export function PreferencesWrapper(): JSX.Element | null { - const [hasFetched] = usePromise(preferencesPromise, true); - return hasFetched === true ? : null; + return ( + + + + ); +} + +export function CollectionPreferencesWrapper(): JSX.Element | null { + return ( + + + + ); } diff --git a/specifyweb/frontend/js_src/lib/components/Router/Routes.tsx b/specifyweb/frontend/js_src/lib/components/Router/Routes.tsx index e932a03d020..5ddcf520692 100644 --- a/specifyweb/frontend/js_src/lib/components/Router/Routes.tsx +++ b/specifyweb/frontend/js_src/lib/components/Router/Routes.tsx @@ -376,6 +376,14 @@ export const routes: RA = [ ({ PreferencesWrapper }) => PreferencesWrapper ), }, + { + path: 'collection-preferences', + title: preferencesText.collectionPreferences(), + element: () => + import('../Preferences').then( + ({ CollectionPreferencesWrapper }) => CollectionPreferencesWrapper + ), + }, { path: 'schema-config', title: schemaText.schemaConfig(), diff --git a/specifyweb/frontend/js_src/lib/components/Security/registry.ts b/specifyweb/frontend/js_src/lib/components/Security/registry.ts index 5c9f7ca2417..c3b94093b5b 100644 --- a/specifyweb/frontend/js_src/lib/components/Security/registry.ts +++ b/specifyweb/frontend/js_src/lib/components/Security/registry.ts @@ -1,6 +1,7 @@ import type { LocalizedString } from 'typesafe-i18n'; import { commonText } from '../../localization/common'; +import { preferencesText } from '../../localization/preferences'; import { queryText } from '../../localization/query'; import { resourcesText } from '../../localization/resources'; import { schemaText } from '../../localization/schema'; @@ -65,6 +66,13 @@ export const isUncommonPermissionTable = ({ /** Build a registry of all permissions, their labels and possible actions */ const buildRegistry = f.store((): IR => { + const frontEndLocalizedOverrides: Record> = { + '/preferences/collection': [ + preferencesText.preferences(), + preferencesText.collectionPreferences(), + ], + }; + const rules: RA<{ readonly resource: string; readonly localized: RA; @@ -95,7 +103,9 @@ const buildRegistry = f.store((): IR => { })), ...Object.entries(frontEndPermissions).map(([resource, actions]) => ({ resource, - localized: resourceNameToParts(resource).map(lowerToHuman), + localized: + frontEndLocalizedOverrides[resource] ?? + resourceNameToParts(resource).map(lowerToHuman), actions, groupName: localized(''), })), diff --git a/specifyweb/frontend/js_src/lib/components/Security/utils.ts b/specifyweb/frontend/js_src/lib/components/Security/utils.ts index 2d0149e7f8f..c328c417e97 100644 --- a/specifyweb/frontend/js_src/lib/components/Security/utils.ts +++ b/specifyweb/frontend/js_src/lib/components/Security/utils.ts @@ -1,5 +1,6 @@ import type { LocalizedString } from 'typesafe-i18n'; +import { commonText } from '../../localization/common'; import { userText } from '../../localization/user'; import { ajax } from '../../utils/ajax'; import { Http } from '../../utils/ajax/definitions'; @@ -141,7 +142,11 @@ export function getCollectionRegistriesFromPath(resourceParts: RA) { * Localize action name */ export const actionToLabel = (action: string): LocalizedString => - action === anyAction ? userText.allActions() : lowerToHuman(action); + action === anyAction + ? userText.allActions() + : action === 'edit_collection' + ? commonText.edit() + : lowerToHuman(action); export const toolPermissionPrefix = 'tools'; export const anyAction = '%'; diff --git a/specifyweb/frontend/js_src/lib/components/TreeView/Actions.tsx b/specifyweb/frontend/js_src/lib/components/TreeView/Actions.tsx index 984a52bdfe1..400aebfcf3d 100644 --- a/specifyweb/frontend/js_src/lib/components/TreeView/Actions.tsx +++ b/specifyweb/frontend/js_src/lib/components/TreeView/Actions.tsx @@ -16,8 +16,8 @@ import type { AnySchema, AnyTree } from '../DataModel/helperTypes'; import type { SpecifyResource } from '../DataModel/legacyTypes'; import type { SpecifyTable } from '../DataModel/specifyTable'; import { genericTables } from '../DataModel/tables'; +import { getSynonymPreferenceForTree } from '../DataModel/treeBusinessRules'; import { DeleteButton } from '../Forms/DeleteButton'; -import { getPref } from '../InitialContext/remotePrefs'; import { Dialog } from '../Molecules/Dialog'; import { ResourceLink } from '../Molecules/ResourceLink'; import { hasPermission, hasTablePermission } from '../Permissions/helpers'; @@ -69,9 +69,38 @@ export function TreeViewActions({ const resourceName = `/tree/edit/${toLowerCase(tableName)}` as const; const isSynonym = typeof focusedRow?.acceptedId === 'number'; - const doExpandSynonymActionsPref = getPref( - `sp7.allow_adding_child_to_synonymized_parent.${tableName}` - ); + const [doExpandSynonymActionsPref, setDoExpandSynonymActionsPref] = + React.useState(false); + + React.useEffect(() => { + let isMounted = true; + + const update = (): void => { + getSynonymPreferenceForTree(tableName) + .then((value) => { + if (isMounted) setDoExpandSynonymActionsPref(value); + }) + .catch(() => { + if (isMounted) setDoExpandSynonymActionsPref(false); + }); + }; + + update(); + + let unsubscribe: (() => void) | undefined; + + import('../Preferences/collectionPreferences') + .then(({ collectionPreferences }) => { + if (!isMounted) return; + unsubscribe = collectionPreferences.events.on('update', update); + }) + .catch(() => undefined); + + return () => { + isMounted = false; + unsubscribe?.(); + }; + }, [tableName]); const disableButtons = focusedRow === undefined || typeof currentAction === 'string'; diff --git a/specifyweb/frontend/js_src/lib/components/TreeView/Tree.tsx b/specifyweb/frontend/js_src/lib/components/TreeView/Tree.tsx index 6ebf98e5ab4..f0c8d6d9f6a 100644 --- a/specifyweb/frontend/js_src/lib/components/TreeView/Tree.tsx +++ b/specifyweb/frontend/js_src/lib/components/TreeView/Tree.tsx @@ -102,6 +102,7 @@ export function Tree< treeToPref[tableName], 'statsThreshold' ); + const getStats = React.useCallback( async (nodeId: number | 'null', rankId: number): Promise => rankId >= statsThreshold diff --git a/specifyweb/frontend/js_src/lib/localization/attachments.ts b/specifyweb/frontend/js_src/lib/localization/attachments.ts index aa5078d7ec4..c36797baaeb 100644 --- a/specifyweb/frontend/js_src/lib/localization/attachments.ts +++ b/specifyweb/frontend/js_src/lib/localization/attachments.ts @@ -733,11 +733,18 @@ export const attachmentsText = createDictionary({ }, attachmentDelition: { 'en-us': 'Attachment deletion', - 'de-ch': 'Anhang löschen', + 'de-ch': 'Löschen von Anhängen', 'es-es': 'Eliminación de archivos adjuntos', - 'fr-fr': 'Suppression de la pièce jointe', + 'fr-fr': 'Suppression des pièces jointes', 'pt-br': 'Exclusão de anexos', 'ru-ru': 'Удаление вложения', 'uk-ua': 'Видалення вкладень', }, + publicDefault: { + 'en-us': 'Make Attachments Public by Default', + }, + publicDefaultDescription: { + 'en-us': + 'This controls whether or not new attachments added to this collection are flagged as "Public" by default. Public attachments will automatically be visible on a Specify Web Portal. This setting can be overridden on a per-attachment basis and does not affect existing attachments.', + }, } as const); diff --git a/specifyweb/frontend/js_src/lib/localization/forms.ts b/specifyweb/frontend/js_src/lib/localization/forms.ts index 22496694fe2..e311085c5bd 100644 --- a/specifyweb/frontend/js_src/lib/localization/forms.ts +++ b/specifyweb/frontend/js_src/lib/localization/forms.ts @@ -836,7 +836,7 @@ export const formsText = createDictionary({ 'es-es': 'Llevar adelante', 'fr-fr': 'Reporter', 'uk-ua': 'Перенести далі', - 'de-ch': 'Übertrag', + 'de-ch': 'Weitertragen', 'pt-br': 'Levar adiante', }, carryForwardEnabled: { diff --git a/specifyweb/frontend/js_src/lib/localization/preferences.behavior.ts b/specifyweb/frontend/js_src/lib/localization/preferences.behavior.ts new file mode 100644 index 00000000000..0e0aef8ea43 --- /dev/null +++ b/specifyweb/frontend/js_src/lib/localization/preferences.behavior.ts @@ -0,0 +1,1171 @@ +/** + * Localization strings for behavioral preferences. + * + * @module + */ +import { createDictionary } from './utils'; +// Refer to "Guidelines for Programmers" in ./README.md before editing this file +export const preferencesBehaviorDictionary = { + alwaysPrompt: { + 'en-us': 'Always prompt to choose collection', + 'ru-ru': 'Всегда предлагайте выбрать коллекцию', + 'es-es': 'Siempre dispuesto a elegir la colección', + 'fr-fr': 'Toujours invité à choisir la collection', + 'uk-ua': 'Завжди підкажуть вибрати колекцію', + 'de-ch': 'Immer zur Auswahl der Sammlung auffordern', + 'pt-br': 'Sempre pronto para escolher a coleção', + }, + showNewDataSetWarning: { + 'en-us': 'Show new Data Set warning', + 'ru-ru': 'Показать предупреждение о новом наборе данных', + 'es-es': 'Mostrar nueva advertencia de conjunto de datos', + 'fr-fr': "Afficher un nouvel avertissement sur l'ensemble de données", + 'uk-ua': 'Показати попередження про новий набір даних', + 'de-ch': 'Warnung für neuen Datensatz anzeigen', + 'pt-br': 'Mostrar novo aviso de conjunto de dados', + }, + showNewDataSetWarningDescription: { + 'en-us': 'Show an informational message when creating a new Data Set.', + 'ru-ru': + 'Показывать информационное сообщение при создании нового набора данных.', + 'es-es': + 'Mostrar un mensaje informativo al crear un nuevo conjunto de datos.', + 'fr-fr': + "Afficher un message d'information lors de la création d'un nouvel ensemble de données.", + 'uk-ua': + 'Показувати інформаційне повідомлення під час створення нового набору даних.', + 'de-ch': 'Zeige eine Meldung beim erstellen eines neuen Datensatzes an.', + 'pt-br': + 'Exibir uma mensagem informativa ao criar um novo conjunto de dados.', + }, + allowDismissingErrors: { + 'en-us': 'Allow dismissing error messages', + 'ru-ru': 'Разрешить отклонять сообщения об ошибках', + 'es-es': 'Permitir descartar mensajes de error', + 'fr-fr': "Autoriser le rejet des messages d'erreur", + 'uk-ua': 'Дозволити закривати повідомлення про помилки', + 'de-ch': 'Erlaube das Verwerfen von Fehlermeldungen', + 'pt-br': 'Permitir descartar mensagens de erro', + }, + updatePageTitle: { + 'en-us': 'Update page title', + 'ru-ru': 'Обновить заголовок страницы', + 'es-es': 'Actualizar el título de la página', + 'fr-fr': 'Mettre à jour le titre de la page', + 'uk-ua': 'Оновити назву сторінки', + 'de-ch': 'Seitentitel aktualisieren', + 'pt-br': 'Atualizar título da página', + }, + updatePageTitleDescription: { + 'en-us': + "Whether to update the title of the page to match dialog's header.", + 'ru-ru': + 'Обновлять ли заголовок страницы в соответствии с заголовком диалогового окна.', + 'es-es': + 'Si se debe actualizar el título de la página para que coincida con el encabezado del cuadro de diálogo.', + 'fr-fr': + "S'il faut mettre à jour le titre de la page pour qu'il corresponde à l'en-tête de la boîte de dialogue.", + 'uk-ua': + 'Чи оновлювати назву сторінки відповідно до заголовка діалогового вікна.', + 'de-ch': + 'Titel der Seite so aktualisieren, dass er mit der Kopfzeile des Dialogs übereinstimmt.', + 'pt-br': + 'Se o título da página deve ser atualizado para corresponder ao cabeçalho da caixa de diálogo.', + }, + updatePageTitleFormDescription: { + 'en-us': 'Whether to update the title of the page to match current record.', + 'ru-ru': + 'Следует ли обновить заголовок страницы в соответствии с текущей записью.', + 'es-es': + 'Si desea actualizar el título de la página para que coincida con el registro actual.', + 'fr-fr': + "S'il faut mettre à jour le titre de la page pour qu'il corresponde à l'enregistrement actuel.", + 'uk-ua': 'Чи оновлювати назву сторінки відповідно до поточного запису.', + 'de-ch': + 'Titel der Seite aktualisieren, damit er mit dem aktuellen Datensatz übereinstimmt.', + 'pt-br': + 'Se o título da página deve ser atualizado para corresponder ao registro atual.', + }, + queryComboBox: { + 'en-us': 'Query Combo Box', + 'ru-ru': 'Поле со списком запросов', + 'es-es': 'Cuadro combinado de consulta', + 'uk-ua': 'Поле зі списком запитів', + 'de-ch': 'Abfrage-Kombinationsfeld', + 'fr-fr': 'Zone de liste déroulante de requête', + 'pt-br': 'Caixa de combinação de consulta', + }, + searchAlgorithm: { + 'en-us': 'Search Algorithm', + 'ru-ru': 'Алгоритм поиска', + 'es-es': 'Algoritmo de búsqueda', + 'fr-fr': 'Algorithme de recherche', + 'uk-ua': 'Алгоритм пошуку', + 'de-ch': 'Suchalgorithmus', + 'pt-br': 'Algoritmo de Busca', + }, + treeSearchAlgorithm: { + 'en-us': 'Search Algorithm (for relationships with tree tables)', + 'ru-ru': 'Алгоритм поиска (для связей с древовидными таблицами)', + 'es-es': 'Algoritmo de búsqueda (para relaciones con tablas de árboles)', + 'fr-fr': + 'Algorithme de recherche (pour les relations avec les tables arborescentes)', + 'uk-ua': 'Алгоритм пошуку (для зв’язків із деревоподібними таблицями)', + 'de-ch': 'Suchalgorithmus (für Beziehungen mit Baumtabellen)', + 'pt-br': 'Algoritmo de busca (para relacionamentos com tabelas de árvore)', + }, + startsWithInsensitive: { + 'en-us': 'Starts With (case-insensitive)', + 'ru-ru': 'Начинается с (без учета регистра)', + 'es-es': 'Comienza con (sin distinguir entre mayúsculas y minúsculas)', + 'fr-fr': 'Commence par (insensible à la casse)', + 'uk-ua': 'Починається з (без урахування регістру)', + 'de-ch': 'Beginnt mit (Groß-/Kleinschreibung wird nicht beachtet)', + 'pt-br': 'Começa com (sem distinção entre maiúsculas e minúsculas)', + }, + startsWithDescription: { + 'en-us': 'Search for values that begin with a given query string.', + 'ru-ru': 'Поиск значений, начинающихся с заданной строки запроса.', + 'es-es': + 'Busque valores que comiencen con una cadena de consulta determinada.', + 'fr-fr': + 'Rechercher des valeurs commençant par une chaîne de requête donnée.', + 'uk-ua': 'Пошук значень, які починаються з заданого рядка запиту.', + 'de-ch': + 'Suchen Sie nach Werten, die mit einer bestimmten Abfragezeichenfolge beginnen.', + 'pt-br': + 'Pesquise valores que começam com uma determinada sequência de consulta.', + }, + startsWithCaseSensitive: { + 'en-us': 'Starts With (case-sensitive)', + 'ru-ru': 'Начинается с (с учетом регистра)', + 'es-es': 'Comienza con (sensible a mayúsculas y minúsculas)', + 'fr-fr': 'Commence par (sensible à la casse)', + 'uk-ua': 'Починається з (з урахуванням регістру)', + 'de-ch': 'Beginnt mit (Groß-/Kleinschreibung beachten)', + 'pt-br': 'Começa com (diferencia maiúsculas de minúsculas)', + }, + startsWithCaseSensitiveDescription: { + 'en-us': 'Search for values that begin with a given query string.', + 'ru-ru': 'Поиск значений, начинающихся с заданной строки запроса.', + 'es-es': + 'Busque valores que comiencen con una cadena de consulta determinada.', + 'fr-fr': + 'Recherchez les valeurs qui commencent par une chaîne de requête donnée.', + 'uk-ua': 'Пошук значень, які починаються з заданого рядка запиту.', + 'de-ch': + 'Suchen Sie nach Werten, die mit einer bestimmten Abfragezeichenfolge beginnen.', + 'pt-br': + 'Pesquise valores que começam com uma determinada sequência de consulta.', + }, + containsInsensitive: { + 'en-us': 'Contains (case-insensitive)', + 'ru-ru': 'Содержит (без учета регистра)', + 'es-es': 'Contiene (sin distinguir entre mayúsculas y minúsculas)', + 'fr-fr': 'Contient (insensible à la casse)', + 'uk-ua': 'Містить (незалежно від регістру)', + 'de-ch': 'Enthält (Groß-/Kleinschreibung wird nicht beachtet)', + 'pt-br': 'Contém (sem distinção entre maiúsculas e minúsculas)', + }, + containsCaseSensitive: { + 'en-us': 'Contains (case-sensitive)', + 'ru-ru': 'Содержит (с учетом регистра)', + 'es-es': 'Contiene (sensible a mayúsculas y minúsculas)', + 'fr-fr': 'Contient (sensible à la casse)', + 'uk-ua': 'Містить (з урахуванням регістру)', + 'de-ch': 'Enthält (Groß-/Kleinschreibung beachten)', + 'pt-br': 'Contém (diferencia maiúsculas de minúsculas)', + }, + containsDescription: { + 'en-us': + 'Search for values that contain a given query string (case-insensitive).', + 'ru-ru': + 'Поиск значений, содержащих заданную строку запроса (без учета регистра).', + 'es-es': + 'Busque valores que contengan una cadena de consulta determinada (sin distinguir entre mayúsculas y minúsculas).', + 'uk-ua': + 'Пошук значень, які містять заданий рядок запиту (незалежно від регістру).', + 'de-ch': + 'Suchen Sie nach Werten, die eine bestimmte Abfragezeichenfolge enthalten (ohne Berücksichtigung der Groß-/Kleinschreibung).', + 'fr-fr': + 'Recherchez les valeurs contenant une chaîne de requête donnée (insensible à la casse).', + 'pt-br': + 'Pesquisar valores que contenham uma determinada sequência de consulta (sem distinção de maiúsculas e minúsculas).', + }, + containsCaseSensitiveDescription: { + 'en-us': + 'Search for values that contain a given query string (case-sensitive).', + 'ru-ru': + 'Поиск значений, содержащих заданную строку запроса (с учетом регистра).', + 'es-es': + 'Busque valores que contengan una cadena de consulta determinada (distingue entre mayúsculas y minúsculas).', + 'fr-fr': + 'Recherchez les valeurs contenant une chaîne de requête donnée (sensible à la casse).', + 'uk-ua': + 'Пошук значень, які містять заданий рядок запиту (з урахуванням регістру).', + 'de-ch': + 'Suchen Sie nach Werten, die eine bestimmte Abfragezeichenfolge enthalten (Groß-/Kleinschreibung beachten).', + 'pt-br': + 'Pesquisar valores que contenham uma determinada sequência de consulta (diferencia maiúsculas de minúsculas).', + }, + containsSecondDescription: { + 'en-us': + 'Can use _ to match any single character or % to match any number of characters.', + 'ru-ru': + 'Можно использовать _ для соответствия любому отдельному символу или % для соответствия любому количеству символов.', + 'es-es': + 'Puede utilizar _ para que coincida con cualquier carácter individual o % para que coincida con cualquier número de caracteres.', + 'fr-fr': + "Peut utiliser _ pour correspondre à n'importe quel caractère ou % pour correspondre à n'importe quel nombre de caractères.", + 'uk-ua': + 'Можна використовувати _ для відповідності будь-якому одному символу або % для відповідності будь-якій кількості символів.', + 'de-ch': + 'Sie können _ verwenden, um ein beliebiges einzelnes Zeichen abzugleichen, oder %, um eine beliebige Anzahl von Zeichen abzugleichen.', + 'pt-br': + 'Pode usar _ para corresponder a qualquer caractere único ou % para corresponder a qualquer número de caracteres.', + }, + highlightMatch: { + 'en-us': 'Highlight matched substring', + 'ru-ru': 'Выделить совпавшую подстроку', + 'es-es': 'Resaltar la subcadena coincidente', + 'fr-fr': 'Mettre en surbrillance la sous-chaîne correspondante', + 'uk-ua': 'Виділіть збіг підрядка', + 'de-ch': 'Markieren Sie übereinstimmende Teilzeichenfolgen', + 'pt-br': 'Destacar substring correspondente', + }, + languageDescription: { + 'en-us': 'Determines field captions, usage notes and table captions.', + 'ru-ru': + 'Определяет заголовки полей, примечания по использованию и заголовки таблиц.', + 'es-es': 'Determina títulos de campos, notas de uso y títulos de tablas.', + 'fr-fr': + "Détermine les légendes des champs, les notes d'utilisation et les légendes des tableaux.", + 'uk-ua': + 'Визначає підписи полів, примітки щодо використання та підписи таблиць.', + 'de-ch': + 'Legt Feldbeschriftungen, Verwendungshinweise und Tabellenbeschriftungen fest.', + 'pt-br': 'Determina legendas de campo, notas de uso e legendas de tabela.', + }, + behavior: { + 'en-us': 'Behavior', + 'ru-ru': 'Поведение', + 'es-es': 'Comportamiento', + 'fr-fr': 'Comportement', + 'uk-ua': 'Поведінка', + 'de-ch': 'Verhalten', + 'pt-br': 'Comportamento', + }, + noRestrictionsMode: { + 'en-us': 'No restrictions mode', + 'ru-ru': 'Режим без ограничений', + 'es-es': 'Modo sin restricciones', + 'fr-fr': 'Mode sans restriction', + 'uk-ua': 'Режим без обмежень', + 'de-ch': 'Modus „Keine Einschränkungen“', + 'pt-br': 'Modo sem restrições', + }, + noRestrictionsModeWbDescription: { + 'en-us': 'Allows uploading data to any field in any table.', + 'ru-ru': 'Позволяет загружать данные в любое поле любой таблицы.', + 'es-es': 'Permite cargar datos a cualquier campo de cualquier tabla.', + 'fr-fr': + "Permet de télécharger des données dans n'importe quel champ de n'importe quelle table.", + 'uk-ua': 'Дозволяє завантажувати дані в будь-яке поле будь-якої таблиці.', + 'de-ch': + 'Ermöglicht das Hochladen von Daten in jedes Feld einer beliebigen Tabelle.', + 'pt-br': 'Permite carregar dados em qualquer campo de qualquer tabela.', + }, + noRestrictionsModeQueryDescription: { + 'en-us': 'Allows querying data from any field in any table.', + 'ru-ru': 'Позволяет запрашивать данные из любого поля любой таблицы.', + 'es-es': 'Permite consultar datos de cualquier campo de cualquier tabla.', + 'fr-fr': + "Permet d'interroger les données de n'importe quel champ de n'importe quelle table.", + 'uk-ua': 'Дозволяє запитувати дані з будь-якого поля будь-якої таблиці.', + 'de-ch': + 'Ermöglicht das Abfragen von Daten aus jedem Feld in jeder Tabelle.', + 'pt-br': 'Permite consultar dados de qualquer campo em qualquer tabela.', + }, + noRestrictionsModeWarning: { + 'en-us': + 'WARNING: enabling this may lead to data loss or database corruption. Please make sure you know what you are doing.', + 'ru-ru': + 'ВНИМАНИЕ: включение этой функции может привести к потере данных или повреждению базы данных. Убедитесь, что вы понимаете, что делаете.', + 'es-es': + 'ADVERTENCIA: Habilitar esta opción podría provocar la pérdida de datos o la corrupción de la base de datos. Asegúrese de saber lo que está haciendo.', + 'uk-ua': + 'ПОПЕРЕДЖЕННЯ: увімкнення цієї функції може призвести до втрати даних або пошкодження бази даних. Переконайтеся, що ви знаєте, що робите.', + 'de-ch': + 'WARNUNG: Das Aktivieren dieser Option kann zu Datenverlust oder Datenbankbeschädigung führen. Bitte stellen Sie sicher, dass Sie wissen, was Sie tun.', + 'fr-fr': + "AVERTISSEMENT : l'activation de cette option peut entraîner une perte de données ou une corruption de la base de données. Veuillez vous assurer que vous savez ce que vous faites.", + 'pt-br': + 'AVISO: habilitar esta opção pode levar à perda de dados ou à corrupção do banco de dados. Certifique-se de saber o que está fazendo.', + }, + adminsOnlyPreference: { + 'en-us': "You don't have permission to change this option", + 'ru-ru': 'У вас нет разрешения на изменение этой опции.', + 'es-es': 'No tienes permiso para cambiar esta opción', + 'fr-fr': "Vous n'êtes pas autorisé à modifier cette option", + 'uk-ua': 'Ви не маєте дозволу змінювати цей параметр', + 'de-ch': 'Sie haben keine Berechtigung, diese Option zu ändern', + 'pt-br': 'Você não tem permissão para alterar esta opção', + }, + stickyScrolling: { + 'en-us': 'Sticky scroll bar', + 'ru-ru': 'Липкая полоса прокрутки', + 'es-es': 'Barra de desplazamiento fija', + 'fr-fr': 'Barre de défilement collante', + 'uk-ua': 'Липка смуга прокрутки', + 'de-ch': 'Klebrige Bildlaufleiste', + 'pt-br': 'Barra de rolagem fixa', + }, + spreadsheet: { + 'en-us': 'Spreadsheet', + 'ru-ru': 'Электронная таблица', + 'es-es': 'Hoja de cálculo', + 'fr-fr': 'Tableur', + 'uk-ua': 'Електронна таблиця', + 'de-ch': 'Kalkulationstabelle', + 'pt-br': 'Planilha', + }, + minSpareRows: { + 'en-us': 'Number of blank rows at the end', + 'ru-ru': 'Количество пустых строк в конце', + 'es-es': 'Número de filas en blanco al final', + 'fr-fr': 'Nombre de lignes vides à la fin', + 'uk-ua': 'Кількість порожніх рядків у кінці', + 'de-ch': 'Anzahl der leeren Zeilen am Ende', + 'pt-br': 'Número de linhas em branco no final', + }, + autoWrapCols: { + 'en-us': 'Navigate to the other side when reaching the edge column', + 'ru-ru': 'Достигнув крайней колонны, перейдите на другую сторону.', + 'es-es': 'Navegue hacia el otro lado al llegar a la columna del borde.', + 'fr-fr': + 'Naviguez de l’autre côté lorsque vous atteignez la colonne de bord', + 'uk-ua': 'Перейдіть на іншу сторону, коли досягнете краю колонки', + 'de-ch': + 'Navigieren Sie zur anderen Seite, wenn Sie die Randspalte erreichen', + 'pt-br': 'Navegue para o outro lado ao atingir a coluna da borda', + }, + autoWrapRows: { + 'en-us': 'Navigate to the other side when reaching the edge row', + 'ru-ru': 'Достигнув крайнего ряда, перейдите на другую сторону.', + 'es-es': 'Navegue hacia el otro lado al llegar a la fila del borde.', + 'fr-fr': + 'Naviguez de l’autre côté lorsque vous atteignez la rangée de bord', + 'uk-ua': 'Перейдіть на іншу сторону, коли досягнете крайнього ряду', + 'de-ch': + 'Navigieren Sie zur anderen Seite, wenn Sie die Randreihe erreichen', + 'pt-br': 'Navegue para o outro lado ao atingir a fileira de bordas', + }, + enterBeginsEditing: { + 'en-us': 'Enter key begins editing cell', + 'ru-ru': 'Клавиша Enter начинает редактирование ячейки.', + 'es-es': 'La tecla Enter inicia la edición de la celda', + 'fr-fr': 'La touche Entrée commence à modifier la cellule', + 'uk-ua': 'Клавіша Enter починає редагування клітинки', + 'de-ch': 'Mit der Eingabetaste beginnt die Bearbeitung der Zelle', + 'pt-br': 'A tecla Enter inicia a edição da célula', + }, + tabMoveDirection: { + 'en-us': 'Direction of movement when Tab key is pressed', + 'ru-ru': 'Направление движения при нажатии клавиши Tab', + 'es-es': + 'Dirección de movimiento cuando se presiona la tecla Tab', + 'fr-fr': + 'Sens de déplacement lorsque la touche Tabulation est enfoncée', + 'uk-ua': 'Напрямок руху при натисканні клавіші Tab', + 'de-ch': 'Bewegungsrichtung beim Drücken der Tab-Taste', + 'pt-br': 'Direção do movimento quando a tecla Tab é pressionada', + }, + tabMoveDirectionDescription: { + 'en-us': + 'You can move in the opposite direction by pressing Shift+Tab.', + 'ru-ru': + 'Вы можете двигаться в обратном направлении, нажав Shift+Tab.', + 'es-es': + 'Puedes moverte en la dirección opuesta presionando Shift+Tab.', + 'fr-fr': + 'Vous pouvez vous déplacer dans la direction opposée en appuyant sur Shift+Tab.', + 'uk-ua': + 'Ви можете рухатися в протилежному напрямку, натискаючи Shift+Tab.', + 'de-ch': + 'Sie können sich in die entgegengesetzte Richtung bewegen, indem Sie Umschalt+Tab drücken.', + 'pt-br': + 'Você pode mover na direção oposta pressionando Shift+Tab.', + }, + column: { + 'en-us': 'Column', + 'ru-ru': 'Столбец', + 'es-es': 'Columna', + 'fr-fr': 'Colonne', + 'uk-ua': 'Колонка', + 'de-ch': 'Spalte', + 'pt-br': 'Coluna', + }, + row: { + 'en-us': 'Row', + 'ru-ru': 'Ряд', + 'es-es': 'Fila', + 'fr-fr': 'Rangée', + 'uk-ua': 'рядок', + 'de-ch': 'Reihe', + 'pt-br': 'Linha', + }, + enterMoveDirection: { + 'en-us': 'Direction of movement when Enter key is pressed', + 'ru-ru': 'Направление движения при нажатии клавиши Enter', + 'es-es': + 'Dirección de movimiento cuando se presiona la tecla Enter', + 'uk-ua': 'Напрямок руху, коли натиснуто клавішу Enter', + 'de-ch': 'Bewegungsrichtung beim Drücken der Taste Enter', + 'fr-fr': + 'Direction du mouvement lorsque la touche Entrer est enfoncée', + 'pt-br': + 'Direção do movimento quando a tecla Enter é pressionada', + }, + enterMoveDirectionDescription: { + 'en-us': + 'You can move in the opposite direction by pressing Shift+Enter.', + 'ru-ru': + 'Вы можете двигаться в противоположном направлении, нажав Shift+Enter.', + 'es-es': + 'Puedes moverte en la dirección opuesta presionando Shift+Enter.', + 'fr-fr': 'Synonyme couleur.', + 'uk-ua': + 'Ви можете рухатися у протилежному напрямку, натискаючи Shift+Enter.', + 'de-ch': + 'Sie können sich in die entgegengesetzte Richtung bewegen, indem Sie Umschalt+Eingabe drücken.', + 'pt-br': + 'Você pode mover na direção oposta pressionando Shift+Enter.', + }, + filterPickLists: { + 'en-us': 'Pick List Filtering', + 'ru-ru': 'Фильтрация списка выбора', + 'es-es': 'Filtrado de lista de selección', + 'fr-fr': 'Filtrage de la liste de sélection', + 'uk-ua': 'Фільтрація списку вибору', + 'de-ch': 'Picklistenfilterung', + 'pt-br': 'Filtragem de lista de seleção', + }, + exportFileDelimiter: { + 'en-us': 'Export file delimiter', + 'ru-ru': 'Разделитель файлов экспорта', + 'es-es': 'Delimitador de archivo de exportación', + 'fr-fr': "Délimiteur de fichier d'exportation", + 'uk-ua': 'Роздільник файлу експорту', + 'de-ch': 'Dateitrennzeichen exportieren', + 'pt-br': 'Delimitador de arquivo de exportação', + }, + exportCsvUtf8Bom: { + 'en-us': 'Add UTF-8 BOM to CSV file exports', + 'ru-ru': 'Добавить UTF-8 BOM в экспорт CSV-файла', + 'es-es': 'Agregar BOM UTF-8 a las exportaciones de archivos CSV', + 'fr-fr': 'Ajouter UTF-8 BOM aux exportations de fichiers CSV', + 'uk-ua': 'Додайте специфікацію UTF-8 до експорту файлу CSVу', + 'de-ch': 'UTF-8 BOM zum CSV-Dateiexport hinzufügen', + 'pt-br': 'Adicionar UTF-8 BOM às exportações de arquivos CSV', + }, + exportCsvUtf8BomDescription: { + 'en-us': + 'Adds a BOM (Byte Order Mark) to exported CSV files to ensure that the file is correctly recognized and displayed by various programs (Excel, OpenRefine, etc.), preventing issues with special characters and formatting.', + 'ru-ru': 'Корректное отображение экспортированных CSV-файлов в Excel.', + 'es-es': + 'Agrega una BOM (marca de orden de bytes) a los archivos CSV exportados para garantizar que el archivo sea reconocido y mostrado correctamente por varios programas (Excel, OpenRefine, etc.), evitando problemas con caracteres especiales y formato.', + 'fr-fr': + "Permet aux exportations de fichiers CSV de s'afficher correctement dans Excel.", + 'uk-ua': 'Змушує експорт файлів CSV правильно відображатися в Excel.', + 'de-ch': + 'Sorgt dafür, dass CSV-Dateiexporte in Excel korrekt angezeigt werden.', + 'pt-br': + 'Adiciona uma BOM (Byte Order Mark) aos arquivos CSV exportados para garantir que o arquivo seja reconhecido e exibido corretamente por vários programas (Excel, OpenRefine, etc.), evitando problemas com caracteres especiais e formatação.', + }, + caseSensitive: { + 'en-us': 'Case-sensitive', + 'ru-ru': 'С учетом регистра', + 'es-es': 'Distingue mayúsculas y minúsculas', + 'fr-fr': 'Sensible aux majuscules et minuscules', + 'uk-ua': 'Чутливий до регістру', + 'de-ch': 'Groß- und Kleinschreibung beachten', + 'pt-br': 'Maiúsculas e minúsculas', + }, + caseInsensitive: { + 'en-us': 'Case-insensitive', + 'ru-ru': 'Без учета регистра', + 'es-es': 'No distingue entre mayúsculas y minúsculas', + 'fr-fr': 'Insensible à la casse', + 'uk-ua': 'Регістр не враховується', + 'de-ch': 'Groß- und Kleinschreibung wird nicht berücksichtigt', + 'pt-br': 'Não diferencia maiúsculas de minúsculas', + }, + showNoReadTables: { + 'en-us': 'Show tables without "Read" access', + 'ru-ru': 'Показывать таблицы без доступа «Чтение»', + 'es-es': 'Mostrar tablas sin acceso de "Lectura"', + 'fr-fr': 'Afficher les tableaux sans accès "Lecture"', + 'uk-ua': 'Показувати таблиці без доступу «Читання»', + 'de-ch': 'Tabellen ohne Lesezugriff anzeigen', + 'pt-br': 'Mostrar tabelas sem acesso de "Leitura"', + }, + showNoAccessTables: { + 'en-us': 'Show tables without "Create" access', + 'ru-ru': 'Показывать таблицы без права «Создать»', + 'es-es': 'Mostrar tablas sin acceso "Crear"', + 'fr-fr': 'Afficher les tableaux sans accès "Créer"', + 'uk-ua': 'Показувати таблиці без доступу «Створити»', + 'de-ch': 'Tabellen ohne „Erstellen“-Zugriff anzeigen', + 'pt-br': 'Mostrar tabelas sem acesso "Criar"', + }, + textAreaAutoGrow: { + 'en-us': 'Text boxes grow automatically', + 'ru-ru': 'Текстовые поля увеличиваются автоматически', + 'es-es': 'Los cuadros de texto crecen automáticamente', + 'fr-fr': "Les zones de texte s'agrandissent automatiquement", + 'uk-ua': 'Текстові поля збільшуються автоматично', + 'de-ch': 'Textfelder werden automatisch vergrößert', + 'pt-br': 'As caixas de texto crescem automaticamente', + }, + clearQueryFilters: { + 'en-us': 'Reset query filters', + 'ru-ru': 'Сбросить фильтры запроса', + 'es-es': 'Restablecer filtros de consulta', + 'fr-fr': 'Réinitialiser les filtres de requête', + 'uk-ua': 'Скинути фільтри запитів', + 'de-ch': 'Abfragefilter zurücksetzen', + 'pt-br': 'Redefinir filtros de consulta', + }, + clearQueryFiltersDescription: { + 'en-us': 'Clears all query filters when running a Report from a Form.', + 'de-ch': + 'Löscht alle Abfragefilter, wenn ein Bericht aus einem Formular ausgeführt wird.', + 'es-es': + 'Borra todos los filtros de consulta al ejecutar un informe desde un formulario.', + 'fr-fr': + "Efface tous les filtres de requête lors de l'exécution d'un rapport à partir d'un formulaire.", + 'ru-ru': 'Очищает все фильтры запроса при запуске отчета из формы.', + 'uk-ua': 'Очищає всі фільтри запитів під час запуску звіту з форми.', + 'pt-br': + 'Limpa todos os filtros de consulta ao executar um relatório de um formulário.', + }, + queryParamtersFromForm: { + 'en-us': 'Show query filters when running a Report from a Form', + 'de-ch': + 'Abfragefilter anzeigen, wenn ein Bericht aus einem Formular ausgeführt wird', + 'es-es': + 'Mostrar filtros de consulta al ejecutar un informe desde un formulario', + 'fr-fr': + "Afficher les filtres de requête lors de l'exécution d'un rapport à partir d'un formulaire", + 'ru-ru': 'Показывать фильтры запроса при запуске отчета из формы', + 'uk-ua': 'Показувати фільтри запитів під час запуску звіту з форми', + 'pt-br': + 'Mostrar filtros de consulta ao executar um relatório de um formulário', + }, + autoGrowAutoComplete: { + 'en-us': 'Allow autocomplete to grow as wide as need', + 'ru-ru': + 'Разрешить автозаполнению расширяться настолько, насколько это необходимо', + 'es-es': 'Permitir que el autocompletado crezca tanto como sea necesario', + 'fr-fr': + 'Sens de déplacement lorsque la touche [X27X]Tabulation[X35X] est enfoncée', + 'uk-ua': + 'Дозволити автозаповнення розширюватися настільки, наскільки потрібно', + 'de-ch': + 'Erlauben Sie der Autovervollständigung, so weit wie nötig zu wachsen', + 'pt-br': + 'Permitir que o preenchimento automático cresça o quanto for necessário', + }, + tableNameInTitle: { + 'en-us': 'Include table name in the browser page title', + 'ru-ru': 'Включить имя таблицы в заголовок страницы браузера', + 'es-es': + 'Incluir el nombre de la tabla en el título de la página del navegador', + 'fr-fr': + 'Inclure le nom de la table dans le titre de la page du navigateur', + 'uk-ua': 'Включіть назву таблиці в заголовок сторінки браузера', + 'de-ch': 'Tabellennamen in den Seitentitel des Browsers aufnehmen', + 'pt-br': 'Incluir nome da tabela no título da página do navegador', + }, + focusFirstField: { + 'en-us': 'Focus first field', + 'de-ch': 'Fokus erstes Feld', + 'es-es': 'Enfoque el primer campo', + 'fr-fr': 'Concentrez-vous sur le premier champ', + 'ru-ru': 'Фокус первого поля', + 'uk-ua': 'Перейти до першого поля', + 'pt-br': 'Foco primeiro campo', + }, + doubleClickZoom: { + 'en-us': 'Double click to zoom', + 'ru-ru': 'Дважды щелкните, чтобы увеличить', + 'es-es': 'Haga doble clic para ampliar', + 'fr-fr': 'Double-cliquez pour zoomer', + 'uk-ua': 'Двічі клацніть, щоб збільшити', + 'de-ch': 'Zum Vergrößern doppelklicken', + 'pt-br': 'Clique duas vezes para ampliar', + }, + closePopupOnClick: { + 'en-us': 'Close pop-up on outside click', + 'ru-ru': 'Закрытие всплывающего окна при внешнем щелчке', + 'es-es': 'Cerrar ventana emergente al hacer clic desde fuera', + 'fr-fr': "Fermer la pop-up lors d'un clic extérieur", + 'uk-ua': 'Закрити спливаюче вікно при зовнішньому клацанні', + 'de-ch': 'Popup bei externem Klick schließen', + 'pt-br': 'Fechar pop-up ao clicar fora', + }, + animateTransitions: { + 'en-us': 'Animate transitions', + 'ru-ru': 'Анимированные переходы', + 'es-es': 'Animar transiciones', + 'fr-fr': 'Animer les transitions', + 'uk-ua': 'Анімація переходів', + 'de-ch': 'Übergänge animieren', + 'pt-br': 'Transições animadas', + }, + panInertia: { + 'en-us': 'Pan inertia', + 'ru-ru': 'Инерция пан', + 'es-es': 'Inercia de la sartén', + 'fr-fr': 'Inertie du bac', + 'uk-ua': 'Інерція панорами', + 'de-ch': 'Schwenkträgheit', + 'pt-br': 'Inércia da panela', + }, + mouseDrags: { + 'en-us': 'Mouse drags', + 'ru-ru': 'Перетаскивание мышью', + 'es-es': 'El ratón arrastra', + 'uk-ua': 'Виділіть відповідний підрядок', + 'de-ch': 'Maus zieht', + 'fr-fr': 'Mettre en surbrillance la sous-chaîne correspondante', + 'pt-br': 'Arrastos do mouse', + }, + scrollWheelZoom: { + 'en-us': 'Scroll wheel zoom', + 'ru-ru': 'Масштабирование с помощью колеса прокрутки', + 'es-es': 'Zoom con rueda de desplazamiento', + 'fr-fr': 'Zoom avec la molette de défilement', + 'uk-ua': 'Масштаб колеса прокрутки', + 'de-ch': 'Scrollrad-Zoom', + 'pt-br': 'Zoom da roda de rolagem', + }, + flexibleColumnWidth: { + 'en-us': 'Flexible column width', + 'ru-ru': 'Гибкая ширина столбца', + 'es-es': 'Ancho de columna flexible', + 'fr-fr': 'Largeur de colonne flexible', + 'uk-ua': 'Гнучка ширина колонки', + 'de-ch': 'Flexible Spaltenbreite', + 'pt-br': 'Largura de coluna flexível', + }, + flexibleSubGridColumnWidth: { + 'en-us': 'Flexible subview grid column width', + 'ru-ru': 'Гибкая ширина столбца сетки подпредставлений', + 'es-es': 'Ancho de columna de cuadrícula de subvista flexible', + 'fr-fr': 'Largeur de colonne de grille de sous-vue flexible', + 'uk-ua': 'Гнучка ширина стовпця сітки вкладеного перегляду', + 'de-ch': 'Flexible Rasterspaltenbreite der Unteransicht', + 'pt-br': 'Largura flexível da coluna da grade de subvisualização', + }, + closeOnEsc: { + 'en-us': 'Close on ESC key press', + 'ru-ru': 'Закрыть нажатием клавиши ESC', + 'es-es': 'Cerrar al presionar la tecla ESC', + 'fr-fr': 'Icône et nom de la table', + 'uk-ua': 'Закриття натисканням клавіші ESC', + 'de-ch': 'Schließen durch Drücken der Taste ESC', + 'pt-br': 'Fechar ao pressionar a tecla ESC', + }, + closeOnOutsideClick: { + 'en-us': 'Close on outside click', + 'ru-ru': 'Закрытие по внешнему щелчку', + 'es-es': 'Cerrar al hacer clic desde fuera', + 'fr-fr': 'Fermer sur clic extérieur', + 'uk-ua': 'Закрийте зовнішнім клацанням', + 'de-ch': 'Schließen durch Klicken von außen', + 'pt-br': 'Fechar com clique externo', + }, + scopeEntireTablePicklists: { + 'en-us': 'Scope "Entire Table" Picklists', + }, + scopeEntireTablePicklistsDescription: { + 'en-us': + 'If enabled, picklists of type "Entire Table" will only show items that are in use within the current collection.', + }, + useAccessibleFullDatePicker: { + 'en-us': 'Use accessible full date picker', + 'ru-ru': 'Используйте доступный полный выбор даты', + 'es-es': 'Utilice el selector de fecha completo y accesible', + 'fr-fr': 'Utiliser un sélecteur de date complet accessible', + 'uk-ua': 'Використовуйте доступний повний засіб вибору дати', + 'de-ch': 'Verwenden Sie eine barrierefreie Datumsauswahl', + 'pt-br': 'Use o seletor de data completo acessível', + }, + useAccessibleMonthPicker: { + 'en-us': 'Use accessible month picker', + 'ru-ru': 'Используйте доступный выбор месяца', + 'es-es': 'Utilice el selector de meses accesible', + 'fr-fr': 'Utiliser le sélecteur de mois accessible', + 'uk-ua': 'Використовуйте доступний засіб вибору місяця', + 'de-ch': 'Verwenden Sie die barrierefreie Monatsauswahl', + 'pt-br': 'Use o seletor de meses acessível', + }, + collectionSortOrderDescription: { + 'en-us': 'This determines the visual order of collections.', + 'ru-ru': 'Это определяет визуальный порядок коллекций.', + 'es-es': 'Esto determina el orden visual de las colecciones.', + 'fr-fr': "Ceci détermine l'ordre visuel des collections.", + 'uk-ua': 'Це визначає візуальний порядок колекцій.', + 'de-ch': 'Dies bestimmt die visuelle Reihenfolge der Sammlungen.', + 'pt-br': 'Isso determina a ordem visual das coleções.', + }, + recordSetRecordToOpen: { + 'en-us': 'Record to open by default', + 'ru-ru': 'Запись для открытия по умолчанию', + 'es-es': 'Registro para abrir por defecto', + 'fr-fr': 'Enregistrement à ouvrir par défaut', + 'uk-ua': 'Запис відкривається за умовчанням', + 'de-ch': 'Standardmäßig zu öffnender Datensatz', + 'pt-br': 'Gravar para abrir por padrão', + }, + altClickToSupressNewTab: { + 'en-us': + '{altKeyName:string}+Click to suppress new tab', + 'ru-ru': + '{altKeyName:string}+Нажмите , чтобы скрыть новую вкладку', + 'es-es': + '{altKeyName:string}+Haga clic en para suprimir la nueva pestaña', + 'fr-fr': + '{altKeyName:string}+Cliquez sur pour supprimer le nouvel onglet', + 'uk-ua': + '{altKeyName:string}+Натисніть , щоб закрити нову вкладку', + 'de-ch': + '{altKeyName:string}+Klicken Sie auf, um neue Registerkarten zu unterdrücken', + 'pt-br': + '{altKeyName:string}+Clique em para suprimir a nova guia', + }, + altClickToSupressNewTabDescription: { + 'en-us': + '{altKeyName:string}+Click a link that usually opens in a new tab to open it in the current tab.', + 'ru-ru': + '{altKeyName:string}+Нажмите на ссылку, которая обычно открывается в новой вкладке, чтобы открыть ее в текущей вкладке.', + 'es-es': + '{altKeyName:string}+Haga clic en un enlace que normalmente se abre en una nueva pestaña para abrirlo en la pestaña actual.', + 'fr-fr': 'Utiliser le sélecteur de mois accessible.', + 'uk-ua': + '{altKeyName:string}+Натисніть посилання, яке зазвичай відкривається в новій вкладці, щоб відкрити його в поточній вкладці.', + 'de-ch': + '{altKeyName:string}+Klicken Sie auf einen Link, der normalerweise in einem neuen Tab geöffnet wird, um ihn im aktuellen Tab zu öffnen.', + 'pt-br': + '{altKeyName:string}+Clique em um link que geralmente abre em uma nova aba para abri-lo na aba atual.', + }, + makeFormDialogsModal: { + 'en-us': 'Make form dialogs gray out the background', + 'ru-ru': 'Сделать фон диалоговых окон серым', + 'es-es': + 'Hacer que los cuadros de diálogo del formulario tengan el fondo en gris', + 'fr-fr': + "Rendre les boîtes de dialogue de formulaire grisées sur l'arrière-plan", + 'uk-ua': 'Зробіть діалогові вікна форми сірими фоном', + 'de-ch': 'Den Hintergrund von Formulardialogen ausgrauen', + 'pt-br': + 'Faça com que as caixas de diálogo do formulário fiquem com o fundo acinzentado', + }, + autoScrollTree: { + 'en-us': 'Auto scroll tree to focused node', + 'ru-ru': 'Автоматическая прокрутка дерева к выбранному узлу', + 'es-es': 'Desplazamiento automático del árbol al nodo enfocado', + 'fr-fr': 'Arbre de défilement automatique vers le nœud ciblé', + 'uk-ua': 'Автоматичне прокручування дерева до виділеного вузла', + 'de-ch': 'Automatisches Scrollen des Baums zum fokussierten Knoten', + 'pt-br': 'Rolagem automática da árvore para o nó em foco', + }, + sortByField: { + 'en-us': 'Order By Field', + 'de-ch': 'Nach Feld sortieren', + 'es-es': 'Ordenar por campo', + 'fr-fr': 'Trier par champ', + 'pt-br': 'Ordenar por campo', + 'ru-ru': 'Сортировать по полю', + 'uk-ua': 'Сортувати за полем', + }, + createInteractions: { + 'en-us': 'Creating an interaction', + 'ru-ru': 'Создание взаимодействия', + 'es-es': 'Creando una interacción', + 'fr-fr': 'Créer une interaction', + 'uk-ua': 'Створення взаємодії', + 'de-ch': 'Erstellen einer Interaktion', + 'pt-br': 'Criando uma interação', + }, + useSpaceAsDelimiter: { + 'en-us': 'Use space as delimiter', + 'ru-ru': 'Используйте пробел в качестве разделителя', + 'es-es': 'Utilice el espacio como delimitador', + 'fr-fr': "Utiliser l'espace comme délimiteur", + 'uk-ua': 'Використовуйте пробіл як роздільник', + 'de-ch': 'Leerzeichen als Trennzeichen verwenden', + 'pt-br': 'Use espaço como delimitador', + }, + useCommaAsDelimiter: { + 'en-us': 'Use comma as delimiter', + 'ru-ru': 'Используйте запятую в качестве разделителя', + 'es-es': 'Utilice la coma como delimitador', + 'fr-fr': 'Utiliser la virgule comme délimiteur', + 'uk-ua': 'Використовуйте кому як роздільник', + 'de-ch': 'Verwenden Sie Kommas als Trennzeichen', + 'pt-br': 'Use vírgula como delimitador', + }, + useNewLineAsDelimiter: { + 'en-us': 'Use new line as delimiter', + 'ru-ru': 'Использовать новую строку в качестве разделителя', + 'es-es': 'Utilice nueva línea como delimitador', + 'fr-fr': 'Utiliser une nouvelle ligne comme délimiteur', + 'uk-ua': 'Використовуйте новий рядок як роздільник', + 'de-ch': 'Neue Zeile als Trennzeichen verwenden', + 'pt-br': 'Use nova linha como delimitador', + }, + useCustomDelimiters: { + 'en-us': 'Use custom delimiters', + 'ru-ru': 'Используйте пользовательские разделители', + 'es-es': 'Utilice delimitadores personalizados', + 'fr-fr': 'Utiliser des délimiteurs personnalisés', + 'uk-ua': 'Використовуйте спеціальні роздільники', + 'de-ch': 'Benutzerdefinierte Trennzeichen verwenden', + 'pt-br': 'Use delimitadores personalizados', + }, + useCustomDelimitersDescription: { + 'en-us': + 'A list of delimiters to use, in addition to the ones defined above. Put one delimiter per line.', + 'ru-ru': + 'Список разделителей, которые можно использовать в дополнение к указанным выше. Используйте по одному разделителю на строку.', + 'es-es': + 'Una lista de delimitadores para usar, además de los definidos anteriormente. Coloque un delimitador por línea.', + 'fr-fr': + 'Une liste de délimiteurs à utiliser, en plus de ceux définis ci-dessus. Mettez un délimiteur par ligne.', + 'uk-ua': + 'Список розділювачів для використання на додаток до визначених вище. Поставте один роздільник на рядок.', + 'de-ch': + 'Eine Liste der zu verwendenden Trennzeichen zusätzlich zu den oben definierten. Geben Sie pro Zeile ein Trennzeichen ein.', + 'pt-br': + 'Uma lista de delimitadores a serem usados, além dos definidos acima. Coloque um delimitador por linha.', + }, + detectAutomaticallyDescription: { + 'en-us': 'Detect automatically based on catalog number format.', + 'ru-ru': 'Автоматическое определение на основе формата каталожного номера.', + 'es-es': + 'Detectar automáticamente según el formato del número de catálogo.', + 'fr-fr': + 'Détecter automatiquement en fonction du format du numéro de catalogue.', + 'uk-ua': 'Визначати автоматично на основі формату номера каталогу.', + 'de-ch': 'Automatische Erkennung basierend auf dem Katalognummernformat.', + 'pt-br': + 'Detectar automaticamente com base no formato do número de catálogo.', + }, + use: { + comment: 'Verb', + 'en-us': 'Use', + 'ru-ru': 'Использовать', + 'es-es': 'Usar', + 'fr-fr': 'Utiliser', + 'uk-ua': 'використання', + 'de-ch': 'Verwenden', + 'pt-br': 'Usar', + }, + dontUse: { + 'en-us': 'Don’t use', + 'ru-ru': 'Не использовать', + 'es-es': 'No utilizar', + 'fr-fr': 'Zoom avec la molette de défilement', + 'uk-ua': 'Масштаб колеса прокрутки', + 'de-ch': 'Nicht verwenden', + 'pt-br': 'Não use', + }, + position: { + 'en-us': 'Position', + 'es-es': 'Posición', + 'fr-fr': 'Position', + 'ru-ru': 'Позиция', + 'uk-ua': 'Позиція', + 'de-ch': 'Position', + 'pt-br': 'Posição', + }, + top: { + 'en-us': 'Top', + 'es-es': 'Arriba', + 'fr-fr': 'Haut', + 'ru-ru': 'Вершина', + 'uk-ua': 'Топ', + 'de-ch': 'Spitze', + 'pt-br': 'Principal', + }, + bottom: { + 'en-us': 'Bottom', + 'es-es': 'Abajo', + 'ru-ru': 'Нижний', + 'uk-ua': 'Дно', + 'de-ch': 'Unten', + 'fr-fr': 'Bas', + 'pt-br': 'Fundo', + }, + left: { + 'en-us': 'Left', + 'es-es': 'Izquierda', + 'fr-fr': 'Gauche', + 'ru-ru': 'Левый', + 'uk-ua': 'Ліворуч', + 'de-ch': 'Links', + 'pt-br': 'Esquerda', + }, + right: { + 'en-us': 'Right', + 'es-es': 'Bien', + 'fr-fr': 'Droite', + 'ru-ru': 'Верно', + 'uk-ua': 'правильно', + 'de-ch': 'Rechts', + 'pt-br': 'Certo', + }, + showUnsavedIndicator: { + 'en-us': 'Show unsaved changes indicator', + 'ru-ru': 'Показать индикатор несохраненных изменений', + 'es-es': 'Mostrar indicador de cambios no guardados', + 'fr-fr': "Afficher l'indicateur de modifications non enregistrées", + 'uk-ua': 'Показати індикатор незбережених змін', + 'de-ch': 'Indikator für nicht gespeicherte Änderungen anzeigen', + 'pt-br': 'Mostrar indicador de alterações não salvas', + }, + showUnsavedIndicatorDescription: { + 'en-us': + 'Show an "*" in the tab title when there are unsaved changes in the current tab.', + 'es-es': + 'Mostrar un "*" en el título de la pestaña cuando haya cambios sin guardar en la pestaña actual.', + 'fr-fr': + "Afficher un \"*\" dans le titre de l'onglet lorsqu'il y a des modifications non enregistrées dans l'onglet actuel.", + 'ru-ru': + 'Отображать «*» в заголовке вкладки, если на текущей вкладке есть несохраненные изменения.', + 'uk-ua': + 'Показувати «*» у заголовку вкладки, якщо в поточній вкладці є незбережені зміни.', + 'de-ch': + 'Zeigen Sie im Registerkartentitel ein „*“ an, wenn in der aktuellen Registerkarte nicht gespeicherte Änderungen vorhanden sind.', + 'pt-br': + 'Exibir um "*" no título da aba quando houver alterações não salvas na aba atual.', + }, + autoPopulateDescription: { + 'en-us': + 'Auto populate the merged record with values from duplicates when opening the merging dialog.', + 'ru-ru': + 'Автоматически заполнять объединенную запись значениями из дубликатов при открытии диалогового окна слияния.', + 'de-ch': + 'Füllen Sie den zusammengeführten Datensatz beim Öffnen des Zusammenführungsdialogs automatisch mit Werten aus Duplikaten.', + 'es-es': + 'Rellene automáticamente el registro fusionado con valores de duplicados al abrir el cuadro de diálogo de fusión.', + 'fr-fr': + "Remplir automatiquement l'enregistrement fusionné avec les valeurs des doublons lors de l'ouverture de la boîte de dialogue de fusion.", + 'uk-ua': + 'Автоматичне заповнення об’єднаного запису значеннями з дублікатів під час відкриття діалогового вікна об’єднання.', + 'pt-br': + 'Preencha automaticamente o registro mesclado com valores de duplicatas ao abrir a caixa de diálogo de mesclagem.', + }, + autoCreateVariants: { + 'en-us': 'Automatically create {agentVariantTable:string} records', + 'ru-ru': 'Автоматически создавать записи {agentVariantTable:string}', + 'de-ch': '{agentVariantTable:string}-Datensätze automatisch erstellen', + 'es-es': 'Crear automáticamente registros {agentVariantTable:string}', + 'fr-fr': + 'Créer automatiquement des enregistrements {agentVariantTable:string}', + 'uk-ua': 'Автоматично створювати записи {agentVariantTable:string}', + 'pt-br': 'Criar automaticamente registros {agentVariantTable:string}', + }, + autoCreateVariantsDescription: { + 'en-us': + 'When merging agents, automatically create {agentVariantTable:string} records based on the variations of first name/last name.', + 'ru-ru': + 'При объединении агентов автоматически создавать записи {agentVariantTable:string} на основе вариаций имени/фамилии.', + 'de-ch': + 'Beim Zusammenführen von Agenten werden automatisch {agentVariantTable:string}-Datensätze basierend auf den Variationen von Vorname/Nachname erstellt.', + 'es-es': + 'Al fusionar agentes, se crean automáticamente registros {agentVariantTable:string} basados en las variaciones de nombre/apellido.', + 'fr-fr': + "Lors de la fusion d'agents, créez automatiquement des enregistrements {agentVariantTable:string} en fonction des variations du prénom/nom.", + 'uk-ua': + 'Під час об’єднання агентів автоматично створювати записи {agentVariantTable:string} на основі варіацій імені/прізвища.', + 'pt-br': + 'Ao mesclar agentes, crie automaticamente registros {agentVariantTable:string} com base nas variações de nome/sobrenome.', + }, + collectionPreferences: { + 'en-us': 'Collection Preferences', + 'de-ch': 'Sammlungseinstellungen', + 'es-es': 'Preferencias de colección', + 'fr-fr': 'Personnalisation', + 'ru-ru': 'Настройки коллекции', + 'uk-ua': 'Налаштування', + 'pt-br': 'Preferências de coleção', + }, + rememberDialogSizes: { + 'en-us': 'Remember dialog window sizes', + 'ru-ru': 'Запомните размеры диалоговых окон', + 'es-es': 'Recordar los tamaños de las ventanas de diálogo', + 'fr-fr': 'Mémoriser les tailles des fenêtres de dialogue', + 'uk-ua': "Запам'ятайте розміри діалогових вікон", + 'de-ch': 'Dialogfenstergrößen merken', + 'pt-br': 'Lembrar tamanhos de janelas de diálogo', + }, + rememberDialogPositions: { + 'en-us': 'Remember dialog window positions', + 'ru-ru': 'Запомнить позиции диалоговых окон', + 'es-es': 'Recordar las posiciones de las ventanas de diálogo', + 'fr-fr': 'Mémoriser les positions des fenêtres de dialogue', + 'uk-ua': "Запам'ятовуйте положення діалогового вікна", + 'de-ch': 'Dialogfensterpositionen merken', + 'pt-br': 'Lembrar posições da janela de diálogo', + }, + autoPlayMedia: { + 'en-us': 'Automatically play media', + 'ru-ru': 'Автоматически воспроизводить медиа', + 'es-es': 'Reproducir automáticamente medios', + 'fr-fr': 'Lire automatiquement les médias', + 'uk-ua': 'Автоматичне відтворення медіа', + 'de-ch': 'Medien automatisch abspielen', + 'pt-br': 'Reproduzir mídia automaticamente', + }, + useCustomTooltips: { + 'en-us': 'Use modern tooltips', + 'ru-ru': 'Используйте современные подсказки', + 'es-es': 'Utilice información sobre herramientas moderna', + 'fr-fr': 'Utiliser des info-bulles modernes', + 'uk-ua': 'Використовуйте сучасні підказки', + 'de-ch': 'Verwenden Sie moderne Tooltips', + 'pt-br': 'Use dicas de ferramentas modernas', + }, + alwaysUseQueryBuilder: { + 'en-us': 'Always use query builder search inside of search form', + 'de-ch': + 'Verwenden Sie innerhalb des Suchformulars immer die Abfragegeneratorsuche', + 'es-es': + 'Utilice siempre la búsqueda del generador de consultas dentro del formulario de búsqueda', + 'fr-fr': + 'Utilisez toujours la recherche du générateur de requêtes dans le formulaire de recherche', + 'ru-ru': 'Всегда используйте конструктор запросов внутри формы поиска.', + 'uk-ua': 'Завжди використовуйте пошук конструктора запитів у формі пошуку', + 'pt-br': + 'Sempre use a pesquisa do construtor de consultas dentro do formulário de pesquisa', + }, + localizeResourceNames: { + 'en-us': 'Localize the names of recognized app resources', + 'de-ch': 'Lokalisieren Sie die Namen erkannter App-Ressourcen', + 'es-es': + 'Localizar los nombres de los recursos de aplicaciones reconocidos', + 'fr-fr': "Localiser les noms des ressources d'application reconnues", + 'ru-ru': 'Локализуйте названия распознанных ресурсов приложения', + 'uk-ua': 'Локалізувати назви розпізнаних ресурсів програми', + 'pt-br': 'Localize os nomes dos recursos de aplicativos reconhecidos', + }, + splitLongXml: { + 'en-us': 'Split long lines of XML into multiple lines', + 'de-ch': 'Teilen Sie lange XML-Zeilen in mehrere Zeilen auf', + 'es-es': 'Dividir líneas largas de XML en varias líneas', + 'fr-fr': 'Diviser les longues lignes de XML en plusieurs lignes', + 'ru-ru': 'Разделить длинные строки XML на несколько строк', + 'uk-ua': 'Розділіть довгі рядки XML на кілька рядків', + 'pt-br': 'Dividir longas linhas de XML em várias linhas', + }, + openAsReadOnly: { + 'en-us': 'Open all records in read-only mode', + 'de-ch': 'Alle Datensätze im schreibgeschützten Modus öffnen', + 'es-es': 'Abrir todos los registros en modo de solo lectura', + 'fr-fr': 'Ouvrir tous les enregistrements en mode lecture seule', + 'ru-ru': 'Открыть все записи в режиме только для чтения', + 'uk-ua': 'Відкрити всі записи в режимі лише для читання', + 'pt-br': 'Abra todos os registros no modo somente leitura', + }, + displayBasicView: { + 'en-us': 'Display basic view', + 'de-ch': 'Basisansicht anzeigen', + 'es-es': 'Mostrar vista básica', + 'fr-fr': 'Afficher la vue de base', + 'ru-ru': 'Отобразить базовый вид', + 'uk-ua': 'Відобразити базовий вигляд', + 'pt-br': 'Exibir visualização básica', + }, + showComparisonOperatorsForString: { + 'en-us': 'Show comparison operators for text-based fields', + 'de-ch': 'Vergleichsoperatoren für textbasierte Felder anzeigen', + 'es-es': 'Mostrar operadores de comparación para campos basados en texto', + 'fr-fr': 'Afficher les opérateurs de comparaison pour les champs textuels', + 'pt-br': 'Mostrar operadores de comparação para campos baseados em texto', + 'ru-ru': 'Показать операторы сравнения для текстовых полей', + 'uk-ua': 'Показати оператори порівняння для текстових полів', + }, + showComparisonOperatorsDescription: { + 'en-us': + 'Allows the following filters to apply to text fields: Greater Than, Less Than, Greater Than or Equal to, and Less Than or Equal to', + 'de-ch': + 'Ermöglicht die Anwendung der folgenden Filter auf Textfelder: Größer als, Kleiner als, Größer als oder gleich und Kleiner als oder gleich', + 'es-es': + 'Permite aplicar los siguientes filtros a los campos de texto: Mayor que, Menor que, Mayor o igual que y Menor o igual que', + 'fr-fr': + "Permet d'appliquer les filtres suivants aux champs de texte : Supérieur à, Inférieur à, Supérieur ou égal à et Inférieur ou égal à", + 'pt-br': + 'Permite que os seguintes filtros sejam aplicados aos campos de texto: Maior que, Menor que, Maior ou igual a e Menor ou igual a', + 'ru-ru': + 'Позволяет применять к текстовым полям следующие фильтры: «Больше», «Меньше», «Больше или равно» и «Меньше или равно».', + 'uk-ua': + 'Дозволяє застосовувати до текстових полів такі фільтри: «Більше ніж», «Менше ніж», «Більше або дорівнює» та «Менше або дорівнює»', + }, + basicView: { + 'en-us': 'Basic view', + 'de-ch': 'Basisansicht', + 'es-es': 'Vista básica', + 'fr-fr': 'Vue de base', + 'ru-ru': 'Базовый вид', + 'uk-ua': 'Основний вигляд', + 'pt-br': 'Visão básica', + }, + detailedView: { + 'en-us': 'Detailed view', + 'de-ch': 'Detailansicht', + 'es-es': 'Vista detallada', + 'fr-fr': 'Vue détaillée', + 'ru-ru': 'Подробный вид', + 'uk-ua': 'Детальний вигляд', + 'pt-br': 'Visão detalhada', + }, + inheritanceCatNumberPref: { + 'en-us': + 'Enable {catalogNumber:string} Inheritance From Primary {collectionObject:string}', + }, + inheritanceCatNumberPrefDescription: { + 'en-us': + 'Control whether component records inherit the {catalogNumber:string} from the primary {collectionObject:string}.', + }, + inheritanceCatNumberParentCOPref: { + 'en-us': + 'Enable {catalogNumber:string} Inheritance From Parent {collectionObject:string}', + }, + inheritanceCatNumberParentCOPrefDescription: { + 'en-us': + 'Control whether component records inherit the {catalogNumber:string} from the parent {collectionObject:string}.', + }, + uniqueCatNumberAcrossCompAndCo: { + 'en-us': + 'Catalog Number field need to be unique across Component and CO tables', + 'de-ch': + 'Das Feld „Katalognummer“ muss in allen Komponenten- und CO-Tabellen eindeutig sein', + 'es-es': + 'El campo Número de catálogo debe ser único en las tablas de componentes y CO', + 'fr-fr': + 'Le champ Numéro de catalogue doit être unique dans les tables Composant et CO', + 'pt-br': + 'O campo Número de catálogo precisa ser exclusivo nas tabelas Componente e CO', + 'ru-ru': + 'Поле «Номер каталога» должно быть уникальным в таблицах «Компонент» и «CO».', + 'uk-ua': + 'Поле «Номер у каталозі» має бути унікальним у таблицях «Компонент» та «CO».', + }, +} as const; + +export const preferencesBehaviorText = createDictionary( + preferencesBehaviorDictionary +); diff --git a/specifyweb/frontend/js_src/lib/localization/preferences.content.ts b/specifyweb/frontend/js_src/lib/localization/preferences.content.ts new file mode 100644 index 00000000000..aa371bc7816 --- /dev/null +++ b/specifyweb/frontend/js_src/lib/localization/preferences.content.ts @@ -0,0 +1,192 @@ +/** + * Localization strings for content-related preferences. + * + * @module + */ +import { createDictionary } from './utils'; +// Refer to "Guidelines for Programmers" in ./README.md before editing this file +export const preferencesContentDictionary = { + displayAuthor: { + 'en-us': 'Show author in the tree', + 'ru-ru': 'Показать автора в дереве', + 'es-es': 'Mostrar autor en el árbol', + 'fr-fr': "Afficher l'auteur dans l'arbre", + 'uk-ua': 'Показати автора в дереві', + 'de-ch': 'Autor im Baum anzeigen', + 'pt-br': 'Mostrar autor', + }, + welcomePage: { + 'en-us': 'Home Page', + 'ru-ru': 'Домашняя страница', + 'es-es': 'Página de inicio', + 'fr-fr': "Page d'accueil", + 'uk-ua': 'Домашня сторінка', + 'de-ch': 'Startseite', + 'pt-br': 'Página inicial', + }, + content: { + 'en-us': 'Content', + 'ru-ru': 'Содержание', + 'es-es': 'Contenido', + 'fr-fr': 'Contenu', + 'uk-ua': 'Зміст', + 'de-ch': 'Inhalt', + 'pt-br': 'Contente', + }, + defaultImage: { + 'en-us': 'Specify Logo', + 'ru-ru': 'Укажите логотип', + 'es-es': 'Especificar logotipo', + 'fr-fr': 'Spécifier le logo', + 'uk-ua': 'Вкажіть логотип', + 'de-ch': 'Logo angeben', + 'pt-br': 'Especificar logotipo', + }, + customImage: { + 'en-us': 'Custom Image', + 'ru-ru': 'Пользовательское изображение', + 'es-es': 'Imagen personalizada', + 'fr-fr': 'Image personnalisée', + 'uk-ua': 'Спеціальне зображення', + 'de-ch': 'Benutzerdefiniertes Bild', + 'pt-br': 'Imagem personalizada', + }, + embeddedWebpage: { + 'en-us': 'Embedded web page', + 'ru-ru': 'Встроенная веб-страница', + 'es-es': 'Página web incrustada', + 'fr-fr': 'Page Web intégrée', + 'uk-ua': 'Вбудована веб-сторінка', + 'de-ch': 'Eingebettete Webseite', + 'pt-br': 'Página da web incorporada', + }, + embeddedWebpageDescription: { + 'en-us': 'A URL to a page that would be embedded on the home page:', + 'ru-ru': 'URL-адрес страницы, которая будет встроена в домашнюю страницу:', + 'es-es': 'Una URL a una página que se integrará en la página de inicio:', + 'fr-fr': "Une URL vers une page qui serait intégrée à la page d'accueil :", + 'uk-ua': 'URL-адреса сторінки, яка буде вбудована на домашній сторінці:', + 'de-ch': + 'Eine URL zu einer Seite, die auf der Startseite eingebettet werden soll:', + 'pt-br': 'Um URL para uma página que seria incorporada na página inicial:', + }, + specifyNetworkBadge: { + 'en-us': 'Specify Network Badge', + 'ru-ru': 'Укажите сетевой значок', + 'es-es': 'Especificar la insignia de red', + 'fr-fr': 'Spécifier le badge réseau', + 'uk-ua': 'Укажіть значок мережі', + 'de-ch': 'Netzwerk-Badge angeben', + 'pt-br': 'Especificar emblema de rede', + }, + url: { + 'en-us': 'URL', + 'de-ch': 'URL', + 'es-es': 'URL', + 'fr-fr': 'URL', + 'uk-ua': 'URL', + 'ru-ru': 'URL', + 'pt-br': 'URL', + }, + pickAttachment: { + 'en-us': 'Pick an attachment', + 'es-es': 'Elige un archivo adjunto', + 'fr-fr': 'Choisissez une pièce jointe', + 'ru-ru': 'Выберите вложение', + 'uk-ua': 'Виберіть вкладення', + 'de-ch': 'Wählen Sie einen Anhang', + 'pt-br': 'Escolha um anexo', + }, + attachmentFailed: { + 'en-us': 'The attachment failed to load.', + 'de-ch': 'Der Anhang konnte nicht geladen werden.', + 'es-es': 'No se pudo cargar el archivo adjunto.', + 'fr-fr': "La pièce jointe n'a pas pu être chargée.", + 'ru-ru': 'Не удалось загрузить вложение.', + 'uk-ua': 'Не вдалося завантажити вкладений файл.', + 'pt-br': 'O anexo não pôde ser carregado.', + }, + pickImage: { + 'en-us': 'Pick an image', + 'de-ch': 'Wählen Sie ein Bild aus', + 'es-es': 'Elige una imagen', + 'fr-fr': 'Choisissez une image', + 'ru-ru': 'Выберите изображение', + 'uk-ua': 'Виберіть зображення', + 'pt-br': 'Escolha uma imagem', + }, + customLogo: { + 'en-us': 'Expanded Image URL', + 'de-ch': 'Erweiterte Bild-URL', + 'es-es': 'URL de imagen expandida', + 'fr-fr': "URL de l'image étendue", + 'ru-ru': 'URL-адрес развернутого изображения', + 'uk-ua': 'Розширена URL-адреса зображення', + 'pt-br': 'URL da imagem expandida', + }, + customLogoCollapsed: { + 'en-us': 'Collapsed Image URL', + 'de-ch': 'URL des minimierten Bildes', + 'es-es': 'URL de imagen contraída', + 'fr-fr': "URL de l'image réduite", + 'ru-ru': 'URL-адрес свернутого изображения', + 'uk-ua': 'URL-адреса згорнутого зображення', + 'pt-br': 'URL da imagem recolhida', + }, + customLogoDescription: { + 'en-us': + 'A URL to an image that would be displayed next to the Specify logo in the navigation menu.', + 'de-ch': + 'Eine URL zu einem Bild, das neben dem angegebenen Logo im Navigationsmenü angezeigt wird.', + 'es-es': + 'Una URL a una imagen que se mostrará junto al logotipo Especificar en el menú de navegación.', + 'fr-fr': + 'Une URL vers une image qui serait affichée à côté du logo Specify dans le menu de navigation.', + 'ru-ru': + 'URL-адрес изображения, которое будет отображаться рядом с логотипом «Укажите» в меню навигации.', + 'uk-ua': + 'URL-адреса зображення, яке відображатиметься поруч із «Вказати логотип» у меню навігації.', + 'pt-br': + 'Um URL para uma imagem que seria exibida ao lado do logotipo Especificar no menu de navegação.', + }, + attachmentPreviewMode: { + 'en-us': 'Attachment preview mode', + 'de-ch': 'Anhangsvorschaumodus', + 'es-es': 'Modo de vista previa de archivos adjuntos', + 'fr-fr': "Mode d'aperçu des pièces jointes", + 'ru-ru': 'Режим предварительного просмотра вложений', + 'uk-ua': 'Режим попереднього перегляду вкладених файлів', + 'pt-br': 'Modo de visualização de anexos', + }, + fullResolution: { + 'en-us': 'Full Resolution', + 'de-ch': 'Volle Auflösung', + 'es-es': 'Resolución completa', + 'fr-fr': 'Pleine résolution', + 'ru-ru': 'Полное разрешение', + 'uk-ua': 'Повна роздільна здатність', + 'pt-br': 'Resolução completa', + }, + thumbnail: { + 'en-us': 'Thumbnail', + 'de-ch': 'Miniaturansicht', + 'es-es': 'Uña del pulgar', + 'fr-fr': 'Vignette', + 'ru-ru': 'Миниатюра', + 'uk-ua': 'Мініатюра', + 'pt-br': 'Miniatura', + }, + addSearchBarHomePage: { + 'en-us': 'Add Search Bar on home page', + 'de-ch': 'Suchleiste auf der Startseite hinzufügen', + 'es-es': 'Agregar barra de búsqueda en la página de inicio', + 'fr-fr': "Ajouter une barre de recherche sur la page d'accueil", + 'ru-ru': 'Добавить панель поиска на домашнюю страницу', + 'uk-ua': 'Додайте рядок пошуку на головну сторінку', + 'pt-br': 'Adicionar barra de pesquisa na página inicial', + }, +} as const; + +export const preferencesContentText = createDictionary( + preferencesContentDictionary +); diff --git a/specifyweb/frontend/js_src/lib/localization/preferences.general.ts b/specifyweb/frontend/js_src/lib/localization/preferences.general.ts new file mode 100644 index 00000000000..be5f0f1b3a9 --- /dev/null +++ b/specifyweb/frontend/js_src/lib/localization/preferences.general.ts @@ -0,0 +1,2123 @@ +/** + * Localization strings for general preferences. + * + * @module + */ +import { createDictionary } from './utils'; +// Refer to "Guidelines for Programmers" in ./README.md before editing this file +export const preferencesGeneralDictionary = { + preferences: { + 'en-us': 'Preferences', + 'ru-ru': 'Настройки', + 'es-es': 'Preferencias', + 'fr-fr': 'Préférences', + 'uk-ua': 'Уподобання', + 'de-ch': 'Einstellungen', + 'pt-br': 'Preferências', + }, + customization: { + 'en-us': 'Customization', + 'ru-ru': 'Настройка', + 'es-es': 'Personalización', + 'fr-fr': 'Personnalisation', + 'uk-ua': 'Спеціальнізація', + 'de-ch': 'Anpassung', + 'pt-br': 'Personalização', + }, + userPreferences: { + 'en-us': 'User Preferences', + 'ru-ru': 'Настройки пользователя', + 'es-es': 'Preferencias del usuario', + 'fr-fr': "Préférences de l'utilisateur", + 'uk-ua': 'Налаштування користувача', + 'de-ch': 'Benutzereinstellungen', + 'pt-br': 'Preferências do usuário', + }, + defaultUserPreferences: { + 'en-us': 'Default User Preferences', + 'ru-ru': 'Настройки пользователя по умолчанию', + 'es-es': 'Preferencias de usuario predeterminadas', + 'fr-fr': 'Préférences utilisateur par défaut', + 'uk-ua': 'Параметри користувача за умовчанням', + 'de-ch': 'Standardbenutzereinstellungen', + 'pt-br': 'Preferências de usuário padrão', + }, + general: { + 'en-us': 'General', + 'ru-ru': 'Общий', + 'es-es': 'General', + 'fr-fr': 'Image personnalisée', + 'uk-ua': 'Спеціальне зображення', + 'de-ch': 'Allgemein', + 'pt-br': 'Em geral', + }, + ui: { + 'en-us': 'User Interface', + 'ru-ru': 'Пользовательский интерфейс', + 'es-es': 'Interfaz de usuario', + 'fr-fr': 'Interface utilisateur', + 'uk-ua': 'Інтерфейс користувача', + 'de-ch': 'Benutzeroberfläche', + 'pt-br': 'Interface do usuário', + }, + theme: { + 'en-us': 'Theme', + 'ru-ru': 'Тема', + 'es-es': 'Tema', + 'fr-fr': 'Thème', + 'uk-ua': 'Тема', + 'de-ch': 'Thema', + 'pt-br': 'Tema', + }, + useSystemSetting: { + 'en-us': 'Use system setting', + 'ru-ru': 'Использовать системные настройки', + 'es-es': 'Utilizar la configuración del sistema', + 'fr-fr': 'Utiliser les paramètres du système', + 'uk-ua': 'Використовуйте налаштування системи', + 'de-ch': 'Systemeinstellung verwenden', + 'pt-br': 'Usar configuração do sistema', + }, + inheritOsSettings: { + 'en-us': 'Copies value from your Operating System settings', + 'ru-ru': 'Копирует значение из настроек вашей операционной системы', + 'es-es': 'Copia el valor de la configuración de su sistema operativo', + 'fr-fr': "Copie la valeur des paramètres de votre système d'exploitation", + 'uk-ua': 'Копіює значення з налаштувань вашої операційної системи', + 'de-ch': 'Übernimmt den Wert aus Ihren Betriebssystemeinstellungen', + 'pt-br': 'Copia o valor das configurações do seu sistema operacional', + }, + light: { + comment: 'Light mode', + 'en-us': 'Light', + 'ru-ru': 'Свет', + 'es-es': 'Luz', + 'fr-fr': 'Lumière', + 'uk-ua': 'світло', + 'de-ch': 'Hell', + 'pt-br': 'Luz', + }, + dark: { + comment: 'Dark mode', + 'en-us': 'Dark', + 'ru-ru': 'Темный', + 'es-es': 'Oscuro', + 'fr-fr': 'Sombre', + 'uk-ua': 'Темний', + 'de-ch': 'Dunkel', + 'pt-br': 'Escuro', + }, + reduceMotion: { + 'en-us': 'Reduce motion', + 'ru-ru': 'Уменьшите движение', + 'es-es': 'Reducir el movimiento', + 'fr-fr': 'Réduire les mouvements', + 'uk-ua': 'Зменшити рух', + 'de-ch': 'Bewegung reduzieren', + 'pt-br': 'Reduzir movimento', + }, + reduceMotionDescription: { + 'en-us': 'Disable non-essential animations and transitions.', + 'ru-ru': 'Отключите ненужные анимации и переходы.', + 'es-es': 'Desactivar animaciones y transiciones no esenciales.', + 'fr-fr': 'Désactivez les animations et les transitions non essentielles.', + 'uk-ua': "Вимкніть необов'язкову анімацію та переходи.", + 'de-ch': 'Nicht erforderliche Animationen und Übergänge deaktivieren.', + 'pt-br': 'Desabilite animações e transições não essenciais.', + }, + reduceTransparency: { + 'en-us': 'Reduce transparency', + 'ru-ru': 'Уменьшить прозрачность', + 'es-es': 'Reducir la transparencia', + 'fr-fr': 'Réduire la transparence', + 'uk-ua': 'Зменшити прозорість', + 'de-ch': 'Transparenz reduzieren', + 'pt-br': 'Reduzir a transparência', + }, + reduceTransparencyDescription: { + 'en-us': + 'Whether to disable translucent backgrounds for user interface components whenever possible (e.g. table headers in tree view).', + 'ru-ru': + 'Следует ли отключать полупрозрачный фон для компонентов пользовательского интерфейса, когда это возможно (например, заголовки таблиц в древовидной структуре).', + 'es-es': + 'Si se deben deshabilitar los fondos translúcidos para los componentes de la interfaz de usuario siempre que sea posible (por ejemplo, encabezados de tabla en la vista de árbol).', + 'fr-fr': + "S'il faut désactiver les arrière-plans translucides pour les composants de l'interface utilisateur chaque fois que possible (par exemple, les en-têtes de tableau dans l'arborescence).", + 'uk-ua': + 'Чи вимикати напівпрозорий фон для компонентів інтерфейсу користувача, коли це можливо (наприклад, заголовки таблиць у перегляді дерева).', + 'de-ch': + 'Durchsichtige Hintergründe für Benutzeroberflächenkomponenten wann immer möglich deaktivieren (z. B. Tabellenüberschriften in der Baumansicht).', + 'pt-br': + 'Se deve ou não desabilitar fundos translúcidos para componentes da interface do usuário sempre que possível (por exemplo, cabeçalhos de tabela na visualização em árvore).', + }, + contrast: { + 'en-us': 'Contrast', + 'ru-ru': 'Контраст', + 'es-es': 'Contraste', + 'fr-fr': 'Contraste', + 'uk-ua': 'Контраст', + 'de-ch': 'Kontrast', + 'pt-br': 'Contraste', + }, + increase: { + 'en-us': 'Increase', + 'ru-ru': 'Увеличивать', + 'es-es': 'Aumentar', + 'fr-fr': 'Augmenter', + 'uk-ua': 'Збільшити', + 'de-ch': 'Erhöhen', + 'pt-br': 'Aumentar', + }, + reduce: { + 'en-us': 'Reduce', + 'ru-ru': 'Уменьшать', + 'es-es': 'Reducir', + 'fr-fr': 'Réduire', + 'uk-ua': 'Зменшити', + 'de-ch': 'Verringern', + 'pt-br': 'Reduzir', + }, + noPreference: { + 'en-us': 'No preference', + 'ru-ru': 'Нет предпочтений', + 'es-es': 'Sin preferencia', + 'fr-fr': 'Pas de préférence', + 'uk-ua': 'Без переваг', + 'de-ch': 'Keine Präferenz', + 'pt-br': 'Sem preferência', + }, + fontSize: { + 'en-us': 'Font size', + 'ru-ru': 'Размер шрифта', + 'es-es': 'Tamaño de fuente', + 'fr-fr': 'Taille de police', + 'uk-ua': 'Розмір шрифту', + 'de-ch': 'Schriftgrösse', + 'pt-br': 'Tamanho da fonte', + }, + fontFamily: { + 'en-us': 'Font family', + 'ru-ru': 'Семейство шрифтов', + 'es-es': 'Familia de fuentes', + 'fr-fr': 'Famille de polices', + 'uk-ua': 'Сімейство шрифтів', + 'de-ch': 'Schrift-Familie', + 'pt-br': 'Família de fontes', + }, + fontFamilyDescription: { + 'en-us': + 'You can specify any font that is on your computer, even if it is not in the list. A comma-separated list of fonts is also supported, where each subsequent font will be used if the previous one is not available.', + 'ru-ru': + 'Вы можете указать любой шрифт, установленный на вашем компьютере, даже если его нет в списке. Также поддерживается список шрифтов, разделённый запятыми, где каждый последующий шрифт будет использоваться, если предыдущий недоступен.', + 'es-es': + 'Puede especificar cualquier fuente de su ordenador, incluso si no está en la lista. También se admite una lista de fuentes separadas por comas, donde se usará cada fuente subsiguiente si la anterior no está disponible.', + 'fr-fr': + "Vous pouvez spécifier n'importe quelle police présente sur votre ordinateur, même si elle ne figure pas dans la liste. Une liste de polices séparées par des virgules est également prise en charge ; chaque police suivante sera utilisée si la précédente n'est pas disponible.", + 'uk-ua': + "Ви можете вказати будь-який шрифт, який є на вашому комп'ютері, навіть якщо його немає в списку. Також підтримується розділений комами список шрифтів, у якому використовуватиметься другий шрифт, якщо перший недоступний тощо.", + 'de-ch': + 'Sie können jede Schriftart angeben, die sich auf Ihrem Computer befindet, auch wenn diese nicht in der Liste enthalten ist. Eine durch Kommas getrennte Liste von Schriftarten wird ebenfalls unterstützt, wobei die zweite Schriftart verwendet wird, wenn die erste nicht verfügbar ist usw.', + 'pt-br': + 'Você pode especificar qualquer fonte que esteja no seu computador, mesmo que ela não esteja na lista. Uma lista de fontes separadas por vírgulas também é suportada, onde cada fonte subsequente será usada se a anterior não estiver disponível.', + }, + defaultFont: { + 'en-us': '(default font)', + 'ru-ru': '(шрифт по умолчанию)', + 'es-es': '(fuente predeterminada)', + 'fr-fr': '(police par défaut)', + 'uk-ua': '(типовий шрифт)', + 'de-ch': '(Standardschriftart)', + 'pt-br': '(fonte padrão)', + }, + maxFormWidth: { + 'en-us': 'Max form width', + 'ru-ru': 'Максимальная ширина формы', + 'es-es': 'Ancho máximo del formulario', + 'fr-fr': 'Largeur maximale du formulaire', + 'uk-ua': 'Максимальна ширина форми', + 'de-ch': 'Maximale Formularbreite', + 'pt-br': 'Largura máxima do formulário', + }, + fieldBackgrounds: { + 'en-us': 'Field backgrounds', + 'ru-ru': 'Фоны полей', + 'es-es': 'Fondos de campo', + 'fr-fr': 'Milieux de terrain', + 'uk-ua': 'Польові фони', + 'de-ch': 'Feldhintergründe', + 'pt-br': 'Fundos de campo', + }, + fieldBackground: { + 'en-us': 'Field background', + 'ru-ru': 'Фон поля', + 'es-es': 'Fondo de campo', + 'fr-fr': 'Contexte du terrain', + 'uk-ua': 'Поле фону', + 'de-ch': 'Feldhintergrund', + 'pt-br': 'Contexto de campo', + }, + disabledFieldBackground: { + 'en-us': 'Disabled field background', + 'ru-ru': 'Отключенный фон поля', + 'es-es': 'Fondo de campo deshabilitado', + 'fr-fr': 'Fond de champ désactivé', + 'uk-ua': 'Вимкнений фон поля', + 'de-ch': 'Deaktivierter Feldhintergrund', + 'pt-br': 'Fundo de campo desativado', + }, + invalidFieldBackground: { + 'en-us': 'Invalid field background', + 'ru-ru': 'Неверный фон поля', + 'es-es': 'Fondo de campo no válido', + 'fr-fr': 'Fond de champ invalide', + 'uk-ua': 'Недійсний фон поля', + 'de-ch': 'Ungültiger Feldhintergrund', + 'pt-br': 'Fundo de campo inválido', + }, + requiredFieldBackground: { + 'en-us': 'Required field background', + 'ru-ru': 'Обязательное поле фон', + 'es-es': 'Antecedentes del campo obligatorio', + 'fr-fr': 'Contexte du champ obligatoire', + 'uk-ua': "Обов'язковий фон поля", + 'de-ch': 'Feldhintergrund erforderlich', + 'pt-br': 'Histórico de campo obrigatório', + }, + darkFieldBackground: { + 'en-us': 'Field background (dark theme)', + 'ru-ru': 'Фон поля (тёмная тема)', + 'es-es': 'Fondo de campo (tema oscuro)', + 'fr-fr': 'Fond de champ (thème sombre)', + 'uk-ua': 'Фон поля (темна тема)', + 'de-ch': 'Feldhintergrund (Dunkles Thema)', + 'pt-br': 'Fundo de campo (tema escuro)', + }, + darkDisabledFieldBackground: { + 'en-us': 'Disabled field background (dark theme)', + 'ru-ru': 'Отключенный фон поля (тёмная тема)', + 'es-es': 'Fondo de campo deshabilitado (tema oscuro)', + 'fr-fr': 'Fond de champ désactivé (thème sombre)', + 'uk-ua': 'Вимкнений фон поля (темна тема)', + 'de-ch': 'Deaktivierter Feldhintergrund (Dunkles Thema)', + 'pt-br': 'Fundo de campo desativado (tema escuro)', + }, + darkInvalidFieldBackground: { + 'en-us': 'Invalid field background (dark theme)', + 'ru-ru': 'Недопустимый фон поля (тёмная тема)', + 'es-es': 'Fondo de campo no válido (tema oscuro)', + 'fr-fr': 'Largeur de colonne de grille de sous-vue flexible', + 'uk-ua': 'Гнучка ширина стовпця сітки вкладеного перегляду', + 'de-ch': 'Ungültiger Feldhintergrund (Dunkles Thema)', + 'pt-br': 'Fundo de campo inválido (tema escuro)', + }, + darkRequiredFieldBackground: { + 'en-us': 'Required field background (dark theme)', + 'ru-ru': 'Обязательное поле фон (тёмная тема)', + 'es-es': 'Fondo del campo obligatorio (tema oscuro)', + 'fr-fr': 'Fond de champ obligatoire (thème sombre)', + 'uk-ua': 'Обов’язковий фон поля (темна тема)', + 'de-ch': 'Feldhintergrund erforderlich (Dunkles Thema)', + 'pt-br': 'Fundo de campo obrigatório (tema escuro)', + }, + dialogs: { + 'en-us': 'Dialogs', + 'ru-ru': 'Диалоги', + 'es-es': 'Diálogos', + 'fr-fr': 'Boîtes de dialogue', + 'uk-ua': 'Діалоги', + 'de-ch': 'Dialoge', + 'pt-br': 'Diálogos', + }, + appearance: { + 'en-us': 'Appearance', + 'ru-ru': 'Появление', + 'es-es': 'Apariencia', + 'fr-fr': 'Apparence', + 'uk-ua': 'Зовнішній вигляд', + 'de-ch': 'Aussehen', + 'pt-br': 'Aparência', + }, + buttonsLight: { + 'en-us': 'Buttons (light mode)', + 'de-ch': 'Buttons (Helles Thema)', + 'es-es': 'Botones (modo luz)', + 'fr-fr': 'Boutons (mode lumière)', + 'ru-ru': 'Кнопки (световой режим)', + 'uk-ua': 'Кнопки (світлий режим)', + 'pt-br': 'Botões (modo claro)', + }, + buttonsDark: { + 'en-us': 'Buttons (dark mode)', + 'de-ch': 'Buttons (Dunkles Thema)', + 'es-es': 'Botones (modo oscuro)', + 'fr-fr': 'Boutons (mode sombre)', + 'ru-ru': 'Кнопки (темный режим)', + 'uk-ua': 'Кнопки (темний режим)', + 'pt-br': 'Botões (modo escuro)', + }, + translucentDialog: { + 'en-us': 'Translucent dialogs', + 'ru-ru': 'Прозрачные диалоги', + 'es-es': 'Diálogos translúcidos', + 'fr-fr': 'Dialogues translucides', + 'uk-ua': 'Напівпрозорі діалоги', + 'de-ch': 'Durchscheinende Dialoge', + 'pt-br': 'Diálogos translúcidos', + }, + translucentDialogDescription: { + 'en-us': 'Whether dialogs have translucent background.', + 'ru-ru': 'Имеют ли диалоговые окна полупрозрачный фон.', + 'es-es': 'Si los diálogos tienen fondo translúcido.', + 'fr-fr': 'Si les boîtes de dialogue ont un fond translucide.', + 'uk-ua': 'Чи мають діалоги прозорий фон.', + 'de-ch': 'Dialogfenster mit durchscheinenden Hintergrund.', + 'pt-br': 'Se os diálogos têm fundo translúcido.', + }, + alwaysPrompt: { + 'en-us': 'Always prompt to choose collection', + 'ru-ru': 'Всегда предлагайте выбрать коллекцию', + 'es-es': 'Siempre dispuesto a elegir la colección', + 'fr-fr': 'Toujours invité à choisir la collection', + 'uk-ua': 'Завжди підкажуть вибрати колекцію', + 'de-ch': 'Immer zur Auswahl der Sammlung auffordern', + 'pt-br': 'Sempre pronto para escolher a coleção', + }, + treeEditor: { + 'en-us': 'Tree Editor', + 'ru-ru': 'Редактор деревьев', + 'es-es': 'Editor de árboles', + 'fr-fr': "Éditeur d'arborescence", + 'uk-ua': 'Редактор дерева', + 'de-ch': 'Baumeditor', + 'pt-br': 'Editor de Árvore', + }, + treeAccentColor: { + 'en-us': 'Tree accent color', + 'ru-ru': 'Акцентный цвет дерева', + 'es-es': 'Color de acento del árbol', + 'fr-fr': "Couleur d'accent d'arbre", + 'uk-ua': 'Колір акценту дерева', + 'de-ch': 'Baumakzentfarbe', + 'pt-br': 'Cor de destaque da árvore', + }, + synonymColor: { + 'en-us': 'Synonym color', + 'ru-ru': 'Синоним цвет', + 'es-es': 'Color sinónimo', + 'fr-fr': 'Synonyme couleur', + 'uk-ua': 'Синонім кольору', + 'de-ch': 'Synonymfarbe', + 'pt-br': 'Cor sinônimo', + }, + treeStatsThreshold: { + 'en-us': 'Minimum rank for Collection Object counts', + 'ru-ru': 'Минимальный ранг для подсчета коллекционных объектов', + 'es-es': 'Rango mínimo para recuentos de objetos de colección', + 'fr-fr': 'Rang minimal pour les comptes des objets de collection', + 'uk-ua': 'Мінімальний ранг для підрахунку колекційних об’єктів', + 'de-ch': 'Minimaler Rang für Sammlungsobjektzählungen', + 'pt-br': 'Classificação mínima para contagens de objetos de coleção', + }, + treeStatsThresholdDescription: { + 'en-us': + 'Show Collection Object counts only for nodes with RankID greater than or equal to this value.', + 'ru-ru': + 'Показывать количество коллекционных объектов только для узлов с RankID больше или равным этому значению.', + 'es-es': + 'Mostrar recuentos de objetos de colección solo para nodos con RankID mayor o igual que este valor.', + 'fr-fr': + 'Afficher les comptes d’objets de collection uniquement pour les nœuds dont le RankID est supérieur ou égal à cette valeur.', + 'uk-ua': + 'Показувати кількість колекційних об’єктів лише для вузлів з RankID, що дорівнює або перевищує це значення.', + 'de-ch': + 'Zeige Zählungen von Sammlungsobjekten nur für Knoten mit einem RankID grösser oder gleich diesem Wert.', + 'pt-br': + 'Mostrar contagens de objetos de coleção apenas para nós com RankID maior ou igual a este valor.', + }, + showNewDataSetWarning: { + 'en-us': 'Show new Data Set warning', + 'ru-ru': 'Показать предупреждение о новом наборе данных', + 'es-es': 'Mostrar nueva advertencia de conjunto de datos', + 'fr-fr': "Afficher un nouvel avertissement sur l'ensemble de données", + 'uk-ua': 'Показати попередження про новий набір даних', + 'de-ch': 'Warnung für neuen Datensatz anzeigen', + 'pt-br': 'Mostrar novo aviso de conjunto de dados', + }, + showNewDataSetWarningDescription: { + 'en-us': 'Show an informational message when creating a new Data Set.', + 'ru-ru': + 'Показывать информационное сообщение при создании нового набора данных.', + 'es-es': + 'Mostrar un mensaje informativo al crear un nuevo conjunto de datos.', + 'fr-fr': + "Afficher un message d'information lors de la création d'un nouvel ensemble de données.", + 'uk-ua': + 'Показувати інформаційне повідомлення під час створення нового набору даних.', + 'de-ch': 'Zeige eine Meldung beim erstellen eines neuen Datensatzes an.', + 'pt-br': + 'Exibir uma mensagem informativa ao criar um novo conjunto de dados.', + }, + header: { + 'en-us': 'Navigation Menu', + 'ru-ru': 'Меню навигации', + 'es-es': 'Menú de navegación', + 'fr-fr': 'le menu de navigation', + 'uk-ua': 'Навігаційне меню', + 'de-ch': 'Navigationsmenü', + 'pt-br': 'Menu de navegação', + }, + application: { + 'en-us': 'Application', + 'ru-ru': 'Приложение', + 'es-es': 'Solicitud', + 'fr-fr': 'Application', + 'uk-ua': 'застосування', + 'de-ch': 'Anwendung', + 'pt-br': 'Aplicativo', + }, + allowDismissingErrors: { + 'en-us': 'Allow dismissing error messages', + 'ru-ru': 'Разрешить отклонять сообщения об ошибках', + 'es-es': 'Permitir descartar mensajes de error', + 'fr-fr': "Autoriser le rejet des messages d'erreur", + 'uk-ua': 'Дозволити закривати повідомлення про помилки', + 'de-ch': 'Erlaube das Verwerfen von Fehlermeldungen', + 'pt-br': 'Permitir descartar mensagens de erro', + }, + updatePageTitle: { + 'en-us': 'Update page title', + 'ru-ru': 'Обновить заголовок страницы', + 'es-es': 'Actualizar el título de la página', + 'fr-fr': 'Mettre à jour le titre de la page', + 'uk-ua': 'Оновити назву сторінки', + 'de-ch': 'Seitentitel aktualisieren', + 'pt-br': 'Atualizar título da página', + }, + updatePageTitleDescription: { + 'en-us': + "Whether to update the title of the page to match dialog's header.", + 'ru-ru': + 'Обновлять ли заголовок страницы в соответствии с заголовком диалогового окна.', + 'es-es': + 'Si se debe actualizar el título de la página para que coincida con el encabezado del cuadro de diálogo.', + 'fr-fr': + "S'il faut mettre à jour le titre de la page pour qu'il corresponde à l'en-tête de la boîte de dialogue.", + 'uk-ua': + 'Чи оновлювати назву сторінки відповідно до заголовка діалогового вікна.', + 'de-ch': + 'Titel der Seite so aktualisieren, dass er mit der Kopfzeile des Dialogs übereinstimmt.', + 'pt-br': + 'Se o título da página deve ser atualizado para corresponder ao cabeçalho da caixa de diálogo.', + }, + updatePageTitleFormDescription: { + 'en-us': 'Whether to update the title of the page to match current record.', + 'ru-ru': + 'Следует ли обновить заголовок страницы в соответствии с текущей записью.', + 'es-es': + 'Si desea actualizar el título de la página para que coincida con el registro actual.', + 'fr-fr': + "S'il faut mettre à jour le titre de la page pour qu'il corresponde à l'enregistrement actuel.", + 'uk-ua': 'Чи оновлювати назву сторінки відповідно до поточного запису.', + 'de-ch': + 'Titel der Seite aktualisieren, damit er mit dem aktuellen Datensatz übereinstimmt.', + 'pt-br': + 'Se o título da página deve ser atualizado para corresponder ao registro atual.', + }, + queryComboBox: { + 'en-us': 'Query Combo Box', + 'ru-ru': 'Поле со списком запросов', + 'es-es': 'Cuadro combinado de consulta', + 'uk-ua': 'Поле зі списком запитів', + 'de-ch': 'Abfrage-Kombinationsfeld', + 'fr-fr': 'Zone de liste déroulante de requête', + 'pt-br': 'Caixa de combinação de consulta', + }, + searchAlgorithm: { + 'en-us': 'Search Algorithm', + 'ru-ru': 'Алгоритм поиска', + 'es-es': 'Algoritmo de búsqueda', + 'fr-fr': 'Algorithme de recherche', + 'uk-ua': 'Алгоритм пошуку', + 'de-ch': 'Suchalgorithmus', + 'pt-br': 'Algoritmo de Busca', + }, + treeSearchAlgorithm: { + 'en-us': 'Search Algorithm (for relationships with tree tables)', + 'ru-ru': 'Алгоритм поиска (для связей с древовидными таблицами)', + 'es-es': 'Algoritmo de búsqueda (para relaciones con tablas de árbol)', + 'fr-fr': + 'Algorithme de recherche (pour les relations avec les tables arborescentes)', + 'uk-ua': 'Алгоритм пошуку (для зв’язків із деревоподібними таблицями)', + 'de-ch': 'Suchalgorithmus (für Beziehungen mit Baumtabellen)', + 'pt-br': 'Algoritmo de busca (para relacionamentos com tabelas de árvore)', + }, + startsWithInsensitive: { + 'en-us': 'Starts With (case-insensitive)', + 'ru-ru': 'Начинается с (без учета регистра)', + 'es-es': 'Comienza con (sin distinguir entre mayúsculas y minúsculas)', + 'fr-fr': 'Commence par (insensible à la casse)', + 'uk-ua': 'Починається з (без урахування регістру)', + 'de-ch': 'Beginnt mit (Groß-/Kleinschreibung wird nicht beachtet)', + 'pt-br': 'Começa com (sem distinção de maiúsculas e minúsculas)', + }, + startsWithDescription: { + 'en-us': 'Search for values that begin with a given query string.', + 'ru-ru': 'Поиск значений, начинающихся с заданной строки запроса.', + 'es-es': + 'Busque valores que comiencen con una cadena de consulta determinada.', + 'fr-fr': + 'Rechercher des valeurs commençant par une chaîne de requête donnée.', + 'uk-ua': 'Пошук значень, які починаються з заданого рядка запиту.', + 'de-ch': + 'Suchen Sie nach Werten, die mit einer bestimmten Abfragezeichenfolge beginnen.', + 'pt-br': + 'Pesquisar valores que começam com uma determinada sequência de consulta.', + }, + startsWithCaseSensitive: { + 'en-us': 'Starts With (case-sensitive)', + 'ru-ru': 'Начинается с (с учетом регистра)', + 'es-es': 'Comienza con (sensible a mayúsculas y minúsculas)', + 'fr-fr': 'Commence par (sensible à la casse)', + 'uk-ua': 'Починається з (з урахуванням регістру)', + 'de-ch': 'Beginnt mit (Groß-/Kleinschreibung beachten)', + 'pt-br': 'Começa com (diferencia maiúsculas de minúsculas)', + }, + startsWithCaseSensitiveDescription: { + 'en-us': 'Search for values that begin with a given query string.', + 'ru-ru': 'Поиск значений, начинающихся с заданной строки запроса.', + 'es-es': + 'Busque valores que comiencen con una cadena de consulta determinada.', + 'fr-fr': + 'Recherchez les valeurs qui commencent par une chaîne de requête donnée.', + 'uk-ua': 'Пошук значень, які починаються з заданого рядка запиту.', + 'de-ch': + 'Suchen Sie nach Werten, die mit einer bestimmten Abfragezeichenfolge beginnen.', + 'pt-br': + 'Pesquisar valores que começam com uma determinada sequência de consulta.', + }, + containsInsensitive: { + 'en-us': 'Contains (case-insensitive)', + 'ru-ru': 'Содержит (без учета регистра)', + 'es-es': 'Contiene (sin distinguir entre mayúsculas y minúsculas)', + 'fr-fr': 'Contient (insensible à la casse)', + 'uk-ua': 'Містить (незалежно від регістру)', + 'de-ch': 'Enthält (Groß-/Kleinschreibung wird nicht beachtet)', + 'pt-br': 'Contém (sem distinção entre maiúsculas e minúsculas)', + }, + containsCaseSensitive: { + 'en-us': 'Contains (case-sensitive)', + 'ru-ru': 'Содержит (с учетом регистра)', + 'es-es': 'Contiene (sensible a mayúsculas y minúsculas)', + 'fr-fr': 'Contient (sensible à la casse)', + 'uk-ua': 'Містить (з урахуванням регістру)', + 'de-ch': 'Enthält (Groß-/Kleinschreibung beachten)', + 'pt-br': 'Contém (diferencia maiúsculas de minúsculas)', + }, + containsDescription: { + 'en-us': + 'Search for values that contain a given query string (case-insensitive).', + 'ru-ru': + 'Поиск значений, содержащих заданную строку запроса (без учета регистра).', + 'es-es': + 'Busque valores que contengan una cadena de consulta determinada (sin distinguir entre mayúsculas y minúsculas).', + 'uk-ua': + 'Пошук значень, які містять заданий рядок запиту (незалежно від регістру).', + 'de-ch': + 'Suchen Sie nach Werten, die eine bestimmte Abfragezeichenfolge enthalten (ohne Berücksichtigung der Groß-/Kleinschreibung).', + 'fr-fr': + 'Recherchez les valeurs contenant une chaîne de requête donnée (insensible à la casse).', + 'pt-br': + 'Pesquisar valores que contenham uma determinada sequência de consulta (sem distinção de maiúsculas e minúsculas).', + }, + containsCaseSensitiveDescription: { + 'en-us': + 'Search for values that contain a given query string (case-sensitive).', + 'ru-ru': + 'Поиск значений, содержащих заданную строку запроса (с учетом регистра).', + 'es-es': + 'Busque valores que contengan una cadena de consulta determinada (distingue entre mayúsculas y minúsculas).', + 'fr-fr': + 'Recherchez les valeurs contenant une chaîne de requête donnée (sensible à la casse).', + 'uk-ua': + 'Пошук значень, які містять заданий рядок запиту (з урахуванням регістру).', + 'de-ch': + 'Suchen Sie nach Werten, die eine bestimmte Abfragezeichenfolge enthalten (Groß-/Kleinschreibung beachten).', + 'pt-br': + 'Pesquisar valores que contenham uma determinada sequência de consulta (diferencia maiúsculas de minúsculas).', + }, + containsSecondDescription: { + 'en-us': + 'Can use _ to match any single character or % to match any number of characters.', + 'ru-ru': + 'Можно использовать _ для соответствия любому отдельному символу или % для соответствия любому количеству символов.', + 'es-es': + 'Puede utilizar _ para que coincida con cualquier carácter individual o % para que coincida con cualquier número de caracteres.', + 'fr-fr': + "Peut utiliser _ pour correspondre à n'importe quel caractère ou % pour correspondre à n'importe quel nombre de caractères.", + 'uk-ua': + 'Можна використовувати _ для відповідності будь-якому одному символу або % для відповідності будь-якій кількості символів.', + 'de-ch': + 'Sie können _ verwenden, um ein beliebiges einzelnes Zeichen abzugleichen, oder %, um eine beliebige Anzahl von Zeichen abzugleichen.', + 'pt-br': + 'Pode usar _ para corresponder a qualquer caractere único ou % para corresponder a qualquer número de caracteres.', + }, + highlightMatch: { + 'en-us': 'Highlight matched substring', + 'ru-ru': 'Выделить совпавшую подстроку', + 'es-es': 'Resaltar la subcadena coincidente', + 'fr-fr': 'Mettre en surbrillance la sous-chaîne correspondante', + 'uk-ua': 'Виділіть збіг підрядка', + 'de-ch': 'Markieren Sie übereinstimmende Teilzeichenfolgen', + 'pt-br': 'Destacar substring correspondente', + }, + languageDescription: { + 'en-us': 'Determines field captions, usage notes and table captions.', + 'ru-ru': + 'Определяет заголовки полей, примечания по использованию и заголовки таблиц.', + 'es-es': 'Determina títulos de campos, notas de uso y títulos de tablas.', + 'fr-fr': + "Détermine les légendes des champs, les notes d'utilisation et les légendes des tableaux.", + 'uk-ua': + 'Визначає підписи полів, примітки щодо використання та підписи таблиць.', + 'de-ch': + 'Legt Feldbeschriftungen, Verwendungshinweise und Tabellenbeschriftungen fest.', + 'pt-br': 'Determina legendas de campo, notas de uso e legendas de tabela.', + }, + showDialogIcon: { + 'en-us': 'Show icon in the header', + 'ru-ru': 'Показывать значок в заголовке', + 'es-es': 'Mostrar icono en el encabezado', + 'fr-fr': "Afficher l'icône dans l'en-tête", + 'uk-ua': 'Показати значок у заголовку', + 'de-ch': 'Symbol in der Kopfzeile anzeigen', + 'pt-br': 'Mostrar ícone no cabeçalho', + }, + scaleInterface: { + 'en-us': 'Scale Interface', + 'ru-ru': 'Интерфейс масштабирования', + 'es-es': 'Interfaz de escala', + 'fr-fr': 'Interface de balance', + 'uk-ua': 'Інтерфейс масштабу', + 'de-ch': 'Waagenschnittstelle', + 'pt-br': 'Interface de escala', + }, + scaleInterfaceDescription: { + 'en-us': 'Scale interface to match font size.', + 'ru-ru': 'Масштабируйте интерфейс в соответствии с размером шрифта.', + 'es-es': 'Escala la interfaz para que coincida con el tamaño de la fuente.', + 'fr-fr': "Adapter l'interface à la taille de la police.", + 'uk-ua': 'Масштабуйте інтерфейс відповідно до розміру шрифту.', + 'de-ch': + 'Skalieren Sie die Benutzeroberfläche, um sie an die Schriftgröße anzupassen.', + 'pt-br': 'Dimensione a interface para corresponder ao tamanho da fonte.', + }, + displayAuthor: { + 'en-us': 'Show author in the tree', + 'ru-ru': 'Показать автора в дереве', + 'es-es': 'Mostrar autor en el árbol', + 'fr-fr': "Afficher l'auteur dans l'arbre", + 'uk-ua': 'Показати автора в дереві', + 'de-ch': 'Autor im Baum anzeigen', + 'pt-br': 'Mostrar autor', + }, + welcomePage: { + 'en-us': 'Home Page', + 'ru-ru': 'Домашняя страница', + 'es-es': 'Página de inicio', + 'fr-fr': "Page d'accueil", + 'uk-ua': 'Домашня сторінка', + 'de-ch': 'Startseite', + 'pt-br': 'Página inicial', + }, + content: { + 'en-us': 'Content', + 'ru-ru': 'Содержание', + 'es-es': 'Contenido', + 'fr-fr': 'Contenu', + 'uk-ua': 'Зміст', + 'de-ch': 'Inhalt', + 'pt-br': 'Contente', + }, + defaultImage: { + 'en-us': 'Specify Logo', + 'ru-ru': 'Укажите логотип', + 'es-es': 'Especificar logotipo', + 'fr-fr': 'Spécifier le logo', + 'uk-ua': 'Вкажіть логотип', + 'de-ch': 'Logo angeben', + 'pt-br': 'Especificar logotipo', + }, + customImage: { + 'en-us': 'Custom Image', + 'ru-ru': 'Пользовательское изображение', + 'es-es': 'Imagen personalizada', + 'fr-fr': 'Image personnalisée', + 'uk-ua': 'Спеціальне зображення', + 'de-ch': 'Benutzerdefiniertes Bild', + 'pt-br': 'Imagem personalizada', + }, + embeddedWebpage: { + 'en-us': 'Embedded web page', + 'ru-ru': 'Встроенная веб-страница', + 'es-es': 'Página web incrustada', + 'fr-fr': 'Page Web intégrée', + 'uk-ua': 'Вбудована веб-сторінка', + 'de-ch': 'Eingebettete Webseite', + 'pt-br': 'Página da web incorporada', + }, + embeddedWebpageDescription: { + 'en-us': 'A URL to a page that would be embedded on the home page:', + 'ru-ru': 'URL-адрес страницы, которая будет встроена в домашнюю страницу:', + 'es-es': 'Una URL a una página que se integrará en la página de inicio:', + 'fr-fr': "Une URL vers une page qui serait intégrée à la page d'accueil :", + 'uk-ua': 'URL-адреса сторінки, яка буде вбудована на домашній сторінці:', + 'de-ch': + 'Eine URL zu einer Seite, die auf der Startseite eingebettet werden soll:', + 'pt-br': 'Um URL para uma página que seria incorporada na página inicial:', + }, + behavior: { + 'en-us': 'Behavior', + 'ru-ru': 'Поведение', + 'es-es': 'Comportamiento', + 'fr-fr': 'Comportement', + 'uk-ua': 'Поведінка', + 'de-ch': 'Verhalten', + 'pt-br': 'Comportamento', + }, + noRestrictionsMode: { + 'en-us': 'No restrictions mode', + 'ru-ru': 'Режим без ограничений', + 'es-es': 'Modo sin restricciones', + 'fr-fr': 'Mode sans restriction', + 'uk-ua': 'Режим без обмежень', + 'de-ch': 'Modus „Keine Einschränkungen“', + 'pt-br': 'Modo sem restrições', + }, + noRestrictionsModeWbDescription: { + 'en-us': 'Allows uploading data to any field in any table.', + 'ru-ru': 'Позволяет загружать данные в любое поле любой таблицы.', + 'es-es': 'Permite cargar datos a cualquier campo de cualquier tabla.', + 'fr-fr': + "Permet de télécharger des données dans n'importe quel champ de n'importe quelle table.", + 'uk-ua': 'Дозволяє завантажувати дані в будь-яке поле будь-якої таблиці.', + 'de-ch': + 'Ermöglicht das Hochladen von Daten in jedes Feld einer beliebigen Tabelle.', + 'pt-br': 'Permite carregar dados em qualquer campo de qualquer tabela.', + }, + noRestrictionsModeQueryDescription: { + 'en-us': 'Allows querying data from any field in any table.', + 'ru-ru': 'Позволяет запрашивать данные из любого поля любой таблицы.', + 'es-es': 'Permite consultar datos de cualquier campo de cualquier tabla.', + 'fr-fr': + "Permet d'interroger les données de n'importe quel champ de n'importe quelle table.", + 'uk-ua': 'Дозволяє запитувати дані з будь-якого поля будь-якої таблиці.', + 'de-ch': + 'Ermöglicht das Abfragen von Daten aus jedem Feld in jeder Tabelle.', + 'pt-br': 'Permite consultar dados de qualquer campo em qualquer tabela.', + }, + noRestrictionsModeWarning: { + 'en-us': + 'WARNING: enabling this may lead to data loss or database corruption. Please make sure you know what you are doing.', + 'ru-ru': + 'ВНИМАНИЕ: включение этой функции может привести к потере данных или повреждению базы данных. Убедитесь, что вы понимаете, что делаете.', + 'es-es': + 'ADVERTENCIA: Habilitar esta opción podría provocar la pérdida de datos o la corrupción de la base de datos. Asegúrese de saber lo que está haciendo.', + 'uk-ua': + 'ПОПЕРЕДЖЕННЯ: увімкнення цієї функції може призвести до втрати даних або пошкодження бази даних. Переконайтеся, що ви знаєте, що робите.', + 'de-ch': + 'WARNUNG: Das Aktivieren dieser Option kann zu Datenverlust oder Datenbankbeschädigung führen. Bitte stellen Sie sicher, dass Sie wissen, was Sie tun.', + 'fr-fr': + "AVERTISSEMENT : l'activation de cette option peut entraîner une perte de données ou une corruption de la base de données. Veuillez vous assurer que vous savez ce que vous faites.", + 'pt-br': + 'AVISO: habilitar esta opção pode levar à perda de dados ou à corrupção do banco de dados. Certifique-se de saber o que está fazendo.', + }, + adminsOnlyPreference: { + 'en-us': "You don't have permission to change this option", + 'ru-ru': 'У вас нет разрешения на изменение этой опции.', + 'es-es': 'No tienes permiso para cambiar esta opción', + 'fr-fr': "Vous n'êtes pas autorisé à modifier cette option", + 'uk-ua': 'Ви не маєте дозволу змінювати цей параметр', + 'de-ch': 'Sie haben keine Berechtigung, diese Option zu ändern', + 'pt-br': 'Você não tem permissão para alterar esta opção', + }, + stickyScrolling: { + 'en-us': 'Sticky scroll bar', + 'ru-ru': 'Липкая полоса прокрутки', + 'es-es': 'Barra de desplazamiento fija', + 'fr-fr': 'Barre de défilement collante', + 'uk-ua': 'Липка смуга прокрутки', + 'de-ch': 'Klebrige Bildlaufleiste', + 'pt-br': 'Barra de rolagem fixa', + }, + foreground: { + 'en-us': 'Foreground', + 'ru-ru': 'Передний план', + 'es-es': 'Primer plano', + 'fr-fr': 'Premier plan', + 'uk-ua': 'Передній план', + 'de-ch': 'Vordergrund', + 'pt-br': 'Primeiro plano', + }, + background: { + 'en-us': 'Background', + 'ru-ru': 'Фон', + 'es-es': 'Fondo', + 'fr-fr': 'Arrière-plan', + 'uk-ua': 'Фон', + 'de-ch': 'Hintergrund', + 'pt-br': 'Fundo', + }, + sidebarTheme: { + 'en-us': 'Sidebar theme', + 'de-ch': 'Seitenleistenthema', + 'es-es': 'Tema de la barra lateral', + 'fr-fr': 'Thème de la barre latérale', + 'ru-ru': 'Тема боковой панели', + 'uk-ua': 'Тема бічної панелі', + 'pt-br': 'Tema da barra lateral', + }, + darkForeground: { + 'en-us': 'Foreground (dark theme)', + 'ru-ru': 'Передний план (тёмная тема)', + 'es-es': 'Primer plano (tema oscuro)', + 'fr-fr': 'Premier plan (thème sombre)', + 'uk-ua': 'Передній план (темна тема)', + 'de-ch': 'Vordergrund (dunkles Design)', + 'pt-br': 'Primeiro plano (tema escuro)', + }, + darkBackground: { + 'en-us': 'Background (dark theme)', + 'ru-ru': 'Фон (тёмная тема)', + 'es-es': 'Fondo (tema oscuro)', + 'fr-fr': 'Arrière-plan (thème sombre)', + 'uk-ua': 'Фон (темна тема)', + 'de-ch': 'Hintergrund (dunkles Design)', + 'pt-br': 'Plano de fundo (tema escuro)', + }, + accentColor1: { + 'en-us': 'Accent color 1', + 'ru-ru': 'Акцентный цвет 1', + 'es-es': 'Color de acento 1', + 'fr-fr': "Couleur d'accent 1", + 'uk-ua': 'Акцентний колір 1', + 'de-ch': 'Akzentfarbe 1', + 'pt-br': 'Cor de destaque 1', + }, + accentColor2: { + 'en-us': 'Accent color 2', + 'ru-ru': 'Акцентный цвет 2', + 'es-es': 'Color de acento 2', + 'fr-fr': "Couleur d'accent 2", + 'uk-ua': 'Акцентний колір 2', + 'de-ch': 'Akzentfarbe 2', + 'pt-br': 'Cor de destaque 2', + }, + accentColor3: { + 'en-us': 'Accent color 3', + 'ru-ru': 'Акцентный цвет 3', + 'es-es': 'Color de acento 3', + 'fr-fr': "Couleur d'accent 3", + 'uk-ua': 'Акцентний колір 3', + 'de-ch': 'Akzentfarbe 3', + 'pt-br': 'Cor de destaque 3', + }, + accentColor4: { + 'en-us': 'Accent color 4', + 'ru-ru': 'Акцентный цвет 4', + 'es-es': 'Color de acento 4', + 'fr-fr': "Couleur d'accent 4", + 'uk-ua': 'Акцентний колір 4', + 'de-ch': 'Akzentfarbe 4', + 'pt-br': 'Cor de destaque 4', + }, + accentColor5: { + 'en-us': 'Accent color 5', + 'ru-ru': 'Акцентный цвет 5', + 'es-es': 'Color de acento 5', + 'fr-fr': "Couleur d'accent 5", + 'uk-ua': 'Акцентний колір 5', + 'de-ch': 'Akzentfarbe 5', + 'pt-br': 'Cor de destaque 5', + }, + spreadsheet: { + 'en-us': 'Spreadsheet', + 'ru-ru': 'Электронная таблица', + 'es-es': 'Hoja de cálculo', + 'fr-fr': 'Tableur', + 'uk-ua': 'Електронна таблиця', + 'de-ch': 'Kalkulationstabelle', + 'pt-br': 'Planilha', + }, + minSpareRows: { + 'en-us': 'Number of blank rows at the end', + 'ru-ru': 'Количество пустых строк в конце', + 'es-es': 'Número de filas en blanco al final', + 'fr-fr': 'Nombre de lignes vides à la fin', + 'uk-ua': 'Кількість порожніх рядків у кінці', + 'de-ch': 'Anzahl der leeren Zeilen am Ende', + 'pt-br': 'Número de linhas em branco no final', + }, + autoWrapCols: { + 'en-us': 'Navigate to the other side when reaching the edge column', + 'ru-ru': 'Достигнув крайней колонны, перейдите на другую сторону.', + 'es-es': 'Navegue hacia el otro lado al llegar a la columna del borde.', + 'fr-fr': + 'Naviguez de l’autre côté lorsque vous atteignez la colonne de bord', + 'uk-ua': 'Перейдіть на іншу сторону, коли досягнете краю колонки', + 'de-ch': + 'Navigieren Sie zur anderen Seite, wenn Sie die Randspalte erreichen', + 'pt-br': 'Navegue para o outro lado ao atingir a coluna da borda', + }, + autoWrapRows: { + 'en-us': 'Navigate to the other side when reaching the edge row', + 'ru-ru': 'Достигнув крайнего ряда, перейдите на другую сторону.', + 'es-es': 'Navegue hacia el otro lado al llegar a la fila del borde.', + 'fr-fr': + 'Naviguez de l’autre côté lorsque vous atteignez la rangée de bord', + 'uk-ua': 'Перейдіть на іншу сторону, коли досягнете крайнього ряду', + 'de-ch': + 'Navigieren Sie zur anderen Seite, wenn Sie die Randreihe erreichen', + 'pt-br': 'Navegue para o outro lado ao atingir a fileira de bordas', + }, + enterBeginsEditing: { + 'en-us': 'Enter key begins editing cell', + 'ru-ru': 'Клавиша Enter начинает редактирование ячейки.', + 'es-es': 'La tecla Enter inicia la edición de la celda', + 'fr-fr': 'La touche Entrée commence à modifier la cellule', + 'uk-ua': 'Клавіша Enter починає редагування клітинки', + 'de-ch': 'Mit der Eingabetaste beginnt die Bearbeitung der Zelle', + 'pt-br': 'A tecla Enter inicia a edição da célula', + }, + tabMoveDirection: { + 'en-us': 'Direction of movement when Tab key is pressed', + 'ru-ru': 'Направление движения при нажатии клавиши Tab', + 'es-es': + 'Dirección de movimiento cuando se presiona la tecla Tab', + 'fr-fr': + 'Sens de déplacement lorsque la touche Tabulation est enfoncée', + 'uk-ua': 'Напрямок руху при натисканні клавіші Tab', + 'de-ch': 'Bewegungsrichtung beim Drücken der Tab-Taste', + 'pt-br': 'Direção do movimento quando a tecla Tab é pressionada', + }, + tabMoveDirectionDescription: { + 'en-us': + 'You can move in the opposite direction by pressing Shift+Tab.', + 'ru-ru': + 'Вы можете двигаться в обратном направлении, нажав Shift+Tab.', + 'es-es': + 'Puedes moverte en la dirección opuesta presionando Shift+Tab.', + 'fr-fr': + 'Vous pouvez vous déplacer dans la direction opposée en appuyant sur Shift+Tab.', + 'uk-ua': + 'Ви можете рухатися в протилежному напрямку, натискаючи Shift+Tab.', + 'de-ch': + 'Sie können sich in die entgegengesetzte Richtung bewegen, indem Sie Umschalt+Tab drücken.', + 'pt-br': + 'Você pode mover na direção oposta pressionando Shift+Tab.', + }, + column: { + 'en-us': 'Column', + 'ru-ru': 'Столбец', + 'es-es': 'Columna', + 'fr-fr': 'Colonne', + 'uk-ua': 'Колонка', + 'de-ch': 'Spalte', + 'pt-br': 'Coluna', + }, + row: { + 'en-us': 'Row', + 'ru-ru': 'Ряд', + 'es-es': 'Fila', + 'fr-fr': 'Rangée', + 'uk-ua': 'рядок', + 'de-ch': 'Reihe', + 'pt-br': 'Linha', + }, + enterMoveDirection: { + 'en-us': 'Direction of movement when Enter key is pressed', + 'ru-ru': 'Направление движения при нажатии клавиши Enter', + 'es-es': + 'Dirección de movimiento cuando se presiona la tecla Enter', + 'uk-ua': 'Напрямок руху, коли натиснуто клавішу Enter', + 'de-ch': 'Bewegungsrichtung beim Drücken der Taste Enter', + 'fr-fr': + 'Direction du mouvement lorsque la touche Entrer est enfoncée', + 'pt-br': + 'Direção do movimento quando a tecla Enter é pressionada', + }, + enterMoveDirectionDescription: { + 'en-us': + 'You can move in the opposite direction by pressing Shift+Enter.', + 'ru-ru': + 'Вы можете двигаться в противоположном направлении, нажав Shift+Enter.', + 'es-es': + 'Puedes moverte en la dirección opuesta presionando Shift+Enter.', + 'fr-fr': 'Synonyme couleur.', + 'uk-ua': + 'Ви можете рухатися у протилежному напрямку, натискаючи Shift+Enter.', + 'de-ch': + 'Sie können sich in die entgegengesetzte Richtung bewegen, indem Sie Umschalt+Eingabe drücken.', + 'pt-br': + 'Você pode mover na direção oposta pressionando Shift+Enter.', + }, + filterPickLists: { + 'en-us': 'Filter pick list items', + 'ru-ru': 'Фильтрация элементов списка выбора', + 'es-es': 'Filtrar elementos de la lista de selección', + 'fr-fr': 'Filtrer les éléments de la liste de sélection', + 'uk-ua': 'Фільтр вибору елементів списку', + 'de-ch': 'Auswahllistenelemente filtern', + 'pt-br': 'Filtrar itens da lista de seleção', + }, + exportFileDelimiter: { + 'en-us': 'Export file delimiter', + 'ru-ru': 'Разделитель файлов экспорта', + 'es-es': 'Delimitador de archivo de exportación', + 'fr-fr': "Délimiteur de fichier d'exportation", + 'uk-ua': 'Роздільник файлу експорту', + 'de-ch': 'Dateitrennzeichen exportieren', + 'pt-br': 'Delimitador de arquivo de exportação', + }, + exportCsvUtf8Bom: { + 'en-us': 'Add UTF-8 BOM to CSV file exports', + 'ru-ru': 'Добавить UTF-8 BOM в экспорт CSV-файла', + 'es-es': 'Agregar BOM UTF-8 a las exportaciones de archivos CSV', + 'fr-fr': 'Ajouter UTF-8 BOM aux exportations de fichiers CSV', + 'uk-ua': 'Додайте специфікацію UTF-8 до експорту файлу CSVу', + 'de-ch': 'UTF-8 BOM zum CSV-Dateiexport hinzufügen', + 'pt-br': 'Adicionar UTF-8 BOM às exportações de arquivos CSV', + }, + exportCsvUtf8BomDescription: { + 'en-us': + 'Adds a BOM (Byte Order Mark) to exported CSV files to ensure that the file is correctly recognized and displayed by various programs (Excel, OpenRefine, etc.), preventing issues with special characters and formatting.', + 'ru-ru': 'Корректное отображение экспортированных CSV-файлов в Excel.', + 'es-es': + 'Agrega una BOM (marca de orden de bytes) a los archivos CSV exportados para garantizar que el archivo sea reconocido y mostrado correctamente por varios programas (Excel, OpenRefine, etc.), evitando problemas con caracteres especiales y formato.', + 'fr-fr': + "Permet aux exportations de fichiers CSV de s'afficher correctement dans Excel.", + 'uk-ua': 'Змушує експорт файлів CSV правильно відображатися в Excel.', + 'de-ch': + 'Sorgt dafür, dass CSV-Dateiexporte in Excel korrekt angezeigt werden.', + 'pt-br': + 'Adiciona uma BOM (Byte Order Mark) aos arquivos CSV exportados para garantir que o arquivo seja reconhecido e exibido corretamente por vários programas (Excel, OpenRefine, etc.), evitando problemas com caracteres especiais e formatação.', + }, + caseSensitive: { + 'en-us': 'Case-sensitive', + 'ru-ru': 'С учетом регистра', + 'es-es': 'Distingue mayúsculas y minúsculas', + 'fr-fr': 'Sensible aux majuscules et minuscules', + 'uk-ua': 'Чутливий до регістру', + 'de-ch': 'Groß- und Kleinschreibung beachten', + 'pt-br': 'Maiúsculas e minúsculas', + }, + caseInsensitive: { + 'en-us': 'Case-insensitive', + 'ru-ru': 'Без учета регистра', + 'es-es': 'Sin distinción entre mayúsculas y minúsculas', + 'fr-fr': 'Insensible à la casse', + 'uk-ua': 'Регістр не враховується', + 'de-ch': 'Groß-/Kleinschreibung wird nicht beachtet', + 'pt-br': 'Não diferencia maiúsculas de minúsculas', + }, + showNoReadTables: { + 'en-us': 'Show tables without "Read" access', + 'ru-ru': 'Показывать таблицы без доступа «Чтение»', + 'es-es': 'Mostrar tablas sin acceso de "Lectura"', + 'fr-fr': 'Afficher les tableaux sans accès "Lecture"', + 'uk-ua': 'Показувати таблиці без доступу «Читання»', + 'de-ch': 'Tabellen ohne Lesezugriff anzeigen', + 'pt-br': 'Mostrar tabelas sem acesso de "Leitura"', + }, + showNoAccessTables: { + 'en-us': 'Show tables without "Create" access', + 'ru-ru': 'Показывать таблицы без права «Создать»', + 'es-es': 'Mostrar tablas sin acceso "Crear"', + 'fr-fr': 'Afficher les tableaux sans accès "Créer"', + 'uk-ua': 'Показувати таблиці без доступу «Створити»', + 'de-ch': 'Tabellen ohne „Erstellen“-Zugriff anzeigen', + 'pt-br': 'Mostrar tabelas sem acesso "Criar"', + }, + textAreaAutoGrow: { + 'en-us': 'Text boxes grow automatically', + 'ru-ru': 'Текстовые поля увеличиваются автоматически', + 'es-es': 'Los cuadros de texto crecen automáticamente', + 'fr-fr': "Les zones de texte s'agrandissent automatiquement", + 'uk-ua': 'Текстові поля збільшуються автоматично', + 'de-ch': 'Textfelder werden automatisch vergrößert', + 'pt-br': 'As caixas de texto crescem automaticamente', + }, + clearQueryFilters: { + 'en-us': 'Reset query filters', + 'ru-ru': 'Сбросить фильтры запроса', + 'es-es': 'Restablecer filtros de consulta', + 'fr-fr': 'Réinitialiser les filtres de requête', + 'uk-ua': 'Скинути фільтри запитів', + 'de-ch': 'Abfragefilter zurücksetzen', + 'pt-br': 'Redefinir filtros de consulta', + }, + clearQueryFiltersDescription: { + 'en-us': 'Clears all query filters when running a Report from a Form.', + 'de-ch': + 'Löscht alle Abfragefilter, wenn ein Bericht aus einem Formular ausgeführt wird.', + 'es-es': + 'Borra todos los filtros de consulta al ejecutar un informe desde un formulario.', + 'fr-fr': + "Efface tous les filtres de requête lors de l'exécution d'un rapport à partir d'un formulaire.", + 'ru-ru': 'Очищает все фильтры запроса при запуске отчета из формы.', + 'uk-ua': 'Очищає всі фільтри запитів під час запуску звіту з форми.', + 'pt-br': + 'Limpa todos os filtros de consulta ao executar um relatório de um formulário.', + }, + queryParamtersFromForm: { + 'en-us': 'Show query filters when running a Report from a Form', + 'de-ch': + 'Abfragefilter anzeigen, wenn ein Bericht aus einem Formular ausgeführt wird', + 'es-es': + 'Mostrar filtros de consulta al ejecutar un informe desde un formulario', + 'fr-fr': + "Afficher les filtres de requête lors de l'exécution d'un rapport à partir d'un formulaire", + 'ru-ru': 'Показывать фильтры запроса при запуске отчета из формы', + 'uk-ua': 'Показувати фільтри запитів під час запуску звіту з форми', + 'pt-br': + 'Mostrar filtros de consulta ao executar um relatório de um formulário', + }, + autoGrowAutoComplete: { + 'en-us': 'Allow autocomplete to grow as wide as need', + 'ru-ru': + 'Разрешить автозаполнению расширяться настолько, насколько это необходимо', + 'es-es': 'Permitir que el autocompletado crezca tanto como sea necesario', + 'fr-fr': + 'Sens de déplacement lorsque la touche [X27X]Tabulation[X35X] est enfoncée', + 'uk-ua': + 'Дозволити автозаповнення розширюватися настільки, наскільки потрібно', + 'de-ch': + 'Erlauben Sie der Autovervollständigung, so weit wie nötig zu wachsen', + 'pt-br': + 'Permitir que o preenchimento automático cresça o quanto for necessário', + }, + tableNameInTitle: { + 'en-us': 'Include table name in the browser page title', + 'ru-ru': 'Включить имя таблицы в заголовок страницы браузера', + 'es-es': + 'Incluir el nombre de la tabla en el título de la página del navegador', + 'fr-fr': + 'Inclure le nom de la table dans le titre de la page du navigateur', + 'uk-ua': 'Включіть назву таблиці в заголовок сторінки браузера', + 'de-ch': 'Tabellennamen in den Seitentitel des Browsers aufnehmen', + 'pt-br': 'Incluir nome da tabela no título da página do navegador', + }, + focusFirstField: { + 'en-us': 'Focus first field', + 'de-ch': 'Fokus erstes Feld', + 'es-es': 'Enfoque el primer campo', + 'fr-fr': 'Concentrez-vous sur le premier champ', + 'ru-ru': 'Фокус первого поля', + 'uk-ua': 'Перейти до першого поля', + 'pt-br': 'Foco primeiro no campo', + }, + doubleClickZoom: { + 'en-us': 'Double click to zoom', + 'ru-ru': 'Дважды щелкните, чтобы увеличить', + 'es-es': 'Haga doble clic para ampliar', + 'fr-fr': 'Double-cliquez pour zoomer', + 'uk-ua': 'Двічі клацніть, щоб збільшити', + 'de-ch': 'Zum Vergrößern doppelklicken', + 'pt-br': 'Clique duas vezes para ampliar', + }, + closePopupOnClick: { + 'en-us': 'Close pop-up on outside click', + 'ru-ru': 'Закрытие всплывающего окна при внешнем щелчке', + 'es-es': 'Cerrar ventana emergente al hacer clic desde fuera', + 'fr-fr': "Fermer la pop-up lors d'un clic extérieur", + 'uk-ua': 'Закрити спливаюче вікно при зовнішньому клацанні', + 'de-ch': 'Popup bei externem Klick schließen', + 'pt-br': 'Fechar pop-up ao clicar fora', + }, + animateTransitions: { + 'en-us': 'Animate transitions', + 'ru-ru': 'Анимированные переходы', + 'es-es': 'Transiciones animadas', + 'fr-fr': 'Animer les transitions', + 'uk-ua': 'Анімація переходів', + 'de-ch': 'Übergänge animieren', + 'pt-br': 'Transições animadas', + }, + panInertia: { + 'en-us': 'Pan inertia', + 'ru-ru': 'Инерция пан', + 'es-es': 'Inercia de la sartén', + 'fr-fr': 'Inertie du bac', + 'uk-ua': 'Інерція панорами', + 'de-ch': 'Schwenkträgheit', + 'pt-br': 'Inércia da panela', + }, + mouseDrags: { + 'en-us': 'Mouse drags', + 'ru-ru': 'Перетаскивание мышью', + 'es-es': 'El ratón arrastra', + 'uk-ua': 'Виділіть відповідний підрядок', + 'de-ch': 'Maus zieht', + 'fr-fr': 'Mettre en surbrillance la sous-chaîne correspondante', + 'pt-br': 'Arrastos do mouse', + }, + scrollWheelZoom: { + 'en-us': 'Scroll wheel zoom', + 'ru-ru': 'Масштабирование с помощью колеса прокрутки', + 'es-es': 'Zoom con rueda de desplazamiento', + 'fr-fr': 'Zoom avec la molette de défilement', + 'uk-ua': 'Масштаб колеса прокрутки', + 'de-ch': 'Scrollrad-Zoom', + 'pt-br': 'Zoom da roda de rolagem', + }, + flexibleColumnWidth: { + 'en-us': 'Flexible column width', + 'ru-ru': 'Гибкая ширина столбца', + 'es-es': 'Ancho de columna flexible', + 'fr-fr': 'Largeur de colonne flexible', + 'uk-ua': 'Гнучка ширина колонки', + 'de-ch': 'Flexible Spaltenbreite', + 'pt-br': 'Largura de coluna flexível', + }, + flexibleSubGridColumnWidth: { + 'en-us': 'Flexible subview grid column width', + 'ru-ru': 'Гибкая ширина столбца сетки подпредставлений', + 'es-es': 'Ancho de columna de cuadrícula de subvista flexible', + 'fr-fr': 'Largeur de colonne de grille de sous-vue flexible', + 'uk-ua': 'Гнучка ширина стовпця сітки вкладеного перегляду', + 'de-ch': 'Flexible Spaltenbreite des Unteransichtsrasters', + 'pt-br': 'Largura flexível da coluna da grade de subvisualização', + }, + closeOnEsc: { + 'en-us': 'Close on ESC key press', + 'ru-ru': 'Закрыть нажатием клавиши ESC', + 'es-es': 'Cerrar al presionar la tecla ESC', + 'fr-fr': 'Icône et nom de la table', + 'uk-ua': 'Закриття натисканням клавіші ESC', + 'de-ch': 'Schließen durch Drücken der Taste ESC', + 'pt-br': 'Fechar ao pressionar a tecla ESC', + }, + closeOnOutsideClick: { + 'en-us': 'Close on outside click', + 'ru-ru': 'Закрытие по внешнему щелчку', + 'es-es': 'Cerrar al hacer clic desde fuera', + 'fr-fr': 'Fermer sur clic extérieur', + 'uk-ua': 'Закрийте зовнішнім клацанням', + 'de-ch': 'Schließen durch Klicken von außen', + 'pt-br': 'Fechar com clique externo', + }, + specifyNetworkBadge: { + 'en-us': 'Specify Network Badge', + 'ru-ru': 'Укажите сетевой значок', + 'es-es': 'Especificar la insignia de red', + 'fr-fr': 'Spécifier le badge réseau', + 'uk-ua': 'Укажіть значок мережі', + 'de-ch': 'Netzwerk-Badge angeben', + 'pt-br': 'Especificar emblema de rede', + }, + useAccessibleFullDatePicker: { + 'en-us': 'Use accessible full date picker', + 'ru-ru': 'Используйте доступный полный выбор даты', + 'es-es': 'Utilice el selector de fecha completo y accesible', + 'fr-fr': 'Utiliser un sélecteur de date complet accessible', + 'uk-ua': 'Використовуйте доступний повний засіб вибору дати', + 'de-ch': 'Verwenden Sie eine barrierefreie Datumsauswahl', + 'pt-br': 'Use o seletor de data completo acessível', + }, + useAccessibleMonthPicker: { + 'en-us': 'Use accessible month picker', + 'ru-ru': 'Используйте доступный выбор месяца', + 'es-es': 'Utilice el selector de meses accesible', + 'fr-fr': 'Utiliser le sélecteur de mois accessible', + 'uk-ua': 'Використовуйте доступний засіб вибору місяця', + 'de-ch': 'Verwenden Sie die barrierefreie Monatsauswahl', + 'pt-br': 'Use o seletor de meses acessível', + }, + rightAlignNumberFields: { + 'en-us': 'Right-Justify numeric fields', + 'ru-ru': 'Выравнивание числовых полей по правому краю', + 'es-es': 'Justificar a la derecha los campos numéricos', + 'fr-fr': 'Justifier à droite les champs numériques', + 'uk-ua': 'Вирівнювання по правому краю числових полів', + 'de-ch': 'Rechtsbündige Ausrichtung numerischer Felder', + 'pt-br': 'Justificar à direita campos numéricos', + }, + roundedCorners: { + 'en-us': 'Rounded corners', + 'ru-ru': 'Закругленные углы', + 'es-es': 'esquinas redondeadas', + 'fr-fr': 'Coins arrondis', + 'uk-ua': 'Заокруглені кути', + 'de-ch': 'Abgerundete Ecken', + 'pt-br': 'Cantos arredondados', + }, + showSubviewBorders: { + 'en-us': 'Show borders around subviews', + 'de-ch': 'Rahmen um Unteransichten anzeigen', + 'es-es': 'Mostrar bordes alrededor de las subvistas', + 'fr-fr': 'Afficher les bordures autour des sous-vues', + 'pt-br': 'Mostrar bordas ao redor das subvisualizações', + 'ru-ru': 'Показывать границы вокруг подпредставлений', + 'uk-ua': 'Показати межі навколо підвидів', + }, + limitMaxFieldWidth: { + 'en-us': 'Limit max field width', + 'ru-ru': 'Ограничить максимальную ширину поля', + 'es-es': 'Limitar el ancho máximo del campo', + 'fr-fr': 'Limiter la largeur maximale du champ', + 'uk-ua': 'Обмеження максимальної ширини поля', + 'de-ch': 'Maximale Feldbreite begrenzen', + 'pt-br': 'Limite máximo de largura do campo', + }, + condenseQueryResults: { + 'en-us': 'Condense query results', + 'ru-ru': 'Сжать результаты запроса', + 'es-es': 'Condensar los resultados de la consulta', + 'fr-fr': 'Condenser les résultats de la requête', + 'uk-ua': 'Згорнути результати запиту', + 'de-ch': 'Abfrageergebnisse verdichten', + 'pt-br': 'Condensar resultados da consulta', + }, + blurContentBehindDialog: { + 'en-us': 'Blur content behind the dialog', + 'ru-ru': 'Размытие содержимого за диалогом', + 'es-es': 'Desenfocar el contenido detrás del diálogo', + 'fr-fr': 'Flou le contenu derrière la boîte de dialogue', + 'uk-ua': 'Розмити вміст за діалоговим вікном', + 'de-ch': 'Inhalte hinter dem Dialog verwischen', + 'pt-br': 'Desfocar o conteúdo atrás do diálogo', + }, + collectionSortOrderDescription: { + 'en-us': 'This determines the visual order of collections.', + 'ru-ru': 'Это определяет визуальный порядок коллекций.', + 'es-es': 'Esto determina el orden visual de las colecciones.', + 'fr-fr': "Ceci détermine l'ordre visuel des collections.", + 'uk-ua': 'Це визначає візуальний порядок колекцій.', + 'de-ch': 'Dies bestimmt die visuelle Reihenfolge der Sammlungen.', + 'pt-br': 'Isso determina a ordem visual das coleções.', + }, + recordSetRecordToOpen: { + 'en-us': 'Record to open by default', + 'ru-ru': 'Запись для открытия по умолчанию', + 'es-es': 'Registro para abrir por defecto', + 'fr-fr': 'Enregistrement à ouvrir par défaut', + 'uk-ua': 'Запис відкривається за умовчанням', + 'de-ch': 'Standardmäßig zu öffnender Datensatz', + 'pt-br': 'Gravar para abrir por padrão', + }, + altClickToSupressNewTab: { + 'en-us': + '{altKeyName:string}+Click to suppress new tab', + 'ru-ru': + '{altKeyName:string}+Нажмите , чтобы скрыть новую вкладку', + 'es-es': + '{altKeyName:string}+Haga clic en para suprimir la nueva pestaña', + 'fr-fr': + '{altKeyName:string}+Cliquez sur pour supprimer le nouvel onglet', + 'uk-ua': + '{altKeyName:string}+Натисніть , щоб закрити нову вкладку', + 'de-ch': + '{altKeyName:string}+Klicken Sie auf, um neue Registerkarten zu unterdrücken', + 'pt-br': + '{altKeyName:string}+Clique em para suprimir a nova guia', + }, + altClickToSupressNewTabDescription: { + 'en-us': + '{altKeyName:string}+Click a link that usually opens in a new tab to open it in the current tab.', + 'ru-ru': + '{altKeyName:string}+Нажмите на ссылку, которая обычно открывается в новой вкладке, чтобы открыть ее в текущей вкладке.', + 'es-es': + '{altKeyName:string}+Haga clic en un enlace que normalmente se abre en una nueva pestaña para abrirlo en la pestaña actual.', + 'fr-fr': 'Utiliser le sélecteur de mois accessible.', + 'uk-ua': + '{altKeyName:string}+Натисніть посилання, яке зазвичай відкривається в новій вкладці, щоб відкрити його в поточній вкладці.', + 'de-ch': + '{altKeyName:string}+Klicken Sie auf einen Link, der normalerweise in einem neuen Tab geöffnet wird, um ihn im aktuellen Tab zu öffnen.', + 'pt-br': + '{altKeyName:string}+Clique em um link que geralmente abre em uma nova aba para abri-lo na aba atual.', + }, + makeFormDialogsModal: { + 'en-us': 'Make form dialogs gray out the background', + 'ru-ru': 'Сделать фон диалоговых окон серым', + 'es-es': + 'Hacer que los cuadros de diálogo del formulario tengan el fondo en gris', + 'fr-fr': + "Rendre les boîtes de dialogue de formulaire grisées sur l'arrière-plan", + 'uk-ua': 'Зробіть діалогові вікна форми сірими фоном', + 'de-ch': 'Den Hintergrund von Formulardialogen ausgrauen', + 'pt-br': + 'Faça com que as caixas de diálogo do formulário fiquem com o fundo acinzentado', + }, + autoScrollTree: { + 'en-us': 'Auto scroll tree to focused node', + 'ru-ru': 'Автоматическая прокрутка дерева к выбранному узлу', + 'es-es': 'Desplazamiento automático del árbol al nodo enfocado', + 'fr-fr': 'Arbre de défilement automatique vers le nœud ciblé', + 'uk-ua': 'Автоматичне прокручування дерева до виділеного вузла', + 'de-ch': 'Automatisches Scrollen des Baums zum fokussierten Knoten', + 'pt-br': 'Rolagem automática da árvore para o nó em foco', + }, + sortByField: { + 'en-us': 'Order By Field', + 'de-ch': 'Nach Feld sortieren', + 'es-es': 'Ordenar por campo', + 'fr-fr': 'Trier par champ', + 'pt-br': 'Ordenar por campo', + 'ru-ru': 'Сортировать по полю', + 'uk-ua': 'Сортувати за полем', + }, + lineWrap: { + 'en-us': 'Line wrap', + 'ru-ru': 'Перенос строки', + 'es-es': 'Ajuste de línea', + 'fr-fr': 'Retour à la ligne', + 'uk-ua': 'Обтікання лініями', + 'de-ch': 'Zeilenumbruch', + 'pt-br': 'Quebra de linha', + }, + indentSize: { + 'en-us': 'Indent size', + 'ru-ru': 'Размер отступа', + 'es-es': 'Tamaño de sangría', + 'fr-fr': 'Taille du retrait', + 'uk-ua': 'Розмір відступу', + 'de-ch': 'Einzugsgröße', + 'pt-br': 'Tamanho do recuo', + }, + indentWithTab: { + 'en-us': 'Indent with Tab', + 'ru-ru': 'Отступ с помощью Tab', + 'es-es': 'Sangría con Tab', + 'fr-fr': 'Indenter avec Tabulation', + 'uk-ua': 'Відступ із Tab', + 'de-ch': 'Einrücken mit Tab', + 'pt-br': 'Recuo com Tab', + }, + formHeaderFormat: { + 'en-us': 'Form header format', + 'ru-ru': 'Формат заголовка формы', + 'es-es': 'Formato del encabezado del formulario', + 'fr-fr': "Format d'en-tête de formulaire", + 'uk-ua': 'Формат заголовка форми', + 'de-ch': 'Formularkopfformat', + 'pt-br': 'Formato do cabeçalho do formulário', + }, + iconAndTableName: { + 'en-us': 'Icon and table name', + 'ru-ru': 'Значок и название таблицы', + 'es-es': 'Icono y nombre de la tabla', + 'fr-fr': 'Icône et nom de la table', + 'uk-ua': 'Значок і назва таблиці', + 'de-ch': 'Symbol und Tabellenname', + 'pt-br': 'Ícone e nome da tabela', + }, + tableIcon: { + 'en-us': 'Table icon', + 'ru-ru': 'Значок таблицы', + 'es-es': 'Icono de tabla', + 'fr-fr': 'Icône de tableau', + 'uk-ua': 'Значок таблиці', + 'de-ch': 'Tabellensymbol', + 'pt-br': 'Ícone de tabela', + }, + maxHeight: { + 'en-us': 'Max height', + 'ru-ru': 'Максимальная высота', + 'es-es': 'Altura máxima', + 'fr-fr': 'hauteur maximum', + 'uk-ua': 'Максимальна висота', + 'de-ch': 'Maximale Höhe', + 'pt-br': 'Altura máxima', + }, + autoComplete: { + 'en-us': 'Auto complete', + 'ru-ru': 'Автозаполнение', + 'es-es': 'Autocompletar', + 'fr-fr': + "Détermine les légendes des champs, les notes d'utilisation et les légendes des tableaux", + 'uk-ua': + 'Визначає підписи полів, примітки щодо використання та підписи таблиць', + 'de-ch': 'Autovervollständigung', + 'pt-br': 'Preenchimento automático', + }, + searchCaseSensitive: { + 'en-us': 'Case-sensitive search', + 'es-es': 'Búsqueda que distingue entre mayúsculas y minúsculas', + 'fr-fr': 'Recherche sensible à la casse', + 'uk-ua': 'Пошук з урахуванням регістру', + 'de-ch': 'Groß- und Kleinschreibung beachten', + 'ru-ru': 'Поиск с учетом регистра', + 'pt-br': 'Pesquisa com diferenciação entre maiúsculas e minúsculas', + }, + searchField: { + 'en-us': 'Search Field', + 'ru-ru': 'Поле поиска', + 'es-es': 'Campo de búsqueda', + 'fr-fr': 'Champ de recherche', + 'uk-ua': 'Поле пошуку', + 'de-ch': 'Suchfeld', + 'pt-br': 'Campo de pesquisa', + }, + createInteractions: { + 'en-us': 'Creating an interaction', + 'ru-ru': 'Создание взаимодействия', + 'es-es': 'Creando una interacción', + 'fr-fr': 'Créer une interaction', + 'uk-ua': 'Створення взаємодії', + 'de-ch': 'Erstellen einer Interaktion', + 'pt-br': 'Criando uma interação', + }, + useSpaceAsDelimiter: { + 'en-us': 'Use space as delimiter', + 'ru-ru': 'Используйте пробел в качестве разделителя', + 'es-es': 'Utilice el espacio como delimitador', + 'fr-fr': "Utiliser l'espace comme délimiteur", + 'uk-ua': 'Використовуйте пробіл як роздільник', + 'de-ch': 'Leerzeichen als Trennzeichen verwenden', + 'pt-br': 'Use espaço como delimitador', + }, + useCommaAsDelimiter: { + 'en-us': 'Use comma as delimiter', + 'ru-ru': 'Используйте запятую в качестве разделителя', + 'es-es': 'Utilice la coma como delimitador', + 'fr-fr': 'Utiliser la virgule comme délimiteur', + 'uk-ua': 'Використовуйте кому як роздільник', + 'de-ch': 'Verwenden Sie Kommas als Trennzeichen.', + 'pt-br': 'Use vírgula como delimitador', + }, + useNewLineAsDelimiter: { + 'en-us': 'Use new line as delimiter', + 'ru-ru': 'Использовать новую строку в качестве разделителя', + 'es-es': 'Utilice nueva línea como delimitador', + 'fr-fr': 'Utiliser une nouvelle ligne comme délimiteur', + 'uk-ua': 'Використовуйте новий рядок як роздільник', + 'de-ch': 'Neue Zeile als Trennzeichen verwenden', + 'pt-br': 'Use nova linha como delimitador', + }, + useCustomDelimiters: { + 'en-us': 'Use custom delimiters', + 'ru-ru': 'Используйте пользовательские разделители', + 'es-es': 'Utilice delimitadores personalizados', + 'fr-fr': 'Utiliser des délimiteurs personnalisés', + 'uk-ua': 'Використовуйте спеціальні роздільники', + 'de-ch': 'Benutzerdefinierte Trennzeichen verwenden', + 'pt-br': 'Use delimitadores personalizados', + }, + useCustomDelimitersDescription: { + 'en-us': + 'A list of delimiters to use, in addition to the ones defined above. Put one delimiter per line.', + 'ru-ru': + 'Список разделителей, которые можно использовать в дополнение к указанным выше. Используйте по одному разделителю на строку.', + 'es-es': + 'Una lista de delimitadores para usar, además de los definidos anteriormente. Coloque un delimitador por línea.', + 'fr-fr': + 'Une liste de délimiteurs à utiliser, en plus de ceux définis ci-dessus. Mettez un délimiteur par ligne.', + 'uk-ua': + 'Список розділювачів для використання на додаток до визначених вище. Поставте один роздільник на рядок.', + 'de-ch': + 'Eine Liste der zu verwendenden Trennzeichen zusätzlich zu den oben definierten. Geben Sie pro Zeile ein Trennzeichen ein.', + 'pt-br': + 'Uma lista de delimitadores a serem usados, além dos definidos acima. Coloque um delimitador por linha.', + }, + detectAutomaticallyDescription: { + 'en-us': 'Detect automatically based on catalog number format.', + 'ru-ru': 'Автоматическое определение на основе формата каталожного номера.', + 'es-es': + 'Detectar automáticamente según el formato del número de catálogo.', + 'fr-fr': + 'Détecter automatiquement en fonction du format du numéro de catalogue.', + 'uk-ua': 'Визначати автоматично на основі формату номера каталогу.', + 'de-ch': 'Automatische Erkennung basierend auf dem Katalognummernformat.', + 'pt-br': + 'Detecte automaticamente com base no formato do número de catálogo.', + }, + use: { + comment: 'Verb', + 'en-us': 'Use', + 'ru-ru': 'Использовать', + 'es-es': 'Usar', + 'fr-fr': 'Utiliser', + 'uk-ua': 'використання', + 'de-ch': 'Verwenden', + 'pt-br': 'Usar', + }, + dontUse: { + 'en-us': 'Don’t use', + 'ru-ru': 'Не использовать', + 'es-es': 'No utilizar', + 'fr-fr': 'Zoom avec la molette de défilement', + 'uk-ua': 'Масштаб колеса прокрутки', + 'de-ch': 'Nicht verwenden', + 'pt-br': 'Não use', + }, + position: { + 'en-us': 'Position', + 'es-es': 'Posición', + 'fr-fr': 'Position', + 'ru-ru': 'Позиция', + 'uk-ua': 'Позиція', + 'de-ch': 'Position', + 'pt-br': 'Posição', + }, + top: { + 'en-us': 'Top', + 'es-es': 'Arriba', + 'fr-fr': 'Haut', + 'ru-ru': 'Вершина', + 'uk-ua': 'Топ', + 'de-ch': 'Spitze', + 'pt-br': 'Principal', + }, + bottom: { + 'en-us': 'Bottom', + 'es-es': 'Abajo', + 'ru-ru': 'Нижний', + 'uk-ua': 'Дно', + 'de-ch': 'Unten', + 'fr-fr': 'Bas', + 'pt-br': 'Fundo', + }, + left: { + 'en-us': 'Left', + 'es-es': 'Izquierda', + 'fr-fr': 'Gauche', + 'ru-ru': 'Левый', + 'uk-ua': 'Ліворуч', + 'de-ch': 'Links', + 'pt-br': 'Esquerda', + }, + right: { + 'en-us': 'Right', + 'es-es': 'Bien', + 'fr-fr': 'Droite', + 'ru-ru': 'Верно', + 'uk-ua': 'правильно', + 'de-ch': 'Rechts', + 'pt-br': 'Certo', + }, + showUnsavedIndicator: { + 'en-us': 'Show unsaved changes indicator', + 'ru-ru': 'Показать индикатор несохраненных изменений', + 'es-es': 'Mostrar indicador de cambios no guardados', + 'fr-fr': "Afficher l'indicateur de modifications non enregistrées", + 'uk-ua': 'Показати індикатор незбережених змін', + 'de-ch': 'Indikator für nicht gespeicherte Änderungen anzeigen', + 'pt-br': 'Mostrar indicador de alterações não salvas', + }, + showUnsavedIndicatorDescription: { + 'en-us': + 'Show an "*" in the tab title when there are unsaved changes in the current tab.', + 'es-es': + 'Mostrar un "*" en el título de la pestaña cuando haya cambios sin guardar en la pestaña actual.', + 'fr-fr': + "Afficher un \"*\" dans le titre de l'onglet lorsqu'il y a des modifications non enregistrées dans l'onglet actuel.", + 'ru-ru': + 'Отображать «*» в заголовке вкладки, если на текущей вкладке есть несохраненные изменения.', + 'uk-ua': + 'Показувати «*» у заголовку вкладки, якщо в поточній вкладці є незбережені зміни.', + 'de-ch': + 'Zeigen Sie im Registerkartentitel ein „*“ an, wenn in der aktuellen Registerkarte nicht gespeicherte Änderungen vorhanden sind.', + 'pt-br': + 'Exibir um "*" no título da aba quando houver alterações não salvas na aba atual.', + }, + autoPopulateDescription: { + 'en-us': + 'Auto populate the merged record with values from duplicates when opening the merging dialog.', + 'ru-ru': + 'Автоматически заполнять объединенную запись значениями из дубликатов при открытии диалогового окна слияния.', + 'de-ch': + 'Füllen Sie den zusammengeführten Datensatz beim Öffnen des Zusammenführungsdialogs automatisch mit Werten aus Duplikaten.', + 'es-es': + 'Rellene automáticamente el registro fusionado con valores de duplicados al abrir el cuadro de diálogo de fusión.', + 'fr-fr': + "Remplir automatiquement l'enregistrement fusionné avec les valeurs des doublons lors de l'ouverture de la boîte de dialogue de fusion.", + 'uk-ua': + 'Автоматичне заповнення об’єднаного запису значеннями з дублікатів під час відкриття діалогового вікна об’єднання.', + 'pt-br': + 'Preencha automaticamente o registro mesclado com valores de duplicatas ao abrir a caixa de diálogo de mesclagem.', + }, + autoCreateVariants: { + 'en-us': 'Automatically create {agentVariantTable:string} records', + 'ru-ru': 'Автоматически создавать записи {agentVariantTable:string}', + 'de-ch': '{agentVariantTable:string}-Datensätze automatisch erstellen', + 'es-es': 'Crear automáticamente registros {agentVariantTable:string}', + 'fr-fr': + 'Créer automatiquement des enregistrements {agentVariantTable:string}', + 'uk-ua': 'Автоматично створювати записи {agentVariantTable:string}', + 'pt-br': 'Criar automaticamente registros {agentVariantTable:string}', + }, + autoCreateVariantsDescription: { + 'en-us': + 'When merging agents, automatically create {agentVariantTable:string} records based on the variations of first name/last name.', + 'ru-ru': + 'При объединении агентов автоматически создавать записи {agentVariantTable:string} на основе вариаций имени/фамилии.', + 'de-ch': + 'Beim Zusammenführen von Agenten werden automatisch {agentVariantTable:string}-Datensätze basierend auf den Variationen von Vorname/Nachname erstellt.', + 'es-es': + 'Al fusionar agentes, cree automáticamente registros {agentVariantTable:string} basados en las variaciones de nombre/apellido.', + 'fr-fr': + "Lors de la fusion d'agents, créez automatiquement des enregistrements {agentVariantTable:string} en fonction des variations du prénom/nom.", + 'uk-ua': + 'Під час об’єднання агентів автоматично створювати записи {agentVariantTable:string} на основі варіацій імені/прізвища.', + 'pt-br': + 'Ao mesclar agentes, crie automaticamente registros {agentVariantTable:string} com base nas variações de nome/sobrenome.', + }, + collectionPreferences: { + 'en-us': 'Collection Preferences', + 'de-ch': 'Sammlungseinstellungen', + 'es-es': 'Preferencias de colección', + 'fr-fr': 'Personnalisation', + 'ru-ru': 'Настройки коллекции', + 'uk-ua': 'Налаштування', + 'pt-br': 'Preferências de coleção', + }, + rememberDialogSizes: { + 'en-us': 'Remember dialog window sizes', + 'ru-ru': 'Запомните размеры диалоговых окон', + 'es-es': 'Recordar los tamaños de las ventanas de diálogo', + 'fr-fr': 'Mémoriser les tailles des fenêtres de dialogue', + 'uk-ua': "Запам'ятайте розміри діалогових вікон", + 'de-ch': 'Dialogfenstergrößen merken', + 'pt-br': 'Lembrar tamanhos de janelas de diálogo', + }, + rememberDialogPositions: { + 'en-us': 'Remember dialog window positions', + 'ru-ru': 'Запомнить позиции диалоговых окон', + 'es-es': 'Recordar las posiciones de las ventanas de diálogo', + 'fr-fr': 'Mémoriser les positions des fenêtres de dialogue', + 'uk-ua': "Запам'ятовуйте положення діалогового вікна", + 'de-ch': 'Dialogfensterpositionen merken', + 'pt-br': 'Lembrar posições da janela de diálogo', + }, + autoPlayMedia: { + 'en-us': 'Automatically play media', + 'ru-ru': 'Автоматически воспроизводить медиа', + 'es-es': 'Reproducir automáticamente medios', + 'fr-fr': 'Lire automatiquement les médias', + 'uk-ua': 'Автоматичне відтворення медіа', + 'de-ch': 'Medien automatisch abspielen', + 'pt-br': 'Reproduzir mídia automaticamente', + }, + useCustomTooltips: { + 'en-us': 'Use modern tooltips', + 'ru-ru': 'Используйте современные подсказки', + 'es-es': 'Utilice información sobre herramientas moderna', + 'fr-fr': 'Utiliser des info-bulles modernes', + 'uk-ua': 'Використовуйте сучасні підказки', + 'de-ch': 'Verwenden Sie moderne Tooltips', + 'pt-br': 'Use dicas de ferramentas modernas', + }, + alwaysUseQueryBuilder: { + 'en-us': 'Always use query builder search inside of search form', + 'de-ch': + 'Verwenden Sie innerhalb des Suchformulars immer die Abfragegeneratorsuche', + 'es-es': + 'Utilice siempre la búsqueda del generador de consultas dentro del formulario de búsqueda', + 'fr-fr': + 'Utilisez toujours la recherche du générateur de requêtes dans le formulaire de recherche', + 'ru-ru': 'Всегда используйте конструктор запросов внутри формы поиска.', + 'uk-ua': 'Завжди використовуйте пошук конструктора запитів у формі пошуку', + 'pt-br': + 'Sempre use a pesquisa do construtor de consultas dentro do formulário de pesquisa', + }, + localizeResourceNames: { + 'en-us': 'Localize the names of recognized app resources', + 'de-ch': 'Lokalisieren Sie die Namen erkannter App-Ressourcen', + 'es-es': + 'Localizar los nombres de los recursos de aplicaciones reconocidos', + 'fr-fr': "Localiser les noms des ressources d'application reconnues", + 'ru-ru': 'Локализуйте названия распознанных ресурсов приложения', + 'uk-ua': 'Локалізувати назви розпізнаних ресурсів програми', + 'pt-br': 'Localize os nomes dos recursos de aplicativos reconhecidos', + }, + splitLongXml: { + 'en-us': 'Split long lines of XML into multiple lines', + 'de-ch': 'Teilen Sie lange XML-Zeilen in mehrere Zeilen auf', + 'es-es': 'Dividir líneas largas de XML en varias líneas', + 'fr-fr': 'Diviser les longues lignes de XML en plusieurs lignes', + 'ru-ru': 'Разделить длинные строки XML на несколько строк', + 'uk-ua': 'Розділіть довгі рядки XML на кілька рядків', + 'pt-br': 'Dividir longas linhas de XML em várias linhas', + }, + url: { + 'en-us': 'URL', + 'de-ch': 'URL', + 'es-es': 'URL', + 'fr-fr': 'URL', + 'uk-ua': 'URL', + 'ru-ru': 'URL', + 'pt-br': 'URL', + }, + pickAttachment: { + 'en-us': 'Pick an attachment', + 'es-es': 'Elige un archivo adjunto', + 'fr-fr': 'Choisissez une pièce jointe', + 'ru-ru': 'Выберите вложение', + 'uk-ua': 'Виберіть вкладення', + 'de-ch': 'Wählen Sie einen Anhang', + 'pt-br': 'Escolha um anexo', + }, + attachmentFailed: { + 'en-us': 'The attachment failed to load.', + 'de-ch': 'Der Anhang konnte nicht geladen werden.', + 'es-es': 'No se pudo cargar el archivo adjunto.', + 'fr-fr': "La pièce jointe n'a pas pu être chargée.", + 'ru-ru': 'Не удалось загрузить вложение.', + 'uk-ua': 'Не вдалося завантажити вкладений файл.', + 'pt-br': 'O anexo não pôde ser carregado.', + }, + pickImage: { + 'en-us': 'Pick an image', + 'de-ch': 'Wählen Sie ein Bild aus', + 'es-es': 'Elige una imagen', + 'fr-fr': 'Choisissez une image', + 'ru-ru': 'Выберите изображение', + 'uk-ua': 'Виберіть зображення', + 'pt-br': 'Escolha uma imagem', + }, + customLogo: { + 'en-us': 'Expanded Image URL', + 'de-ch': 'Erweiterte Bild-URL', + 'es-es': 'URL de imagen expandida', + 'fr-fr': "URL de l'image étendue", + 'ru-ru': 'URL-адрес развернутого изображения', + 'uk-ua': 'Розширена URL-адреса зображення', + 'pt-br': 'URL da imagem expandida', + }, + customLogoCollapsed: { + 'en-us': 'Collapsed Image URL', + 'de-ch': 'URL des minimierten Bildes', + 'es-es': 'URL de imagen contraída', + 'fr-fr': "URL de l'image réduite", + 'ru-ru': 'URL-адрес свернутого изображения', + 'uk-ua': 'URL-адреса згорнутого зображення', + 'pt-br': 'URL da imagem recolhida', + }, + customLogoDescription: { + 'en-us': + 'A URL to an image that would be displayed next to the Specify logo in the navigation menu.', + 'de-ch': + 'Eine URL zu einem Bild, das neben dem angegebenen Logo im Navigationsmenü angezeigt wird.', + 'es-es': + 'Una URL a una imagen que se mostrará junto al logotipo Especificar en el menú de navegación.', + 'fr-fr': + 'Une URL vers une image qui serait affichée à côté du logo Specify dans le menu de navigation.', + 'ru-ru': + 'URL-адрес изображения, которое будет отображаться рядом с логотипом «Укажите» в меню навигации.', + 'uk-ua': + 'URL-адреса зображення, яке відображатиметься поруч із «Вказати логотип» у меню навігації.', + 'pt-br': + 'Um URL para uma imagem que seria exibida ao lado do logotipo Especificar no menu de navegação.', + }, + showLineNumber: { + 'en-us': 'Show query result line number', + 'de-ch': 'Zeilennummer des Abfrageergebnisses anzeigen', + 'es-es': 'Mostrar el número de línea del resultado de la consulta', + 'fr-fr': 'Afficher le numéro de ligne du résultat de la requête', + 'ru-ru': 'Показать номер строки результата запроса', + 'uk-ua': 'Показати номер рядка результату запиту', + 'pt-br': 'Mostrar número da linha do resultado da consulta', + }, + saveButtonColor: { + 'en-us': 'Save button color', + 'de-ch': 'Farbe der Schaltfläche „Speichern“', + 'es-es': 'Guardar el color del botón', + 'fr-fr': 'Couleur du bouton Enregistrer', + 'ru-ru': 'Сохранить цвет кнопки', + 'uk-ua': 'Зберегти колір кнопки', + 'pt-br': 'Cor do botão Salvar', + }, + secondaryButtonColor: { + 'en-us': 'Secondary button color', + 'es-es': 'Color del botón secundario', + 'fr-fr': 'Couleur du bouton secondaire', + 'ru-ru': 'Цвет вторичной кнопки', + 'uk-ua': 'Колір вторинної кнопки', + 'de-ch': 'Sekundäre Schaltflächenfarbe', + 'pt-br': 'Cor do botão secundário', + }, + secondaryLightButtonColor: { + 'en-us': 'Secondary light button color', + 'de-ch': 'Farbe der sekundären Lichttaste', + 'es-es': 'Color del botón de luz secundaria', + 'fr-fr': 'Couleur du bouton lumineux secondaire', + 'ru-ru': 'Цвет кнопки дополнительного освещения', + 'uk-ua': 'Колір вторинної світлової кнопки', + 'pt-br': 'Cor do botão de luz secundária', + }, + dangerButtonColor: { + 'en-us': 'Danger button color', + 'de-ch': 'Farbe der Gefahrenschaltfläche', + 'es-es': 'Color del botón de peligro', + 'fr-fr': 'Couleur du bouton de danger', + 'ru-ru': 'Цвет кнопки «Опасность»', + 'uk-ua': 'Колір кнопки небезпеки', + 'pt-br': 'Cor do botão de perigo', + }, + infoButtonColor: { + 'en-us': 'Info button color', + 'de-ch': 'Farbe der Info-Schaltfläche', + 'es-es': 'Color del botón de información', + 'fr-fr': "Couleur du bouton d'information", + 'ru-ru': 'Цвет кнопки информации', + 'uk-ua': 'Колір інформаційної кнопки', + 'pt-br': 'Cor do botão de informações', + }, + warningButtonColor: { + 'en-us': 'Warning button color', + 'de-ch': 'Farbe der Warnschaltfläche', + 'es-es': 'Color del botón de advertencia', + 'fr-fr': "Couleur du bouton d'avertissement", + 'ru-ru': 'Цвет кнопки предупреждения', + 'uk-ua': 'Колір кнопки попередження', + 'pt-br': 'Cor do botão de aviso', + }, + successButtonColor: { + 'en-us': 'Success button color', + 'de-ch': 'Farbe der Schaltfläche „Erfolg“', + 'es-es': 'Color del botón de éxito', + 'fr-fr': 'Couleur du bouton de réussite', + 'ru-ru': 'Цвет кнопки «Успех»', + 'uk-ua': 'Колір кнопки успіху', + 'pt-br': 'Cor do botão de sucesso', + }, + openAsReadOnly: { + 'en-us': 'Open all records in read-only mode', + 'de-ch': 'Alle Datensätze im schreibgeschützten Modus öffnen', + 'es-es': 'Abrir todos los registros en modo de solo lectura', + 'fr-fr': 'Ouvrir tous les enregistrements en mode lecture seule', + 'ru-ru': 'Открыть все записи в режиме только для чтения', + 'uk-ua': 'Відкрити всі записи в режимі лише для читання', + 'pt-br': 'Abra todos os registros no modo somente leitura', + }, + displayBasicView: { + 'en-us': 'Display basic view', + 'de-ch': 'Basisansicht anzeigen', + 'es-es': 'Mostrar vista básica', + 'fr-fr': 'Afficher la vue de base', + 'ru-ru': 'Отобразить базовый вид', + 'uk-ua': 'Відобразити базовий вигляд', + 'pt-br': 'Exibir visualização básica', + }, + showComparisonOperatorsForString: { + 'en-us': 'Show comparison operators for text-based fields', + 'de-ch': 'Vergleichsoperatoren für textbasierte Felder anzeigen', + 'es-es': 'Mostrar operadores de comparación para campos basados en texto', + 'fr-fr': 'Afficher les opérateurs de comparaison pour les champs textuels', + 'pt-br': 'Mostrar operadores de comparação para campos baseados em texto', + 'ru-ru': 'Показать операторы сравнения для текстовых полей', + 'uk-ua': 'Показати оператори порівняння для текстових полів', + }, + showComparisonOperatorsDescription: { + 'en-us': + 'Allows the following filters to apply to text fields: Greater Than, Less Than, Greater Than or Equal to, and Less Than or Equal to', + 'de-ch': + 'Ermöglicht die Anwendung der folgenden Filter auf Textfelder: Größer als, Kleiner als, Größer als oder gleich und Kleiner als oder gleich', + 'es-es': + 'Permite aplicar los siguientes filtros a los campos de texto: Mayor que, Menor que, Mayor o igual que y Menor o igual que', + 'fr-fr': + "Permet d'appliquer les filtres suivants aux champs de texte : Supérieur à, Inférieur à, Supérieur ou égal à et Inférieur ou égal à", + 'pt-br': + 'Permite que os seguintes filtros sejam aplicados aos campos de texto: Maior que, Menor que, Maior ou igual a e Menor ou igual a', + 'ru-ru': + 'Позволяет применять к текстовым полям следующие фильтры: «Больше», «Меньше», «Больше или равно» и «Меньше или равно».', + 'uk-ua': + 'Дозволяє застосовувати до текстових полів такі фільтри: «Більше ніж», «Менше ніж», «Більше або дорівнює» та «Менше або дорівнює»', + }, + basicView: { + 'en-us': 'Basic view', + 'de-ch': 'Basisansicht', + 'es-es': 'Vista básica', + 'fr-fr': 'Vue de base', + 'ru-ru': 'Базовый вид', + 'uk-ua': 'Основний вигляд', + 'pt-br': 'Visão básica', + }, + detailedView: { + 'en-us': 'Detailed view', + 'de-ch': 'Detailansicht', + 'es-es': 'Vista detallada', + 'fr-fr': 'Vue détaillée', + 'ru-ru': 'Подробный вид', + 'uk-ua': 'Детальний вигляд', + 'pt-br': 'Visão detalhada', + }, + attachmentPreviewMode: { + 'en-us': 'Attachment preview mode', + 'de-ch': 'Anhangsvorschaumodus', + 'es-es': 'Modo de vista previa de archivos adjuntos', + 'fr-fr': "Mode d'aperçu des pièces jointes", + 'ru-ru': 'Режим предварительного просмотра вложений', + 'uk-ua': 'Режим попереднього перегляду вкладених файлів', + 'pt-br': 'Modo de visualização de anexos', + }, + fullResolution: { + 'en-us': 'Full Resolution', + 'de-ch': 'Volle Auflösung', + 'es-es': 'Resolución completa', + 'fr-fr': 'Pleine résolution', + 'ru-ru': 'Полное разрешение', + 'uk-ua': 'Повна роздільна здатність', + 'pt-br': 'Resolução completa', + }, + thumbnail: { + 'en-us': 'Thumbnail', + 'de-ch': 'Miniaturansicht', + 'es-es': 'Uña del pulgar', + 'fr-fr': 'Vignette', + 'ru-ru': 'Миниатюра', + 'uk-ua': 'Мініатюра', + 'pt-br': 'Miniatura', + }, + addSearchBarHomePage: { + 'en-us': 'Add Search Bar on home page', + 'de-ch': 'Suchleiste auf der Startseite hinzufügen', + 'es-es': 'Agregar barra de búsqueda en la página de inicio', + 'fr-fr': "Ajouter une barre de recherche sur la page d'accueil", + 'ru-ru': 'Добавить панель поиска на домашнюю страницу', + 'uk-ua': 'Додайте рядок пошуку на головну сторінку', + 'pt-br': 'Adicionar barra de pesquisa na página inicial', + }, + inheritanceCatNumberPref: { + 'en-us': + 'Enable the inheritance of the primary catalog number to its empty siblings.', + 'de-ch': + 'Aktivieren Sie die Vererbung der primären Katalognummer an ihre leeren Geschwister.', + 'es-es': + 'Habilitar la herencia del número de catálogo principal a sus hermanos vacíos.', + 'fr-fr': + "Activez l'héritage du numéro de catalogue principal vers ses frères vides.", + 'pt-br': + 'Habilitar a herança do número do catálogo primário para seus irmãos vazios.', + 'ru-ru': + 'Включить наследование основного каталожного номера его пустыми родственными номерами.', + 'uk-ua': + 'Увімкнути успадкування основного каталожного номера його порожнім братам і сестрам.', + }, + inheritanceCatNumberParentCOPref: { + 'en-us': + 'Enable the inheritance of the parent catalog number to its empty children.', + 'de-ch': + 'Aktivieren Sie die Vererbung der übergeordneten Katalognummer an ihre leeren untergeordneten Elemente.', + 'es-es': + 'Habilitar la herencia del número de catálogo padre a sus hijos vacíos.', + 'fr-fr': + "Activer l'héritage du numéro de catalogue parent à ses enfants vides.", + 'pt-br': + 'Habilitar a herança do número do catálogo pai para seus filhos vazios.', + 'ru-ru': + 'Включить наследование родительского каталожного номера его пустыми дочерними элементами.', + 'uk-ua': + 'Увімкнути успадкування батьківського каталожного номера його порожнім дочірнім елементам.', + }, + rankThreshold: { + 'en-us': 'Show object count below', + }, + rankThresholdDescription: { + 'en-us': + 'Show the count of linked Collection Objects for all ranks below this one.', + }, +} as const; + +export const preferencesGeneralText = createDictionary( + preferencesGeneralDictionary +); diff --git a/specifyweb/frontend/js_src/lib/localization/preferences.ts b/specifyweb/frontend/js_src/lib/localization/preferences.ts index 7314c2cf328..0bf175a8b64 100644 --- a/specifyweb/frontend/js_src/lib/localization/preferences.ts +++ b/specifyweb/frontend/js_src/lib/localization/preferences.ts @@ -1,2131 +1,17 @@ /** - * Localization strings for the preferences menu + * Localization strings for the preferences menu (aggregated from modular sections). * * @module */ - +import { preferencesBehaviorDictionary } from './preferences.behavior'; +import { preferencesContentDictionary } from './preferences.content'; +import { preferencesGeneralDictionary } from './preferences.general'; import { createDictionary } from './utils'; - // Refer to "Guidelines for Programmers" in ./README.md before editing this file +const preferencesDictionary = { + ...preferencesGeneralDictionary, + ...preferencesContentDictionary, + ...preferencesBehaviorDictionary, +} as const; -export const preferencesText = createDictionary({ - preferences: { - 'en-us': 'Preferences', - 'ru-ru': 'Настройки', - 'es-es': 'Preferencias', - 'fr-fr': 'Préférences', - 'uk-ua': 'Уподобання', - 'de-ch': 'Einstellungen', - 'pt-br': 'Preferências', - }, - customization: { - 'en-us': 'Customization', - 'ru-ru': 'Настройка', - 'es-es': 'Personalización', - 'fr-fr': 'Personnalisation', - 'uk-ua': 'Спеціальнізація', - 'de-ch': 'Anpassung', - 'pt-br': 'Personalização', - }, - userPreferences: { - 'en-us': 'User Preferences', - 'ru-ru': 'Настройки пользователя', - 'es-es': 'Preferencias del usuario', - 'fr-fr': "Préférences de l'utilisateur", - 'uk-ua': 'Налаштування користувача', - 'de-ch': 'Benutzereinstellungen', - 'pt-br': 'Preferências do usuário', - }, - defaultUserPreferences: { - 'en-us': 'Default User Preferences', - 'ru-ru': 'Настройки пользователя по умолчанию', - 'es-es': 'Preferencias de usuario predeterminadas', - 'fr-fr': 'Préférences utilisateur par défaut', - 'uk-ua': 'Параметри користувача за умовчанням', - 'de-ch': 'Standardbenutzereinstellungen', - 'pt-br': 'Preferências de usuário padrão', - }, - general: { - 'en-us': 'General', - 'ru-ru': 'Общий', - 'es-es': 'General', - 'fr-fr': 'Image personnalisée', - 'uk-ua': 'Спеціальне зображення', - 'de-ch': 'Allgemein', - 'pt-br': 'Em geral', - }, - ui: { - 'en-us': 'User Interface', - 'ru-ru': 'Пользовательский интерфейс', - 'es-es': 'Interfaz de usuario', - 'fr-fr': 'Interface utilisateur', - 'uk-ua': 'Інтерфейс користувача', - 'de-ch': 'Benutzeroberfläche', - 'pt-br': 'Interface do usuário', - }, - theme: { - 'en-us': 'Theme', - 'ru-ru': 'Тема', - 'es-es': 'Tema', - 'fr-fr': 'Thème', - 'uk-ua': 'Тема', - 'de-ch': 'Thema', - 'pt-br': 'Tema', - }, - useSystemSetting: { - 'en-us': 'Use system setting', - 'ru-ru': 'Использовать системные настройки', - 'es-es': 'Utilizar la configuración del sistema', - 'fr-fr': 'Utiliser les paramètres du système', - 'uk-ua': 'Використовуйте налаштування системи', - 'de-ch': 'Systemeinstellung verwenden', - 'pt-br': 'Usar configuração do sistema', - }, - inheritOsSettings: { - 'en-us': 'Copies value from your Operating System settings', - 'ru-ru': 'Копирует значение из настроек вашей операционной системы', - 'es-es': 'Copia el valor de la configuración de su sistema operativo', - 'fr-fr': "Copie la valeur des paramètres de votre système d'exploitation", - 'uk-ua': 'Копіює значення з налаштувань вашої операційної системи', - 'de-ch': 'Übernimmt den Wert aus Ihren Betriebssystemeinstellungen', - 'pt-br': 'Copia o valor das configurações do seu sistema operacional', - }, - light: { - comment: 'Light mode', - 'en-us': 'Light', - 'ru-ru': 'Свет', - 'es-es': 'Claro', - 'fr-fr': 'Lumière', - 'uk-ua': 'світло', - 'de-ch': 'Hell', - 'pt-br': 'Luz', - }, - dark: { - comment: 'Dark mode', - 'en-us': 'Dark', - 'ru-ru': 'Темный', - 'es-es': 'Oscuro', - 'fr-fr': 'Sombre', - 'uk-ua': 'Темний', - 'de-ch': 'Dunkel', - 'pt-br': 'Escuro', - }, - reduceMotion: { - 'en-us': 'Reduce motion', - 'ru-ru': 'Уменьшите движение', - 'es-es': 'Reducir el movimiento', - 'fr-fr': 'Réduire les mouvements', - 'uk-ua': 'Зменшити рух', - 'de-ch': 'Bewegung reduzieren', - 'pt-br': 'Reduzir movimento', - }, - reduceMotionDescription: { - 'en-us': 'Disable non-essential animations and transitions.', - 'ru-ru': 'Отключите ненужные анимации и переходы.', - 'es-es': 'Desactivar animaciones y transiciones no esenciales.', - 'fr-fr': 'Désactivez les animations et les transitions non essentielles.', - 'uk-ua': "Вимкніть необов'язкову анімацію та переходи.", - 'de-ch': 'Nicht erforderliche Animationen und Übergänge deaktivieren.', - 'pt-br': 'Desabilite animações e transições não essenciais.', - }, - reduceTransparency: { - 'en-us': 'Reduce transparency', - 'ru-ru': 'Уменьшить прозрачность', - 'es-es': 'Reducir la transparencia', - 'fr-fr': 'Réduire la transparence', - 'uk-ua': 'Зменшити прозорість', - 'de-ch': 'Transparenz reduzieren', - 'pt-br': 'Reduzir a transparência', - }, - reduceTransparencyDescription: { - 'en-us': - 'Whether to disable translucent backgrounds for user interface components whenever possible (e.g. table headers in tree view).', - 'ru-ru': - 'Следует ли отключать полупрозрачный фон для компонентов пользовательского интерфейса, когда это возможно (например, заголовки таблиц в древовидной структуре).', - 'es-es': - 'Si se deben deshabilitar los fondos translúcidos para los componentes de la interfaz de usuario siempre que sea posible (por ejemplo, encabezados de tabla en la vista de árbol).', - 'fr-fr': - "S'il faut désactiver les arrière-plans translucides pour les composants de l'interface utilisateur chaque fois que possible (par exemple, les en-têtes de tableau dans l'arborescence).", - 'uk-ua': - 'Чи вимикати напівпрозорий фон для компонентів інтерфейсу користувача, коли це можливо (наприклад, заголовки таблиць у перегляді дерева).', - 'de-ch': - 'Durchsichtige Hintergründe für Benutzeroberflächenkomponenten wann immer möglich deaktivieren (z. B. Tabellenüberschriften in der Baumansicht).', - 'pt-br': - 'Se deve desabilitar fundos translúcidos para componentes da interface do usuário sempre que possível (por exemplo, cabeçalhos de tabela na visualização em árvore).', - }, - contrast: { - 'en-us': 'Contrast', - 'ru-ru': 'Контраст', - 'es-es': 'Contraste', - 'fr-fr': 'Contraste', - 'uk-ua': 'Контраст', - 'de-ch': 'Kontrast', - 'pt-br': 'Contraste', - }, - increase: { - 'en-us': 'Increase', - 'ru-ru': 'Увеличивать', - 'es-es': 'Aumentar', - 'fr-fr': 'Augmenter', - 'uk-ua': 'Збільшити', - 'de-ch': 'Erhöhen', - 'pt-br': 'Aumentar', - }, - reduce: { - 'en-us': 'Reduce', - 'ru-ru': 'Уменьшать', - 'es-es': 'Reducir', - 'fr-fr': 'Réduire', - 'uk-ua': 'Зменшити', - 'de-ch': 'Verringern', - 'pt-br': 'Reduzir', - }, - noPreference: { - 'en-us': 'No preference', - 'ru-ru': 'Нет предпочтений', - 'es-es': 'Sin preferencia', - 'fr-fr': 'Pas de préférence', - 'uk-ua': 'Без переваг', - 'de-ch': 'Keine Präferenz', - 'pt-br': 'Sem preferência', - }, - fontSize: { - 'en-us': 'Font size', - 'ru-ru': 'Размер шрифта', - 'es-es': 'Tamaño de fuente', - 'fr-fr': 'Taille de police', - 'uk-ua': 'Розмір шрифту', - 'de-ch': 'Schriftgrösse', - 'pt-br': 'Tamanho da fonte', - }, - fontFamily: { - 'en-us': 'Font family', - 'ru-ru': 'Семейство шрифтов', - 'es-es': 'Familia de fuentes', - 'fr-fr': 'Famille de polices', - 'uk-ua': 'Сімейство шрифтів', - 'de-ch': 'Schrift-Familie', - 'pt-br': 'Família de fontes', - }, - fontFamilyDescription: { - 'en-us': - 'You can specify any font that is on your computer, even if it is not in the list. A comma-separated list of fonts is also supported, where each subsequent font will be used if the previous one is not available.', - 'ru-ru': - 'Вы можете указать любой шрифт, установленный на вашем компьютере, даже если его нет в списке. Также поддерживается список шрифтов, разделённый запятыми, где каждый последующий шрифт будет использоваться, если предыдущий недоступен.', - 'es-es': - 'Puede especificar cualquier fuente de su ordenador, incluso si no está en la lista. También se admite una lista de fuentes separadas por comas, donde se usará cada fuente subsiguiente si la anterior no está disponible.', - 'fr-fr': - "Vous pouvez spécifier n'importe quelle police présente sur votre ordinateur, même si elle ne figure pas dans la liste. Une liste de polices séparées par des virgules est également prise en charge ; chaque police suivante sera utilisée si la précédente n'est pas disponible.", - 'uk-ua': - "Ви можете вказати будь-який шрифт, який є на вашому комп'ютері, навіть якщо його немає в списку. Також підтримується розділений комами список шрифтів, у якому використовуватиметься другий шрифт, якщо перший недоступний тощо.", - 'de-ch': - 'Sie können jede Schriftart angeben, die sich auf Ihrem Computer befindet, auch wenn diese nicht in der Liste enthalten ist. Eine durch Kommas getrennte Liste von Schriftarten wird ebenfalls unterstützt, wobei die zweite Schriftart verwendet wird, wenn die erste nicht verfügbar ist usw.', - 'pt-br': - 'Você pode especificar qualquer fonte que esteja no seu computador, mesmo que ela não esteja na lista. Uma lista de fontes separadas por vírgulas também é suportada, onde cada fonte subsequente será usada se a anterior não estiver disponível.', - }, - defaultFont: { - 'en-us': '(default font)', - 'ru-ru': '(шрифт по умолчанию)', - 'es-es': '(fuente predeterminada)', - 'fr-fr': '(police par défaut)', - 'uk-ua': '(типовий шрифт)', - 'de-ch': '(Standardschriftart)', - 'pt-br': '(fonte padrão)', - }, - maxFormWidth: { - 'en-us': 'Max form width', - 'ru-ru': 'Максимальная ширина формы', - 'es-es': 'Ancho máximo del formulario', - 'fr-fr': 'Largeur maximale du formulaire', - 'uk-ua': 'Максимальна ширина форми', - 'de-ch': 'Maximale Formularbreite', - 'pt-br': 'Largura máxima do formulário', - }, - fieldBackgrounds: { - 'en-us': 'Field backgrounds', - 'ru-ru': 'Фоны полей', - 'es-es': 'Fondos de campo', - 'fr-fr': 'Milieux de terrain', - 'uk-ua': 'Польові фони', - 'de-ch': 'Feldhintergründe', - 'pt-br': 'Fundos de campo', - }, - fieldBackground: { - 'en-us': 'Field background', - 'ru-ru': 'Фон поля', - 'es-es': 'Fondo de campo', - 'fr-fr': 'Contexte du terrain', - 'uk-ua': 'Поле фону', - 'de-ch': 'Feldhintergrund', - 'pt-br': 'Contexto de campo', - }, - disabledFieldBackground: { - 'en-us': 'Disabled field background', - 'ru-ru': 'Отключенный фон поля', - 'es-es': 'Fondo de campo deshabilitado', - 'fr-fr': 'Fond de champ désactivé', - 'uk-ua': 'Вимкнений фон поля', - 'de-ch': 'Deaktivierter Feldhintergrund', - 'pt-br': 'Fundo de campo desativado', - }, - invalidFieldBackground: { - 'en-us': 'Invalid field background', - 'ru-ru': 'Неверный фон поля', - 'es-es': 'Fondo de campo no válido', - 'fr-fr': 'Fond de champ invalide', - 'uk-ua': 'Недійсний фон поля', - 'de-ch': 'Ungültiger Feldhintergrund', - 'pt-br': 'Fundo de campo inválido', - }, - requiredFieldBackground: { - 'en-us': 'Required field background', - 'ru-ru': 'Обязательное поле фон', - 'es-es': 'Fondo del campo obligatorio', - 'fr-fr': 'Contexte du champ obligatoire', - 'uk-ua': "Обов'язковий фон поля", - 'de-ch': 'Feldhintergrund erforderlich', - 'pt-br': 'Histórico de campo obrigatório', - }, - darkFieldBackground: { - 'en-us': 'Field background (dark theme)', - 'ru-ru': 'Фон поля (тёмная тема)', - 'es-es': 'Fondo de campo (tema oscuro)', - 'fr-fr': 'Fond de champ (thème sombre)', - 'uk-ua': 'Фон поля (темна тема)', - 'de-ch': 'Feldhintergrund (Dunkles Thema)', - 'pt-br': 'Fundo de campo (tema escuro)', - }, - darkDisabledFieldBackground: { - 'en-us': 'Disabled field background (dark theme)', - 'ru-ru': 'Отключенный фон поля (тёмная тема)', - 'es-es': 'Fondo de campo deshabilitado (tema oscuro)', - 'fr-fr': 'Fond de champ désactivé (thème sombre)', - 'uk-ua': 'Вимкнений фон поля (темна тема)', - 'de-ch': 'Deaktivierter Feldhintergrund (Dunkles Thema)', - 'pt-br': 'Fundo de campo desativado (tema escuro)', - }, - darkInvalidFieldBackground: { - 'en-us': 'Invalid field background (dark theme)', - 'ru-ru': 'Недопустимый фон поля (тёмная тема)', - 'es-es': 'Fondo de campo no válido (tema oscuro)', - 'fr-fr': 'Largeur de colonne de grille de sous-vue flexible', - 'uk-ua': 'Гнучка ширина стовпця сітки вкладеного перегляду', - 'de-ch': 'Ungültiger Feldhintergrund (Dunkles Thema)', - 'pt-br': 'Fundo de campo inválido (tema escuro)', - }, - darkRequiredFieldBackground: { - 'en-us': 'Required field background (dark theme)', - 'ru-ru': 'Обязательное поле фон (тёмная тема)', - 'es-es': 'Fondo del campo obligatorio (tema oscuro)', - 'fr-fr': 'Fond de champ obligatoire (thème sombre)', - 'uk-ua': 'Обов’язковий фон поля (темна тема)', - 'de-ch': 'Feldhintergrund erforderlich (Dunkles Thema)', - 'pt-br': 'Fundo de campo obrigatório (tema escuro)', - }, - dialogs: { - 'en-us': 'Dialogs', - 'ru-ru': 'Диалоги', - 'es-es': 'Diálogos', - 'fr-fr': 'Boîtes de dialogue', - 'uk-ua': 'Діалоги', - 'de-ch': 'Dialoge', - 'pt-br': 'Diálogos', - }, - appearance: { - 'en-us': 'Appearance', - 'ru-ru': 'Появление', - 'es-es': 'Apariencia', - 'fr-fr': 'Apparence', - 'uk-ua': 'Зовнішній вигляд', - 'de-ch': 'Aussehen', - 'pt-br': 'Aparência', - }, - buttonsLight: { - 'en-us': 'Buttons (light mode)', - 'de-ch': 'Buttons (Helles Thema)', - 'es-es': 'Botones (modo luz)', - 'fr-fr': 'Boutons (mode lumière)', - 'ru-ru': 'Кнопки (световой режим)', - 'uk-ua': 'Кнопки (світлий режим)', - 'pt-br': 'Botões (modo claro)', - }, - buttonsDark: { - 'en-us': 'Buttons (dark mode)', - 'de-ch': 'Buttons (Dunkles Thema)', - 'es-es': 'Botones (modo oscuro)', - 'fr-fr': 'Boutons (mode sombre)', - 'ru-ru': 'Кнопки (темный режим)', - 'uk-ua': 'Кнопки (темний режим)', - 'pt-br': 'Botões (modo escuro)', - }, - translucentDialog: { - 'en-us': 'Translucent dialogs', - 'ru-ru': 'Прозрачные диалоги', - 'es-es': 'Diálogos translúcidos', - 'fr-fr': 'Dialogues translucides', - 'uk-ua': 'Напівпрозорі діалоги', - 'de-ch': 'Durchscheinende Dialoge', - 'pt-br': 'Diálogos translúcidos', - }, - translucentDialogDescription: { - 'en-us': 'Whether dialogs have translucent background.', - 'ru-ru': 'Имеют ли диалоговые окна полупрозрачный фон.', - 'es-es': 'Si los diálogos tienen fondo translúcido.', - 'fr-fr': 'Si les boîtes de dialogue ont un fond translucide.', - 'uk-ua': 'Чи мають діалоги прозорий фон.', - 'de-ch': 'Dialogfenster mit durchscheinenden Hintergrund.', - 'pt-br': 'Se os diálogos têm fundo translúcido.', - }, - alwaysPrompt: { - 'en-us': 'Always prompt to choose collection', - 'ru-ru': 'Всегда предлагайте выбрать коллекцию', - 'es-es': 'Siempre dispuesto a elegir la colección', - 'fr-fr': 'Toujours invité à choisir la collection', - 'uk-ua': 'Завжди підкажуть вибрати колекцію', - 'de-ch': 'Immer zur Auswahl der Sammlung auffordern', - 'pt-br': 'Sempre pronto para escolher a coleção', - }, - treeEditor: { - 'en-us': 'Tree Editor', - 'ru-ru': 'Редактор деревьев', - 'es-es': 'Editor de árboles', - 'fr-fr': "Éditeur d'arborescence", - 'uk-ua': 'Редактор дерева', - 'de-ch': 'Baumeditor', - 'pt-br': 'Editor de Árvore', - }, - treeAccentColor: { - 'en-us': 'Tree accent color', - 'ru-ru': 'Акцентный цвет дерева', - 'es-es': 'Color de acento del árbol', - 'fr-fr': "Couleur d'accent d'arbre", - 'uk-ua': 'Колір акценту дерева', - 'de-ch': 'Baumakzentfarbe', - 'pt-br': 'Cor de destaque da árvore', - }, - synonymColor: { - 'en-us': 'Synonym color', - 'ru-ru': 'Синоним цвет', - 'es-es': 'Color sinónimo', - 'fr-fr': 'Synonyme couleur', - 'uk-ua': 'Синонім кольору', - 'de-ch': 'Synonymfarbe', - 'pt-br': 'Cor sinônimo', - }, - showNewDataSetWarning: { - 'en-us': 'Show new Data Set warning', - 'ru-ru': 'Показать предупреждение о новом наборе данных', - 'es-es': 'Mostrar nueva advertencia de conjunto de datos', - 'fr-fr': "Afficher un nouvel avertissement sur l'ensemble de données", - 'uk-ua': 'Показати попередження про новий набір даних', - 'de-ch': 'Warnung für neuen Datensatz anzeigen', - 'pt-br': 'Mostrar novo aviso de conjunto de dados', - }, - showNewDataSetWarningDescription: { - 'en-us': 'Show an informational message when creating a new Data Set.', - 'ru-ru': - 'Показывать информационное сообщение при создании нового набора данных.', - 'es-es': - 'Mostrar un mensaje informativo al crear un nuevo conjunto de datos.', - 'fr-fr': - "Afficher un message d'information lors de la création d'un nouvel ensemble de données.", - 'uk-ua': - 'Показувати інформаційне повідомлення під час створення нового набору даних.', - 'de-ch': 'Zeige eine Meldung beim erstellen eines neuen Datensatzes an.', - 'pt-br': - 'Exibir uma mensagem informativa ao criar um novo conjunto de dados.', - }, - header: { - 'en-us': 'Navigation Menu', - 'ru-ru': 'Меню навигации', - 'es-es': 'Menú de navegación', - 'fr-fr': 'le menu de navigation', - 'uk-ua': 'Навігаційне меню', - 'de-ch': 'Navigationsmenü', - 'pt-br': 'Menu de navegação', - }, - application: { - 'en-us': 'Application', - 'ru-ru': 'Приложение', - 'es-es': 'Solicitud', - 'fr-fr': 'Application', - 'uk-ua': 'застосування', - 'de-ch': 'Anwendung', - 'pt-br': 'Aplicativo', - }, - allowDismissingErrors: { - 'en-us': 'Allow dismissing error messages', - 'ru-ru': 'Разрешить отклонять сообщения об ошибках', - 'es-es': 'Permitir descartar mensajes de error', - 'fr-fr': "Autoriser le rejet des messages d'erreur", - 'uk-ua': 'Дозволити закривати повідомлення про помилки', - 'de-ch': 'Erlaube das Verwerfen von Fehlermeldungen', - 'pt-br': 'Permitir descartar mensagens de erro', - }, - updatePageTitle: { - 'en-us': 'Update page title', - 'ru-ru': 'Обновить заголовок страницы', - 'es-es': 'Actualizar el título de la página', - 'fr-fr': 'Mettre à jour le titre de la page', - 'uk-ua': 'Оновити назву сторінки', - 'de-ch': 'Seitentitel aktualisieren', - 'pt-br': 'Atualizar título da página', - }, - updatePageTitleDescription: { - 'en-us': - "Whether to update the title of the page to match dialog's header.", - 'ru-ru': - 'Обновлять ли заголовок страницы в соответствии с заголовком диалогового окна.', - 'es-es': - 'Si se debe actualizar el título de la página para que coincida con el encabezado del cuadro de diálogo.', - 'fr-fr': - "S'il faut mettre à jour le titre de la page pour qu'il corresponde à l'en-tête de la boîte de dialogue.", - 'uk-ua': - 'Чи оновлювати назву сторінки відповідно до заголовка діалогового вікна.', - 'de-ch': - 'Titel der Seite so aktualisieren, dass er mit der Kopfzeile des Dialogs übereinstimmt.', - 'pt-br': - 'Se o título da página deve ser atualizado para corresponder ao cabeçalho da caixa de diálogo.', - }, - updatePageTitleFormDescription: { - 'en-us': 'Whether to update the title of the page to match current record.', - 'ru-ru': - 'Следует ли обновить заголовок страницы в соответствии с текущей записью.', - 'es-es': - 'Si desea actualizar el título de la página para que coincida con el registro actual.', - 'fr-fr': - "S'il faut mettre à jour le titre de la page pour qu'il corresponde à l'enregistrement actuel.", - 'uk-ua': 'Чи оновлювати назву сторінки відповідно до поточного запису.', - 'de-ch': - 'Titel der Seite aktualisieren, damit er mit dem aktuellen Datensatz übereinstimmt.', - 'pt-br': - 'Se o título da página deve ser atualizado para corresponder ao registro atual.', - }, - queryComboBox: { - 'en-us': 'Query Combo Box', - 'ru-ru': 'Поле со списком запросов', - 'es-es': 'Cuadro combinado de consulta', - 'uk-ua': 'Поле зі списком запитів', - 'de-ch': 'Abfrage-Kombinationsfeld', - 'fr-fr': 'Zone de liste déroulante de requête', - 'pt-br': 'Caixa de combinação de consulta', - }, - searchAlgorithm: { - 'en-us': 'Search Algorithm', - 'ru-ru': 'Алгоритм поиска', - 'es-es': 'Algoritmo de búsqueda', - 'fr-fr': 'Algorithme de recherche', - 'uk-ua': 'Алгоритм пошуку', - 'de-ch': 'Suchalgorithmus', - 'pt-br': 'Algoritmo de Busca', - }, - treeSearchAlgorithm: { - 'en-us': 'Search Algorithm (for relationships with tree tables)', - 'ru-ru': 'Алгоритм поиска (для связей с древовидными таблицами)', - 'es-es': 'Algoritmo de búsqueda (para relaciones con tablas de árbol)', - 'fr-fr': - 'Algorithme de recherche (pour les relations avec les tables arborescentes)', - 'uk-ua': 'Алгоритм пошуку (для зв’язків із деревоподібними таблицями)', - 'de-ch': 'Suchalgorithmus (für Beziehungen mit Baumtabellen)', - 'pt-br': 'Algoritmo de busca (para relacionamentos com tabelas de árvore)', - }, - startsWithInsensitive: { - 'en-us': 'Starts With (case-insensitive)', - 'ru-ru': 'Начинается с (без учета регистра)', - 'es-es': 'Comienza con (sin distinguir entre mayúsculas y minúsculas)', - 'fr-fr': 'Commence par (insensible à la casse)', - 'uk-ua': 'Починається з (без урахування регістру)', - 'de-ch': 'Beginnt mit (Groß-/Kleinschreibung wird nicht beachtet)', - 'pt-br': 'Começa com (sem distinção entre maiúsculas e minúsculas)', - }, - startsWithDescription: { - 'en-us': 'Search for values that begin with a given query string.', - 'ru-ru': 'Поиск значений, начинающихся с заданной строки запроса.', - 'es-es': - 'Busque valores que comiencen con una cadena de consulta determinada.', - 'fr-fr': - 'Rechercher des valeurs commençant par une chaîne de requête donnée.', - 'uk-ua': 'Пошук значень, які починаються з заданого рядка запиту.', - 'de-ch': - 'Suchen Sie nach Werten, die mit einer bestimmten Abfragezeichenfolge beginnen.', - 'pt-br': - 'Pesquise valores que começam com uma determinada sequência de consulta.', - }, - startsWithCaseSensitive: { - 'en-us': 'Starts With (case-sensitive)', - 'ru-ru': 'Начинается с (с учетом регистра)', - 'es-es': 'Comienza con (sensible a mayúsculas y minúsculas)', - 'fr-fr': 'Commence par (sensible à la casse)', - 'uk-ua': 'Починається з (з урахуванням регістру)', - 'de-ch': 'Beginnt mit (Groß-/Kleinschreibung beachten)', - 'pt-br': 'Começa com (diferencia maiúsculas de minúsculas)', - }, - startsWithCaseSensitiveDescription: { - 'en-us': 'Search for values that begin with a given query string.', - 'ru-ru': 'Поиск значений, начинающихся с заданной строки запроса.', - 'es-es': - 'Busque valores que comiencen con una cadena de consulta determinada.', - 'fr-fr': - 'Recherchez les valeurs qui commencent par une chaîne de requête donnée.', - 'uk-ua': 'Пошук значень, які починаються з заданого рядка запиту.', - 'de-ch': - 'Suchen Sie nach Werten, die mit einer bestimmten Abfragezeichenfolge beginnen.', - 'pt-br': - 'Pesquise valores que começam com uma determinada sequência de consulta.', - }, - containsInsensitive: { - 'en-us': 'Contains (case-insensitive)', - 'ru-ru': 'Содержит (без учета регистра)', - 'es-es': 'Contiene (sin distinguir entre mayúsculas y minúsculas)', - 'fr-fr': 'Contient (insensible à la casse)', - 'uk-ua': 'Містить (незалежно від регістру)', - 'de-ch': 'Enthält (Groß-/Kleinschreibung wird nicht beachtet)', - 'pt-br': 'Contém (sem distinção entre maiúsculas e minúsculas)', - }, - containsCaseSensitive: { - 'en-us': 'Contains (case-sensitive)', - 'ru-ru': 'Содержит (с учетом регистра)', - 'es-es': 'Contiene (sensible a mayúsculas y minúsculas)', - 'fr-fr': 'Contient (sensible à la casse)', - 'uk-ua': 'Містить (з урахуванням регістру)', - 'de-ch': 'Enthält (Groß-/Kleinschreibung beachten)', - 'pt-br': 'Contém (diferencia maiúsculas de minúsculas)', - }, - containsDescription: { - 'en-us': - 'Search for values that contain a given query string (case-insensitive).', - 'ru-ru': - 'Поиск значений, содержащих заданную строку запроса (без учета регистра).', - 'es-es': - 'Busque valores que contengan una cadena de consulta determinada (sin distinguir entre mayúsculas y minúsculas).', - 'uk-ua': - 'Пошук значень, які містять заданий рядок запиту (незалежно від регістру).', - 'de-ch': - 'Suchen Sie nach Werten, die eine bestimmte Abfragezeichenfolge enthalten (ohne Berücksichtigung der Groß-/Kleinschreibung).', - 'fr-fr': - 'Recherchez les valeurs contenant une chaîne de requête donnée (insensible à la casse).', - 'pt-br': - 'Pesquisar valores que contenham uma determinada sequência de consulta (sem distinção de maiúsculas e minúsculas).', - }, - containsCaseSensitiveDescription: { - 'en-us': - 'Search for values that contain a given query string (case-sensitive).', - 'ru-ru': - 'Поиск значений, содержащих заданную строку запроса (с учетом регистра).', - 'es-es': - 'Busque valores que contengan una cadena de consulta determinada (distingue entre mayúsculas y minúsculas).', - 'fr-fr': - 'Recherchez les valeurs contenant une chaîne de requête donnée (sensible à la casse).', - 'uk-ua': - 'Пошук значень, які містять заданий рядок запиту (з урахуванням регістру).', - 'de-ch': - 'Suchen Sie nach Werten, die eine bestimmte Abfragezeichenfolge enthalten (Groß-/Kleinschreibung beachten).', - 'pt-br': - 'Pesquisar valores que contenham uma determinada sequência de consulta (diferencia maiúsculas de minúsculas).', - }, - containsSecondDescription: { - 'en-us': - 'Can use _ to match any single character or % to match any number of characters.', - 'ru-ru': - 'Можно использовать _ для соответствия любому отдельному символу или % для соответствия любому количеству символов.', - 'es-es': - 'Puede utilizar _ para que coincida con cualquier carácter individual o % para que coincida con cualquier número de caracteres.', - 'fr-fr': - "Peut utiliser _ pour correspondre à n'importe quel caractère ou % pour correspondre à n'importe quel nombre de caractères.", - 'uk-ua': - 'Можна використовувати _ для відповідності будь-якому одному символу або % для відповідності будь-якій кількості символів.', - 'de-ch': - 'Sie können _ verwenden, um ein beliebiges einzelnes Zeichen abzugleichen, oder %, um eine beliebige Anzahl von Zeichen abzugleichen.', - 'pt-br': - 'Pode usar _ para corresponder a qualquer caractere único ou % para corresponder a qualquer número de caracteres.', - }, - highlightMatch: { - 'en-us': 'Highlight matched substring', - 'ru-ru': 'Выделить совпавшую подстроку', - 'es-es': 'Resaltar la subcadena coincidente', - 'fr-fr': 'Mettre en surbrillance la sous-chaîne correspondante', - 'uk-ua': 'Виділіть збіг підрядка', - 'de-ch': 'Markieren Sie übereinstimmende Teilzeichenfolgen', - 'pt-br': 'Destacar substring correspondente', - }, - languageDescription: { - 'en-us': 'Determines field captions, usage notes and table captions.', - 'ru-ru': - 'Определяет заголовки полей, примечания по использованию и заголовки таблиц.', - 'es-es': 'Determina títulos de campos, notas de uso y títulos de tablas.', - 'fr-fr': - "Détermine les légendes des champs, les notes d'utilisation et les légendes des tableaux.", - 'uk-ua': - 'Визначає підписи полів, примітки щодо використання та підписи таблиць.', - 'de-ch': - 'Legt Feldbeschriftungen, Verwendungshinweise und Tabellenbeschriftungen fest.', - 'pt-br': 'Determina legendas de campo, notas de uso e legendas de tabela.', - }, - showDialogIcon: { - 'en-us': 'Show icon in the header', - 'ru-ru': 'Показывать значок в заголовке', - 'es-es': 'Mostrar icono en el encabezado', - 'fr-fr': "Afficher l'icône dans l'en-tête", - 'uk-ua': 'Показати значок у заголовку', - 'de-ch': 'Symbol in der Kopfzeile anzeigen', - 'pt-br': 'Mostrar ícone no cabeçalho', - }, - scaleInterface: { - 'en-us': 'Scale Interface', - 'ru-ru': 'Интерфейс масштабирования', - 'es-es': 'Interfaz de escala', - 'fr-fr': 'Interface de balance', - 'uk-ua': 'Інтерфейс масштабу', - 'de-ch': 'Waagenschnittstelle', - 'pt-br': 'Interface de escala', - }, - scaleInterfaceDescription: { - 'en-us': 'Scale interface to match font size.', - 'ru-ru': 'Масштабируйте интерфейс в соответствии с размером шрифта.', - 'es-es': 'Escala la interfaz para que coincida con el tamaño de la fuente.', - 'fr-fr': "Adapter l'interface à la taille de la police.", - 'uk-ua': 'Масштабуйте інтерфейс відповідно до розміру шрифту.', - 'de-ch': - 'Skalieren Sie die Benutzeroberfläche, um sie an die Schriftgröße anzupassen.', - 'pt-br': 'Dimensione a interface para corresponder ao tamanho da fonte.', - }, - displayAuthor: { - 'en-us': 'Show author in the tree', - 'ru-ru': 'Показать автора в дереве', - 'es-es': 'Mostrar autor en el árbol', - 'fr-fr': "Afficher l'auteur dans l'arbre", - 'uk-ua': 'Показати автора в дереві', - 'de-ch': 'Autor im Baum anzeigen', - 'pt-br': 'Mostrar autor', - }, - welcomePage: { - 'en-us': 'Home Page', - 'ru-ru': 'Домашняя страница', - 'es-es': 'Página de inicio', - 'fr-fr': "Page d'accueil", - 'uk-ua': 'Домашня сторінка', - 'de-ch': 'Startseite', - 'pt-br': 'Página inicial', - }, - content: { - 'en-us': 'Content', - 'ru-ru': 'Содержание', - 'es-es': 'Contenido', - 'fr-fr': 'Contenu', - 'uk-ua': 'Зміст', - 'de-ch': 'Inhalt', - 'pt-br': 'Contente', - }, - defaultImage: { - 'en-us': 'Specify Logo', - 'ru-ru': 'Укажите логотип', - 'es-es': 'Especificar logotipo', - 'fr-fr': 'Spécifier le logo', - 'uk-ua': 'Вкажіть логотип', - 'de-ch': 'Logo angeben', - 'pt-br': 'Especificar logotipo', - }, - customImage: { - 'en-us': 'Custom Image', - 'ru-ru': 'Пользовательское изображение', - 'es-es': 'Imagen personalizada', - 'fr-fr': 'Image personnalisée', - 'uk-ua': 'Спеціальне зображення', - 'de-ch': 'Benutzerdefiniertes Bild', - 'pt-br': 'Imagem personalizada', - }, - embeddedWebpage: { - 'en-us': 'Embedded web page', - 'ru-ru': 'Встроенная веб-страница', - 'es-es': 'Página web incrustada', - 'fr-fr': 'Page Web intégrée', - 'uk-ua': 'Вбудована веб-сторінка', - 'de-ch': 'Eingebettete Webseite', - 'pt-br': 'Página da web incorporada', - }, - embeddedWebpageDescription: { - 'en-us': 'A URL to a page that would be embedded on the home page:', - 'ru-ru': 'URL-адрес страницы, которая будет встроена в домашнюю страницу:', - 'es-es': 'Una URL a una página que se integrará en la página de inicio:', - 'fr-fr': "Une URL vers une page qui serait intégrée à la page d'accueil :", - 'uk-ua': 'URL-адреса сторінки, яка буде вбудована на домашній сторінці:', - 'de-ch': - 'Eine URL zu einer Seite, die auf der Startseite eingebettet werden soll:', - 'pt-br': 'Um URL para uma página que seria incorporada na página inicial:', - }, - behavior: { - 'en-us': 'Behavior', - 'ru-ru': 'Поведение', - 'es-es': 'Comportamiento', - 'fr-fr': 'Comportement', - 'uk-ua': 'Поведінка', - 'de-ch': 'Verhalten', - 'pt-br': 'Comportamento', - }, - noRestrictionsMode: { - 'en-us': 'No restrictions mode', - 'ru-ru': 'Режим без ограничений', - 'es-es': 'Modo sin restricciones', - 'fr-fr': 'Mode sans restriction', - 'uk-ua': 'Режим без обмежень', - 'de-ch': 'Modus „Keine Einschränkungen“', - 'pt-br': 'Modo sem restrições', - }, - noRestrictionsModeWbDescription: { - 'en-us': 'Allows uploading data to any field in any table.', - 'ru-ru': 'Позволяет загружать данные в любое поле любой таблицы.', - 'es-es': 'Permite cargar datos a cualquier campo de cualquier tabla.', - 'fr-fr': - "Permet de télécharger des données dans n'importe quel champ de n'importe quelle table.", - 'uk-ua': 'Дозволяє завантажувати дані в будь-яке поле будь-якої таблиці.', - 'de-ch': - 'Ermöglicht das Hochladen von Daten in jedes Feld einer beliebigen Tabelle.', - 'pt-br': 'Permite carregar dados em qualquer campo de qualquer tabela.', - }, - noRestrictionsModeQueryDescription: { - 'en-us': 'Allows querying data from any field in any table.', - 'ru-ru': 'Позволяет запрашивать данные из любого поля любой таблицы.', - 'es-es': 'Permite consultar datos de cualquier campo de cualquier tabla.', - 'fr-fr': - "Permet d'interroger les données de n'importe quel champ de n'importe quelle table.", - 'uk-ua': 'Дозволяє запитувати дані з будь-якого поля будь-якої таблиці.', - 'de-ch': - 'Ermöglicht das Abfragen von Daten aus jedem Feld in jeder Tabelle.', - 'pt-br': 'Permite consultar dados de qualquer campo em qualquer tabela.', - }, - noRestrictionsModeWarning: { - 'en-us': - 'WARNING: enabling this may lead to data loss or database corruption. Please make sure you know what you are doing.', - 'ru-ru': - 'ВНИМАНИЕ: включение этой функции может привести к потере данных или повреждению базы данных. Убедитесь, что вы понимаете, что делаете.', - 'es-es': - 'ADVERTENCIA: Habilitar esta opción podría provocar la pérdida de datos o la corrupción de la base de datos. Asegúrese de saber lo que está haciendo.', - 'uk-ua': - 'ПОПЕРЕДЖЕННЯ: увімкнення цієї функції може призвести до втрати даних або пошкодження бази даних. Переконайтеся, що ви знаєте, що робите.', - 'de-ch': - 'WARNUNG: Das Aktivieren dieser Option kann zu Datenverlust oder Datenbankbeschädigung führen. Bitte stellen Sie sicher, dass Sie wissen, was Sie tun.', - 'fr-fr': - "AVERTISSEMENT : l'activation de cette option peut entraîner une perte de données ou une corruption de la base de données. Veuillez vous assurer que vous savez ce que vous faites.", - 'pt-br': - 'AVISO: habilitar esta opção pode levar à perda de dados ou à corrupção do banco de dados. Certifique-se de saber o que está fazendo.', - }, - adminsOnlyPreference: { - 'en-us': "You don't have permission to change this option", - 'ru-ru': 'У вас нет разрешения на изменение этой опции.', - 'es-es': 'No tienes permiso para cambiar esta opción', - 'fr-fr': "Vous n'êtes pas autorisé à modifier cette option", - 'uk-ua': 'Ви не маєте дозволу змінювати цей параметр', - 'de-ch': 'Sie haben keine Berechtigung, diese Option zu ändern', - 'pt-br': 'Você não tem permissão para alterar esta opção', - }, - stickyScrolling: { - 'en-us': 'Sticky scroll bar', - 'ru-ru': 'Липкая полоса прокрутки', - 'es-es': 'Barra de desplazamiento fija', - 'fr-fr': 'Barre de défilement collante', - 'uk-ua': 'Липка смуга прокрутки', - 'de-ch': 'Klebrige Bildlaufleiste', - 'pt-br': 'Barra de rolagem fixa', - }, - foreground: { - 'en-us': 'Foreground', - 'ru-ru': 'Передний план', - 'es-es': 'Primer plano', - 'fr-fr': 'Premier plan', - 'uk-ua': 'Передній план', - 'de-ch': 'Vordergrund', - 'pt-br': 'Primeiro plano', - }, - background: { - 'en-us': 'Background', - 'ru-ru': 'Фон', - 'es-es': 'Fondo', - 'fr-fr': 'Arrière-plan', - 'uk-ua': 'Фон', - 'de-ch': 'Hintergrund', - 'pt-br': 'Fundo', - }, - sidebarTheme: { - 'en-us': 'Sidebar theme', - 'de-ch': 'Seitenleistenthema', - 'es-es': 'Tema de la barra lateral', - 'fr-fr': 'Thème de la barre latérale', - 'ru-ru': 'Тема боковой панели', - 'uk-ua': 'Тема бічної панелі', - 'pt-br': 'Tema da barra lateral', - }, - darkForeground: { - 'en-us': 'Foreground (dark theme)', - 'ru-ru': 'Передний план (тёмная тема)', - 'es-es': 'Primer plano (tema oscuro)', - 'fr-fr': 'Premier plan (thème sombre)', - 'uk-ua': 'Передній план (темна тема)', - 'de-ch': 'Vordergrund (dunkles Design)', - 'pt-br': 'Primeiro plano (tema escuro)', - }, - darkBackground: { - 'en-us': 'Background (dark theme)', - 'ru-ru': 'Фон (тёмная тема)', - 'es-es': 'Fondo (tema oscuro)', - 'fr-fr': 'Arrière-plan (thème sombre)', - 'uk-ua': 'Фон (темна тема)', - 'de-ch': 'Hintergrund (dunkles Design)', - 'pt-br': 'Plano de fundo (tema escuro)', - }, - accentColor1: { - 'en-us': 'Accent color 1', - 'ru-ru': 'Акцентный цвет 1', - 'es-es': 'Color de acento 1', - 'fr-fr': "Couleur d'accent 1", - 'uk-ua': 'Акцентний колір 1', - 'de-ch': 'Akzentfarbe 1', - 'pt-br': 'Cor de destaque 1', - }, - accentColor2: { - 'en-us': 'Accent color 2', - 'ru-ru': 'Акцентный цвет 2', - 'es-es': 'Color de acento 2', - 'fr-fr': "Couleur d'accent 2", - 'uk-ua': 'Акцентний колір 2', - 'de-ch': 'Akzentfarbe 2', - 'pt-br': 'Cor de destaque 2', - }, - accentColor3: { - 'en-us': 'Accent color 3', - 'ru-ru': 'Акцентный цвет 3', - 'es-es': 'Color de acento 3', - 'fr-fr': "Couleur d'accent 3", - 'uk-ua': 'Акцентний колір 3', - 'de-ch': 'Akzentfarbe 3', - 'pt-br': 'Cor de destaque 3', - }, - accentColor4: { - 'en-us': 'Accent color 4', - 'ru-ru': 'Акцентный цвет 4', - 'es-es': 'Color de acento 4', - 'fr-fr': "Couleur d'accent 4", - 'uk-ua': 'Акцентний колір 4', - 'de-ch': 'Akzentfarbe 4', - 'pt-br': 'Cor de destaque 4', - }, - accentColor5: { - 'en-us': 'Accent color 5', - 'ru-ru': 'Акцентный цвет 5', - 'es-es': 'Color de acento 5', - 'fr-fr': "Couleur d'accent 5", - 'uk-ua': 'Акцентний колір 5', - 'de-ch': 'Akzentfarbe 5', - 'pt-br': 'Cor de destaque 5', - }, - spreadsheet: { - 'en-us': 'Spreadsheet', - 'ru-ru': 'Электронная таблица', - 'es-es': 'Hoja de cálculo', - 'fr-fr': 'Tableur', - 'uk-ua': 'Електронна таблиця', - 'de-ch': 'Kalkulationstabelle', - 'pt-br': 'Planilha', - }, - minSpareRows: { - 'en-us': 'Number of blank rows at the end', - 'ru-ru': 'Количество пустых строк в конце', - 'es-es': 'Número de filas en blanco al final', - 'fr-fr': 'Nombre de lignes vides à la fin', - 'uk-ua': 'Кількість порожніх рядків у кінці', - 'de-ch': 'Anzahl der leeren Zeilen am Ende', - 'pt-br': 'Número de linhas em branco no final', - }, - autoWrapCols: { - 'en-us': 'Navigate to the other side when reaching the edge column', - 'ru-ru': 'Достигнув крайней колонны, перейдите на другую сторону.', - 'es-es': 'Navegue hacia el otro lado al llegar a la columna del borde.', - 'fr-fr': - 'Naviguez de l’autre côté lorsque vous atteignez la colonne de bord', - 'uk-ua': 'Перейдіть на іншу сторону, коли досягнете краю колонки', - 'de-ch': - 'Navigieren Sie zur anderen Seite, wenn Sie die Randspalte erreichen', - 'pt-br': 'Navegue para o outro lado ao atingir a coluna da borda', - }, - autoWrapRows: { - 'en-us': 'Navigate to the other side when reaching the edge row', - 'ru-ru': 'Достигнув крайнего ряда, перейдите на другую сторону.', - 'es-es': 'Navegue hacia el otro lado al llegar a la fila del borde.', - 'fr-fr': - 'Naviguez de l’autre côté lorsque vous atteignez la rangée de bord', - 'uk-ua': 'Перейдіть на іншу сторону, коли досягнете крайнього ряду', - 'de-ch': - 'Navigieren Sie zur anderen Seite, wenn Sie die Randreihe erreichen', - 'pt-br': 'Navegue para o outro lado ao atingir a fileira de bordas', - }, - enterBeginsEditing: { - 'en-us': 'Enter key begins editing cell', - 'ru-ru': 'Клавиша Enter начинает редактирование ячейки.', - 'es-es': 'La tecla Enter inicia la edición de la celda', - 'fr-fr': 'La touche Entrée commence à modifier la cellule', - 'uk-ua': 'Клавіша Enter починає редагування клітинки', - 'de-ch': 'Mit der Eingabetaste beginnt die Bearbeitung der Zelle', - 'pt-br': 'A tecla Enter inicia a edição da célula', - }, - tabMoveDirection: { - 'en-us': 'Direction of movement when Tab key is pressed', - 'ru-ru': 'Направление движения при нажатии клавиши Tab', - 'es-es': - 'Dirección de movimiento cuando se presiona la tecla Tab', - 'fr-fr': - 'Sens de déplacement lorsque la touche Tabulation est enfoncée', - 'uk-ua': 'Напрямок руху при натисканні клавіші Tab', - 'de-ch': 'Bewegungsrichtung beim Drücken der Tab-Taste', - 'pt-br': 'Direção do movimento quando a tecla Tab é pressionada', - }, - tabMoveDirectionDescription: { - 'en-us': - 'You can move in the opposite direction by pressing Shift+Tab.', - 'ru-ru': - 'Вы можете двигаться в обратном направлении, нажав Shift+Tab.', - 'es-es': - 'Puedes moverte en la dirección opuesta presionando Shift+Tab.', - 'fr-fr': - 'Vous pouvez vous déplacer dans la direction opposée en appuyant sur Shift+Tab.', - 'uk-ua': - 'Ви можете рухатися в протилежному напрямку, натискаючи Shift+Tab.', - 'de-ch': - 'Sie können sich in die entgegengesetzte Richtung bewegen, indem Sie Umschalt+Tab drücken.', - 'pt-br': - 'Você pode mover na direção oposta pressionando Shift+Tab.', - }, - column: { - 'en-us': 'Column', - 'ru-ru': 'Столбец', - 'es-es': 'Columna', - 'fr-fr': 'Colonne', - 'uk-ua': 'Колонка', - 'de-ch': 'Spalte', - 'pt-br': 'Coluna', - }, - row: { - 'en-us': 'Row', - 'ru-ru': 'Ряд', - 'es-es': 'Fila', - 'fr-fr': 'Rangée', - 'uk-ua': 'рядок', - 'de-ch': 'Reihe', - 'pt-br': 'Linha', - }, - enterMoveDirection: { - 'en-us': 'Direction of movement when Enter key is pressed', - 'ru-ru': 'Направление движения при нажатии клавиши Enter', - 'es-es': - 'Dirección de movimiento cuando se presiona la tecla Enter', - 'uk-ua': 'Напрямок руху, коли натиснуто клавішу Enter', - 'de-ch': 'Bewegungsrichtung beim Drücken der Taste Enter', - 'fr-fr': - 'Direction du mouvement lorsque la touche Entrer est enfoncée', - 'pt-br': - 'Direção do movimento quando a tecla Enter é pressionada', - }, - enterMoveDirectionDescription: { - 'en-us': - 'You can move in the opposite direction by pressing Shift+Enter.', - 'ru-ru': - 'Вы можете двигаться в противоположном направлении, нажав Shift+Enter.', - 'es-es': - 'Puedes moverte en la dirección opuesta presionando Shift+Enter.', - 'fr-fr': 'Synonyme couleur.', - 'uk-ua': - 'Ви можете рухатися у протилежному напрямку, натискаючи Shift+Enter.', - 'de-ch': - 'Sie können sich in die entgegengesetzte Richtung bewegen, indem Sie Umschalt+Eingabe drücken.', - 'pt-br': - 'Você pode mover na direção oposta pressionando Shift+Enter.', - }, - filterPickLists: { - 'en-us': 'Filter pick list items', - 'ru-ru': 'Фильтрация элементов списка выбора', - 'es-es': 'Filtrar elementos de la lista de selección', - 'fr-fr': 'Filtrer les éléments de la liste de sélection', - 'uk-ua': 'Фільтр вибору елементів списку', - 'de-ch': 'Auswahllistenelemente filtern', - 'pt-br': 'Filtrar itens da lista de seleção', - }, - exportFileDelimiter: { - 'en-us': 'Export file delimiter', - 'ru-ru': 'Разделитель файлов экспорта', - 'es-es': 'Delimitador de archivo de exportación', - 'fr-fr': "Délimiteur de fichier d'exportation", - 'uk-ua': 'Роздільник файлу експорту', - 'de-ch': 'Dateitrennzeichen exportieren', - 'pt-br': 'Delimitador de arquivo de exportação', - }, - exportCsvUtf8Bom: { - 'en-us': 'Add UTF-8 BOM to CSV file exports', - 'ru-ru': 'Добавить UTF-8 BOM в экспорт CSV-файла', - 'es-es': 'Agregar BOM UTF-8 a las exportaciones de archivos CSV', - 'fr-fr': 'Ajouter UTF-8 BOM aux exportations de fichiers CSV', - 'uk-ua': 'Додайте специфікацію UTF-8 до експорту файлу CSVу', - 'de-ch': 'UTF-8 BOM zum CSV-Dateiexport hinzufügen', - 'pt-br': 'Adicionar UTF-8 BOM às exportações de arquivos CSV', - }, - exportCsvUtf8BomDescription: { - 'en-us': - 'Adds a BOM (Byte Order Mark) to exported CSV files to ensure that the file is correctly recognized and displayed by various programs (Excel, OpenRefine, etc.), preventing issues with special characters and formatting.', - 'ru-ru': 'Корректное отображение экспортированных CSV-файлов в Excel.', - 'es-es': - 'Agrega una BOM (marca de orden de bytes) a los archivos CSV exportados para garantizar que el archivo sea reconocido y mostrado correctamente por varios programas (Excel, OpenRefine, etc.), evitando problemas con caracteres especiales y formato.', - 'fr-fr': - "Permet aux exportations de fichiers CSV de s'afficher correctement dans Excel.", - 'uk-ua': 'Змушує експорт файлів CSV правильно відображатися в Excel.', - 'de-ch': - 'Sorgt dafür, dass CSV-Dateiexporte in Excel korrekt angezeigt werden.', - 'pt-br': - 'Adiciona uma BOM (Byte Order Mark) aos arquivos CSV exportados para garantir que o arquivo seja reconhecido e exibido corretamente por vários programas (Excel, OpenRefine, etc.), evitando problemas com caracteres especiais e formatação.', - }, - caseSensitive: { - 'en-us': 'Case-sensitive', - 'ru-ru': 'С учетом регистра', - 'es-es': 'Distingue mayúsculas y minúsculas', - 'fr-fr': 'Sensible aux majuscules et minuscules', - 'uk-ua': 'Чутливий до регістру', - 'de-ch': 'Groß- und Kleinschreibung beachten', - 'pt-br': 'Maiúsculas e minúsculas', - }, - caseInsensitive: { - 'en-us': 'Case-insensitive', - 'ru-ru': 'Без учета регистра', - 'es-es': 'No distingue entre mayúsculas y minúsculas', - 'fr-fr': 'Insensible à la casse', - 'uk-ua': 'Регістр не враховується', - 'de-ch': 'Groß- und Kleinschreibung wird nicht berücksichtigt', - 'pt-br': 'Não diferencia maiúsculas de minúsculas', - }, - showNoReadTables: { - 'en-us': 'Show tables without "Read" access', - 'ru-ru': 'Показывать таблицы без доступа «Чтение»', - 'es-es': 'Mostrar tablas sin acceso de "Lectura"', - 'fr-fr': 'Afficher les tableaux sans accès "Lecture"', - 'uk-ua': 'Показувати таблиці без доступу «Читання»', - 'de-ch': 'Tabellen ohne Lesezugriff anzeigen', - 'pt-br': 'Mostrar tabelas sem acesso de "Leitura"', - }, - showNoAccessTables: { - 'en-us': 'Show tables without "Create" access', - 'ru-ru': 'Показывать таблицы без права «Создать»', - 'es-es': 'Mostrar tablas sin acceso "Crear"', - 'fr-fr': 'Afficher les tableaux sans accès "Créer"', - 'uk-ua': 'Показувати таблиці без доступу «Створити»', - 'de-ch': 'Tabellen ohne „Erstellen“-Zugriff anzeigen', - 'pt-br': 'Mostrar tabelas sem acesso "Criar"', - }, - textAreaAutoGrow: { - 'en-us': 'Text boxes grow automatically', - 'ru-ru': 'Текстовые поля увеличиваются автоматически', - 'es-es': 'Los cuadros de texto crecen automáticamente', - 'fr-fr': "Les zones de texte s'agrandissent automatiquement", - 'uk-ua': 'Текстові поля збільшуються автоматично', - 'de-ch': 'Textfelder werden automatisch vergrößert', - 'pt-br': 'As caixas de texto crescem automaticamente', - }, - clearQueryFilters: { - 'en-us': 'Reset query filters', - 'ru-ru': 'Сбросить фильтры запроса', - 'es-es': 'Restablecer filtros de consulta', - 'fr-fr': 'Réinitialiser les filtres de requête', - 'uk-ua': 'Скинути фільтри запитів', - 'de-ch': 'Abfragefilter zurücksetzen', - 'pt-br': 'Redefinir filtros de consulta', - }, - clearQueryFiltersDescription: { - 'en-us': 'Clears all query filters when running a Report from a Form.', - 'de-ch': - 'Löscht alle Abfragefilter, wenn ein Bericht aus einem Formular ausgeführt wird.', - 'es-es': - 'Borra todos los filtros de consulta al ejecutar un informe desde un formulario.', - 'fr-fr': - "Efface tous les filtres de requête lors de l'exécution d'un rapport à partir d'un formulaire.", - 'ru-ru': 'Очищает все фильтры запроса при запуске отчета из формы.', - 'uk-ua': 'Очищає всі фільтри запитів під час запуску звіту з форми.', - 'pt-br': - 'Limpa todos os filtros de consulta ao executar um relatório de um formulário.', - }, - queryParamtersFromForm: { - 'en-us': 'Show query filters when running a Report from a Form', - 'de-ch': - 'Abfragefilter anzeigen, wenn ein Bericht aus einem Formular ausgeführt wird', - 'es-es': - 'Mostrar filtros de consulta al ejecutar un informe desde un formulario', - 'fr-fr': - "Afficher les filtres de requête lors de l'exécution d'un rapport à partir d'un formulaire", - 'ru-ru': 'Показывать фильтры запроса при запуске отчета из формы', - 'uk-ua': 'Показувати фільтри запитів під час запуску звіту з форми', - 'pt-br': - 'Mostrar filtros de consulta ao executar um relatório de um formulário', - }, - autoGrowAutoComplete: { - 'en-us': 'Allow autocomplete to grow as wide as need', - 'ru-ru': - 'Разрешить автозаполнению расширяться настолько, насколько это необходимо', - 'es-es': 'Permitir que el autocompletado crezca tanto como sea necesario', - 'fr-fr': - 'Sens de déplacement lorsque la touche [X27X]Tabulation[X35X] est enfoncée', - 'uk-ua': - 'Дозволити автозаповнення розширюватися настільки, наскільки потрібно', - 'de-ch': - 'Erlauben Sie der Autovervollständigung, so weit wie nötig zu wachsen', - 'pt-br': - 'Permitir que o preenchimento automático cresça o quanto for necessário', - }, - tableNameInTitle: { - 'en-us': 'Include table name in the browser page title', - 'ru-ru': 'Включить имя таблицы в заголовок страницы браузера', - 'es-es': - 'Incluir el nombre de la tabla en el título de la página del navegador', - 'fr-fr': - 'Inclure le nom de la table dans le titre de la page du navigateur', - 'uk-ua': 'Включіть назву таблиці в заголовок сторінки браузера', - 'de-ch': 'Tabellennamen in den Seitentitel des Browsers aufnehmen', - 'pt-br': 'Incluir nome da tabela no título da página do navegador', - }, - focusFirstField: { - 'en-us': 'Focus first field', - 'de-ch': 'Fokus erstes Feld', - 'es-es': 'Enfoque el primer campo', - 'fr-fr': 'Concentrez-vous sur le premier champ', - 'ru-ru': 'Фокус первого поля', - 'uk-ua': 'Перейти до першого поля', - 'pt-br': 'Foco primeiro campo', - }, - doubleClickZoom: { - 'en-us': 'Double click to zoom', - 'ru-ru': 'Дважды щелкните, чтобы увеличить', - 'es-es': 'Haga doble clic para ampliar', - 'fr-fr': 'Double-cliquez pour zoomer', - 'uk-ua': 'Двічі клацніть, щоб збільшити', - 'de-ch': 'Zum Vergrößern doppelklicken', - 'pt-br': 'Clique duas vezes para ampliar', - }, - closePopupOnClick: { - 'en-us': 'Close pop-up on outside click', - 'ru-ru': 'Закрытие всплывающего окна при внешнем щелчке', - 'es-es': 'Cerrar ventana emergente al hacer clic desde fuera', - 'fr-fr': "Fermer la pop-up lors d'un clic extérieur", - 'uk-ua': 'Закрити спливаюче вікно при зовнішньому клацанні', - 'de-ch': 'Popup bei externem Klick schließen', - 'pt-br': 'Fechar pop-up ao clicar fora', - }, - animateTransitions: { - 'en-us': 'Animate transitions', - 'ru-ru': 'Анимированные переходы', - 'es-es': 'Animar transiciones', - 'fr-fr': 'Animer les transitions', - 'uk-ua': 'Анімація переходів', - 'de-ch': 'Übergänge animieren', - 'pt-br': 'Transições animadas', - }, - panInertia: { - 'en-us': 'Pan inertia', - 'ru-ru': 'Инерция пан', - 'es-es': 'Inercia de la sartén', - 'fr-fr': 'Inertie du bac', - 'uk-ua': 'Інерція панорами', - 'de-ch': 'Schwenkträgheit', - 'pt-br': 'Inércia da panela', - }, - mouseDrags: { - 'en-us': 'Mouse drags', - 'ru-ru': 'Перетаскивание мышью', - 'es-es': 'El ratón arrastra', - 'uk-ua': 'Виділіть відповідний підрядок', - 'de-ch': 'Maus zieht', - 'fr-fr': 'Mettre en surbrillance la sous-chaîne correspondante', - 'pt-br': 'Arrastos do mouse', - }, - scrollWheelZoom: { - 'en-us': 'Scroll wheel zoom', - 'ru-ru': 'Масштабирование с помощью колеса прокрутки', - 'es-es': 'Zoom con rueda de desplazamiento', - 'fr-fr': 'Zoom avec la molette de défilement', - 'uk-ua': 'Масштаб колеса прокрутки', - 'de-ch': 'Scrollrad-Zoom', - 'pt-br': 'Zoom da roda de rolagem', - }, - flexibleColumnWidth: { - 'en-us': 'Flexible column width', - 'ru-ru': 'Гибкая ширина столбца', - 'es-es': 'Ancho de columna flexible', - 'fr-fr': 'Largeur de colonne flexible', - 'uk-ua': 'Гнучка ширина колонки', - 'de-ch': 'Flexible Spaltenbreite', - 'pt-br': 'Largura de coluna flexível', - }, - flexibleSubGridColumnWidth: { - 'en-us': 'Flexible subview grid column width', - 'ru-ru': 'Гибкая ширина столбца сетки подпредставлений', - 'es-es': 'Ancho de columna de cuadrícula de subvista flexible', - 'fr-fr': 'Largeur de colonne de grille de sous-vue flexible', - 'uk-ua': 'Гнучка ширина стовпця сітки вкладеного перегляду', - 'de-ch': 'Flexible Rasterspaltenbreite der Unteransicht', - 'pt-br': 'Largura flexível da coluna da grade de subvisualização', - }, - closeOnEsc: { - 'en-us': 'Close on ESC key press', - 'ru-ru': 'Закрыть нажатием клавиши ESC', - 'es-es': 'Cerrar al presionar la tecla ESC', - 'fr-fr': 'Icône et nom de la table', - 'uk-ua': 'Закриття натисканням клавіші ESC', - 'de-ch': 'Schließen durch Drücken der Taste ESC', - 'pt-br': 'Fechar ao pressionar a tecla ESC', - }, - closeOnOutsideClick: { - 'en-us': 'Close on outside click', - 'ru-ru': 'Закрытие по внешнему щелчку', - 'es-es': 'Cerrar al hacer clic desde fuera', - 'fr-fr': 'Fermer sur clic extérieur', - 'uk-ua': 'Закрийте зовнішнім клацанням', - 'de-ch': 'Schließen durch Klicken von außen', - 'pt-br': 'Fechar com clique externo', - }, - specifyNetworkBadge: { - 'en-us': 'Specify Network Badge', - 'ru-ru': 'Укажите сетевой значок', - 'es-es': 'Especificar la insignia de red', - 'fr-fr': 'Spécifier le badge réseau', - 'uk-ua': 'Укажіть значок мережі', - 'de-ch': 'Netzwerk-Badge angeben', - 'pt-br': 'Especificar emblema de rede', - }, - useAccessibleFullDatePicker: { - 'en-us': 'Use accessible full date picker', - 'ru-ru': 'Используйте доступный полный выбор даты', - 'es-es': 'Utilice el selector de fecha completo y accesible', - 'fr-fr': 'Utiliser un sélecteur de date complet accessible', - 'uk-ua': 'Використовуйте доступний повний засіб вибору дати', - 'de-ch': 'Verwenden Sie eine barrierefreie Datumsauswahl', - 'pt-br': 'Use o seletor de data completo acessível', - }, - useAccessibleMonthPicker: { - 'en-us': 'Use accessible month picker', - 'ru-ru': 'Используйте доступный выбор месяца', - 'es-es': 'Utilice el selector de meses accesible', - 'fr-fr': 'Utiliser le sélecteur de mois accessible', - 'uk-ua': 'Використовуйте доступний засіб вибору місяця', - 'de-ch': 'Verwenden Sie die barrierefreie Monatsauswahl', - 'pt-br': 'Use o seletor de meses acessível', - }, - rightAlignNumberFields: { - 'en-us': 'Right-Justify numeric fields', - 'ru-ru': 'Выравнивание числовых полей по правому краю', - 'es-es': 'Justificar a la derecha los campos numéricos', - 'fr-fr': 'Justifier à droite les champs numériques', - 'uk-ua': 'Вирівнювання по правому краю числових полів', - 'de-ch': 'Rechtsbündige Ausrichtung numerischer Felder', - 'pt-br': 'Justificar à direita campos numéricos', - }, - roundedCorners: { - 'en-us': 'Rounded corners', - 'ru-ru': 'Закругленные углы', - 'es-es': 'esquinas redondeadas', - 'fr-fr': 'Coins arrondis', - 'uk-ua': 'Заокруглені кути', - 'de-ch': 'Abgerundete Ecken', - 'pt-br': 'Cantos arredondados', - }, - showSubviewBorders: { - 'en-us': 'Show borders around subviews', - 'de-ch': 'Rahmen um Unteransichten anzeigen', - 'es-es': 'Mostrar bordes alrededor de las subvistas', - 'fr-fr': 'Afficher les bordures autour des sous-vues', - 'pt-br': 'Mostrar bordas ao redor das subvisualizações', - 'ru-ru': 'Показывать границы вокруг подпредставлений', - 'uk-ua': 'Показати межі навколо підвидів', - }, - limitMaxFieldWidth: { - 'en-us': 'Limit max field width', - 'ru-ru': 'Ограничить максимальную ширину поля', - 'es-es': 'Limitar el ancho máximo del campo', - 'fr-fr': 'Limiter la largeur maximale du champ', - 'uk-ua': 'Обмеження максимальної ширини поля', - 'de-ch': 'Maximale Feldbreite begrenzen', - 'pt-br': 'Limite a largura máxima do campo', - }, - condenseQueryResults: { - 'en-us': 'Condense query results', - 'ru-ru': 'Сжать результаты запроса', - 'es-es': 'Condensar los resultados de la consulta', - 'fr-fr': 'Condenser les résultats de la requête', - 'uk-ua': 'Згорнути результати запиту', - 'de-ch': 'Abfrageergebnisse verdichten', - 'pt-br': 'Condensar resultados da consulta', - }, - blurContentBehindDialog: { - 'en-us': 'Blur content behind the dialog', - 'ru-ru': 'Размытие содержимого за диалогом', - 'es-es': 'Desenfocar el contenido detrás del diálogo', - 'fr-fr': 'Flou le contenu derrière la boîte de dialogue', - 'uk-ua': 'Розмити вміст за діалоговим вікном', - 'de-ch': 'Inhalte hinter dem Dialog verwischen', - 'pt-br': 'Desfocar o conteúdo atrás do diálogo', - }, - collectionSortOrderDescription: { - 'en-us': 'This determines the visual order of collections.', - 'ru-ru': 'Это определяет визуальный порядок коллекций.', - 'es-es': 'Esto determina el orden visual de las colecciones.', - 'fr-fr': "Ceci détermine l'ordre visuel des collections.", - 'uk-ua': 'Це визначає візуальний порядок колекцій.', - 'de-ch': 'Dies bestimmt die visuelle Reihenfolge der Sammlungen.', - 'pt-br': 'Isso determina a ordem visual das coleções.', - }, - recordSetRecordToOpen: { - 'en-us': 'Record to open by default', - 'ru-ru': 'Запись для открытия по умолчанию', - 'es-es': 'Registro para abrir por defecto', - 'fr-fr': 'Enregistrement à ouvrir par défaut', - 'uk-ua': 'Запис відкривається за умовчанням', - 'de-ch': 'Standardmäßig zu öffnender Datensatz', - 'pt-br': 'Gravar para abrir por padrão', - }, - altClickToSupressNewTab: { - 'en-us': - '{altKeyName:string}+Click to suppress new tab', - 'ru-ru': - '{altKeyName:string}+Нажмите , чтобы скрыть новую вкладку', - 'es-es': - '{altKeyName:string}+Haga clic en para suprimir la nueva pestaña', - 'fr-fr': - '{altKeyName:string}+Cliquez sur pour supprimer le nouvel onglet', - 'uk-ua': - '{altKeyName:string}+Натисніть , щоб закрити нову вкладку', - 'de-ch': - '{altKeyName:string}+Klicken Sie auf, um neue Registerkarten zu unterdrücken', - 'pt-br': - '{altKeyName:string}+Clique em para suprimir a nova guia', - }, - altClickToSupressNewTabDescription: { - 'en-us': - '{altKeyName:string}+Click a link that usually opens in a new tab to open it in the current tab.', - 'ru-ru': - '{altKeyName:string}+Нажмите на ссылку, которая обычно открывается в новой вкладке, чтобы открыть ее в текущей вкладке.', - 'es-es': - '{altKeyName:string}+Haga clic en un enlace que normalmente se abre en una nueva pestaña para abrirlo en la pestaña actual.', - 'fr-fr': 'Utiliser le sélecteur de mois accessible.', - 'uk-ua': - '{altKeyName:string}+Натисніть посилання, яке зазвичай відкривається в новій вкладці, щоб відкрити його в поточній вкладці.', - 'de-ch': - '{altKeyName:string}+Klicken Sie auf einen Link, der normalerweise in einem neuen Tab geöffnet wird, um ihn im aktuellen Tab zu öffnen.', - 'pt-br': - '{altKeyName:string}+Clique em um link que geralmente abre em uma nova aba para abri-lo na aba atual.', - }, - makeFormDialogsModal: { - 'en-us': 'Make form dialogs gray out the background', - 'ru-ru': 'Сделать фон диалоговых окон серым', - 'es-es': - 'Hacer que los cuadros de diálogo del formulario tengan el fondo en gris', - 'fr-fr': - "Rendre les boîtes de dialogue de formulaire grisées sur l'arrière-plan", - 'uk-ua': 'Зробіть діалогові вікна форми сірими фоном', - 'de-ch': 'Den Hintergrund von Formulardialogen ausgrauen', - 'pt-br': - 'Faça com que as caixas de diálogo do formulário fiquem com o fundo acinzentado', - }, - autoScrollTree: { - 'en-us': 'Auto scroll tree to focused node', - 'ru-ru': 'Автоматическая прокрутка дерева к выбранному узлу', - 'es-es': 'Desplazamiento automático del árbol al nodo enfocado', - 'fr-fr': 'Arbre de défilement automatique vers le nœud ciblé', - 'uk-ua': 'Автоматичне прокручування дерева до виділеного вузла', - 'de-ch': 'Automatisches Scrollen des Baums zum fokussierten Knoten', - 'pt-br': 'Rolagem automática da árvore para o nó em foco', - }, - sortByField: { - 'en-us': 'Order By Field', - 'de-ch': 'Nach Feld sortieren', - 'es-es': 'Ordenar por campo', - 'fr-fr': 'Trier par champ', - 'pt-br': 'Ordenar por campo', - 'ru-ru': 'Сортировать по полю', - 'uk-ua': 'Сортувати за полем', - }, - treeStatsThreshold: { - 'en-us': 'Minimum rank for Collection Object counts', - 'ru-ru': 'Минимальный ранг для подсчета коллекционных объектов', - 'es-es': 'Rango mínimo para recuentos de objetos de colección', - 'fr-fr': 'Rang minimal pour les comptes des objets de collection', - 'uk-ua': 'Мінімальний ранг для підрахунку колекційних об’єктів', - 'de-ch': 'Minimaler Rang für Sammlungsobjektzählungen', - 'pt-br': 'Classificação mínima para contagens de objetos de coleção', - }, - treeStatsThresholdDescription: { - 'en-us': - 'Show Collection Object counts only for nodes with RankID greater than or equal to this value.', - 'ru-ru': - 'Показывать количество коллекционных объектов только для узлов с RankID больше или равным этому значению.', - 'es-es': - 'Mostrar recuentos de objetos de colección solo para nodos con RankID mayor o igual que este valor.', - 'fr-fr': - 'Afficher les comptes d’objets de collection uniquement pour les nœuds dont le RankID est supérieur ou égal à cette valeur.', - 'uk-ua': - 'Показувати кількість колекційних об’єктів лише для вузлів з RankID, що дорівнює або перевищує це значення.', - 'de-ch': - 'Zeige Zählungen von Sammlungsobjekten nur für Knoten mit einem RankID grösser oder gleich diesem Wert.', - 'pt-br': - 'Mostrar contagens de objetos de coleção apenas para nós com RankID maior ou igual a este valor.', - }, - lineWrap: { - 'en-us': 'Line wrap', - 'ru-ru': 'Перенос строки', - 'es-es': 'Ajuste de línea', - 'fr-fr': 'Retour à la ligne', - 'uk-ua': 'Обтікання лініями', - 'de-ch': 'Zeilenumbruch', - 'pt-br': 'Quebra de linha', - }, - indentSize: { - 'en-us': 'Indent size', - 'ru-ru': 'Размер отступа', - 'es-es': 'Tamaño de sangría', - 'fr-fr': 'Taille du retrait', - 'uk-ua': 'Розмір відступу', - 'de-ch': 'Einzugsgröße', - 'pt-br': 'Tamanho do recuo', - }, - indentWithTab: { - 'en-us': 'Indent with Tab', - 'ru-ru': 'Отступ с помощью Tab', - 'es-es': 'Sangría con Tab', - 'fr-fr': 'Indenter avec Tabulation', - 'uk-ua': 'Відступ із Tab', - 'de-ch': 'Einrücken mit Tab', - 'pt-br': 'Recuo com Tab', - }, - formHeaderFormat: { - 'en-us': 'Form header format', - 'ru-ru': 'Формат заголовка формы', - 'es-es': 'Formato del encabezado del formulario', - 'fr-fr': "Format d'en-tête de formulaire", - 'uk-ua': 'Формат заголовка форми', - 'de-ch': 'Formularkopfformat', - 'pt-br': 'Formato do cabeçalho do formulário', - }, - iconAndTableName: { - 'en-us': 'Icon and table name', - 'ru-ru': 'Значок и название таблицы', - 'es-es': 'Icono y nombre de la tabla', - 'fr-fr': 'Icône et nom de la table', - 'uk-ua': 'Значок і назва таблиці', - 'de-ch': 'Symbol und Tabellenname', - 'pt-br': 'Ícone e nome da tabela', - }, - tableIcon: { - 'en-us': 'Table icon', - 'ru-ru': 'Значок таблицы', - 'es-es': 'Icono de tabla', - 'fr-fr': 'Icône de tableau', - 'uk-ua': 'Значок таблиці', - 'de-ch': 'Tabellensymbol', - 'pt-br': 'Ícone de tabela', - }, - maxHeight: { - 'en-us': 'Max height', - 'ru-ru': 'Максимальная высота', - 'es-es': 'Altura máxima', - 'fr-fr': 'hauteur maximum', - 'uk-ua': 'Максимальна висота', - 'de-ch': 'Maximale Höhe', - 'pt-br': 'Altura máxima', - }, - autoComplete: { - 'en-us': 'Auto complete', - 'ru-ru': 'Автозаполнение', - 'es-es': 'Autocompletar', - 'fr-fr': - "Détermine les légendes des champs, les notes d'utilisation et les légendes des tableaux", - 'uk-ua': - 'Визначає підписи полів, примітки щодо використання та підписи таблиць', - 'de-ch': 'Autovervollständigung', - 'pt-br': 'Preenchimento automático', - }, - searchCaseSensitive: { - 'en-us': 'Case-sensitive search', - 'es-es': 'Búsqueda que distingue entre mayúsculas y minúsculas', - 'fr-fr': 'Recherche sensible à la casse', - 'uk-ua': 'Пошук з урахуванням регістру', - 'de-ch': 'Groß- und Kleinschreibung beachten', - 'ru-ru': 'Поиск с учетом регистра', - 'pt-br': 'Pesquisa com diferenciação entre maiúsculas e minúsculas', - }, - searchField: { - 'en-us': 'Search Field', - 'ru-ru': 'Поле поиска', - 'es-es': 'Campo de búsqueda', - 'fr-fr': 'Champ de recherche', - 'uk-ua': 'Поле пошуку', - 'de-ch': 'Suchfeld', - 'pt-br': 'Campo de pesquisa', - }, - createInteractions: { - 'en-us': 'Creating an interaction', - 'ru-ru': 'Создание взаимодействия', - 'es-es': 'Creando una interacción', - 'fr-fr': 'Créer une interaction', - 'uk-ua': 'Створення взаємодії', - 'de-ch': 'Erstellen einer Interaktion', - 'pt-br': 'Criando uma interação', - }, - useSpaceAsDelimiter: { - 'en-us': 'Use space as delimiter', - 'ru-ru': 'Используйте пробел в качестве разделителя', - 'es-es': 'Utilice el espacio como delimitador', - 'fr-fr': "Utiliser l'espace comme délimiteur", - 'uk-ua': 'Використовуйте пробіл як роздільник', - 'de-ch': 'Leerzeichen als Trennzeichen verwenden', - 'pt-br': 'Use espaço como delimitador', - }, - useCommaAsDelimiter: { - 'en-us': 'Use comma as delimiter', - 'ru-ru': 'Используйте запятую в качестве разделителя', - 'es-es': 'Utilice la coma como delimitador', - 'fr-fr': 'Utiliser la virgule comme délimiteur', - 'uk-ua': 'Використовуйте кому як роздільник', - 'de-ch': 'Verwenden Sie Kommas als Trennzeichen.', - 'pt-br': 'Use vírgula como delimitador', - }, - useNewLineAsDelimiter: { - 'en-us': 'Use new line as delimiter', - 'ru-ru': 'Использовать новую строку в качестве разделителя', - 'es-es': 'Utilice nueva línea como delimitador', - 'fr-fr': 'Utiliser une nouvelle ligne comme délimiteur', - 'uk-ua': 'Використовуйте новий рядок як роздільник', - 'de-ch': 'Neue Zeile als Trennzeichen verwenden', - 'pt-br': 'Use nova linha como delimitador', - }, - useCustomDelimiters: { - 'en-us': 'Use custom delimiters', - 'ru-ru': 'Используйте пользовательские разделители', - 'es-es': 'Utilice delimitadores personalizados', - 'fr-fr': 'Utiliser des délimiteurs personnalisés', - 'uk-ua': 'Використовуйте спеціальні роздільники', - 'de-ch': 'Benutzerdefinierte Trennzeichen verwenden', - 'pt-br': 'Use delimitadores personalizados', - }, - useCustomDelimitersDescription: { - 'en-us': - 'A list of delimiters to use, in addition to the ones defined above. Put one delimiter per line.', - 'ru-ru': - 'Список разделителей, которые можно использовать в дополнение к указанным выше. Используйте по одному разделителю на строку.', - 'es-es': - 'Una lista de delimitadores para usar, además de los definidos anteriormente. Coloque un delimitador por línea.', - 'fr-fr': - 'Une liste de délimiteurs à utiliser, en plus de ceux définis ci-dessus. Mettez un délimiteur par ligne.', - 'uk-ua': - 'Список розділювачів для використання на додаток до визначених вище. Поставте один роздільник на рядок.', - 'de-ch': - 'Eine Liste der zu verwendenden Trennzeichen zusätzlich zu den oben definierten. Geben Sie pro Zeile ein Trennzeichen ein.', - 'pt-br': - 'Uma lista de delimitadores a serem usados, além dos definidos acima. Coloque um delimitador por linha.', - }, - detectAutomaticallyDescription: { - 'en-us': 'Detect automatically based on catalog number format.', - 'ru-ru': 'Автоматическое определение на основе формата каталожного номера.', - 'es-es': - 'Detectar automáticamente según el formato del número de catálogo.', - 'fr-fr': - 'Détecter automatiquement en fonction du format du numéro de catalogue.', - 'uk-ua': 'Визначати автоматично на основі формату номера каталогу.', - 'de-ch': 'Automatische Erkennung basierend auf dem Katalognummernformat.', - 'pt-br': - 'Detectar automaticamente com base no formato do número de catálogo.', - }, - use: { - comment: 'Verb', - 'en-us': 'Use', - 'ru-ru': 'Использовать', - 'es-es': 'Usar', - 'fr-fr': 'Utiliser', - 'uk-ua': 'використання', - 'de-ch': 'Verwenden', - 'pt-br': 'Usar', - }, - dontUse: { - 'en-us': 'Don’t use', - 'ru-ru': 'Не использовать', - 'es-es': 'No utilizar', - 'fr-fr': 'Zoom avec la molette de défilement', - 'uk-ua': 'Масштаб колеса прокрутки', - 'de-ch': 'Nicht verwenden', - 'pt-br': 'Não use', - }, - position: { - 'en-us': 'Position', - 'es-es': 'Posición', - 'fr-fr': 'Position', - 'ru-ru': 'Позиция', - 'uk-ua': 'Позиція', - 'de-ch': 'Position', - 'pt-br': 'Posição', - }, - top: { - 'en-us': 'Top', - 'es-es': 'Arriba', - 'fr-fr': 'Haut', - 'ru-ru': 'Вершина', - 'uk-ua': 'Топ', - 'de-ch': 'Spitze', - 'pt-br': 'Principal', - }, - bottom: { - 'en-us': 'Bottom', - 'es-es': 'Abajo', - 'ru-ru': 'Нижний', - 'uk-ua': 'Дно', - 'de-ch': 'Unten', - 'fr-fr': 'Bas', - 'pt-br': 'Fundo', - }, - left: { - 'en-us': 'Left', - 'es-es': 'Izquierda', - 'fr-fr': 'Gauche', - 'ru-ru': 'Левый', - 'uk-ua': 'Ліворуч', - 'de-ch': 'Links', - 'pt-br': 'Esquerda', - }, - right: { - 'en-us': 'Right', - 'es-es': 'Bien', - 'fr-fr': 'Droite', - 'ru-ru': 'Верно', - 'uk-ua': 'правильно', - 'de-ch': 'Rechts', - 'pt-br': 'Certo', - }, - showUnsavedIndicator: { - 'en-us': 'Show unsaved changes indicator', - 'ru-ru': 'Показать индикатор несохраненных изменений', - 'es-es': 'Mostrar indicador de cambios no guardados', - 'fr-fr': "Afficher l'indicateur de modifications non enregistrées", - 'uk-ua': 'Показати індикатор незбережених змін', - 'de-ch': 'Indikator für nicht gespeicherte Änderungen anzeigen', - 'pt-br': 'Mostrar indicador de alterações não salvas', - }, - showUnsavedIndicatorDescription: { - 'en-us': - 'Show an "*" in the tab title when there are unsaved changes in the current tab.', - 'es-es': - 'Mostrar un "*" en el título de la pestaña cuando haya cambios sin guardar en la pestaña actual.', - 'fr-fr': - "Afficher un \"*\" dans le titre de l'onglet lorsqu'il y a des modifications non enregistrées dans l'onglet actuel.", - 'ru-ru': - 'Отображать «*» в заголовке вкладки, если на текущей вкладке есть несохраненные изменения.', - 'uk-ua': - 'Показувати «*» у заголовку вкладки, якщо в поточній вкладці є незбережені зміни.', - 'de-ch': - 'Zeigen Sie im Registerkartentitel ein „*“ an, wenn in der aktuellen Registerkarte nicht gespeicherte Änderungen vorhanden sind.', - 'pt-br': - 'Exibir um "*" no título da aba quando houver alterações não salvas na aba atual.', - }, - autoPopulateDescription: { - 'en-us': - 'Auto populate the merged record with values from duplicates when opening the merging dialog.', - 'ru-ru': - 'Автоматически заполнять объединенную запись значениями из дубликатов при открытии диалогового окна слияния.', - 'de-ch': - 'Füllen Sie den zusammengeführten Datensatz beim Öffnen des Zusammenführungsdialogs automatisch mit Werten aus Duplikaten.', - 'es-es': - 'Rellene automáticamente el registro fusionado con valores de duplicados al abrir el cuadro de diálogo de fusión.', - 'fr-fr': - "Remplir automatiquement l'enregistrement fusionné avec les valeurs des doublons lors de l'ouverture de la boîte de dialogue de fusion.", - 'uk-ua': - 'Автоматичне заповнення об’єднаного запису значеннями з дублікатів під час відкриття діалогового вікна об’єднання.', - 'pt-br': - 'Preencha automaticamente o registro mesclado com valores de duplicatas ao abrir a caixa de diálogo de mesclagem.', - }, - autoCreateVariants: { - 'en-us': 'Automatically create {agentVariantTable:string} records', - 'ru-ru': 'Автоматически создавать записи {agentVariantTable:string}', - 'de-ch': '{agentVariantTable:string}-Datensätze automatisch erstellen', - 'es-es': 'Crear automáticamente registros {agentVariantTable:string}', - 'fr-fr': - 'Créer automatiquement des enregistrements {agentVariantTable:string}', - 'uk-ua': 'Автоматично створювати записи {agentVariantTable:string}', - 'pt-br': 'Criar automaticamente registros {agentVariantTable:string}', - }, - autoCreateVariantsDescription: { - 'en-us': - 'When merging agents, automatically create {agentVariantTable:string} records based on the variations of first name/last name.', - 'ru-ru': - 'При объединении агентов автоматически создавать записи {agentVariantTable:string} на основе вариаций имени/фамилии.', - 'de-ch': - 'Beim Zusammenführen von Agenten werden automatisch {agentVariantTable:string}-Datensätze basierend auf den Variationen von Vorname/Nachname erstellt.', - 'es-es': - 'Al fusionar agentes, se crean automáticamente registros {agentVariantTable:string} basados en las variaciones de nombre/apellido.', - 'fr-fr': - "Lors de la fusion d'agents, créez automatiquement des enregistrements {agentVariantTable:string} en fonction des variations du prénom/nom.", - 'uk-ua': - 'Під час об’єднання агентів автоматично створювати записи {agentVariantTable:string} на основі варіацій імені/прізвища.', - 'pt-br': - 'Ao mesclar agentes, crie automaticamente registros {agentVariantTable:string} com base nas variações de nome/sobrenome.', - }, - collectionPreferences: { - 'en-us': 'Collection Preferences', - 'de-ch': 'Sammlungseinstellungen', - 'es-es': 'Preferencias de colección', - 'fr-fr': 'Personnalisation', - 'ru-ru': 'Настройки коллекции', - 'uk-ua': 'Налаштування', - 'pt-br': 'Preferências de coleção', - }, - rememberDialogSizes: { - 'en-us': 'Remember dialog window sizes', - 'ru-ru': 'Запомните размеры диалоговых окон', - 'es-es': 'Recordar los tamaños de las ventanas de diálogo', - 'fr-fr': 'Mémoriser les tailles des fenêtres de dialogue', - 'uk-ua': "Запам'ятайте розміри діалогових вікон", - 'de-ch': 'Dialogfenstergrößen merken', - 'pt-br': 'Lembrar tamanhos de janelas de diálogo', - }, - rememberDialogPositions: { - 'en-us': 'Remember dialog window positions', - 'ru-ru': 'Запомнить позиции диалоговых окон', - 'es-es': 'Recordar las posiciones de las ventanas de diálogo', - 'fr-fr': 'Mémoriser les positions des fenêtres de dialogue', - 'uk-ua': "Запам'ятовуйте положення діалогового вікна", - 'de-ch': 'Dialogfensterpositionen merken', - 'pt-br': 'Lembrar posições da janela de diálogo', - }, - autoPlayMedia: { - 'en-us': 'Automatically play media', - 'ru-ru': 'Автоматически воспроизводить медиа', - 'es-es': 'Reproducir automáticamente medios', - 'fr-fr': 'Lire automatiquement les médias', - 'uk-ua': 'Автоматичне відтворення медіа', - 'de-ch': 'Medien automatisch abspielen', - 'pt-br': 'Reproduzir mídia automaticamente', - }, - useCustomTooltips: { - 'en-us': 'Use modern tooltips', - 'ru-ru': 'Используйте современные подсказки', - 'es-es': 'Utilice información sobre herramientas moderna', - 'fr-fr': 'Utiliser des info-bulles modernes', - 'uk-ua': 'Використовуйте сучасні підказки', - 'de-ch': 'Verwenden Sie moderne Tooltips', - 'pt-br': 'Use dicas de ferramentas modernas', - }, - alwaysUseQueryBuilder: { - 'en-us': 'Always use query builder search inside of search form', - 'de-ch': - 'Verwenden Sie innerhalb des Suchformulars immer die Abfragegeneratorsuche', - 'es-es': - 'Utilice siempre la búsqueda del generador de consultas dentro del formulario de búsqueda', - 'fr-fr': - 'Utilisez toujours la recherche du générateur de requêtes dans le formulaire de recherche', - 'ru-ru': 'Всегда используйте конструктор запросов внутри формы поиска.', - 'uk-ua': 'Завжди використовуйте пошук конструктора запитів у формі пошуку', - 'pt-br': - 'Sempre use a pesquisa do construtor de consultas dentro do formulário de pesquisa', - }, - localizeResourceNames: { - 'en-us': 'Localize the names of recognized app resources', - 'de-ch': 'Lokalisieren Sie die Namen erkannter App-Ressourcen', - 'es-es': - 'Localizar los nombres de los recursos de aplicaciones reconocidos', - 'fr-fr': "Localiser les noms des ressources d'application reconnues", - 'ru-ru': 'Локализуйте названия распознанных ресурсов приложения', - 'uk-ua': 'Локалізувати назви розпізнаних ресурсів програми', - 'pt-br': 'Localize os nomes dos recursos de aplicativos reconhecidos', - }, - splitLongXml: { - 'en-us': 'Split long lines of XML into multiple lines', - 'de-ch': 'Teilen Sie lange XML-Zeilen in mehrere Zeilen auf', - 'es-es': 'Dividir líneas largas de XML en varias líneas', - 'fr-fr': 'Diviser les longues lignes de XML en plusieurs lignes', - 'ru-ru': 'Разделить длинные строки XML на несколько строк', - 'uk-ua': 'Розділіть довгі рядки XML на кілька рядків', - 'pt-br': 'Dividir longas linhas de XML em várias linhas', - }, - url: { - 'en-us': 'URL', - 'de-ch': 'URL', - 'es-es': 'URL', - 'fr-fr': 'URL', - 'uk-ua': 'URL', - 'ru-ru': 'URL', - 'pt-br': 'URL', - }, - pickAttachment: { - 'en-us': 'Pick an attachment', - 'es-es': 'Elige un archivo adjunto', - 'fr-fr': 'Choisissez une pièce jointe', - 'ru-ru': 'Выберите вложение', - 'uk-ua': 'Виберіть вкладення', - 'de-ch': 'Wählen Sie einen Anhang', - 'pt-br': 'Escolha um anexo', - }, - attachmentFailed: { - 'en-us': 'The attachment failed to load.', - 'de-ch': 'Der Anhang konnte nicht geladen werden.', - 'es-es': 'No se pudo cargar el archivo adjunto.', - 'fr-fr': "La pièce jointe n'a pas pu être chargée.", - 'ru-ru': 'Не удалось загрузить вложение.', - 'uk-ua': 'Не вдалося завантажити вкладений файл.', - 'pt-br': 'O anexo não pôde ser carregado.', - }, - pickImage: { - 'en-us': 'Pick an image', - 'de-ch': 'Wählen Sie ein Bild aus', - 'es-es': 'Elige una imagen', - 'fr-fr': 'Choisissez une image', - 'ru-ru': 'Выберите изображение', - 'uk-ua': 'Виберіть зображення', - 'pt-br': 'Escolha uma imagem', - }, - customLogo: { - 'en-us': 'Expanded Image URL', - 'de-ch': 'Erweiterte Bild-URL', - 'es-es': 'URL de imagen expandida', - 'fr-fr': "URL de l'image étendue", - 'ru-ru': 'URL-адрес развернутого изображения', - 'uk-ua': 'Розширена URL-адреса зображення', - 'pt-br': 'URL da imagem expandida', - }, - customLogoCollapsed: { - 'en-us': 'Collapsed Image URL', - 'de-ch': 'URL des minimierten Bildes', - 'es-es': 'URL de imagen contraída', - 'fr-fr': "URL de l'image réduite", - 'ru-ru': 'URL-адрес свернутого изображения', - 'uk-ua': 'URL-адреса згорнутого зображення', - 'pt-br': 'URL da imagem recolhida', - }, - customLogoDescription: { - 'en-us': - 'A URL to an image that would be displayed next to the Specify logo in the navigation menu.', - 'de-ch': - 'Eine URL zu einem Bild, das neben dem angegebenen Logo im Navigationsmenü angezeigt wird.', - 'es-es': - 'Una URL a una imagen que se mostrará junto al logotipo Especificar en el menú de navegación.', - 'fr-fr': - 'Une URL vers une image qui serait affichée à côté du logo Specify dans le menu de navigation.', - 'ru-ru': - 'URL-адрес изображения, которое будет отображаться рядом с логотипом «Укажите» в меню навигации.', - 'uk-ua': - 'URL-адреса зображення, яке відображатиметься поруч із «Вказати логотип» у меню навігації.', - 'pt-br': - 'Um URL para uma imagem que seria exibida ao lado do logotipo Especificar no menu de navegação.', - }, - showLineNumber: { - 'en-us': 'Show query result line number', - 'de-ch': 'Zeilennummer des Abfrageergebnisses anzeigen', - 'es-es': 'Mostrar el número de línea del resultado de la consulta', - 'fr-fr': 'Afficher le numéro de ligne du résultat de la requête', - 'ru-ru': 'Показать номер строки результата запроса', - 'uk-ua': 'Показати номер рядка результату запиту', - 'pt-br': 'Mostrar número da linha do resultado da consulta', - }, - saveButtonColor: { - 'en-us': 'Save button color', - 'de-ch': 'Farbe der Schaltfläche „Speichern“', - 'es-es': 'Guardar color del botón', - 'fr-fr': 'Couleur du bouton Enregistrer', - 'ru-ru': 'Сохранить цвет кнопки', - 'uk-ua': 'Зберегти колір кнопки', - 'pt-br': 'Cor do botão Salvar', - }, - secondaryButtonColor: { - 'en-us': 'Secondary button color', - 'es-es': 'Color del botón secundario', - 'fr-fr': 'Couleur du bouton secondaire', - 'ru-ru': 'Цвет вторичной кнопки', - 'uk-ua': 'Колір вторинної кнопки', - 'de-ch': 'Sekundäre Schaltflächenfarbe', - 'pt-br': 'Cor do botão secundário', - }, - secondaryLightButtonColor: { - 'en-us': 'Secondary light button color', - 'de-ch': 'Farbe der sekundären Lichttaste', - 'es-es': 'Color del botón de luz secundaria', - 'fr-fr': 'Couleur du bouton lumineux secondaire', - 'ru-ru': 'Цвет кнопки дополнительного освещения', - 'uk-ua': 'Колір вторинної світлової кнопки', - 'pt-br': 'Cor do botão de luz secundária', - }, - dangerButtonColor: { - 'en-us': 'Danger button color', - 'de-ch': 'Farbe der Gefahrenschaltfläche', - 'es-es': 'Color del botón de peligro', - 'fr-fr': 'Couleur du bouton de danger', - 'ru-ru': 'Цвет кнопки «Опасность»', - 'uk-ua': 'Колір кнопки небезпеки', - 'pt-br': 'Cor do botão de perigo', - }, - infoButtonColor: { - 'en-us': 'Info button color', - 'de-ch': 'Farbe der Info-Schaltfläche', - 'es-es': 'Color del botón de información', - 'fr-fr': "Couleur du bouton d'information", - 'ru-ru': 'Цвет кнопки информации', - 'uk-ua': 'Колір інформаційної кнопки', - 'pt-br': 'Cor do botão de informações', - }, - warningButtonColor: { - 'en-us': 'Warning button color', - 'de-ch': 'Farbe der Warnschaltfläche', - 'es-es': 'Color del botón de advertencia', - 'fr-fr': "Couleur du bouton d'avertissement", - 'ru-ru': 'Цвет кнопки предупреждения', - 'uk-ua': 'Колір кнопки попередження', - 'pt-br': 'Cor do botão de aviso', - }, - successButtonColor: { - 'en-us': 'Success button color', - 'de-ch': 'Farbe der Schaltfläche „Erfolg“', - 'es-es': 'Color del botón de éxito', - 'fr-fr': 'Couleur du bouton de réussite', - 'ru-ru': 'Цвет кнопки «Успех»', - 'uk-ua': 'Колір кнопки успіху', - 'pt-br': 'Cor do botão de sucesso', - }, - openAsReadOnly: { - 'en-us': 'Open all records in read-only mode', - 'de-ch': 'Alle Datensätze im schreibgeschützten Modus öffnen', - 'es-es': 'Abrir todos los registros en modo de solo lectura', - 'fr-fr': 'Ouvrir tous les enregistrements en mode lecture seule', - 'ru-ru': 'Открыть все записи в режиме только для чтения', - 'uk-ua': 'Відкрити всі записи в режимі лише для читання', - 'pt-br': 'Abra todos os registros no modo somente leitura', - }, - displayBasicView: { - 'en-us': 'Display basic view', - 'de-ch': 'Basisansicht anzeigen', - 'es-es': 'Mostrar vista básica', - 'fr-fr': 'Afficher la vue de base', - 'ru-ru': 'Отобразить базовый вид', - 'uk-ua': 'Відобразити базовий вигляд', - 'pt-br': 'Exibir visualização básica', - }, - showComparisonOperatorsForString: { - 'en-us': 'Show comparison operators for text-based fields', - 'de-ch': 'Vergleichsoperatoren für textbasierte Felder anzeigen', - 'es-es': 'Mostrar operadores de comparación para campos basados en texto', - 'fr-fr': 'Afficher les opérateurs de comparaison pour les champs textuels', - 'pt-br': 'Mostrar operadores de comparação para campos baseados em texto', - 'ru-ru': 'Показать операторы сравнения для текстовых полей', - 'uk-ua': 'Показати оператори порівняння для текстових полів', - }, - showComparisonOperatorsDescription: { - 'en-us': - 'Allows the following filters to apply to text fields: Greater Than, Less Than, Greater Than or Equal to, and Less Than or Equal to', - 'de-ch': - 'Ermöglicht die Anwendung der folgenden Filter auf Textfelder: Größer als, Kleiner als, Größer als oder gleich und Kleiner als oder gleich', - 'es-es': - 'Permite aplicar los siguientes filtros a los campos de texto: Mayor que, Menor que, Mayor o igual que y Menor o igual que', - 'fr-fr': - "Permet d'appliquer les filtres suivants aux champs de texte : Supérieur à, Inférieur à, Supérieur ou égal à et Inférieur ou égal à", - 'pt-br': - 'Permite que os seguintes filtros sejam aplicados aos campos de texto: Maior que, Menor que, Maior ou igual a e Menor ou igual a', - 'ru-ru': - 'Позволяет применять к текстовым полям следующие фильтры: «Больше», «Меньше», «Больше или равно» и «Меньше или равно».', - 'uk-ua': - 'Дозволяє застосовувати до текстових полів такі фільтри: «Більше ніж», «Менше ніж», «Більше або дорівнює» та «Менше або дорівнює»', - }, - basicView: { - 'en-us': 'Basic view', - 'de-ch': 'Basisansicht', - 'es-es': 'Vista básica', - 'fr-fr': 'Vue de base', - 'ru-ru': 'Базовый вид', - 'uk-ua': 'Основний вигляд', - 'pt-br': 'Visão básica', - }, - detailedView: { - 'en-us': 'Detailed view', - 'de-ch': 'Detailansicht', - 'es-es': 'Vista detallada', - 'fr-fr': 'Vue détaillée', - 'ru-ru': 'Подробный вид', - 'uk-ua': 'Детальний вигляд', - 'pt-br': 'Visão detalhada', - }, - attachmentPreviewMode: { - 'en-us': 'Attachment preview mode', - 'de-ch': 'Anhangsvorschaumodus', - 'es-es': 'Modo de vista previa de archivos adjuntos', - 'fr-fr': "Mode d'aperçu des pièces jointes", - 'ru-ru': 'Режим предварительного просмотра вложений', - 'uk-ua': 'Режим попереднього перегляду вкладених файлів', - 'pt-br': 'Modo de visualização de anexos', - }, - fullResolution: { - 'en-us': 'Full Resolution', - 'de-ch': 'Volle Auflösung', - 'es-es': 'Resolución completa', - 'fr-fr': 'Pleine résolution', - 'ru-ru': 'Полное разрешение', - 'uk-ua': 'Повна роздільна здатність', - 'pt-br': 'Resolução completa', - }, - thumbnail: { - 'en-us': 'Thumbnail', - 'de-ch': 'Miniaturansicht', - 'es-es': 'Uña del pulgar', - 'fr-fr': 'Vignette', - 'ru-ru': 'Миниатюра', - 'uk-ua': 'Мініатюра', - 'pt-br': 'Miniatura', - }, - addSearchBarHomePage: { - 'en-us': 'Add Search Bar on home page', - 'de-ch': 'Suchleiste auf der Startseite hinzufügen', - 'es-es': 'Agregar barra de búsqueda en la página de inicio', - 'fr-fr': "Ajouter une barre de recherche sur la page d'accueil", - 'ru-ru': 'Добавить панель поиска на домашнюю страницу', - 'uk-ua': 'Додайте рядок пошуку на головну сторінку', - 'pt-br': 'Adicionar barra de pesquisa na página inicial', - }, - inheritanceCatNumberPref: { - 'en-us': - 'Enable the inheritance of the primary catalog number to its empty siblings.', - 'de-ch': - 'Aktivieren Sie die Vererbung der primären Katalognummer an ihre leeren Geschwister.', - 'es-es': - 'Habilitar la herencia del número de catálogo principal a sus hermanos vacíos.', - 'fr-fr': - "Activer l'héritage du numéro de catalogue principal à ses frères vides.", - 'pt-br': - 'Habilitar a herança do número de catálogo primário para seus irmãos vazios.', - 'ru-ru': - 'Включить наследование основного каталожного номера его пустыми родственными номерами.', - 'uk-ua': - 'Увімкнути успадкування основного каталожного номера його порожнім братам і сестрам.', - }, - inheritanceCatNumberParentCOPref: { - 'en-us': - 'Enable the inheritance of the parent catalog number to its empty children.', - 'de-ch': - 'Aktivieren Sie die Vererbung der übergeordneten Katalognummer an ihre leeren untergeordneten Elemente.', - 'es-es': - 'Habilitar la herencia del número de catálogo padre a sus hijos vacíos.', - 'fr-fr': - "Activer l'héritage du numéro de catalogue parent à ses enfants vides.", - 'pt-br': - 'Habilita a herança do número do catálogo pai para seus filhos vazios.', - 'ru-ru': - 'Включить наследование родительского каталожного номера его пустыми дочерними элементами.', - 'uk-ua': - 'Увімкнути успадкування батьківського каталожного номера його порожнім дочірнім елементам.', - }, - uniqueCatNumberAcrossCompAndCo: { - 'en-us': - 'Catalog Number field need to be unique across Component and CO tables', - 'de-ch': - 'Das Feld „Katalognummer“ muss in allen Komponenten- und CO-Tabellen eindeutig sein.', - 'es-es': - 'El campo Número de catálogo debe ser único en las tablas de componentes y CO.', - 'fr-fr': - 'Le champ Numéro de catalogue doit être unique dans les tables Composant et CO', - 'pt-br': - 'O campo Número de Catálogo precisa ser único em todas as tabelas de Componente e CO.', - 'ru-ru': - 'Поле «Номер каталога» должно быть уникальным в таблицах «Компонент» и «CO».', - 'uk-ua': - 'Поле «Номер у каталозі» має бути унікальним у таблицях «Компонент» та «CO»', - }, -} as const); +export const preferencesText = createDictionary(preferencesDictionary); diff --git a/specifyweb/frontend/js_src/lib/localization/query.ts b/specifyweb/frontend/js_src/lib/localization/query.ts index d964fc8612e..cacc91baf73 100644 --- a/specifyweb/frontend/js_src/lib/localization/query.ts +++ b/specifyweb/frontend/js_src/lib/localization/query.ts @@ -955,16 +955,6 @@ export const queryText = createDictionary({ 'ru-ru': 'Наследование каталожного номера', 'uk-ua': 'Успадкування каталожних номерів', }, - catalogNumberParentCOInheritance: { - 'en-us': 'Catalog Number Parent Collection Object Inheritance', - 'de-ch': 'Katalognummer Übergeordnete Sammlung Objektvererbung', - 'es-es': - 'Herencia de objetos de la colección principal del número de catálogo', - 'fr-fr': "Numéro de catalogue Collection parente Héritage d'objet", - 'pt-br': 'Herança de objeto de coleção pai de número de catálogo', - 'ru-ru': 'Номер каталога Родительская коллекция Объект Наследование', - 'uk-ua': "Успадкування батьківського об'єкта колекції за номером каталогу", - }, uniqueCatalogNumberAcrossComponentAndCo: { 'en-us': 'Catalog Number Uniqueness Across Component And CO tables', 'de-ch': diff --git a/specifyweb/frontend/js_src/lib/localization/specifyNetwork.ts b/specifyweb/frontend/js_src/lib/localization/specifyNetwork.ts index 8af7cf05095..08d37debb90 100644 --- a/specifyweb/frontend/js_src/lib/localization/specifyNetwork.ts +++ b/specifyweb/frontend/js_src/lib/localization/specifyNetwork.ts @@ -16,6 +16,20 @@ export const specifyNetworkText = createDictionary({ 'de-ch': 'Specify Network', 'pt-br': 'Especificar rede', }, + publishingOrganizationKey: { + 'en-us': 'GBIF Publishing Organization Key', + }, + publishingOrganizationKeyDescription: { + 'en-us': + 'The GBIF "publishingOrgKey" (a UUID) for this collection, used for Specify Network integration.', + }, + collectionKey: { + 'en-us': 'GBIF Data Set Key', + }, + collectionKeyDescription: { + 'en-us': + 'The GBIF "dataSetKey" (a UUID) for this collection, used for Specify Network integration.', + }, occurrenceOrGuidRequired: { 'en-us': 'Species Name or GUID must be provided to display this page', 'de-ch': diff --git a/specifyweb/frontend/js_src/lib/localization/stats.tsx b/specifyweb/frontend/js_src/lib/localization/stats.tsx index d7e726cd65d..e71a12cd578 100644 --- a/specifyweb/frontend/js_src/lib/localization/stats.tsx +++ b/specifyweb/frontend/js_src/lib/localization/stats.tsx @@ -313,5 +313,22 @@ export const statsText = createDictionary({ categoryToDelete: { 'en-us': 'This will permanently delete the following category', }, + layoutPreference: { + 'en-us': 'Defines the layout of the statistics page', + }, + showPreparationsTotal: { + 'en-us': 'Show Preparations Totals', + }, + showPreparationsTotalDescription: { + 'en-us': + 'If enabled, the default Preparations statistics panel will include a total count for each preparation of a particular preparation type alongside the overall total. This is useful for lot-based collections.', + }, + autoRefreshRate: { + 'en-us': 'Auto-Refresh Rate (Hours)', + }, + autoRefreshRateDescription: { + 'en-us': + 'The time interval, in hours, at which the statistics page will automatically refresh its data. Default is 24.', + }, }); /* eslint-enable @typescript-eslint/naming-convention */ diff --git a/specifyweb/frontend/js_src/lib/localization/tree.ts b/specifyweb/frontend/js_src/lib/localization/tree.ts index a6a5ce09643..d8d610518b9 100644 --- a/specifyweb/frontend/js_src/lib/localization/tree.ts +++ b/specifyweb/frontend/js_src/lib/localization/tree.ts @@ -703,4 +703,14 @@ export const treeText = createDictionary({ 'ru-ru': 'Метеориты', 'uk-ua': 'Метеорити', }, + treeManagement: { + 'en-us': 'Tree Management', + }, + synonymizedNodes: { + 'en-us': 'Expand Synonym Behavior', + }, + synonymizedNodesDescription: { + 'en-us': + 'If enabled, this allows users to add children to synonymized parents and to synonymize a node with children.', + }, } as const); diff --git a/specifyweb/frontend/js_src/lib/localization/utils/scanUsages.ts b/specifyweb/frontend/js_src/lib/localization/utils/scanUsages.ts index 7de4b6cacd0..94e8790abf2 100644 --- a/specifyweb/frontend/js_src/lib/localization/utils/scanUsages.ts +++ b/specifyweb/frontend/js_src/lib/localization/utils/scanUsages.ts @@ -12,6 +12,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { formatConjunction } from '../../components/Atoms/Internationalization'; import { f } from '../../utils/functools'; @@ -76,13 +77,11 @@ export async function extractStrings(): Promise { return undefined; const compiledFilePath = path.join(localizationDirectory, filePath); - const filePathWithoutExtension = compiledFilePath - .split('.') - .slice(0, -1) - .join('.'); - const fileName = filePathWithoutExtension.split('/').at(-1)!; + const fileUrl = pathToFileURL(compiledFilePath).href; + const { name: fileName } = path.parse(compiledFilePath); + if (fileName.includes('.')) return undefined; - const dictionaryFile = await import(filePathWithoutExtension); + const dictionaryFile = await import(fileUrl); const dictionaries = Object.keys(dictionaryFile ?? {}).filter( (dictionaryName) => dictionaryName.endsWith('Text') @@ -168,9 +167,13 @@ export async function scanUsages( { categoryName, strings: Object.fromEntries( - Object.entries(strings).map(([key, strings]) => { - Object.keys(strings) - .filter((key) => !f.has(expectedKeys, key)) + Object.keys(strings).map((key) => { + const rawStrings = (Reflect.getOwnPropertyDescriptor(strings, key) + ?.value ?? + (strings as LanguageDictionary)[key]) as LocalizationEntry; + + Object.keys(rawStrings) + .filter((language) => !f.has(expectedKeys, language)) .forEach((language) => error( [ @@ -189,7 +192,7 @@ export async function scanUsages( ); // Search for blacklisted characters - Object.entries(strings).forEach(([language, string]) => { + Object.entries(rawStrings).forEach(([language, string]) => { if (f.includes(localizationMetaKeys, language)) return; characterBlacklist[language] @@ -211,9 +214,9 @@ export async function scanUsages( key, { strings: { - ...strings, + ...rawStrings, comment: f.maybe( - localized(strings.comment), + localized(rawStrings.comment), whitespaceSensitive ), }, diff --git a/specifyweb/frontend/js_src/lib/tests/ajax/static/context/remoteprefs.properties b/specifyweb/frontend/js_src/lib/tests/ajax/static/context/remoteprefs.properties index e5c9ecce3df..b8573351e58 100644 --- a/specifyweb/frontend/js_src/lib/tests/ajax/static/context/remoteprefs.properties +++ b/specifyweb/frontend/js_src/lib/tests/ajax/static/context/remoteprefs.properties @@ -68,7 +68,9 @@ Treeeditor.SynonymyColor.GeologicTimePeriod=0, 0, 255 AttachmentsTask.OnTaskbar.fish=true Interactions.Doing.Exchanges.fish=false recent_collection_id.testuser.KU_Fish_Tissue=32768 -attachment.is_public_default=true +attachment.is_public_default_32768=true +sp7.allow_adding_child_to_synonymized_parent.Taxon_32768=true +sp7.allow_adding_child_to_synonymized_parent.GeologicTimePeriod_32768=true settings.email.username=abentley checkforupdates= recent_collection_id.sowens.KU_Fish_Tissue=4 diff --git a/specifyweb/frontend/js_src/package-lock.json b/specifyweb/frontend/js_src/package-lock.json index 874f6d4c7cc..a25ba029bea 100644 --- a/specifyweb/frontend/js_src/package-lock.json +++ b/specifyweb/frontend/js_src/package-lock.json @@ -100,6 +100,7 @@ "regenerator-runtime": "^0.13.9", "style-loader": "^3.3.4", "tailwindcss": "^3.4.1", + "ts-node": "^10.9.2", "tsx": "^4.19.3", "typedoc": "^0.23.23", "typescript": "4.8.4", @@ -1971,8 +1972,6 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -1985,8 +1984,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -3983,33 +3980,25 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@types/aria-query": { "version": "4.2.2", @@ -6659,9 +6648,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/crelt": { "version": "1.0.5", @@ -7404,8 +7391,6 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=0.3.1" } @@ -12063,9 +12048,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -16033,12 +16016,11 @@ "dev": true }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "optional": true, - "peer": true, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -16082,8 +16064,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true, - "optional": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -16096,8 +16076,6 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=0.4.0" } @@ -16106,9 +16084,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/tsconfig-paths": { "version": "3.14.1", @@ -16586,9 +16562,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/v8-to-istanbul": { "version": "9.0.1", @@ -17378,8 +17352,6 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=6" } @@ -18606,8 +18578,6 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "optional": true, - "peer": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -18617,8 +18587,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, - "optional": true, - "peer": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -19877,33 +19845,25 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@tsconfig/node16": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@types/aria-query": { "version": "4.2.2", @@ -21886,9 +21846,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "crelt": { "version": "1.0.5" @@ -22371,9 +22329,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "diff-sequences": { "version": "28.1.1", @@ -25735,9 +25691,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "makeerror": { "version": "1.0.12", @@ -28351,12 +28305,10 @@ "dev": true }, "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "optional": true, - "peer": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -28377,25 +28329,19 @@ "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "optional": true, - "peer": true + "dev": true } } }, @@ -28731,9 +28677,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "v8-to-istanbul": { "version": "9.0.1", @@ -29266,9 +29210,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "yocto-queue": { "version": "0.1.0", diff --git a/specifyweb/frontend/js_src/package.json b/specifyweb/frontend/js_src/package.json index 404c36bbad2..33ba472764b 100644 --- a/specifyweb/frontend/js_src/package.json +++ b/specifyweb/frontend/js_src/package.json @@ -120,6 +120,7 @@ "regenerator-runtime": "^0.13.9", "style-loader": "^3.3.4", "tailwindcss": "^3.4.1", + "ts-node": "^10.9.2", "tsx": "^4.19.3", "typedoc": "^0.23.23", "typescript": "4.8.4",