From 0fd3c9558c54c04c342be77da88a26a25cab60ca Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 29 Jun 2024 21:25:26 -0700 Subject: [PATCH 01/33] feat(FieldFormatter): begin implementing visual editor --- .../AppResources/TabDefinitions.tsx | 7 +- .../lib/components/AppResources/types.tsx | 2 +- .../lib/components/DataEntryTables/fetch.ts | 4 +- .../lib/components/FieldFormatters/Editor.tsx | 20 ++++ .../components/FieldFormatters/Element.tsx | 27 +++++ .../FieldFormatters/FieldFormatter.tsx | 87 ++++++++++++++ .../lib/components/FieldFormatters/List.tsx | 78 +++++++++++++ .../lib/components/FieldFormatters/Routes.tsx | 44 ++++++++ .../lib/components/FieldFormatters/Table.tsx | 41 +++++++ .../FieldFormatters/__tests__/index.test.ts | 4 +- .../lib/components/FieldFormatters/index.ts | 59 +++++----- .../lib/components/FieldFormatters/spec.ts | 51 +++++++-- .../js_src/lib/components/FormParse/index.ts | 4 +- .../lib/components/Formatters/Element.tsx | 106 +++++++++++------- .../lib/components/Formatters/Preview.tsx | 2 +- .../lib/components/Formatters/formatters.ts | 4 +- .../lib/components/Forms/dataObjFormatters.ts | 4 +- .../lib/components/InitialContext/index.ts | 4 +- .../components/InitialContext/remotePrefs.ts | 4 +- .../lib/components/Interactions/fetch.ts | 4 +- .../js_src/lib/components/Leaflet/layers.ts | 6 +- .../Preferences/BasePreferences.tsx | 6 +- .../lib/components/Reports/available.ts | 4 +- .../lib/components/Toolbar/Language.tsx | 4 +- .../js_src/lib/components/WebLinks/List.tsx | 9 +- .../js_src/lib/localization/resources.ts | 28 ++++- 26 files changed, 501 insertions(+), 112 deletions(-) create mode 100644 specifyweb/frontend/js_src/lib/components/FieldFormatters/Editor.tsx create mode 100644 specifyweb/frontend/js_src/lib/components/FieldFormatters/Element.tsx create mode 100644 specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx create mode 100644 specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx create mode 100644 specifyweb/frontend/js_src/lib/components/FieldFormatters/Routes.tsx create mode 100644 specifyweb/frontend/js_src/lib/components/FieldFormatters/Table.tsx diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/TabDefinitions.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/TabDefinitions.tsx index f4109fcffe8..b44e76eea67 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/TabDefinitions.tsx +++ b/specifyweb/frontend/js_src/lib/components/AppResources/TabDefinitions.tsx @@ -21,6 +21,8 @@ import type { } from '../DataModel/types'; import { RssExportFeedEditor } from '../ExportFeed'; import { exportFeedSpec } from '../ExportFeed/spec'; +import { FieldFormattersEditor } from '../FieldFormatters/Editor'; +import { fieldFormattersSpec } from '../FieldFormatters/spec'; import { DataObjectFormatter } from '../Formatters'; import { formattersSpec } from '../Formatters/spec'; import { FormEditor } from '../FormEditor'; @@ -168,7 +170,10 @@ export const visualAppResourceEditors = f.store< visual: WebLinkEditor, xml: generateXmlEditor(webLinksSpec), }, - uiFormatters: undefined, + uiFormatters: { + visual: FieldFormattersEditor, + xml: generateXmlEditor(fieldFormattersSpec), + }, dataObjectFormatters: { visual: DataObjectFormatter, xml: generateXmlEditor(formattersSpec), diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/types.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/types.tsx index b344f82c9f4..df9f20cfa46 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/types.tsx +++ b/specifyweb/frontend/js_src/lib/components/AppResources/types.tsx @@ -156,7 +156,7 @@ export const appResourceSubTypes = ensure>()({ documentationUrl: 'https://github.com/specify/specify6/blob/master/config/backstop/uiformatters.xml', icon: icons.hashtag, - label: resourcesText.uiFormatters(), + label: resourcesText.fieldFormatters(), }, dataObjectFormatters: { mimeType: 'text/xml', diff --git a/specifyweb/frontend/js_src/lib/components/DataEntryTables/fetch.ts b/specifyweb/frontend/js_src/lib/components/DataEntryTables/fetch.ts index dff49c0413e..260a3d4a85a 100644 --- a/specifyweb/frontend/js_src/lib/components/DataEntryTables/fetch.ts +++ b/specifyweb/frontend/js_src/lib/components/DataEntryTables/fetch.ts @@ -8,11 +8,11 @@ import type { SpecifyTable } from '../DataModel/specifyTable'; import { fetchContext as fetchSchema, getTable } from '../DataModel/tables'; import type { Tables } from '../DataModel/types'; import { fetchView } from '../FormParse'; -import { cachableUrl } from '../InitialContext'; +import { cacheableUrl } from '../InitialContext'; import { xmlToSpec } from '../Syncer/xmlUtils'; import { dataEntryItems } from './spec'; -const url = cachableUrl(getAppResourceUrl('DataEntryTaskInit')); +const url = cacheableUrl(getAppResourceUrl('DataEntryTaskInit')); export const fetchLegacyForms = f.store( async (): Promise> => diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Editor.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Editor.tsx new file mode 100644 index 00000000000..dbea71331fa --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Editor.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +import type { AppResourceTabProps } from '../AppResources/TabDefinitions'; +import { createXmlContext, XmlEditor } from '../Formatters'; +import { fieldFormattersRoutes } from './Routes'; +import { fieldFormattersSpec } from './spec'; + +export function FieldFormattersEditor(props: AppResourceTabProps): JSX.Element { + return ( + + ); +} + +export const FieldFormattersContext = createXmlContext(fieldFormattersSpec()); diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Element.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Element.tsx new file mode 100644 index 00000000000..8ea50a884bc --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Element.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { useParams } from 'react-router-dom'; + +import { resourcesText } from '../../localization/resources'; +import { ReadOnlyContext } from '../Core/Contexts'; +import { makeXmlEditorShellSlot, XmlEditorShell } from '../Formatters/Element'; +import { FieldFormatterElement } from './FieldFormatter'; +import type { FieldFormattersOutlet } from './List'; +import type { FieldFormatter } from './spec'; + +export function FieldFormatterWrapper(): JSX.Element { + const { index } = useParams(); + const isReadOnly = React.useContext(ReadOnlyContext); + return ( + + header={resourcesText.fieldFormatters()} + > + {makeXmlEditorShellSlot( + (getSet) => ( + + ), + index, + isReadOnly + )} + + ); +} diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx new file mode 100644 index 00000000000..a522221c28c --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx @@ -0,0 +1,87 @@ +import React from 'react'; + +import { formsText } from '../../localization/forms'; +import { resourcesText } from '../../localization/resources'; +import type { GetSet, RA } from '../../utils/types'; +import { ErrorMessage } from '../Atoms'; +import { Input, Label } from '../Atoms/Form'; +import { ReadOnlyContext } from '../Core/Contexts'; +import type { AnySchema } from '../DataModel/helperTypes'; +import type { SpecifyResource } from '../DataModel/legacyTypes'; +import { ResourcePreview } from '../Formatters/Preview'; +import { hasTablePermission } from '../Permissions/helpers'; +import type { UiFormatter } from '.'; +import { resolveFieldFormatter } from '.'; +import { Definitions } from './Definitions'; +import type { FieldFormatter } from './spec'; + +export function FieldFormatterElement({ + item: [fieldFormatter, setFieldFormatter], +}: { + readonly item: GetSet; +}): JSX.Element { + const isReadOnly = React.useContext(ReadOnlyContext); + return ( + <> + + {formsText.autoNumbering()} + + setFieldFormatter({ ...fieldFormatter, autoNumber }) + } + /> + + {fieldFormatter.external === undefined ? ( + + ) : ( + {resourcesText.editorNotAvailable()} + )} + + + ); +} + +function FieldFormatterPreview({ + fieldFormatter, +}: { + readonly fieldFormatter: FieldFormatter; +}): JSX.Element | null { + const doFormatting = React.useCallback( + (resources: RA>) => { + const resolvedFormatter = resolveFieldFormatter(fieldFormatter); + return resources.map((resource) => + formatterToPreview(resource, fieldFormatter, resolvedFormatter) + ); + }, + [fieldFormatter] + ); + return typeof fieldFormatter.table === 'object' && + hasTablePermission(fieldFormatter.table.name, 'read') ? ( + + ) : null; +} + +function formatterToPreview( + resource: SpecifyResource, + fieldFormatter: FieldFormatter, + resolvedFormatter: UiFormatter | undefined +): string { + if (resolvedFormatter === undefined) + return resourcesText.formatterPreviewUnavailable(); + + const field = fieldFormatter.field; + if (field === undefined) return ''; + + const value = String(resource.get(field.name) ?? ''); + if (value.length === 0) + return resolvedFormatter.pattern() ?? resolvedFormatter.valueOrWild(); + + const formatted = resolvedFormatter.format(value); + + return formatted === undefined + ? `${value} ${resourcesText.nonConformingInline()}` + : formatted; +} diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx new file mode 100644 index 00000000000..e0dd5343ed2 --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { useOutletContext, useParams } from 'react-router-dom'; + +import { resourcesText } from '../../localization/resources'; +import type { GetSet, RA } from '../../utils/types'; +import { getUniqueName } from '../../utils/uniquifyName'; +import { XmlEntryList } from '../Formatters/List'; +import { SafeOutlet } from '../Router/RouterUtils'; +import { updateXml } from '../Syncer/xmlToString'; +import { FieldFormattersContext } from './Editor'; +import type { FieldFormatter } from './spec'; + +export type FieldFormattersOutlet = { + readonly items: GetSet>; +}; + +export function FieldFormatterEditorWrapper(): JSX.Element { + const { + parsed: [parsed, setParsed], + syncer: { deserializer }, + onChange: handleChange, + } = React.useContext(FieldFormattersContext)!; + + return ( + + items={[ + parsed.fieldFormatters, + (fieldFormatters): void => { + const newParsed = { ...parsed, fieldFormatters }; + setParsed(newParsed); + handleChange(() => updateXml(deserializer(newParsed))); + }, + ]} + /> + ); +} + +export function FieldFormattersList(): JSX.Element { + const { tableName } = useParams(); + const { items } = useOutletContext(); + + return ( + { + const newName = getUniqueName( + table.name, + currentItems.map((item) => item.name), + undefined, + 'name' + ); + const newTitle = getUniqueName( + table.label, + currentItems.map((item) => item.title ?? '') + ); + return { + isSystem: false, + name: newName, + title: newTitle, + table, + field: undefined, + isDefault: currentItems.length === 0, + legacyType: undefined, + legacyPartialDate: undefined, + autoNumber: false, + external: undefined, + fields: [], + raw: { + javaClass: undefined, + legacyAutoNumber: undefined, + }, + }; + }} + header={resourcesText.availableFieldFormatters()} + items={items} + tableName={tableName} + /> + ); +} diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Routes.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Routes.tsx new file mode 100644 index 00000000000..dbe1c68e99c --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Routes.tsx @@ -0,0 +1,44 @@ +import React from 'react'; + +import { Redirect } from '../Router/Redirect'; +import { toReactRoutes } from '../Router/RouterUtils'; + +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +export const fieldFormattersRoutes = toReactRoutes([ + { + index: true, + element: , + }, + { + path: 'field-formatters', + children: [ + { + index: true, + element: async () => + import('./Table').then( + ({ FieldFormatterTablesList }) => FieldFormatterTablesList + ), + }, + { + path: ':tableName', + element: async () => + import('./List').then( + ({ FieldFormattersList }) => FieldFormattersList + ), + children: [ + { + index: true, + }, + { + path: ':index', + element: async () => + import('./Element').then( + ({ FieldFormatterWrapper }) => FieldFormatterWrapper + ), + }, + ], + }, + ], + }, +]); +/* eslint-enable @typescript-eslint/explicit-function-return-type */ diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Table.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Table.tsx new file mode 100644 index 00000000000..8b636a08b59 --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Table.tsx @@ -0,0 +1,41 @@ +import React from 'react'; + +import { resourcesText } from '../../localization/resources'; +import { filterArray } from '../../utils/types'; +import { group } from '../../utils/utils'; +import { formatNumber } from '../Atoms/Internationalization'; +import { resolveRelative } from '../Router/queryString'; +import { TableList } from '../SchemaConfig/Tables'; +import { FieldFormattersContext } from './Editor'; + +export function FieldFormatterTablesList(): JSX.Element { + const { + parsed: [{ fieldFormatters }], + } = React.useContext(FieldFormattersContext)!; + + const grouped = Object.fromEntries( + group( + filterArray( + fieldFormatters.map((item) => + item.table === undefined ? undefined : [item.table.name, item] + ) + ) + ) + ); + + return ( + <> +

{resourcesText.fieldFormattersDescription()}

+ resolveRelative(`./${name}`)} + > + {({ name }): string | undefined => + grouped[name] === undefined + ? undefined + : `(${formatNumber(grouped[name].length)})` + } + + + ); +} diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts index 16d9d1353f3..c48bbe783eb 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts @@ -1,8 +1,8 @@ import { mockTime, requireContext } from '../../../tests/helpers'; import { getField } from '../../DataModel/helpers'; import { tables } from '../../DataModel/tables'; -import type { UiFormatter } from '../index'; -import { fetchContext, getUiFormatters } from '../index'; +import type { UiFormatter } from '..'; +import { fetchContext, getUiFormatters } from '..'; mockTime(); requireContext(); diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index 50357e95fb2..3ab2724f788 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -16,6 +16,7 @@ import { tables } from '../DataModel/tables'; import { error } from '../Errors/assert'; import { load } from '../InitialContext'; import { xmlToSpec } from '../Syncer/xmlUtils'; +import type { FieldFormatter } from './spec'; import { fieldFormattersSpec } from './spec'; let uiFormatters: IR; @@ -25,34 +26,12 @@ export const fetchContext = Promise.all([ ]).then(([formatters]) => { uiFormatters = Object.fromEntries( filterArray( - xmlToSpec(formatters, fieldFormattersSpec()).formatters.map( + xmlToSpec(formatters, fieldFormattersSpec()).fieldFormatters.map( (formatter) => { - let resolvedFormatter; - if (typeof formatter.external === 'string') { - if ( - parseJavaClassName(formatter.external) === - 'CatalogNumberUIFieldFormatter' - ) - resolvedFormatter = new CatalogNumberNumeric(); - else return undefined; - } else { - const fields = filterArray( - formatter.fields.map((field) => - typeof field.type === 'string' - ? new formatterTypeMapper[field.type](field) - : undefined - ) - ); - resolvedFormatter = new UiFormatter( - formatter.isSystem, - formatter.title ?? formatter.name, - fields, - formatter.table, - formatter.field - ); - } - - return [formatter.name, resolvedFormatter]; + const resolvedFormatter = resolveFieldFormatter(formatter); + return resolvedFormatter === undefined + ? undefined + : [formatter.name, resolvedFormatter]; } ) ) @@ -62,6 +41,32 @@ export const fetchContext = Promise.all([ export const getUiFormatters = (): typeof uiFormatters => uiFormatters ?? error('Tried to access UI formatters before fetching them'); +export function resolveFieldFormatter( + formatter: FieldFormatter +): UiFormatter | undefined { + if (typeof formatter.external === 'string') { + return parseJavaClassName(formatter.external) === + 'CatalogNumberUIFieldFormatter' + ? new CatalogNumberNumeric() + : undefined; + } else { + const fields = filterArray( + formatter.fields.map((field) => + typeof field.type === 'string' + ? new formatterTypeMapper[field.type](field) + : undefined + ) + ); + return new UiFormatter( + formatter.isSystem, + formatter.title ?? formatter.name, + fields, + formatter.table, + formatter.field + ); + } +} + /* eslint-disable functional/no-class */ export class UiFormatter { public constructor( diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts index 351a6335a0d..552dfbae875 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts @@ -1,4 +1,5 @@ import { f } from '../../utils/functools'; +import type { RA } from '../../utils/types'; import { localized } from '../../utils/types'; import type { LiteralField } from '../DataModel/specifyField'; import type { SpecifyTable } from '../DataModel/specifyTable'; @@ -7,24 +8,31 @@ import type { SpecToJson } from '../Syncer'; import { pipe, syncer } from '../Syncer'; import { syncers } from '../Syncer/syncers'; import { createXmlSpec } from '../Syncer/xmlUtils'; -import { formatterTypeMapper } from './index'; +import { formatterTypeMapper } from '.'; export const fieldFormattersSpec = f.store(() => createXmlSpec({ - formatters: pipe( + fieldFormatters: pipe( syncers.xmlChildren('format'), syncers.map( pipe( syncers.object(formatterSpec()), syncer( - ({ javaClass, ...formatter }) => ({ + ({ javaClass, rawAutoNumber, ...formatter }) => ({ ...formatter, table: getTable(javaClass ?? ''), + autoNumber: rawAutoNumber !== undefined, raw: { javaClass, + legacyAutoNumber: rawAutoNumber, }, }), - ({ table, raw: { javaClass }, ...formatter }) => ({ + ({ + table, + autoNumber, + raw: { javaClass, legacyAutoNumber }, + ...formatter + }) => ({ ...formatter, // "javaClass" is not always a database table javaClass: @@ -32,6 +40,10 @@ export const fieldFormattersSpec = f.store(() => (getTable(javaClass ?? '') === undefined ? javaClass : undefined), + rawAutoNumber: autoNumber + ? legacyAutoNumber ?? + inferLegacyAutoNumber(table, formatter.fields) + : undefined, }) ), syncer( @@ -50,9 +62,31 @@ export const fieldFormattersSpec = f.store(() => }) ); +/** + * Specify 6 hardcoded special autonumbering behavior for a few tables. + * Accession table has special auto numbering, and collection object has + * two. Trying our best here to match the intended semantics for backwards + * compatibility. + */ +function inferLegacyAutoNumber( + table: SpecifyTable | undefined, + fields: RA<{ readonly type: keyof typeof formatterTypeMapper | undefined }> +): string { + if (table?.name === 'Accession') + return 'edu.ku.brc.specify.dbsupport.AccessionAutoNumberAlphaNum'; + else if (table?.name === 'CollectionObject') { + const isNumericOnly = fields.every((field) => field.type === 'numeric'); + return isNumericOnly + ? 'edu.ku.brc.specify.dbsupport.CollectionAutoNumber' + : 'edu.ku.brc.specify.dbsupport.CollectionAutoNumberAlphaNum'; + } else return 'edu.ku.brc.af.core.db.AutoNumberGeneric'; +} + export type FieldFormatter = SpecToJson< ReturnType ->['formatters'][number]; +>['fieldFormatters'][number]; + +export type FieldFormatterField = FieldFormatter['fields'][number]; const formatterSpec = f.store(() => createXmlSpec({ @@ -68,14 +102,16 @@ const formatterSpec = f.store(() => title: syncers.xmlAttribute('title', 'empty'), // Some special formatters don't have a class name javaClass: syncers.xmlAttribute('class', 'skip'), - // BUG: enforce no relationship fields rawField: syncers.xmlAttribute('fieldName', 'skip'), isDefault: pipe( syncers.xmlAttribute('default', 'skip'), syncers.maybe(syncers.toBoolean), syncers.default(false) ), - autoNumber: pipe( + // Used only in special meta-formatters - we don't display these in the UI + legacyType: syncers.xmlAttribute('type', 'skip'), + legacyPartialDate: syncers.xmlAttribute('partialDate', 'skip'), + rawAutoNumber: pipe( syncers.xmlChild('autonumber', 'optional'), syncers.maybe(syncers.xmlContent) ), @@ -95,7 +131,6 @@ const fieldSpec = f.store(() => type: pipe( syncers.xmlAttribute('type', 'required'), syncers.fallback(localized('alphanumeric')), - // TEST: check if sp6 defines any other types not present in this list syncers.enum(Object.keys(formatterTypeMapper)) ), size: pipe( diff --git a/specifyweb/frontend/js_src/lib/components/FormParse/index.ts b/specifyweb/frontend/js_src/lib/components/FormParse/index.ts index 424b9d2e131..2ae4158a1fc 100644 --- a/specifyweb/frontend/js_src/lib/components/FormParse/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FormParse/index.ts @@ -33,7 +33,7 @@ import { pushContext, setLogContext, } from '../Errors/logContext'; -import { cachableUrl } from '../InitialContext'; +import { cacheableUrl } from '../InitialContext'; import { getPref } from '../InitialContext/remotePrefs'; import { formatUrl } from '../Router/queryString'; import type { SimpleXmlNode } from '../Syncer/xmlToJson'; @@ -117,7 +117,7 @@ export const fetchView = async ( * NOTE: If getView hasn't yet been invoked, the view URL won't be * marked as cachable */ - cachableUrl(getViewSetApiUrl(name)), + cacheableUrl(getViewSetApiUrl(name)), { headers: { Accept: 'text/plain' }, expectedErrors: [Http.NOT_FOUND], diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/Element.tsx b/specifyweb/frontend/js_src/lib/components/Formatters/Element.tsx index 4b5fbbc4044..9c60a174085 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/Element.tsx +++ b/specifyweb/frontend/js_src/lib/components/Formatters/Element.tsx @@ -16,6 +16,7 @@ import { Form, Input, Label } from '../Atoms/Form'; import { icons } from '../Atoms/Icons'; import { Submit } from '../Atoms/Submit'; import { ReadOnlyContext } from '../Core/Contexts'; +import type { SpecifyTable } from '../DataModel/specifyTable'; import { Dialog } from '../Molecules/Dialog'; import { NotFoundView } from '../Router/NotFoundView'; import { resolveRelative } from '../Router/queryString'; @@ -25,8 +26,9 @@ import type { Aggregator, Formatter } from './spec'; import type { FormatterTypesOutlet } from './Types'; /** - * Display a dialog for editing weblink/formatter/aggregator and calls a - * render prop to render the actual interface + * Display a dialog for editing + * weblink/field formatter/formatter/aggregator and calls a render prop to + * render the actual interface */ export function XmlEditorShell< ITEM extends { readonly name: string }, @@ -36,10 +38,7 @@ export function XmlEditorShell< children, }: { readonly header: LocalizedString; - readonly children: (props: { - readonly items: GetSet>; - readonly item: GetSet; - }) => JSX.Element; + readonly children: (props: XmlEditorShellSlotProps) => JSX.Element; }): JSX.Element { const { index: rawIndex } = useParams(); const { items: allItems } = useOutletContext(); @@ -118,6 +117,11 @@ export function XmlEditorShell< ); } +type XmlEditorShellSlotProps = { + readonly items: GetSet>; + readonly item: GetSet; +}; + export function FormatterWrapper(): JSX.Element { const { type, index } = useParams(); const isReadOnly = React.useContext(ReadOnlyContext); @@ -129,44 +133,66 @@ export function FormatterWrapper(): JSX.Element { : resourcesText.aggregator() } > - {({ item: getSet, items: [items, setItems] }): JSX.Element => ( - <> - - {resourcesText.title()} - - getSet[1]({ ...getSet[0], title }) - } - /> - - - - setItems( - // Ensure there is only one default - items.map((otherItem, itemIndex) => - otherItem.table === getSet[0].table - ? itemIndex.toString() === index - ? { ...getSet[0], isDefault: !getSet[0].isDefault } - : { ...otherItem, isDefault: false } - : otherItem - ) - ) - } - /> - {resourcesText.default()} - - {type === 'formatter' ? ( + {makeXmlEditorShellSlot( + (getSet) => + type === 'formatter' ? ( } /> ) : ( } /> - )} - + ), + index, + isReadOnly )} ); } + +export const makeXmlEditorShellSlot = < + ITEM extends { + readonly name: string; + readonly title: string | undefined; + readonly isDefault: boolean; + readonly table: SpecifyTable | undefined; + } +>( + children: (getSet: GetSet) => JSX.Element, + index: string | undefined, + isReadOnly: boolean +) => + function XmlEditorShellSlot({ + item: getSet, + items: [items, setItems], + }: XmlEditorShellSlotProps): JSX.Element { + return ( + <> + + {resourcesText.title()} + getSet[1]({ ...getSet[0], title })} + /> + + + + setItems( + // Ensure there is only one default + items.map((otherItem, itemIndex) => + otherItem.table === getSet[0].table + ? itemIndex.toString() === index + ? { ...getSet[0], isDefault: !getSet[0].isDefault } + : { ...otherItem, isDefault: false } + : otherItem + ) + ) + } + /> + {resourcesText.default()} + + {children(getSet)} + + ); + }; diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx b/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx index 4ed5cb01b7f..372f9369d28 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx +++ b/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx @@ -125,7 +125,7 @@ export function ResourcePreview({ readonly table: SpecifyTable; readonly doFormatting: ( resources: RA> - ) => Promise>; + ) => Promise> | RA; readonly isAggregator?: boolean; }): JSX.Element | null { const { diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/formatters.ts b/specifyweb/frontend/js_src/lib/components/Formatters/formatters.ts index 48549e48e65..9ad8f37e576 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/formatters.ts +++ b/specifyweb/frontend/js_src/lib/components/Formatters/formatters.ts @@ -23,7 +23,7 @@ import { } from '../DataModel/tables'; import type { Tables } from '../DataModel/types'; import { - cachableUrl, + cacheableUrl, contextUnlockedPromise, foreverFetch, } from '../InitialContext'; @@ -41,7 +41,7 @@ export const fetchFormatters: Promise<{ }> = contextUnlockedPromise.then(async (entrypoint) => entrypoint === 'main' ? Promise.all([ - ajax(cachableUrl(getAppResourceUrl('DataObjFormatters')), { + ajax(cacheableUrl(getAppResourceUrl('DataObjFormatters')), { headers: { Accept: 'text/xml' }, }).then(({ data }) => data), fetchSchema, diff --git a/specifyweb/frontend/js_src/lib/components/Forms/dataObjFormatters.ts b/specifyweb/frontend/js_src/lib/components/Forms/dataObjFormatters.ts index cce72be87a1..24672bcdc15 100644 --- a/specifyweb/frontend/js_src/lib/components/Forms/dataObjFormatters.ts +++ b/specifyweb/frontend/js_src/lib/components/Forms/dataObjFormatters.ts @@ -24,7 +24,7 @@ import type { Tables } from '../DataModel/types'; import { softFail } from '../Errors/Crash'; import { fieldFormat } from '../Formatters/fieldFormat'; import { - cachableUrl, + cacheableUrl, contextUnlockedPromise, foreverFetch, } from '../InitialContext'; @@ -73,7 +73,7 @@ export const fetchFormatters: Promise<{ }> = contextUnlockedPromise.then(async (entrypoint) => entrypoint === 'main' ? ajax( - cachableUrl( + cacheableUrl( formatUrl('/context/app.resource', { name: 'DataObjFormatters' }) ), { diff --git a/specifyweb/frontend/js_src/lib/components/InitialContext/index.ts b/specifyweb/frontend/js_src/lib/components/InitialContext/index.ts index c4fb91e22d5..5b2f5a36efb 100644 --- a/specifyweb/frontend/js_src/lib/components/InitialContext/index.ts +++ b/specifyweb/frontend/js_src/lib/components/InitialContext/index.ts @@ -15,7 +15,7 @@ export const cachableUrls = new Set(); * Mark URL as cachable -> should have its cache cleared when cache buster is * invoked */ -export function cachableUrl(url: string): string { +export function cacheableUrl(url: string): string { cachableUrls.add(url); return url; } @@ -57,7 +57,7 @@ export const load = async (path: string, mimeType: MimeType): Promise => // Doing async import to avoid a circular dependency const { ajax } = await import('../../utils/ajax'); - const { data } = await ajax(cachableUrl(path), { + const { data } = await ajax(cacheableUrl(path), { errorMode: 'visible', headers: { Accept: mimeType }, }); diff --git a/specifyweb/frontend/js_src/lib/components/InitialContext/remotePrefs.ts b/specifyweb/frontend/js_src/lib/components/InitialContext/remotePrefs.ts index 83bf2d6c614..2574f60f53d 100644 --- a/specifyweb/frontend/js_src/lib/components/InitialContext/remotePrefs.ts +++ b/specifyweb/frontend/js_src/lib/components/InitialContext/remotePrefs.ts @@ -11,7 +11,7 @@ import { parseValue } from '../../utils/parser/parse'; import type { IR, R, RA } from '../../utils/types'; import { defined } from '../../utils/types'; import type { JavaType } from '../DataModel/specifyField'; -import { cachableUrl, contextUnlockedPromise } from './index'; +import { cacheableUrl, contextUnlockedPromise } from './index'; const preferences: R = {}; @@ -22,7 +22,7 @@ const preferences: R = {}; */ export const fetchContext = contextUnlockedPromise.then(async (entrypoint) => entrypoint === 'main' - ? ajax(cachableUrl('/context/remoteprefs.properties'), { + ? ajax(cacheableUrl('/context/remoteprefs.properties'), { headers: { Accept: 'text/plain' }, }) .then(({ data: text }) => diff --git a/specifyweb/frontend/js_src/lib/components/Interactions/fetch.ts b/specifyweb/frontend/js_src/lib/components/Interactions/fetch.ts index 2b207f5bfdf..2d8d57dac39 100644 --- a/specifyweb/frontend/js_src/lib/components/Interactions/fetch.ts +++ b/specifyweb/frontend/js_src/lib/components/Interactions/fetch.ts @@ -6,11 +6,11 @@ import { filterArray } from '../../utils/types'; import type { SpecifyTable } from '../DataModel/specifyTable'; import { tables } from '../DataModel/tables'; import type { Tables } from '../DataModel/types'; -import { cachableUrl } from '../InitialContext'; +import { cacheableUrl } from '../InitialContext'; import { xmlToSpec } from '../Syncer/xmlUtils'; import { interactionEntries } from './spec'; -const url = cachableUrl(getAppResourceUrl('InteractionsTaskInit')); +const url = cacheableUrl(getAppResourceUrl('InteractionsTaskInit')); export const fetchLegacyInteractions = f.store(async () => ajax(url, { headers: { Accept: 'text/xml' }, diff --git a/specifyweb/frontend/js_src/lib/components/Leaflet/layers.ts b/specifyweb/frontend/js_src/lib/components/Leaflet/layers.ts index f5e6ff5fce2..3264bc65eda 100644 --- a/specifyweb/frontend/js_src/lib/components/Leaflet/layers.ts +++ b/specifyweb/frontend/js_src/lib/components/Leaflet/layers.ts @@ -6,7 +6,7 @@ import { getAppResourceUrl } from '../../utils/ajax/helpers'; import type { IR, RA, RR } from '../../utils/types'; import { softFail } from '../Errors/Crash'; import { - cachableUrl, + cacheableUrl, contextUnlockedPromise, foreverFetch, } from '../InitialContext'; @@ -190,14 +190,14 @@ export const fetchLeafletLayers = async (): Promise> => const layersPromise: Promise> = contextUnlockedPromise.then(async (entrypoint) => entrypoint === 'main' - ? ajax(cachableUrl(getAppResourceUrl('leaflet-layers', 'quiet')), { + ? ajax(cacheableUrl(getAppResourceUrl('leaflet-layers', 'quiet')), { headers: { Accept: 'text/plain' }, errorMode: 'silent', }) .then(async ({ data, status }) => status === Http.NO_CONTENT ? ajax>( - cachableUrl(leafletLayersEndpoint), + cacheableUrl(leafletLayersEndpoint), { headers: { Accept: 'application/json' }, errorMode: 'silent', diff --git a/specifyweb/frontend/js_src/lib/components/Preferences/BasePreferences.tsx b/specifyweb/frontend/js_src/lib/components/Preferences/BasePreferences.tsx index bc308aaabd7..f8621cfce84 100644 --- a/specifyweb/frontend/js_src/lib/components/Preferences/BasePreferences.tsx +++ b/specifyweb/frontend/js_src/lib/components/Preferences/BasePreferences.tsx @@ -13,7 +13,7 @@ import { keysToLowerCase, replaceKey } from '../../utils/utils'; import { SECOND } from '../Atoms/timeUnits'; import { softFail } from '../Errors/Crash'; import { - cachableUrl, + cacheableUrl, contextUnlockedPromise, foreverFetch, } from '../InitialContext'; @@ -429,7 +429,7 @@ export const fetchResourceId = async ( fetchUrl: string, resourceName: string ): Promise => - ajax>(cachableUrl(fetchUrl), { + ajax>(cacheableUrl(fetchUrl), { headers: { Accept: mimeType }, }).then( ({ data }) => @@ -445,7 +445,7 @@ const fetchResourceData = async ( fetchUrl: string, appResourceId: number ): Promise => - ajax(cachableUrl(`${fetchUrl}${appResourceId}/`), { + ajax(cacheableUrl(`${fetchUrl}${appResourceId}/`), { headers: { Accept: mimeType }, }).then(({ data }) => data); diff --git a/specifyweb/frontend/js_src/lib/components/Reports/available.ts b/specifyweb/frontend/js_src/lib/components/Reports/available.ts index cc1b2b4b7ae..06170c40af6 100644 --- a/specifyweb/frontend/js_src/lib/components/Reports/available.ts +++ b/specifyweb/frontend/js_src/lib/components/Reports/available.ts @@ -1,11 +1,11 @@ import { ajax } from '../../utils/ajax'; -import { cachableUrl, contextUnlockedPromise } from '../InitialContext'; +import { cacheableUrl, contextUnlockedPromise } from '../InitialContext'; export const reportsAvailable = contextUnlockedPromise.then( async (entrypoint) => entrypoint === 'main' ? ajax<{ readonly available: boolean }>( - cachableUrl('/context/report_runner_status.json'), + cacheableUrl('/context/report_runner_status.json'), { headers: { Accept: 'application/json' }, } diff --git a/specifyweb/frontend/js_src/lib/components/Toolbar/Language.tsx b/specifyweb/frontend/js_src/lib/components/Toolbar/Language.tsx index cfde42616e2..710fe2c4636 100644 --- a/specifyweb/frontend/js_src/lib/components/Toolbar/Language.tsx +++ b/specifyweb/frontend/js_src/lib/components/Toolbar/Language.tsx @@ -29,7 +29,7 @@ import { Select } from '../Atoms/Form'; import { Link } from '../Atoms/Link'; import { ReadOnlyContext } from '../Core/Contexts'; import { raise } from '../Errors/Crash'; -import { cachableUrl } from '../InitialContext'; +import { cacheableUrl } from '../InitialContext'; import { Dialog, dialogClassNames } from '../Molecules/Dialog'; import type { PreferenceItem, @@ -170,7 +170,7 @@ export function LanguageSelection({ ); } -const url = cachableUrl( +const url = cacheableUrl( formatUrl('/context/language/', { languages: languages.join(','), }) diff --git a/specifyweb/frontend/js_src/lib/components/WebLinks/List.tsx b/specifyweb/frontend/js_src/lib/components/WebLinks/List.tsx index 05d553dcd84..398e5593ed9 100644 --- a/specifyweb/frontend/js_src/lib/components/WebLinks/List.tsx +++ b/specifyweb/frontend/js_src/lib/components/WebLinks/List.tsx @@ -3,25 +3,22 @@ import { useNavigate } from 'react-router-dom'; import { commonText } from '../../localization/common'; import { resourcesText } from '../../localization/resources'; -import type { GetSet, RA } from '../../utils/types'; import { localized } from '../../utils/types'; import { Ul } from '../Atoms'; import { Button } from '../Atoms/Button'; import { Link } from '../Atoms/Link'; import { TableIcon } from '../Molecules/TableIcon'; import { resolveRelative } from '../Router/queryString'; -import type { WebLink } from './spec'; +import type { WebLinkOutlet } from './Editor'; export function WebLinkList({ items: [items, setItems], -}: { - readonly items: GetSet>; -}): JSX.Element { +}: WebLinkOutlet): JSX.Element { const navigate = useNavigate(); return (
-

{resourcesText.availableWebLink()}

+

{resourcesText.availableWebLinks()}

    {items.map((item, index) => (
  • diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index 568dc4972fd..ff888be9aff 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -145,7 +145,7 @@ export const resourcesText = createDictionary({ 'uk-ua': 'Веб-посилання', 'de-ch': 'Weblinks', }, - uiFormatters: { + fieldFormatters: { 'en-us': 'Field Formatters', 'ru-ru': 'Форматировщики полей', 'es-es': 'Formateadores de campo', @@ -153,6 +153,13 @@ export const resourcesText = createDictionary({ 'uk-ua': 'Форматувальники полів', 'de-ch': 'Feldformatierer', }, + fieldFormattersDescription: { + 'en-us': ` + Field formatter controls how data for a specific table field is + shown in query results, exports, and on the form. It determines + autonumbering and individual parts that make up the field. + `, + }, dataObjectFormatters: { 'en-us': 'Record Formatters', 'ru-ru': 'Форматеры записи', @@ -284,7 +291,7 @@ export const resourcesText = createDictionary({ 'ru-ru': 'Доступные агрегаты таблиц', 'uk-ua': 'Доступні агрегації таблиць', }, - availableWebLink: { + availableWebLinks: { 'en-us': 'Available Web Links', 'de-ch': 'Verfügbare Weblinks', 'es-es': 'Enlaces web disponibles', @@ -292,6 +299,14 @@ export const resourcesText = createDictionary({ 'ru-ru': 'Доступные веб-ссылки', 'uk-ua': 'Доступні веб-посилання', }, + availableFieldFormatters: { + 'en-us': 'Available Field Formatters', + 'de-ch': 'Verfügbare Feldformatierer', + 'es-es': 'Formateadores de campo disponibles', + 'fr-fr': 'Formateurs de champs disponibles', + 'ru-ru': 'Доступные форматеры полей', + 'uk-ua': 'Доступні форматувальники полів', + }, selectDefaultFormatter: { 'en-us': 'Please select a default record formatter for this table', 'de-ch': @@ -846,4 +861,13 @@ export const resourcesText = createDictionary({ 'en-us': 'A Consolidated Collection Object Group must have a primary Collection Object child', }, + formatterPreviewUnavailable: { + 'en-us': 'Preview for formatter of this type is not available', + }, + nonConformingInline: { + 'en-us': '(non-conforming)', + }, + parentCogSameAsChild: { + 'en-us': 'A Collection Object Group cannot be a parent to itself', + }, } as const); From 03acac0256a41760a7c1417c326f4465b0a71ef7 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 6 Jul 2024 10:09:10 -0700 Subject: [PATCH 02/33] refactor(FieldFormatters): cleanup placeholder & regex semantics --- .../__tests__/utils.test.ts | 2 +- .../FieldFormatters/FieldFormatter.tsx | 10 +- .../lib/components/FieldFormatters/Fields.tsx | 253 ++++++++++++++++++ .../FieldFormatters/__tests__/index.test.ts | 22 +- .../lib/components/FieldFormatters/index.ts | 164 +++++------- .../lib/components/FieldFormatters/spec.ts | 16 +- .../lib/components/FormMeta/AutoNumbering.tsx | 4 +- .../lib/components/Formatters/Fields.tsx | 2 +- .../Interactions/InteractionDialog.tsx | 2 +- .../lib/components/QueryBuilder/Line.tsx | 2 +- .../lib/components/SchemaConfig/schemaData.ts | 2 +- .../parser/__tests__/definitions.test.ts | 16 +- .../js_src/lib/utils/parser/definitions.ts | 32 ++- 13 files changed, 377 insertions(+), 150 deletions(-) create mode 100644 specifyweb/frontend/js_src/lib/components/FieldFormatters/Fields.tsx diff --git a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts index cd49c122ba5..2eee6078078 100644 --- a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts +++ b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts @@ -76,7 +76,7 @@ const fileNameTestSpec: TestDefinition = { new formatterTypeMapper.regex({ size: 3, autoIncrement: true, - value: localized('^\\d{1,6}(?:[a-zA-Z]{1,2})?$'), + placeholder: localized('^\\d{1,6}(?:[a-zA-Z]{1,2})?$'), byYear: false, }), ], diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx index a522221c28c..20672227c83 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx @@ -12,7 +12,7 @@ import { ResourcePreview } from '../Formatters/Preview'; import { hasTablePermission } from '../Permissions/helpers'; import type { UiFormatter } from '.'; import { resolveFieldFormatter } from '.'; -import { Definitions } from './Definitions'; +import { FieldFormatterFields } from './Fields'; import type { FieldFormatter } from './spec'; export function FieldFormatterElement({ @@ -21,6 +21,7 @@ export function FieldFormatterElement({ readonly item: GetSet; }): JSX.Element { const isReadOnly = React.useContext(ReadOnlyContext); + // FIXME: add field selector return ( <> @@ -35,7 +36,9 @@ export function FieldFormatterElement({ /> {fieldFormatter.external === undefined ? ( - + ) : ( {resourcesText.editorNotAvailable()} )} @@ -76,8 +79,7 @@ function formatterToPreview( if (field === undefined) return ''; const value = String(resource.get(field.name) ?? ''); - if (value.length === 0) - return resolvedFormatter.pattern() ?? resolvedFormatter.valueOrWild(); + if (value.length === 0) return resolvedFormatter.defaultValue; const formatted = resolvedFormatter.format(value); diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Fields.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Fields.tsx new file mode 100644 index 00000000000..f49f9a6609e --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Fields.tsx @@ -0,0 +1,253 @@ +import React from 'react'; + +import { commonText } from '../../localization/common'; +import { queryText } from '../../localization/query'; +import { resourcesText } from '../../localization/resources'; +import { schemaText } from '../../localization/schema'; +import type { GetSet, RA } from '../../utils/types'; +import { localized } from '../../utils/types'; +import { removeItem, replaceItem } from '../../utils/utils'; +import { Button } from '../Atoms/Button'; +import { className } from '../Atoms/className'; +import { Input, Label } from '../Atoms/Form'; +import { icons } from '../Atoms/Icons'; +import { ReadOnlyContext } from '../Core/Contexts'; +import type { SpecifyTable } from '../DataModel/specifyTable'; +import { fetchContext as fetchFieldFormatters } from '../FieldFormatters'; +import { + GenericFormatterPickList, + ResourceMapping, +} from '../Formatters/Components'; +import type { Formatter } from '../Formatters/spec'; +import { FormattersPickList } from '../PickLists/FormattersPickList'; +import { relationshipIsToMany } from '../WbPlanView/mappingHelpers'; +import type { FieldFormatter } from './spec'; + +export function FieldFormatterFields({ + table, + fieldFormatter: [fieldFormatter, setFieldFormatter], +}: { + readonly table: SpecifyTable; + readonly fieldFormatter: GetSet; +}): JSX.Element { + const isReadOnly = React.useContext(ReadOnlyContext); + + const [displayFormatter, setDisplayFormatter] = React.useState(false); + + /* + * FIXME: display type field + * FIXME: display size field (but hardcode to 4 for year) + * FIXME: display placeholder/regex field (unless type year) + * + * FIXME: display byYear checkbox if type year + * FIXME: display autoIncrement checkbox if type number + */ + + /* + * FIXME: make placeholder field required + * FIXME: infer placeholder in the UI for numeric + */ + return ( + <> + {fieldFormatter.fields.length === 0 ? undefined : ( + + + + + + {displayFormatter && } + + + + {fields.map((field, index) => ( + setFields(replaceItem(fields, index, field)), + ]} + key={index} + table={table} + onRemove={(): void => setFields(removeItem(fields, index))} + /> + ))} + +
    {resourcesText.separator()}{schemaText.field()}{schemaText.customFieldFormat()} +
    + )} + {isReadOnly ? null : ( +
    + + setFields([ + ...fields, + { + separator: localized(' '), + aggregator: undefined, + formatter: undefined, + fieldFormatter: undefined, + field: undefined, + }, + ]) + } + > + {resourcesText.addField()} + + + {fields.length > 0 && ( + + + setDisplayFormatter(!displayFormatter) + } + /> + {resourcesText.customizeFieldFormatters()} + + )} +
    + )} + + ); +} + +function Field({ + table, + field: [field, handleChange], + onRemove: handleRemove, + displayFormatter, +}: { + readonly table: SpecifyTable; + readonly field: GetSet< + Formatter['definition']['fields'][number]['fields'][number] + >; + readonly onRemove: () => void; + readonly displayFormatter: boolean; +}): JSX.Element { + const isReadOnly = React.useContext(ReadOnlyContext); + const [openIndex, setOpenIndex] = React.useState( + undefined + ); + return ( + + + + handleChange({ + ...field, + separator, + }) + } + /> + + + + handleChange({ + ...field, + field: fieldMapping, + }), + ]} + openIndex={[openIndex, setOpenIndex]} + table={table} + /> + + {displayFormatter && ( + + + + )} + + {isReadOnly ? null : ( + + {icons.trash} + + )} + + + ); +} + +function FieldFormatterPicker({ + field: [field, handleChange], +}: { + readonly field: GetSet< + Formatter['definition']['fields'][number]['fields'][number] + >; +}): JSX.Element | null { + const lastField = field.field?.at(-1); + if (lastField === undefined) return null; + else if (!lastField.isRelationship) + return ( + + + handleChange({ + ...field, + fieldFormatter, + }) + } + /> + + ); + else if (relationshipIsToMany(lastField)) + return ( + + + handleChange({ + ...field, + aggregator, + }) + } + /> + + ); + else + return ( + + {resourcesText.formatter()} + + handleChange({ + ...field, + formatter, + }) + } + /> + + ); +} diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts index c48bbe783eb..d33a4c3c215 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts @@ -16,27 +16,31 @@ const getFormatter = (): UiFormatter | undefined => const getSecondFormatter = (): UiFormatter | undefined => getUiFormatters().AccessionNumber; -describe('valueOrWild', () => { +describe('defaultValue', () => { test('catalog number', () => - expect(getFormatter()?.valueOrWild()).toBe('#########')); + expect(getFormatter()?.defaultValue).toBe('#########')); test('accession number', () => - expect(getSecondFormatter()?.valueOrWild()).toBe('2022-AA-###')); + expect(getSecondFormatter()?.defaultValue).toBe('2022-AA-###')); }); -describe('parseRegExp', () => { +describe('placeholder', () => { test('catalog number', () => - expect(getFormatter()?.parseRegExp()).toBe('^(#########|\\d{0,9})$')); + expect(getUiFormatters().CatalogNumberNumericRegex?.placeholder).toBe( + '####[-A]' + )); test('accession number', () => - expect(getSecondFormatter()?.parseRegExp()).toBe( + expect(getSecondFormatter()?.placeholder).toBe( '^(YEAR|\\d{4})(-)([a-zA-Z0-9]{2})(-)(###|\\d{3})$' )); }); -describe('pattern', () => { +describe('regex', () => { test('catalog number', () => - expect(getUiFormatters().CatalogNumberNumericRegex?.pattern()).toBe( - '####[-A]' + expect(getFormatter()?.regex.source).toBe('^(#########|\\d{0,9})$')); + test('accession number', () => + expect(getSecondFormatter()?.regex.source).toBe( + '^(YEAR|\\d{4})(-)([a-zA-Z0-9]{2})(-)(###|\\d{3})$' )); }); diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index 3ab2724f788..b94fa65f6ac 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -16,7 +16,7 @@ import { tables } from '../DataModel/tables'; import { error } from '../Errors/assert'; import { load } from '../InitialContext'; import { xmlToSpec } from '../Syncer/xmlUtils'; -import type { FieldFormatter } from './spec'; +import type { FieldFormatter, FieldFormatterField } from './spec'; import { fieldFormattersSpec } from './spec'; let uiFormatters: IR; @@ -78,23 +78,35 @@ export class UiFormatter { public readonly field: LiteralField | undefined ) {} - /** - * Value or wildcard (placeholders) - */ - public valueOrWild(): string { - return this.fields.map((field) => field.getDefaultValue()).join(''); + public get defaultValue(): string { + return this.fields.map((field) => field.defaultValue).join(''); } - public parseRegExp(): string { - return `^${this.fields - .map((field) => `(${field.wildOrValueRegexp()})`) - .join('')}$`; + public get placeholder(): string { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + return this.regexPlaceholder || this.defaultValue; } - public parse(value: string): RA | undefined { + public get regex(): RegExp { // Regex may be coming from the user, thus disable strict mode // eslint-disable-next-line require-unicode-regexp - const match = new RegExp(this.parseRegExp()).exec(value); + return new RegExp( + `^${this.fields + .map((field) => `(${field.placeholderOrValueAsRegex})`) + .join('')}$` + ); + } + + public get regexPlaceholder(): LocalizedString | undefined { + const placeholders = this.fields + .map((field) => field.regexPlaceholder) + .filter(Boolean) + .join('\n'); + return placeholders.length > 0 ? localized(placeholders) : undefined; + } + + public parse(value: string): RA | undefined { + const match = this.regex.exec(value); return match?.slice(1); } @@ -114,168 +126,119 @@ export class UiFormatter { .join('') ); } - - public pattern(): LocalizedString | undefined { - return this.fields.some((field) => field.pattern) - ? localized(this.fields.map((field) => field.pattern ?? '').join('')) - : undefined; - } } abstract class Field { public readonly size: number; - public readonly value: LocalizedString; + public readonly placeholder: LocalizedString; + + public readonly regexPlaceholder: LocalizedString | undefined; private readonly autoIncrement: boolean; private readonly byYear: boolean; - public readonly pattern: LocalizedString | undefined; - - // eslint-disable-next-line functional/prefer-readonly-type - public type: keyof typeof formatterTypeMapper = undefined!; - public constructor({ size, - value, + placeholder, autoIncrement, byYear, - pattern, - }: { - readonly size: number; - readonly value: LocalizedString; - readonly autoIncrement: boolean; - readonly byYear: boolean; - readonly pattern?: LocalizedString; - }) { + regexPlaceholder, + }: Omit) { this.size = size; - this.value = value; + this.placeholder = placeholder; this.autoIncrement = autoIncrement; this.byYear = byYear; - this.pattern = pattern; + this.regexPlaceholder = regexPlaceholder; } - public canAutonumber(): boolean { - return this.autoIncrement || this.byYear; + public get placeholderAsRegex(): LocalizedString { + return localized(escapeRegExp(this.placeholder)); } - public wildRegexp(): LocalizedString { - return localized(escapeRegExp(this.value)); - } + public get placeholderOrValueAsRegex(): LocalizedString { + const regex = this.regex; + if (!this.canAutonumber()) return this.regex; - public wildOrValueRegexp(): LocalizedString { - return this.canAutonumber() - ? localized(`${this.wildRegexp()}|${this.valueRegexp()}`) - : this.valueRegexp(); + const placeholderAsRegex = this.placeholderAsRegex; + return placeholderAsRegex === regex + ? regex + : localized(`${placeholderAsRegex}|${regex}`); } - public getDefaultValue(): LocalizedString { - return this.value === 'YEAR' + public get defaultValue(): LocalizedString { + return this.placeholder === 'YEAR' ? localized(new Date().getFullYear().toString()) - : this.value; + : this.placeholder; } - public canonicalize(value: string): LocalizedString { - return localized(value); + public abstract get regex(): LocalizedString; + + public canAutonumber(): boolean { + return this.autoIncrement || this.byYear; } - public valueRegexp(): LocalizedString { - throw new Error('not implemented'); + public canonicalize(value: string): LocalizedString { + return localized(value); } } class ConstantField extends Field { - public constructor(options: ConstructorParameters[0]) { - super(options); - this.type = 'constant'; - } - - public valueRegexp(): LocalizedString { - return this.wildRegexp(); + public get regex(): LocalizedString { + return this.placeholderAsRegex; } } class AlphaField extends Field { - public constructor(options: ConstructorParameters[0]) { - super(options); - this.type = 'alpha'; - } - - public valueRegexp(): LocalizedString { + public get regex(): LocalizedString { return localized(`[a-zA-Z]{${this.size}}`); } } class NumericField extends Field { public constructor( - options: Omit[0], 'value'> + options: Omit ) { super({ ...options, - value: localized(''.padStart(options.size, '#')), + placeholder: localized(''.padStart(options.size, '#')), }); - this.type = 'numeric'; } - public valueRegexp(): LocalizedString { + public get regex(): LocalizedString { return localized(`\\d{${this.size}}`); } } class YearField extends Field { - public constructor(options: ConstructorParameters[0]) { - super(options); - this.type = 'year'; - } - - public valueRegexp(): LocalizedString { + public get regex(): LocalizedString { return localized(`\\d{${this.size}}`); } } class AlphaNumberField extends Field { - public constructor(options: ConstructorParameters[0]) { - super(options); - this.type = 'alphanumeric'; - } - - public valueRegexp(): LocalizedString { + public get regex(): LocalizedString { return localized(`[a-zA-Z0-9]{${this.size}}`); } } class AnyCharField extends Field { - public constructor(options: ConstructorParameters[0]) { - super(options); - this.type = 'anychar'; - } - - public valueRegexp(): LocalizedString { + public get regex(): LocalizedString { return localized(`.{${this.size}}`); } } class RegexField extends Field { - public constructor(options: ConstructorParameters[0]) { - super(options); - this.type = 'regex'; - } - - public valueRegexp(): LocalizedString { - return this.value; + public get regex(): LocalizedString { + return this.placeholder; } } -class SeparatorField extends ConstantField { - public constructor(options: ConstructorParameters[0]) { - super(options); - this.type = 'separator'; - } -} +class SeparatorField extends ConstantField {} class CatalogNumberNumericField extends NumericField { - public valueRegexp(): LocalizedString { + public get regex(): LocalizedString { return localized(`\\d{0,${this.size}}`); } @@ -295,6 +258,7 @@ export class CatalogNumberNumeric extends UiFormatter { size: 9, autoIncrement: true, byYear: false, + regexPlaceholder: undefined, }), ], tables.CollectionObject, diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts index 552dfbae875..556b3802bd9 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts @@ -134,14 +134,23 @@ const fieldSpec = f.store(() => syncers.enum(Object.keys(formatterTypeMapper)) ), size: pipe( - syncers.xmlAttribute('size', 'skip'), + syncers.xmlAttribute('size', 'required'), syncers.maybe(syncers.toDecimal), syncers.default(1) ), - value: pipe( + /* + * For most fields, this is a human-friendly placeholder like ### or ABC. + * For regex fields, this contains the actual regular expression + */ + placeholder: pipe( syncers.xmlAttribute('value', 'skip', false), - syncers.default(localized(' ')) + syncers.default(localized('')) ), + /* + * Since regular expressions are less readable, this field is specifically + * for providing human-readable description of a regular expression + */ + regexPlaceholder: syncers.xmlAttribute('pattern', 'skip', false), byYear: pipe( syncers.xmlAttribute('byYear', 'skip'), syncers.maybe(syncers.toBoolean), @@ -152,7 +161,6 @@ const fieldSpec = f.store(() => syncers.maybe(syncers.toBoolean), syncers.default(false) ), - pattern: syncers.xmlAttribute('pattern', 'skip', false), }) ); diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/AutoNumbering.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/AutoNumbering.tsx index a905789f30c..b6763704469 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/AutoNumbering.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/AutoNumbering.tsx @@ -65,7 +65,7 @@ function AutoNumberingDialog({ if (stringValue.length === 0 && resource.isNew()) { const field = resource.specifyTable.strictGetLiteralField(fieldName); const formatter = field.getUiFormatter()!; - const wildCard = formatter.valueOrWild(); + const wildCard = formatter.defaultValue; resource.set(fieldName, wildCard as never); } handleChange([...config, fieldName]); @@ -74,7 +74,7 @@ function AutoNumberingDialog({ function handleDisableAutoNumbering(fieldName: string): void { const field = resource.specifyTable.strictGetLiteralField(fieldName); const formatter = field.getUiFormatter()!; - const wildCard = formatter.valueOrWild(); + const wildCard = formatter.defaultValue; if (resource.get(fieldName) === wildCard) resource.set(fieldName, null as never); handleChange(config.filter((name) => name !== fieldName)); diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx b/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx index 33ddfeece26..2f34a6ca3b2 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx +++ b/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx @@ -47,7 +47,7 @@ export function Fields({ return ( <> - {fields.length === 0 ? null : ( + {fields.length === 0 ? undefined : ( field.value).join('') ?? ''; + formatter?.fields.map((field) => field.placeholder).join('') ?? ''; const formatterHasNewLine = formatted.includes('\n'); const formatterHasSpaces = formatted.includes(' '); const formatterHasCommas = formatted.includes(','); diff --git a/specifyweb/frontend/js_src/lib/components/QueryBuilder/Line.tsx b/specifyweb/frontend/js_src/lib/components/QueryBuilder/Line.tsx index a0d4f635b2a..b1eb555d6d2 100644 --- a/specifyweb/frontend/js_src/lib/components/QueryBuilder/Line.tsx +++ b/specifyweb/frontend/js_src/lib/components/QueryBuilder/Line.tsx @@ -160,7 +160,7 @@ export function QueryLine({ required: false, }; // Remove autoNumbering wildCard from default values - if (dataModelField.getUiFormatter()?.valueOrWild() === parser.value) + if (dataModelField.getUiFormatter()?.defaultValue === parser.value) parser = { ...parser, value: undefined }; fieldType = diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts b/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts index 6b0d8b8ee0e..c49584513b1 100644 --- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts +++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts @@ -67,7 +67,7 @@ export const fetchSchemaData = async (): Promise => .map(([name, formatter]) => ({ name, isSystem: formatter.isSystem, - value: formatter.valueOrWild(), + value: formatter.defaultValue, field: formatter.field, })) .filter(({ value }) => value) diff --git a/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts b/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts index 72e5974d38d..bad02cc244d 100644 --- a/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts +++ b/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts @@ -57,16 +57,16 @@ describe('parserFromType', () => { const formatterFields = [ new formatterTypeMapper.constant({ size: 2, - value: localized('AB'), + placeholder: localized('AB'), + regexPlaceholder: undefined, autoIncrement: false, byYear: false, - pattern: localized('\\d{1,2}'), }), new formatterTypeMapper.numeric({ size: 2, autoIncrement: true, byYear: false, - pattern: localized('\\d{1,2}'), + regexPlaceholder: undefined, }), ]; const uiFormatter = new UiFormatter( @@ -76,7 +76,7 @@ const uiFormatter = new UiFormatter( tables.CollectionObject, undefined ); -const title = formsText.requiredFormat({ format: uiFormatter.pattern()! }); +const title = formsText.requiredFormat({ format: uiFormatter.defaultValue }); describe('resolveParser', () => { test('simple string with parser merger', () => { @@ -242,12 +242,10 @@ describe('formatterToParser', () => { ...parser } = formatterToParser({}, uiFormatter); expect(parser).toEqual({ - // Regex may be coming from the user, thus disable strict mode - // eslint-disable-next-line require-unicode-regexp - pattern: new RegExp(uiFormatter.parseRegExp()), + pattern: uiFormatter.regex, title, - placeholder: uiFormatter.pattern()!, - value: uiFormatter.valueOrWild(), + placeholder: uiFormatter.placeholder, + value: uiFormatter.defaultValue, }); expect(formatters).toBeInstanceOf(Array); diff --git a/specifyweb/frontend/js_src/lib/utils/parser/definitions.ts b/specifyweb/frontend/js_src/lib/utils/parser/definitions.ts index d2c4a7cacc6..83e2c01b7a2 100644 --- a/specifyweb/frontend/js_src/lib/utils/parser/definitions.ts +++ b/specifyweb/frontend/js_src/lib/utils/parser/definitions.ts @@ -29,7 +29,8 @@ import { fullDateFormat } from './dateFormat'; /** Makes sure a wrapped function would receive a string value */ export const stringGuard = - (formatter: (value: string) => unknown) => (value: unknown) => + (formatter: (value: string) => unknown) => + (value: unknown): unknown => typeof value === 'string' ? formatter(value) : error('Value is not a string'); @@ -102,7 +103,8 @@ type ExtendedJavaType = JavaType | 'day' | 'month' | 'year'; * This could be resolved by enabling time mocking globally, but that's not * great as it can alter behavior of the code */ -const getDate = () => (process.env.NODE_ENV === 'test' ? testTime : new Date()); +const getDate = (): Date => + process.env.NODE_ENV === 'test' ? testTime : new Date(); export const parsers = f.store( (): RR => ({ @@ -358,9 +360,8 @@ function resolveDate( if (values.length === 1) return values[0]; const leftDate = new Date(values[0]); const rightDate = new Date(values[1]); - return leftDate.getTime() < rightDate.getTime() === takeMin - ? values[0] - : values[1]; + const isLesser = leftDate < rightDate; + return isLesser === takeMin ? values[0] : values[1]; } const callback = takeMin ? f.min : f.max; return callback(...(values as RA)); @@ -370,9 +371,8 @@ export function formatterToParser( field: Partial, formatter: UiFormatter ): Parser { - const regExpString = formatter.parseRegExp(); const title = formsText.requiredFormat({ - format: formatter.pattern() ?? formatter.valueOrWild(), + format: formatter.placeholder, }); const autoNumberingConfig = userPreferences.get( @@ -385,29 +385,27 @@ export function formatterToParser( typeof tableName === 'string' ? (autoNumberingConfig[tableName] as RA) : undefined; - const canAutoNumber = - formatter.canAutonumber() && - (autoNumberingFields === undefined || - autoNumberingFields.includes(field.name ?? '')); + const autoNumberingEnabled = + autoNumberingFields === undefined || + autoNumberingFields.includes(field.name ?? ''); + const canAutoNumber = formatter.canAutonumber() && autoNumberingEnabled; return { - // Regex may be coming from the user, thus disable strict mode - // eslint-disable-next-line require-unicode-regexp - pattern: regExpString === null ? undefined : new RegExp(regExpString), + pattern: formatter.regex, title, formatters: [stringGuard(formatter.parse.bind(formatter))], validators: [ (value): string | undefined => value === undefined || value === null ? title : undefined, ], - placeholder: formatter.pattern() ?? undefined, + placeholder: formatter.placeholder, type: field.type === undefined ? undefined : parserFromType(field.type as ExtendedJavaType).type, parser: (value: unknown): string => formatter.canonicalize(value as RA), - value: canAutoNumber ? formatter.valueOrWild() : undefined, + value: canAutoNumber ? formatter.defaultValue : undefined, }; } @@ -465,7 +463,7 @@ export function pluralizeParser(rawParser: Parser): Parser { // FEATURE: allow customizing this const separator = ','; -/** Modify a regex pattern to allow a comma separate list of values */ +/** Modify a regex pattern to allow a comma separated list of values */ export function pluralizeRegex(regex: RegExp): RegExp { const pattern = browserifyRegex(regex); // Pattern with whitespace From 4e0cec6566908f9a504a3ec22970d732f61b474f Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 6 Jul 2024 10:27:37 -0700 Subject: [PATCH 03/33] refactor(FieldFormatter): rename "Field" to "Part" - Field is ambiguous name - Field suggests that you can have fields inside of a formatter - you can't - you can only have text parts inside a field formatter --- .../__tests__/utils.test.ts | 10 +- .../components/AttachmentsBulkImport/utils.ts | 25 ++--- .../FieldFormatters/FieldFormatter.tsx | 8 +- .../lib/components/FieldFormatters/List.tsx | 2 +- .../FieldFormatters/{Fields.tsx => Parts.tsx} | 31 +++--- .../lib/components/FieldFormatters/index.ts | 99 +++++++++++-------- .../lib/components/FieldFormatters/spec.ts | 24 ++--- .../Interactions/InteractionDialog.tsx | 3 +- .../parser/__tests__/definitions.test.ts | 8 +- 9 files changed, 108 insertions(+), 102 deletions(-) rename specifyweb/frontend/js_src/lib/components/FieldFormatters/{Fields.tsx => Parts.tsx} (91%) diff --git a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts index 2eee6078078..c3ab8b21a56 100644 --- a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts +++ b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts @@ -1,3 +1,4 @@ +import { LocalizedString } from 'typesafe-i18n'; import { requireContext } from '../../../tests/helpers'; import { formatterToParser } from '../../../utils/parser/definitions'; import type { IR, RA } from '../../../utils/types'; @@ -5,7 +6,7 @@ import { localized } from '../../../utils/types'; import { tables } from '../../DataModel/tables'; import { CatalogNumberNumeric, - formatterTypeMapper, + fieldFormatterTypeMapper, UiFormatter, } from '../../FieldFormatters'; import { syncFieldFormat } from '../../Formatters/fieldFormat'; @@ -49,7 +50,7 @@ const fileNameTestSpec: TestDefinition = { false, localized('testNumeric'), [ - new formatterTypeMapper.numeric({ + new fieldFormatterTypeMapper.numeric({ size: 3, autoIncrement: true, byYear: false, @@ -73,7 +74,7 @@ const fileNameTestSpec: TestDefinition = { false, localized('testRegex'), [ - new formatterTypeMapper.regex({ + new fieldFormatterTypeMapper.regex({ size: 3, autoIncrement: true, placeholder: localized('^\\d{1,6}(?:[a-zA-Z]{1,2})?$'), @@ -101,7 +102,8 @@ describe('file names resolution test', () => { jest.spyOn(console, 'error').mockImplementation(); const field = tables.CollectionObject.getLiteralField('text1')!; const getResultFormatter = - (formatter: UiFormatter) => (value: number | string | undefined) => + (formatter: UiFormatter) => + (value: number | string | undefined): LocalizedString | undefined => value === undefined || value === null ? undefined : syncFieldFormat( diff --git a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/utils.ts b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/utils.ts index 8693c397fc3..fe44fb2fcb5 100644 --- a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/utils.ts +++ b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/utils.ts @@ -28,10 +28,8 @@ import { serializeResource, } from '../DataModel/serializers'; import { strictGetTable, tables } from '../DataModel/tables'; -import type { SpQuery, Tables } from '../DataModel/types'; -import type { CollectionObject } from '../DataModel/types'; +import type { CollectionObject, SpQuery, Tables } from '../DataModel/types'; import type { UiFormatter } from '../FieldFormatters'; -import { formatterTypeMapper } from '../FieldFormatters'; import { queryFieldFilters } from '../QueryBuilder/FieldFilter'; import { makeQueryField } from '../QueryBuilder/fromTree'; import type { QueryFieldWithPath } from '../Statistics/types'; @@ -105,7 +103,7 @@ function generateInQueryResource( }; const { path, ...field } = rawField; return serializeResource( - makeQueryField(baseTable, rawField.path, { ...field, position: index }) + makeQueryField(baseTable, path, { ...field, position: index }) ); }); @@ -232,24 +230,15 @@ export function resolveFileNames( // BUG: Won't catch if formatters begin or end with a space const splitName = stripFileExtension(fileName).trim(); let nameToParse = splitName; - if ( - formatter !== undefined && - formatter.fields.every( - (field) => !(field instanceof formatterTypeMapper.regex) - ) - ) { - const formattedLength = formatter.fields.reduce( - (length, field) => length + field.size, - 0 - ); - nameToParse = fileName.trim().slice(0, formattedLength); + if (formatter?.parts.every((field) => field.type !== 'regex') === true) { + nameToParse = fileName.trim().slice(0, formatter.size); } let formatted = nameToParse === '' ? undefined : getFormatted(nameToParse); - const numericFields = formatter?.fields.filter( - (field) => field instanceof formatterTypeMapper.numeric + const numericFields = formatter?.parts.filter( + (field) => field.type === 'numeric' ); if ( - formatter?.fields?.length === 1 && + formatter?.parts?.length === 1 && numericFields?.length === 1 && formatted === undefined && splitName !== '' diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx index 20672227c83..6ced315dd53 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx @@ -12,7 +12,7 @@ import { ResourcePreview } from '../Formatters/Preview'; import { hasTablePermission } from '../Permissions/helpers'; import type { UiFormatter } from '.'; import { resolveFieldFormatter } from '.'; -import { FieldFormatterFields } from './Fields'; +import { FieldFormatterParts } from './Parts'; import type { FieldFormatter } from './spec'; export function FieldFormatterElement({ @@ -35,9 +35,11 @@ export function FieldFormatterElement({ } /> - {fieldFormatter.external === undefined ? ( - ) : ( {resourcesText.editorNotAvailable()} diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx index e0dd5343ed2..dcd86d260a7 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx @@ -63,7 +63,7 @@ export function FieldFormattersList(): JSX.Element { legacyPartialDate: undefined, autoNumber: false, external: undefined, - fields: [], + parts: [], raw: { javaClass: undefined, legacyAutoNumber: undefined, diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Fields.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx similarity index 91% rename from specifyweb/frontend/js_src/lib/components/FieldFormatters/Fields.tsx rename to specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx index f49f9a6609e..0d7f98aae05 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Fields.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx @@ -13,17 +13,16 @@ import { Input, Label } from '../Atoms/Form'; import { icons } from '../Atoms/Icons'; import { ReadOnlyContext } from '../Core/Contexts'; import type { SpecifyTable } from '../DataModel/specifyTable'; -import { fetchContext as fetchFieldFormatters } from '../FieldFormatters'; import { GenericFormatterPickList, ResourceMapping, } from '../Formatters/Components'; -import type { Formatter } from '../Formatters/spec'; import { FormattersPickList } from '../PickLists/FormattersPickList'; import { relationshipIsToMany } from '../WbPlanView/mappingHelpers'; -import type { FieldFormatter } from './spec'; +import { fetchContext as fetchFieldFormatters } from '.'; +import type { FieldFormatter, FieldFormatterPart } from './spec'; -export function FieldFormatterFields({ +export function FieldFormatterParts({ table, fieldFormatter: [fieldFormatter, setFieldFormatter], }: { @@ -75,7 +74,7 @@ export function FieldFormatterFields({ {fields.map((field, index) => ( - ; + readonly field: GetSet; readonly onRemove: () => void; readonly displayFormatter: boolean; }): JSX.Element { @@ -148,10 +145,10 @@ function Field({ handleChange({ - ...field, + ...part, separator, }) } @@ -160,10 +157,10 @@ function Field({ {displayFormatter && ( )}
    handleChange({ - ...field, + ...part, field: fieldMapping, }), ]} @@ -173,7 +170,7 @@ function Field({ - + @@ -195,9 +192,7 @@ function Field({ function FieldFormatterPicker({ field: [field, handleChange], }: { - readonly field: GetSet< - Formatter['definition']['fields'][number]['fields'][number] - >; + readonly field: GetSet; }): JSX.Element | null { const lastField = field.field?.at(-1); if (lastField === undefined) return null; diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index b94fa65f6ac..5257548783c 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -16,7 +16,7 @@ import { tables } from '../DataModel/tables'; import { error } from '../Errors/assert'; import { load } from '../InitialContext'; import { xmlToSpec } from '../Syncer/xmlUtils'; -import type { FieldFormatter, FieldFormatterField } from './spec'; +import type { FieldFormatter, FieldFormatterPart } from './spec'; import { fieldFormattersSpec } from './spec'; let uiFormatters: IR; @@ -50,17 +50,17 @@ export function resolveFieldFormatter( ? new CatalogNumberNumeric() : undefined; } else { - const fields = filterArray( - formatter.fields.map((field) => - typeof field.type === 'string' - ? new formatterTypeMapper[field.type](field) + const parts = filterArray( + formatter.parts.map((part) => + typeof part.type === 'string' + ? new fieldFormatterTypeMapper[part.type](part) : undefined ) ); return new UiFormatter( formatter.isSystem, formatter.title ?? formatter.name, - fields, + parts, formatter.table, formatter.field ); @@ -72,14 +72,14 @@ export class UiFormatter { public constructor( public readonly isSystem: boolean, public readonly title: LocalizedString, - public readonly fields: RA, + public readonly parts: RA, public readonly table: SpecifyTable | undefined, // The field which this formatter is formatting public readonly field: LiteralField | undefined ) {} public get defaultValue(): string { - return this.fields.map((field) => field.defaultValue).join(''); + return this.parts.map((part) => part.defaultValue).join(''); } public get placeholder(): string { @@ -91,15 +91,19 @@ export class UiFormatter { // Regex may be coming from the user, thus disable strict mode // eslint-disable-next-line require-unicode-regexp return new RegExp( - `^${this.fields - .map((field) => `(${field.placeholderOrValueAsRegex})`) + `^${this.parts + .map((part) => `(${part.placeholderOrValueAsRegex})`) .join('')}$` ); } - public get regexPlaceholder(): LocalizedString | undefined { - const placeholders = this.fields - .map((field) => field.regexPlaceholder) + public get size(): number { + return this.parts.reduce((size, field) => size + field.size, 0); + } + + private get regexPlaceholder(): LocalizedString | undefined { + const placeholders = this.parts + .map((part) => part.regexPlaceholder) .filter(Boolean) .join('\n'); return placeholders.length > 0 ? localized(placeholders) : undefined; @@ -111,7 +115,7 @@ export class UiFormatter { } public canAutonumber(): boolean { - return this.fields.some((field) => field.canAutonumber()); + return this.parts.some((part) => part.canAutonumber()); } public format(value: string): LocalizedString | undefined { @@ -121,14 +125,15 @@ export class UiFormatter { public canonicalize(values: RA): LocalizedString { return localized( - this.fields - .map((field, index) => field.canonicalize(values[index])) - .join('') + this.parts.map((part, index) => part.canonicalize(values[index])).join('') ); } } -abstract class Field { +type PartOptions = Omit & + Partial>; + +abstract class Part { public readonly size: number; public readonly placeholder: LocalizedString; @@ -139,13 +144,15 @@ abstract class Field { private readonly byYear: boolean; + public abstract readonly type: FieldFormatterPart['type']; + public constructor({ size, placeholder, autoIncrement, byYear, regexPlaceholder, - }: Omit) { + }: PartOptions) { this.size = size; this.placeholder = placeholder; this.autoIncrement = autoIncrement; @@ -184,22 +191,26 @@ abstract class Field { } } -class ConstantField extends Field { +class ConstantPart extends Part { + public readonly type = 'constant'; + public get regex(): LocalizedString { return this.placeholderAsRegex; } } -class AlphaField extends Field { +class AlphaPart extends Part { + public readonly type = 'alpha'; + public get regex(): LocalizedString { return localized(`[a-zA-Z]{${this.size}}`); } } -class NumericField extends Field { - public constructor( - options: Omit - ) { +class NumericPart extends Part { + public readonly type = 'numeric'; + + public constructor(options: Omit) { super({ ...options, placeholder: localized(''.padStart(options.size, '#')), @@ -211,33 +222,41 @@ class NumericField extends Field { } } -class YearField extends Field { +class YearPart extends Part { + public readonly type = 'year'; + public get regex(): LocalizedString { return localized(`\\d{${this.size}}`); } } -class AlphaNumberField extends Field { +class AlphaNumberPart extends Part { + public readonly type = 'alpha'; + public get regex(): LocalizedString { return localized(`[a-zA-Z0-9]{${this.size}}`); } } -class AnyCharField extends Field { +class AnyCharPart extends Part { + public readonly type = 'anychar'; + public get regex(): LocalizedString { return localized(`.{${this.size}}`); } } -class RegexField extends Field { +class RegexPart extends Part { + public readonly type = 'regex'; + public get regex(): LocalizedString { return this.placeholder; } } -class SeparatorField extends ConstantField {} +class SeparatorPart extends ConstantPart {} -class CatalogNumberNumericField extends NumericField { +class CatalogNumberNumericField extends NumericPart { public get regex(): LocalizedString { return localized(`\\d{0,${this.size}}`); } @@ -268,13 +287,13 @@ export class CatalogNumberNumeric extends UiFormatter { } /* eslint-enable functional/no-class */ -export const formatterTypeMapper = { - constant: ConstantField, - year: YearField, - alpha: AlphaField, - numeric: NumericField, - alphanumeric: AlphaNumberField, - anychar: AnyCharField, - regex: RegexField, - separator: SeparatorField, +export const fieldFormatterTypeMapper = { + constant: ConstantPart, + year: YearPart, + alpha: AlphaPart, + numeric: NumericPart, + alphanumeric: AlphaNumberPart, + anychar: AnyCharPart, + regex: RegexPart, + separator: SeparatorPart, } as const; diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts index 556b3802bd9..b13519089d7 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts @@ -8,7 +8,7 @@ import type { SpecToJson } from '../Syncer'; import { pipe, syncer } from '../Syncer'; import { syncers } from '../Syncer/syncers'; import { createXmlSpec } from '../Syncer/xmlUtils'; -import { formatterTypeMapper } from '.'; +import { fieldFormatterTypeMapper } from '.'; export const fieldFormattersSpec = f.store(() => createXmlSpec({ @@ -42,7 +42,7 @@ export const fieldFormattersSpec = f.store(() => : undefined), rawAutoNumber: autoNumber ? legacyAutoNumber ?? - inferLegacyAutoNumber(table, formatter.fields) + inferLegacyAutoNumber(table, formatter.parts) : undefined, }) ), @@ -70,7 +70,9 @@ export const fieldFormattersSpec = f.store(() => */ function inferLegacyAutoNumber( table: SpecifyTable | undefined, - fields: RA<{ readonly type: keyof typeof formatterTypeMapper | undefined }> + fields: RA<{ + readonly type: keyof typeof fieldFormatterTypeMapper | undefined; + }> ): string { if (table?.name === 'Accession') return 'edu.ku.brc.specify.dbsupport.AccessionAutoNumberAlphaNum'; @@ -86,7 +88,7 @@ export type FieldFormatter = SpecToJson< ReturnType >['fieldFormatters'][number]; -export type FieldFormatterField = FieldFormatter['fields'][number]; +export type FieldFormatterPart = FieldFormatter['parts'][number]; const formatterSpec = f.store(() => createXmlSpec({ @@ -119,19 +121,19 @@ const formatterSpec = f.store(() => syncers.xmlChild('external', 'optional'), syncers.maybe(syncers.xmlContent) ), - fields: pipe( + parts: pipe( syncers.xmlChildren('field'), - syncers.map(syncers.object(fieldSpec())) + syncers.map(syncers.object(partSpec())) ), }) ); -const fieldSpec = f.store(() => +const partSpec = f.store(() => createXmlSpec({ type: pipe( syncers.xmlAttribute('type', 'required'), syncers.fallback(localized('alphanumeric')), - syncers.enum(Object.keys(formatterTypeMapper)) + syncers.enum(Object.keys(fieldFormatterTypeMapper)) ), size: pipe( syncers.xmlAttribute('size', 'required'), @@ -139,15 +141,15 @@ const fieldSpec = f.store(() => syncers.default(1) ), /* - * For most fields, this is a human-friendly placeholder like ### or ABC. - * For regex fields, this contains the actual regular expression + * For most parts, this is a human-friendly placeholder like ### or ABC. + * For regex parts, this contains the actual regular expression */ placeholder: pipe( syncers.xmlAttribute('value', 'skip', false), syncers.default(localized('')) ), /* - * Since regular expressions are less readable, this field is specifically + * Since regular expressions are less readable, this part is specifically * for providing human-readable description of a regular expression */ regexPlaceholder: syncers.xmlAttribute('pattern', 'skip', false), diff --git a/specifyweb/frontend/js_src/lib/components/Interactions/InteractionDialog.tsx b/specifyweb/frontend/js_src/lib/components/Interactions/InteractionDialog.tsx index bbe4d213ac8..f14e9667d9e 100644 --- a/specifyweb/frontend/js_src/lib/components/Interactions/InteractionDialog.tsx +++ b/specifyweb/frontend/js_src/lib/components/Interactions/InteractionDialog.tsx @@ -447,8 +447,7 @@ function useParser(searchField: LiteralField): { const parser = pluralizeParser(resolveParser(searchField)); // Determine which delimiters are allowed const formatter = searchField.getUiFormatter(); - const formatted = - formatter?.fields.map((field) => field.placeholder).join('') ?? ''; + const formatted = formatter?.defaultValue ?? ''; const formatterHasNewLine = formatted.includes('\n'); const formatterHasSpaces = formatted.includes(' '); const formatterHasCommas = formatted.includes(','); diff --git a/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts b/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts index bad02cc244d..310a5d1fa8f 100644 --- a/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts +++ b/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts @@ -5,7 +5,7 @@ import type { } from '../../../components/DataModel/specifyField'; import { tables } from '../../../components/DataModel/tables'; import { - formatterTypeMapper, + fieldFormatterTypeMapper, UiFormatter, } from '../../../components/FieldFormatters'; import { userPreferences } from '../../../components/Preferences/userPreferences'; @@ -55,18 +55,16 @@ describe('parserFromType', () => { }); const formatterFields = [ - new formatterTypeMapper.constant({ + new fieldFormatterTypeMapper.constant({ size: 2, placeholder: localized('AB'), - regexPlaceholder: undefined, autoIncrement: false, byYear: false, }), - new formatterTypeMapper.numeric({ + new fieldFormatterTypeMapper.numeric({ size: 2, autoIncrement: true, byYear: false, - regexPlaceholder: undefined, }), ]; const uiFormatter = new UiFormatter( From ac18dc47171ec652cf22338c7d4ac4178d831d2d Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 6 Jul 2024 16:42:25 -0700 Subject: [PATCH 04/33] feat(FieldFormatters): complete visual editor --- .../FieldFormatters/FieldFormatter.tsx | 124 ++++++- .../lib/components/FieldFormatters/List.tsx | 4 +- .../lib/components/FieldFormatters/Parts.tsx | 304 +++++++++--------- .../lib/components/FieldFormatters/Routes.tsx | 6 +- .../lib/components/FieldFormatters/index.ts | 23 +- .../lib/components/FormFields/Field.tsx | 40 ++- .../lib/components/Formatters/Components.tsx | 24 +- .../js_src/lib/components/Formatters/List.tsx | 11 + .../lib/components/Formatters/Preview.tsx | 15 +- .../components/QueryBuilder/RelativeDate.tsx | 8 +- .../frontend/js_src/lib/localization/forms.ts | 16 + .../frontend/js_src/lib/localization/query.ts | 34 +- .../js_src/lib/localization/resources.ts | 34 ++ .../js_src/lib/utils/parser/dateFormat.ts | 2 +- .../js_src/lib/utils/parser/dayJsFixes.ts | 4 +- 15 files changed, 426 insertions(+), 223 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx index 6ced315dd53..3e41fc15856 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx @@ -1,31 +1,38 @@ import React from 'react'; +import { useParser } from '../../hooks/resource'; import { formsText } from '../../localization/forms'; import { resourcesText } from '../../localization/resources'; +import { schemaText } from '../../localization/schema'; +import { getValidationAttributes } from '../../utils/parser/definitions'; import type { GetSet, RA } from '../../utils/types'; import { ErrorMessage } from '../Atoms'; import { Input, Label } from '../Atoms/Form'; import { ReadOnlyContext } from '../Core/Contexts'; import type { AnySchema } from '../DataModel/helperTypes'; import type { SpecifyResource } from '../DataModel/legacyTypes'; +import type { LiteralField } from '../DataModel/specifyField'; +import { softError } from '../Errors/assert'; +import { ResourceMapping } from '../Formatters/Components'; import { ResourcePreview } from '../Formatters/Preview'; +import { useRightAlignClassName } from '../FormFields/Field'; import { hasTablePermission } from '../Permissions/helpers'; +import type { MappingLineData } from '../WbPlanView/navigator'; import type { UiFormatter } from '.'; import { resolveFieldFormatter } from '.'; import { FieldFormatterParts } from './Parts'; import type { FieldFormatter } from './spec'; export function FieldFormatterElement({ - item: [fieldFormatter, setFieldFormatter], + item, }: { readonly item: GetSet; }): JSX.Element { + const [fieldFormatter, setFieldFormatter] = item; const isReadOnly = React.useContext(ReadOnlyContext); - // FIXME: add field selector return ( <> - {formsText.autoNumbering()} + {formsText.autoNumbering()} + {fieldFormatter.external === undefined && typeof fieldFormatter.table === 'object' ? ( - + ) : ( {resourcesText.editorNotAvailable()} )} @@ -49,23 +55,81 @@ export function FieldFormatterElement({ ); } +function FieldPicker({ + fieldFormatter: [fieldFormatter, setFieldFormatter], +}: { + readonly fieldFormatter: GetSet; +}): JSX.Element | null { + const openIndex = React.useState(undefined); + const mapping = React.useMemo( + () => (fieldFormatter.field === undefined ? [] : [fieldFormatter.field]), + [fieldFormatter.field] + ); + return fieldFormatter.table === undefined ? null : ( + + {schemaText.field()} + { + if (mapping !== undefined && mapping?.length > 1) + softError('Expected mapping length to be no more than 1'); + const field = mapping?.[0]; + if (field?.isRelationship === true) { + softError( + 'Did not expect relationship field in field formatter mapping' + ); + } else { + setFieldFormatter({ ...fieldFormatter, field }); + } + }, + ]} + openIndex={openIndex} + table={fieldFormatter.table} + /> + + ); +} + +const excludeNonLiteral = (mappingData: MappingLineData): MappingLineData => ({ + ...mappingData, + fieldsData: Object.fromEntries( + Object.entries(mappingData.fieldsData).filter( + ([_name, fieldData]) => fieldData.tableName === undefined + ) + ), +}); + function FieldFormatterPreview({ fieldFormatter, }: { readonly fieldFormatter: FieldFormatter; }): JSX.Element | null { + const resolvedFormatter = React.useMemo( + () => resolveFieldFormatter(fieldFormatter), + [fieldFormatter] + ); const doFormatting = React.useCallback( - (resources: RA>) => { - const resolvedFormatter = resolveFieldFormatter(fieldFormatter); - return resources.map((resource) => + (resources: RA>) => + resources.map((resource) => formatterToPreview(resource, fieldFormatter, resolvedFormatter) - ); - }, - [fieldFormatter] + ), + [fieldFormatter, resolvedFormatter] ); return typeof fieldFormatter.table === 'object' && hasTablePermission(fieldFormatter.table.name, 'read') ? ( - + <> + + + ) : null; } @@ -89,3 +153,35 @@ function formatterToPreview( ? `${value} ${resourcesText.nonConformingInline()}` : formatted; } + +function FieldFormatterPreviewField({ + field, + resolvedFormatter, +}: { + readonly field: LiteralField | undefined; + readonly resolvedFormatter: UiFormatter | undefined; +}): JSX.Element | null { + const [value, setValue] = React.useState(''); + const isConforming = React.useMemo( + () => resolvedFormatter?.parse(value) !== undefined, + [value, resolvedFormatter] + ); + const parser = useParser(field); + + const validationAttributes = getValidationAttributes(parser); + const rightAlignClassName = useRightAlignClassName(parser.type, false); + return resolvedFormatter === undefined ? null : ( + + {`${resourcesText.exampleField()} ${ + isConforming ? '' : resourcesText.nonConformingInline() + }`} + + + ); +} diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx index dcd86d260a7..a050d57b641 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useOutletContext, useParams } from 'react-router-dom'; +import { useNavigate, useOutletContext, useParams } from 'react-router-dom'; import { resourcesText } from '../../localization/resources'; import type { GetSet, RA } from '../../utils/types'; @@ -38,6 +38,7 @@ export function FieldFormatterEditorWrapper(): JSX.Element { export function FieldFormattersList(): JSX.Element { const { tableName } = useParams(); const { items } = useOutletContext(); + const navigate = useNavigate(); return ( navigate('../')} /> ); } diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx index 0d7f98aae05..9e9de7e3458 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx @@ -1,88 +1,68 @@ import React from 'react'; +import { useValidation } from '../../hooks/useValidation'; import { commonText } from '../../localization/common'; +import { formsText } from '../../localization/forms'; import { queryText } from '../../localization/query'; import { resourcesText } from '../../localization/resources'; -import { schemaText } from '../../localization/schema'; -import type { GetSet, RA } from '../../utils/types'; +import type { GetSet } from '../../utils/types'; import { localized } from '../../utils/types'; import { removeItem, replaceItem } from '../../utils/utils'; import { Button } from '../Atoms/Button'; import { className } from '../Atoms/className'; -import { Input, Label } from '../Atoms/Form'; +import { Input, Label, Select } from '../Atoms/Form'; import { icons } from '../Atoms/Icons'; import { ReadOnlyContext } from '../Core/Contexts'; -import type { SpecifyTable } from '../DataModel/specifyTable'; -import { - GenericFormatterPickList, - ResourceMapping, -} from '../Formatters/Components'; -import { FormattersPickList } from '../PickLists/FormattersPickList'; -import { relationshipIsToMany } from '../WbPlanView/mappingHelpers'; -import { fetchContext as fetchFieldFormatters } from '.'; +import { fieldFormatterLocalization, fieldFormatterTypeMapper } from '.'; import type { FieldFormatter, FieldFormatterPart } from './spec'; export function FieldFormatterParts({ - table, fieldFormatter: [fieldFormatter, setFieldFormatter], }: { - readonly table: SpecifyTable; readonly fieldFormatter: GetSet; }): JSX.Element { const isReadOnly = React.useContext(ReadOnlyContext); - const [displayFormatter, setDisplayFormatter] = React.useState(false); + const { parts } = fieldFormatter; - /* - * FIXME: display type field - * FIXME: display size field (but hardcode to 4 for year) - * FIXME: display placeholder/regex field (unless type year) - * - * FIXME: display byYear checkbox if type year - * FIXME: display autoIncrement checkbox if type number - */ + const setParts = (newParts: typeof parts): void => + setFieldFormatter({ + ...fieldFormatter, + parts: newParts, + }); - /* - * FIXME: make placeholder field required - * FIXME: infer placeholder in the UI for numeric - */ return ( <> - {fieldFormatter.fields.length === 0 ? undefined : ( + {parts.length === 0 ? undefined : ( - - - {displayFormatter && } + + + + - {fields.map((field, index) => ( + {parts.map((part, index) => ( setFields(replaceItem(fields, index, field)), - ]} key={index} - table={table} - onRemove={(): void => setFields(removeItem(fields, index))} + part={[ + part, + (part): void => setParts(replaceItem(parts, index, part)), + ]} + onRemove={(): void => setParts(removeItem(parts, index))} /> ))} @@ -92,32 +72,21 @@ export function FieldFormatterParts({
    - setFields([ - ...fields, + setParts([ + ...parts, { - separator: localized(' '), - aggregator: undefined, - formatter: undefined, - fieldFormatter: undefined, - field: undefined, + type: 'constant', + size: 1, + placeholder: localized(''), + regexPlaceholder: undefined, + byYear: false, + autoIncrement: false, }, ]) } > {resourcesText.addField()} - - {fields.length > 0 && ( - - - setDisplayFormatter(!displayFormatter) - } - /> - {resourcesText.customizeFieldFormatters()} - - )}
    )} @@ -125,54 +94,145 @@ export function FieldFormatterParts({ } function Part({ - table, - field: [part, handleChange], + part: [part, handleChange], onRemove: handleRemove, - displayFormatter, }: { - readonly table: SpecifyTable; - readonly field: GetSet; + readonly part: GetSet; readonly onRemove: () => void; - readonly displayFormatter: boolean; }): JSX.Element { const isReadOnly = React.useContext(ReadOnlyContext); - const [openIndex, setOpenIndex] = React.useState( - undefined + + React.useEffect(() => { + if (part.type === 'year') + handleChange({ + ...part, + size: 4, + placeholder: fieldFormatterTypeMapper.year.placeholder, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [part.type]); + + React.useEffect(() => { + if (part.type === 'numeric') + handleChange({ + ...part, + placeholder: fieldFormatterTypeMapper.numeric.buildPlaceholder( + part.size + ), + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [part.size, part.type]); + + const enforcePlaceholderSize = + part.type === 'constant' || + part.type === 'separator' || + part.type === 'year'; + + const placeholderSize = enforcePlaceholderSize ? part.size : undefined; + + /** + * While native browser validation does length enforcement, it only does so + * when the field value is changed by the user - if field already had + * incorrect value, or if validation requirement changed (because size field + * was changed), browser won't automatically re-validate so we have to + */ + const requestedSize = placeholderSize ?? part.placeholder.length; + const actualSize = part.placeholder.length; + const { validationRef } = useValidation( + actualSize > requestedSize + ? queryText.tooLongErrorMessage({ maxLength: requestedSize }) + : actualSize < requestedSize + ? queryText.tooShortErrorMessage({ minLength: requestedSize }) + : undefined ); + return ( + + - {displayFormatter && ( - - )} - + @@ -117,7 +117,7 @@ function Part({ handleChange({ ...part, placeholder: fieldFormatterTypeMapper.numeric.buildPlaceholder( - part.size + part.size ?? 1 ), }); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -128,23 +128,14 @@ function Part({ part.type === 'separator' || part.type === 'year'; - const placeholderSize = enforcePlaceholderSize ? part.size : undefined; - - /** - * While native browser validation does length enforcement, it only does so - * when the field value is changed by the user - if field already had - * incorrect value, or if validation requirement changed (because size field - * was changed), browser won't automatically re-validate so we have to - */ - const requestedSize = placeholderSize ?? part.placeholder.length; - const actualSize = part.placeholder.length; - const { validationRef } = useValidation( - actualSize > requestedSize - ? queryText.tooLongErrorMessage({ maxLength: requestedSize }) - : actualSize < requestedSize - ? queryText.tooShortErrorMessage({ minLength: requestedSize }) - : undefined - ); + React.useEffect(() => { + if (enforcePlaceholderSize) + handleChange({ + ...part, + size: part.placeholder.length, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [part.placeholder, enforcePlaceholderSize]); return ( @@ -152,7 +143,7 @@ function Part({ ); } + +function RegexField({ + value, + onChange: handleChange, +}: { + readonly value: LocalizedString; + readonly onChange: (newValue: LocalizedString) => void; +}): JSX.Element { + const isReadOnly = React.useContext(ReadOnlyContext); + const [pendingValue, setPendingValue] = useTriggerState(value); + return ( + { + try { + // Regex may be coming from the user, thus disable strict mode + // eslint-disable-next-line require-unicode-regexp + void new RegExp(target.value); + handleChange(target.value as LocalizedString); + } catch (error: unknown) { + target.setCustomValidity(String(error)); + target.reportValidity(); + } + }} + onValueChange={setPendingValue} + /> + ); +} diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index 5b5c09bfd62..705550188c2 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -149,7 +149,7 @@ abstract class Part { public abstract readonly type: FieldFormatterPart['type']; public constructor({ - size, + size = 1, placeholder, autoIncrement, byYear, @@ -215,7 +215,7 @@ class NumericPart extends Part { public constructor(options: Omit) { super({ ...options, - placeholder: NumericPart.buildPlaceholder(options.size), + placeholder: NumericPart.buildPlaceholder(options.size ?? 1), }); } diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts index b13519089d7..0b825e64384 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts @@ -136,9 +136,8 @@ const partSpec = f.store(() => syncers.enum(Object.keys(fieldFormatterTypeMapper)) ), size: pipe( - syncers.xmlAttribute('size', 'required'), - syncers.maybe(syncers.toDecimal), - syncers.default(1) + syncers.xmlAttribute('size', 'skip'), + syncers.maybe(syncers.toDecimal) ), /* * For most parts, this is a human-friendly placeholder like ### or ABC. diff --git a/specifyweb/frontend/js_src/lib/localization/query.ts b/specifyweb/frontend/js_src/lib/localization/query.ts index 561c438d1f1..3c493c46897 100644 --- a/specifyweb/frontend/js_src/lib/localization/query.ts +++ b/specifyweb/frontend/js_src/lib/localization/query.ts @@ -742,12 +742,6 @@ export const queryText = createDictionary({ {maxLength:number|formatted} `, }, - tooShortErrorMessage: { - 'en-us': ` - Field value is too short. Min allowed length is - {minLength:number|formatted} - `, - }, future: { 'en-us': 'in the future', 'de-ch': 'Exportdatei wird erstellt', diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index fb58b5f1459..b3d8ad73bd7 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -539,7 +539,7 @@ export const resourcesText = createDictionary({ 'uk-ua': 'Попередній перегляд', }, previewExplainer: { - 'en-us': 'Search your collection records to preview the record formatter', + 'en-us': 'Search your collection records to preview the formatter', 'de-ch': ` Durchsuchen Sie Ihre Sammlungsdatensätze, um eine Vorschau des Datensatzformatierers anzuzeigen @@ -553,12 +553,12 @@ export const resourcesText = createDictionary({ formateur d'enregistrements `, 'ru-ru': ` - Выполните поиск в записях своей коллекции, чтобы просмотреть средство - форматирования записей. + Выполните поиск в записях своей коллекции, чтобы просмотреть + форматирования. `, 'uk-ua': ` - Виконайте пошук у своїх записах колекції, щоб переглянути інструмент - форматування записів + Виконайте пошук у своїх записах колекції, щоб переглянути + форматування `, }, editorNotAvailable: { @@ -901,6 +901,9 @@ export const resourcesText = createDictionary({ 'ru-ru': 'Пример поля', 'uk-ua': 'Приклад поле', }, + pattern: { + 'en-us': 'Pattern', + }, parentCogSameAsChild: { 'en-us': 'A Collection Object Group cannot be a parent to itself', }, From 6850bab037f4bb7fc36f2986adb05faed94d19a3 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 6 Jul 2024 17:25:16 -0700 Subject: [PATCH 06/33] tests(FieldFormatters): update failing tests --- .../__snapshots__/index.test.ts.snap | 360 +++++++++--------- .../FieldFormatters/__tests__/index.test.ts | 4 +- 2 files changed, 181 insertions(+), 183 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap index 95c01ecf9bb..ac849aacaca 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap @@ -4,516 +4,516 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` { "AccessionNumber": UiFormatter { "field": "[literalField Accession.accessionNumber]", - "fields": [ - YearField { + "isSystem": true, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - AlphaNumberField { + AlphaNumberPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "AA", + "regexPlaceholder": undefined, "size": 2, - "type": "alphanumeric", - "value": "AA", + "type": "alpha", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, "table": "[table Accession]", "title": "AccessionNumber", }, "AccessionNumberByYear": UiFormatter { "field": "[literalField Accession.accessionNumber]", - "fields": [ - YearField { + "isSystem": true, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - AlphaNumberField { + AlphaNumberPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "AA", + "regexPlaceholder": undefined, "size": 2, - "type": "alphanumeric", - "value": "AA", + "type": "alpha", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - AlphaNumberField { + AlphaNumberPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "AAA", + "regexPlaceholder": undefined, "size": 3, - "type": "alphanumeric", - "value": "AAA", + "type": "alpha", }, ], - "isSystem": true, "table": "[table Accession]", "title": "AccessionNumberByYear", }, "AccessionStringFormatter": UiFormatter { "field": "[literalField Accession.accessionNumber]", - "fields": [ - AlphaNumberField { + "isSystem": true, + "parts": [ + AlphaNumberPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "AAAAAAAAAA", + "regexPlaceholder": undefined, "size": 10, - "type": "alphanumeric", - "value": "AAAAAAAAAA", + "type": "alpha", }, ], - "isSystem": true, "table": "[table Accession]", "title": "AccessionStringFormatter", }, "CatalogNumber": UiFormatter { "field": "[literalField CollectionObject.catalogNumber]", - "fields": [ - YearField { + "isSystem": false, + "parts": [ + YearPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "######", + "regexPlaceholder": undefined, "size": 6, "type": "numeric", - "value": "######", }, ], - "isSystem": false, "table": "[table CollectionObject]", "title": "CatalogNumber", }, "CatalogNumberAlphaNumByYear": UiFormatter { "field": "[literalField CollectionObject.catalogNumber]", - "fields": [ - YearField { + "isSystem": false, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "######", + "regexPlaceholder": undefined, "size": 6, "type": "numeric", - "value": "######", }, ], - "isSystem": false, "table": "[table CollectionObject]", "title": "CatalogNumberAlphaNumByYear", }, "CatalogNumberNumeric": CatalogNumberNumeric { "field": "[literalField CollectionObject.catalogNumber]", - "fields": [ + "isSystem": true, + "parts": [ CatalogNumberNumericField { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "#########", + "regexPlaceholder": undefined, "size": 9, "type": "numeric", - "value": "#########", }, ], - "isSystem": true, "table": "[table CollectionObject]", "title": "Catalog Number Numeric", }, "CatalogNumberNumericRegex": UiFormatter { "field": "[literalField CollectionObject.catalogNumber]", - "fields": [ - RegexField { + "isSystem": false, + "parts": [ + RegexPart { "autoIncrement": false, "byYear": false, - "pattern": "####[-A]", + "placeholder": "[0-9]{4}(-[A-Z])?", + "regexPlaceholder": "####[-A]", "size": 1, "type": "regex", - "value": "[0-9]{4}(-[A-Z])?", }, ], - "isSystem": false, "table": "[table CollectionObject]", "title": "CatalogNumberNumericRegex", }, "Date": UiFormatter { "field": undefined, - "fields": [], "isSystem": true, + "parts": [], "table": undefined, "title": "Date", }, "DeaccessionNumber": UiFormatter { "field": "[literalField Deaccession.deaccessionNumber]", - "fields": [ - YearField { + "isSystem": true, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - AlphaNumberField { + AlphaNumberPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "AA", + "regexPlaceholder": undefined, "size": 2, - "type": "alphanumeric", - "value": "AA", + "type": "alpha", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, "table": "[table Deaccession]", "title": "DeaccessionNumber", }, "GiftNumber": UiFormatter { "field": "[literalField Gift.giftNumber]", - "fields": [ - YearField { + "isSystem": true, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, "table": "[table Gift]", "title": "GiftNumber", }, "InfoRequestNumber": UiFormatter { "field": "[literalField InfoRequest.infoReqNumber]", - "fields": [ - YearField { + "isSystem": true, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, "table": "[table InfoRequest]", "title": "InfoRequestNumber", }, "KUITeach": CatalogNumberNumeric { "field": "[literalField CollectionObject.catalogNumber]", - "fields": [ + "isSystem": true, + "parts": [ CatalogNumberNumericField { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "#########", + "regexPlaceholder": undefined, "size": 9, "type": "numeric", - "value": "#########", }, ], - "isSystem": true, "table": "[table CollectionObject]", "title": "Catalog Number Numeric", }, "LoanNumber": UiFormatter { "field": "[literalField Loan.loanNumber]", - "fields": [ - YearField { + "isSystem": true, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, "table": "[table Loan]", "title": "LoanNumber", }, "NumericBigDecimal": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "###############", + "regexPlaceholder": undefined, "size": 15, "type": "numeric", - "value": "###############", }, ], - "isSystem": true, "table": undefined, "title": "NumericBigDecimal", }, "NumericByte": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, "table": undefined, "title": "NumericByte", }, "NumericDouble": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "##########", + "regexPlaceholder": undefined, "size": 10, "type": "numeric", - "value": "##########", }, ], - "isSystem": true, "table": undefined, "title": "NumericDouble", }, "NumericFloat": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "##########", + "regexPlaceholder": undefined, "size": 10, "type": "numeric", - "value": "##########", }, ], - "isSystem": true, "table": undefined, "title": "NumericFloat", }, "NumericInteger": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "##########", + "regexPlaceholder": undefined, "size": 10, "type": "numeric", - "value": "##########", }, ], - "isSystem": true, "table": undefined, "title": "NumericInteger", }, "NumericLong": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "##########", + "regexPlaceholder": undefined, "size": 10, "type": "numeric", - "value": "##########", }, ], - "isSystem": true, "table": undefined, "title": "NumericLong", }, "NumericShort": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "#####", + "regexPlaceholder": undefined, "size": 5, "type": "numeric", - "value": "#####", }, ], - "isSystem": true, "table": undefined, "title": "NumericShort", }, "PartialDate": UiFormatter { "field": undefined, - "fields": [], "isSystem": false, + "parts": [], "table": undefined, "title": "PartialDate", }, "PartialDateMonth": UiFormatter { "field": undefined, - "fields": [], "isSystem": false, + "parts": [], "table": undefined, "title": "PartialDateMonth", }, "PartialDateYear": UiFormatter { "field": undefined, - "fields": [], "isSystem": false, + "parts": [], "table": undefined, "title": "PartialDateYear", }, "SearchDate": UiFormatter { "field": undefined, - "fields": [], "isSystem": true, + "parts": [], "table": undefined, "title": "SearchDate", }, diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts index d33a4c3c215..f14a5d9fcab 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/index.test.ts @@ -30,9 +30,7 @@ describe('placeholder', () => { '####[-A]' )); test('accession number', () => - expect(getSecondFormatter()?.placeholder).toBe( - '^(YEAR|\\d{4})(-)([a-zA-Z0-9]{2})(-)(###|\\d{3})$' - )); + expect(getSecondFormatter()?.placeholder).toBe('2022-AA-###')); }); describe('regex', () => { From 4ae2f218db878506ce3057e825b52c8e64d94cf8 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sun, 7 Jul 2024 00:28:54 +0000 Subject: [PATCH 07/33] Lint code with ESLint and Prettier Triggered by 06b3c1a504edb68e1f46e82cd84eb07355d9b480 on branch refs/heads/field-editor --- .../components/AttachmentsBulkImport/__tests__/utils.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts index c3ab8b21a56..5b111d6f55c 100644 --- a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts +++ b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts @@ -1,4 +1,5 @@ -import { LocalizedString } from 'typesafe-i18n'; +import type { LocalizedString } from 'typesafe-i18n'; + import { requireContext } from '../../../tests/helpers'; import { formatterToParser } from '../../../utils/parser/definitions'; import type { IR, RA } from '../../../utils/types'; From 0b5cba02c57124666cc57057fbfe909d379a95b2 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 6 Jul 2024 17:56:10 -0700 Subject: [PATCH 08/33] refactor(FieldFormatters): do a self code review --- .../lib/components/FieldFormatters/Parts.tsx | 10 +++------- .../lib/components/FieldFormatters/Routes.tsx | 18 ++++++++++-------- .../lib/components/FieldFormatters/index.ts | 3 ++- .../lib/components/FieldFormatters/spec.ts | 2 +- .../lib/components/Formatters/Definitions.tsx | 2 +- .../lib/components/Formatters/Element.tsx | 2 +- .../lib/components/Formatters/Fields.tsx | 4 ++-- 7 files changed, 20 insertions(+), 21 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx index fbab9f37fa6..86c0558236e 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx @@ -35,12 +35,8 @@ export function FieldFormatterParts({ <> {parts.length === 0 ? undefined : (
    {resourcesText.separator()}{schemaText.field()}{schemaText.customFieldFormat()}{resourcesText.type()}{commonText.size()}{resourcesText.hint()}{formsText.autoNumber()}
    + + + + handleChange({ + ...part, + size, + }) + } + isReadOnly={isReadOnly} + // Size is hardcoded to 4 for year + disabled={part.type === 'year'} + /> + + maxLength={placeholderSize} + minLength={placeholderSize} + required + value={part.placeholder} + onValueChange={(placeholder): void => handleChange({ ...part, - separator, + placeholder, }) } /> - - handleChange({ - ...part, - field: fieldMapping, - }), - ]} - openIndex={[openIndex, setOpenIndex]} - table={table} - /> + {part.type === 'numeric' ? ( + + + handleChange({ + ...part, + autoIncrement, + }) + } + /> + {formsText.autoNumber()} + + ) : part.type === 'year' ? ( + + + handleChange({ + ...part, + byYear, + }) + } + /> + {formsText.autoNumberByYear()} + + ) : undefined} - - {isReadOnly ? null : ( ); } - -function FieldFormatterPicker({ - field: [field, handleChange], -}: { - readonly field: GetSet; -}): JSX.Element | null { - const lastField = field.field?.at(-1); - if (lastField === undefined) return null; - else if (!lastField.isRelationship) - return ( - - - handleChange({ - ...field, - fieldFormatter, - }) - } - /> - - ); - else if (relationshipIsToMany(lastField)) - return ( - - - handleChange({ - ...field, - aggregator, - }) - } - /> - - ); - else - return ( - - {resourcesText.formatter()} - - handleChange({ - ...field, - formatter, - }) - } - /> - - ); -} diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Routes.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Routes.tsx index dbe1c68e99c..5bbab2fb537 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Routes.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Routes.tsx @@ -23,11 +23,15 @@ export const fieldFormattersRoutes = toReactRoutes([ path: ':tableName', element: async () => import('./List').then( - ({ FieldFormattersList }) => FieldFormattersList + ({ FieldFormatterEditorWrapper }) => FieldFormatterEditorWrapper ), children: [ { index: true, + element: async () => + import('./List').then( + ({ FieldFormattersList }) => FieldFormattersList + ), }, { path: ':index', diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index 5257548783c..5b5c09bfd62 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -5,6 +5,8 @@ import type { LocalizedString } from 'typesafe-i18n'; import { formsText } from '../../localization/forms'; +import { queryText } from '../../localization/query'; +import { resourcesText } from '../../localization/resources'; import { getAppResourceUrl } from '../../utils/ajax/helpers'; import type { IR, RA } from '../../utils/types'; import { filterArray, localized } from '../../utils/types'; @@ -175,7 +177,7 @@ abstract class Part { } public get defaultValue(): LocalizedString { - return this.placeholder === 'YEAR' + return this.placeholder === YearPart.placeholder ? localized(new Date().getFullYear().toString()) : this.placeholder; } @@ -213,16 +215,22 @@ class NumericPart extends Part { public constructor(options: Omit) { super({ ...options, - placeholder: localized(''.padStart(options.size, '#')), + placeholder: NumericPart.buildPlaceholder(options.size), }); } public get regex(): LocalizedString { return localized(`\\d{${this.size}}`); } + + public static buildPlaceholder(size: number): LocalizedString { + return localized(''.padStart(size, '#')); + } } class YearPart extends Part { + public static readonly placeholder = localized('YEAR'); + public readonly type = 'year'; public get regex(): LocalizedString { @@ -297,3 +305,14 @@ export const fieldFormatterTypeMapper = { regex: RegexPart, separator: SeparatorPart, } as const; + +export const fieldFormatterLocalization = { + constant: resourcesText.constant(), + year: queryText.year(), + alpha: resourcesText.alpha(), + numeric: resourcesText.numeric(), + alphanumeric: resourcesText.alphanumeric(), + anychar: resourcesText.anychar(), + regex: resourcesText.regex(), + separator: resourcesText.separator(), +}; diff --git a/specifyweb/frontend/js_src/lib/components/FormFields/Field.tsx b/specifyweb/frontend/js_src/lib/components/FormFields/Field.tsx index 94a54096946..df0c7d8d10a 100644 --- a/specifyweb/frontend/js_src/lib/components/FormFields/Field.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormFields/Field.tsx @@ -117,29 +117,14 @@ function Field({ (field?.isReadOnly === true && !isInSearchDialog); const validationAttributes = getValidationAttributes(parser); - - const [rightAlignNumberFields] = userPreferences.use( - 'form', - 'ui', - 'rightAlignNumberFields' - ); + const rightAlignClassName = useRightAlignClassName(parser.type, isReadOnly); return ( ); } + +export function useRightAlignClassName( + type: Parser['type'], + isReadOnly: boolean +): string | undefined { + const [rightAlignNumberFields] = userPreferences.use( + 'form', + 'ui', + 'rightAlignNumberFields' + ); + + /* + * Disable "text-align: right" in non webkit browsers + * as they don't support spinner's arrow customization + */ + return type === 'number' && + rightAlignNumberFields && + globalThis.navigator.userAgent.toLowerCase().includes('webkit') + ? `text-right ${isReadOnly ? '' : 'pr-6'}` + : ''; +} diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/Components.tsx b/specifyweb/frontend/js_src/lib/components/Formatters/Components.tsx index 483068829a6..49f0c30b457 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/Components.tsx +++ b/specifyweb/frontend/js_src/lib/components/Formatters/Components.tsx @@ -36,6 +36,7 @@ import { relationshipIsToMany, valueIsPartialField, } from '../WbPlanView/mappingHelpers'; +import type { MappingLineData } from '../WbPlanView/navigator'; import { getMappingLineData } from '../WbPlanView/navigator'; import { navigatorSpecs } from '../WbPlanView/navigatorSpecs'; import type { Aggregator, Formatter } from './spec'; @@ -148,11 +149,13 @@ export function ResourceMapping({ mapping: [mapping, setMapping], openIndex: [openIndex, setOpenIndex], isRequired = false, + fieldFilter, }: { readonly table: SpecifyTable; readonly mapping: GetSet | undefined>; readonly openIndex: GetSet; readonly isRequired?: boolean; + readonly fieldFilter?: (mappingData: MappingLineData) => MappingLineData; }): JSX.Element { const sourcePath = React.useMemo(() => { const rawPath = @@ -195,17 +198,16 @@ export function ResourceMapping({ }, [mappingPath, sourcePath]); const isReadOnly = React.useContext(ReadOnlyContext); - const lineData = React.useMemo( - () => - getMappingLineData({ - baseTableName: table.name, - mappingPath, - showHiddenFields: true, - generateFieldData: 'all', - spec: navigatorSpecs.formatterEditor, - }), - [table.name, mappingPath] - ); + const lineData = React.useMemo(() => { + const data = getMappingLineData({ + baseTableName: table.name, + mappingPath, + showHiddenFields: true, + generateFieldData: 'all', + spec: navigatorSpecs.formatterEditor, + }); + return typeof fieldFilter === 'function' ? data.map(fieldFilter) : data; + }, [table.name, mappingPath, fieldFilter]); const validation = React.useMemo( () => diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/List.tsx b/specifyweb/frontend/js_src/lib/components/Formatters/List.tsx index 5c4423aff0f..ea6b6b3f489 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/List.tsx +++ b/specifyweb/frontend/js_src/lib/components/Formatters/List.tsx @@ -9,6 +9,7 @@ import { ensure, localized } from '../../utils/types'; import { getUniqueName } from '../../utils/uniquifyName'; import { ErrorMessage, Ul } from '../Atoms'; import { Button } from '../Atoms/Button'; +import { icons } from '../Atoms/Icons'; import { Link } from '../Atoms/Link'; import { ReadOnlyContext } from '../Core/Contexts'; import type { SpecifyTable } from '../DataModel/specifyTable'; @@ -22,6 +23,7 @@ import type { FormatterTypesOutlet } from './Types'; export function FormatterList(): JSX.Element { const { type, tableName } = useParams(); const { items } = useOutletContext(); + const navigate = useNavigate(); return ( navigate('../')} /> ); } @@ -82,11 +85,13 @@ export function XmlEntryList< tableName, header, getNewItem, + onGoBack: handleGoBack, }: { readonly items: GetSet>; readonly tableName: string | undefined; readonly header: string; readonly getNewItem: (currentItems: RA, table: SpecifyTable) => ITEM; + readonly onGoBack?: () => void; }): JSX.Element { const isReadOnly = React.useContext(ReadOnlyContext); const navigate = useNavigate(); @@ -104,6 +109,12 @@ export function XmlEntryList< ); return (
    + {typeof handleGoBack === 'function' && ( + + {icons.chevronLeft} + {commonText.back()} + + )}

    {table.label}

    {commonText.colonHeader({ header })}
      diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx b/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx index 372f9369d28..7c5ceac76a6 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx +++ b/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx @@ -9,6 +9,7 @@ import type { GetOrSet, RA } from '../../utils/types'; import { removeItem } from '../../utils/utils'; import { Button } from '../Atoms/Button'; import { Link } from '../Atoms/Link'; +import { LoadingContext } from '../Core/Contexts'; import { fetchCollection } from '../DataModel/collection'; import type { AnySchema } from '../DataModel/helperTypes'; import type { SpecifyResource } from '../DataModel/legacyTypes'; @@ -39,7 +40,8 @@ export function useResourcePreview( table.name, { limit: defaultPreviewSize, - domainFilter: false, // REFACTOR: set to true after scoping reimplementation + // REFACTOR: set to true after scoping re-implementation + domainFilter: false, }, { orderBy: [ @@ -57,6 +59,7 @@ export function useResourcePreview( const [resources, setResources] = getSetResources; const [isOpen, handleOpen, handleClose] = useBooleanState(); + const loading = React.useContext(LoadingContext); return { resources: getSetResources, @@ -64,7 +67,7 @@ export function useResourcePreview(
      {resourcesText.preview()} @@ -109,7 +112,13 @@ export function useResourcePreview( onlyUseQueryBuilder table={table} onClose={handleClose} - onSelected={setResources} + onSelected={(selected): void => + void loading( + Promise.all( + selected.map(async (resource) => resource.fetch()) + ).then(setResources) + ) + } /> )}
      diff --git a/specifyweb/frontend/js_src/lib/components/QueryBuilder/RelativeDate.tsx b/specifyweb/frontend/js_src/lib/components/QueryBuilder/RelativeDate.tsx index 96754263e12..8223907a6e3 100644 --- a/specifyweb/frontend/js_src/lib/components/QueryBuilder/RelativeDate.tsx +++ b/specifyweb/frontend/js_src/lib/components/QueryBuilder/RelativeDate.tsx @@ -166,10 +166,10 @@ function DateSplit({ handleChanging?.(); }} > - - - - + + + + ), direction: (direction) => ( diff --git a/specifyweb/frontend/js_src/lib/localization/forms.ts b/specifyweb/frontend/js_src/lib/localization/forms.ts index b9382efdf03..d128c5fc9d4 100644 --- a/specifyweb/frontend/js_src/lib/localization/forms.ts +++ b/specifyweb/frontend/js_src/lib/localization/forms.ts @@ -915,6 +915,22 @@ export const formsText = createDictionary({ 'uk-ua': 'Автоматична нумерація', 'de-ch': 'Automatische Nummerierung', }, + autoNumberByYear: { + 'en-us': 'Auto-number by year', + 'de-ch': 'Auto-Nummer nach Jahr', + 'es-es': 'Auto-número por año', + 'fr-fr': 'Auto-numéro par année', + 'ru-ru': 'Автонумерация по году', + 'uk-ua': 'Автонумерація за роком', + }, + autoNumber: { + 'en-us': 'Auto-number', + 'de-ch': 'Auto-Nummer', + 'es-es': 'Auto-número', + 'fr-fr': 'Auto-numéro', + 'ru-ru': 'Автонумерация', + 'uk-ua': 'Автонумерація', + }, editFormDefinition: { 'en-us': 'Edit Form Definition', 'ru-ru': 'Изменить определение формы', diff --git a/specifyweb/frontend/js_src/lib/localization/query.ts b/specifyweb/frontend/js_src/lib/localization/query.ts index 6ddad842103..561c438d1f1 100644 --- a/specifyweb/frontend/js_src/lib/localization/query.ts +++ b/specifyweb/frontend/js_src/lib/localization/query.ts @@ -742,6 +742,12 @@ export const queryText = createDictionary({ {maxLength:number|formatted} `, }, + tooShortErrorMessage: { + 'en-us': ` + Field value is too short. Min allowed length is + {minLength:number|formatted} + `, + }, future: { 'en-us': 'in the future', 'de-ch': 'Exportdatei wird erstellt', @@ -758,7 +764,7 @@ export const queryText = createDictionary({ 'ru-ru': 'в прошлом', 'uk-ua': 'в минулому', }, - day: { + days: { 'en-us': 'Days', 'es-es': 'Días', 'fr-fr': 'Jours', @@ -766,7 +772,7 @@ export const queryText = createDictionary({ 'uk-ua': 'днів', 'de-ch': 'Tage', }, - week: { + weeks: { 'en-us': 'Weeks', 'de-ch': 'Wochen', 'es-es': 'Semanas', @@ -774,7 +780,7 @@ export const queryText = createDictionary({ 'ru-ru': 'Недели', 'uk-ua': 'тижнів', }, - month: { + months: { 'en-us': 'Months', 'de-ch': 'Monate', 'es-es': 'Meses', @@ -782,7 +788,7 @@ export const queryText = createDictionary({ 'ru-ru': 'Месяцы', 'uk-ua': 'Місяці', }, - year: { + years: { 'en-us': 'Years', 'de-ch': 'Jahre', 'es-es': 'Años', @@ -790,40 +796,36 @@ export const queryText = createDictionary({ 'ru-ru': 'Годы', 'uk-ua': 'років', }, + year: { + 'en-us': 'Year', + 'de-ch': 'Jahr', + 'es-es': 'Año', + 'fr-fr': 'Année', + 'ru-ru': 'Год', + 'uk-ua': 'рік', + }, relativeDate: { comment: ` Used in query builder lines, will be shown as a number followed by a period of time (ie: day, month or week) then a direction (past or future) `, 'en-us': ` - {size:number} {type:string} {direction:string} - `, 'de-ch': ` - {size:number} {type:string} {direction:string} - `, 'es-es': ` - {size:number} {type:string} {direction:string} - `, 'fr-fr': ` - {size:number} {type:string} {direction:string} - `, 'ru-ru': ` - {size:number} {type:string} {direction:string} - `, 'uk-ua': ` - {size:number} {type:string} {direction:string} - `, }, importHiddenFields: { diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index ff888be9aff..fb58b5f1459 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -867,6 +867,40 @@ export const resourcesText = createDictionary({ nonConformingInline: { 'en-us': '(non-conforming)', }, + hint: { + 'en-us': 'Hint', + 'de-ch': 'Hinweis', + 'es-es': 'Sugerencia', + 'fr-fr': 'Indice', + 'ru-ru': 'Подсказка', + 'uk-ua': 'Підказка', + }, + constant: { + 'en-us': 'Constant', + }, + alpha: { + 'en-us': 'Alpha', + }, + numeric: { + 'en-us': 'Numeric', + }, + alphanumeric: { + 'en-us': 'Alphanumeric', + }, + anychar: { + 'en-us': 'Any character', + }, + regex: { + 'en-us': 'Regular expression', + }, + exampleField: { + 'en-us': 'Example Field', + 'de-ch': 'Beispielfeld', + 'es-es': 'Campo de ejemplo', + 'fr-fr': "Champ d'exemple", + 'ru-ru': 'Пример поля', + 'uk-ua': 'Приклад поле', + }, parentCogSameAsChild: { 'en-us': 'A Collection Object Group cannot be a parent to itself', }, diff --git a/specifyweb/frontend/js_src/lib/utils/parser/dateFormat.ts b/specifyweb/frontend/js_src/lib/utils/parser/dateFormat.ts index 53abf3fed53..929e58851af 100644 --- a/specifyweb/frontend/js_src/lib/utils/parser/dateFormat.ts +++ b/specifyweb/frontend/js_src/lib/utils/parser/dateFormat.ts @@ -9,7 +9,7 @@ export const fullDateFormat = (): string => export const monthFormat = (): string => getPref('ui.formatting.scrmonthformat'); -export function formatDateForBackEnd(date: Date) { +export function formatDateForBackEnd(date: Date): string { const year = date.getFullYear(); const month = (date.getMonth() + 1).toString(); const day = date.getDate().toString(); diff --git a/specifyweb/frontend/js_src/lib/utils/parser/dayJsFixes.ts b/specifyweb/frontend/js_src/lib/utils/parser/dayJsFixes.ts index c81288694ac..15490c337ba 100644 --- a/specifyweb/frontend/js_src/lib/utils/parser/dayJsFixes.ts +++ b/specifyweb/frontend/js_src/lib/utils/parser/dayJsFixes.ts @@ -58,7 +58,7 @@ function fixDayJsBugs( function unsafeParseMonthYear( value: string ): ReturnType | undefined { - const parsed = /(\d{2})\D(\d{4})/.exec(value)?.slice(1); + const parsed = /(\d{2})\D(\d{4})/u.exec(value)?.slice(1); if (parsed === undefined) return undefined; const [month, year] = parsed.map(f.unary(Number.parseInt)); return dayjs(new Date(year, month - 1)); @@ -72,7 +72,7 @@ function unsafeParseFullDate( value: string ): ReturnType | undefined { if (fullDateFormat().toUpperCase() !== 'DD/MM/YYYY') return; - const parsed = /(\d{2})\D(\d{2})\D(\d{4})/.exec(value)?.slice(1); + const parsed = /(\d{2})\D(\d{2})\D(\d{4})/u.exec(value)?.slice(1); if (parsed === undefined) return undefined; const [day, month, year] = parsed.map(f.unary(Number.parseInt)); return dayjs(new Date(year, month - 1, day)); From 516a51946fd3489dd1122f94808bb08715a33288 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 6 Jul 2024 17:20:36 -0700 Subject: [PATCH 05/33] feat(FieldFormatters): improve visual editor UX --- .../FieldFormatters/FieldFormatter.tsx | 6 +- .../lib/components/FieldFormatters/Parts.tsx | 94 +++++++++++++------ .../lib/components/FieldFormatters/index.ts | 4 +- .../lib/components/FieldFormatters/spec.ts | 5 +- .../frontend/js_src/lib/localization/query.ts | 6 -- .../js_src/lib/localization/resources.ts | 13 ++- 6 files changed, 77 insertions(+), 51 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx index 3e41fc15856..62d75cab76d 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx @@ -15,7 +15,6 @@ import type { LiteralField } from '../DataModel/specifyField'; import { softError } from '../Errors/assert'; import { ResourceMapping } from '../Formatters/Components'; import { ResourcePreview } from '../Formatters/Preview'; -import { useRightAlignClassName } from '../FormFields/Field'; import { hasTablePermission } from '../Permissions/helpers'; import type { MappingLineData } from '../WbPlanView/navigator'; import type { UiFormatter } from '.'; @@ -34,9 +33,8 @@ export function FieldFormatterElement({ <> setFieldFormatter({ ...fieldFormatter, autoNumber }) } @@ -169,14 +167,12 @@ function FieldFormatterPreviewField({ const parser = useParser(field); const validationAttributes = getValidationAttributes(parser); - const rightAlignClassName = useRightAlignClassName(parser.type, false); return resolvedFormatter === undefined ? null : ( {`${resourcesText.exampleField()} ${ isConforming ? '' : resourcesText.nonConformingInline() }`} {resourcesText.type()}
    {commonText.size()} {resourcesText.hint()}{formsText.autoNumber()}
    handleChange({ ...part, - placeholder, + [part.type === 'regex' ? 'regexPlaceholder' : 'placeholder']: + placeholder, }) } /> @@ -231,6 +223,16 @@ function Part({ /> {formsText.autoNumberByYear()} + ) : part.type === 'regex' ? ( + + handleChange({ + ...part, + placeholder, + }) + } + /> ) : undefined} @@ -248,3 +250,35 @@ function Part({
    )} - {isReadOnly ? null : ( + {isReadOnly ? undefined : (
    @@ -236,7 +232,7 @@ function Part({ ) : undefined}
    - {isReadOnly ? null : ( + {isReadOnly ? undefined : ( import('./List').then( ({ FieldFormattersList }) => FieldFormattersList ), - }, - { - path: ':index', - element: async () => - import('./Element').then( - ({ FieldFormatterWrapper }) => FieldFormatterWrapper - ), + children: [ + { index: true }, + { + path: ':index', + element: async () => + import('./Element').then( + ({ FieldFormatterWrapper }) => FieldFormatterWrapper + ), + }, + ], }, ], }, diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index 705550188c2..b3f08fd9a67 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -61,7 +61,8 @@ export function resolveFieldFormatter( ); return new UiFormatter( formatter.isSystem, - formatter.title ?? formatter.name, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + formatter.title || formatter.name, parts, formatter.table, formatter.field diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts index 0b825e64384..be312f8c6c2 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts @@ -65,7 +65,7 @@ export const fieldFormattersSpec = f.store(() => /** * Specify 6 hardcoded special autonumbering behavior for a few tables. * Accession table has special auto numbering, and collection object has - * two. Trying our best here to match the intended semantics for backwards + * two. Doing a best effort match of intended semantics for backwards * compatibility. */ function inferLegacyAutoNumber( diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/Definitions.tsx b/specifyweb/frontend/js_src/lib/components/Formatters/Definitions.tsx index 03c14ce2985..fe290b3fbf1 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/Definitions.tsx +++ b/specifyweb/frontend/js_src/lib/components/Formatters/Definitions.tsx @@ -186,7 +186,7 @@ function ConditionalFormatter({ )} - {expandedNoCondition || isReadOnly ? null : fields.length === 0 ? ( + {expandedNoCondition || isReadOnly ? undefined : fields.length === 0 ? (
    - {isReadOnly ? null : ( + {isReadOnly ? undefined : ( { setItems(removeItem(items, index)); diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx b/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx index 2f34a6ca3b2..674a7d217dc 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx +++ b/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx @@ -99,7 +99,7 @@ export function Fields({
    )} - {isReadOnly ? null : ( + {isReadOnly ? undefined : (
    @@ -191,7 +191,7 @@ function Field({ )} - {isReadOnly ? null : ( + {isReadOnly ? undefined : ( <> Date: Sat, 6 Jul 2024 18:11:30 -0700 Subject: [PATCH 09/33] feat(SchemaConfig): add link to FieldFormatter VisualEditor --- .../lib/components/SchemaConfig/Format.tsx | 53 +++++++++++++++++++ .../lib/components/SchemaConfig/schemaData.ts | 2 + 2 files changed, 55 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx index c86deed867b..903ff94c6a5 100644 --- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx +++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx @@ -17,6 +17,7 @@ import { LoadingContext, ReadOnlyContext } from '../Core/Contexts'; import { getField } from '../DataModel/helpers'; import type { SerializedResource } from '../DataModel/helperTypes'; import type { LiteralField, Relationship } from '../DataModel/specifyField'; +import type { SpecifyTable } from '../DataModel/specifyTable'; import { tables } from '../DataModel/tables'; import type { SpLocaleContainerItem } from '../DataModel/types'; import { ResourceLink } from '../Molecules/ResourceLink'; @@ -73,6 +74,13 @@ export function SchemaConfigFormat({ /> + } label={schemaText.formatted()} name="formatted" value={item.format} @@ -345,3 +353,48 @@ function WebLinkEditing({ ) : null; } + +function FieldFormatterEditing({ + table, + value, + schemaData, +}: { + readonly table: SpecifyTable; + readonly value: string | null; + readonly schemaData: SchemaData; +}): JSX.Element | null { + const index = schemaData.uiFormatters + .filter((formatter) => formatter.tableName === table.name) + .findIndex(({ name }) => name === value); + const resourceId = appResourceIds.UIFormatters; + const navigate = useNavigate(); + if (resourceId === undefined) return null; + + const baseUrl = `/specify/resources/app-resource/${resourceId}/field-formatters/${table.name}/`; + return ( + <> + {typeof index === 'number' && ( + { + event.preventDefault(); + navigate(`${baseUrl}${index}/`); + }} + /> + )} + { + event.preventDefault(); + navigate(baseUrl); + }} + /> + + ); +} diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts b/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts index c49584513b1..dc71b52b337 100644 --- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts +++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts @@ -46,6 +46,7 @@ type SimpleFieldFormatter = { readonly isSystem: boolean; readonly value: string; readonly field: LiteralField | undefined; + readonly tableName: keyof Tables | undefined; }; export const fetchSchemaData = async (): Promise => @@ -69,6 +70,7 @@ export const fetchSchemaData = async (): Promise => isSystem: formatter.isSystem, value: formatter.defaultValue, field: formatter.field, + tableName: formatter.table?.name, })) .filter(({ value }) => value) ), From 0587e45b2d8f1562e4aa727082fc64d5f300d94e Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 13 Jul 2024 08:01:23 -0700 Subject: [PATCH 10/33] fix(FieldFormatters): normalize ^ and $ in regex Fixes https://github.com/specify/specify7/pull/5075#pullrequestreview-2163560361 --- .../FieldFormatters/__tests__/spec.test.ts | 23 +++++++++ .../lib/components/FieldFormatters/index.ts | 13 ++++- .../lib/components/FieldFormatters/spec.ts | 49 ++++++++++++++++++- .../lib/localization/utils/scanUsages.ts | 1 + specifyweb/specify/uiformatters.py | 11 ++++- 5 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/spec.test.ts diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/spec.test.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/spec.test.ts new file mode 100644 index 00000000000..0157472a33d --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/spec.test.ts @@ -0,0 +1,23 @@ +import { theories } from '../../../tests/utils'; +import { exportsForTests } from '../spec'; + +const { trimRegexString, normalizeRegexString } = exportsForTests; + +theories(trimRegexString, [ + [[''], ''], + [['[a-z]{3,}.*'], '[a-z]{3,}.*'], + [['/^[a-z]{3,}.*$/'], '[a-z]{3,}.*'], + [['^\\d{3}$'], '\\d{3}'], + [['/^(KUI|KUBI|NHM)$/'], 'KUI|KUBI|NHM'], +]); + +theories(normalizeRegexString, [ + [[''], '/^$/'], + [['[a-z]{3,}.*'], '/^[a-z]{3,}.*$/'], + [['/^[a-z]{3,}.*$/'], '/^[a-z]{3,}.*$/'], + [['^\\d{3}$'], '/^\\d{3}$/'], + [['\\d{3}'], '/^\\d{3}$/'], + [['/\\d{3}/'], '/^\\d{3}$/'], + [['KUI|KUBI|NHM'], '/^(KUI|KUBI|NHM)$/'], + [['(KUI|KUBI)|NHM'], '/^((KUI|KUBI)|NHM)$/'], +]); diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index b3f08fd9a67..1e8c2dbfbc5 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -1,5 +1,5 @@ /** - * Parse and use Specify 6 UI Formatters + * Parse and use field formatters */ import type { LocalizedString } from 'typesafe-i18n'; @@ -259,7 +259,16 @@ class RegexPart extends Part { public readonly type = 'regex'; public get regex(): LocalizedString { - return this.placeholder; + let pattern: string = this.placeholder; + /* + * In UiFormatter.getRegex() we are adding ^ and $ as necessary, so trim + * them if they were present here + */ + if (pattern.startsWith('/')) pattern = pattern.slice(1); + if (pattern.startsWith('^')) pattern = pattern.slice(1); + if (pattern.endsWith('/')) pattern = pattern.slice(0, -1); + if (pattern.endsWith('$')) pattern = pattern.slice(0, -1); + return pattern as LocalizedString; } } diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts index be312f8c6c2..a03ac359d05 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts @@ -123,11 +123,56 @@ const formatterSpec = f.store(() => ), parts: pipe( syncers.xmlChildren('field'), - syncers.map(syncers.object(partSpec())) + syncers.map( + pipe( + syncers.object(partSpec()), + syncer( + (part) => ({ + ...part, + placeholder: + part.type === 'regex' + ? localized(trimRegexString(part.placeholder)) + : part.placeholder, + }), + (part) => ({ + ...part, + placeholder: + part.type === 'regex' + ? localized(normalizeRegexString(part.placeholder)) + : part.placeholder, + }) + ) + ) + ) ), }) ); +/** + * Specify 6 expects the regex pattern to start with "/^" and end with "$/" + * because it parts each field part individually. + * In Specify 7, we construct a combined regex that parts all field parts at + * once. + * Thus we do not want the "^" and "$" to be part of the pattern as far as + * Specify 7 front-end is concerned, but we want it to be part of the pattern + * in the .xml to work with Specify 6. + */ +function trimRegexString(regexString: string): string { + let pattern = regexString; + if (pattern.startsWith('/')) pattern = pattern.slice(1); + if (pattern.startsWith('^')) pattern = pattern.slice(1); + if (pattern.endsWith('/')) pattern = pattern.slice(0, -1); + if (pattern.endsWith('$')) pattern = pattern.slice(0, -1); + if (pattern.startsWith('(') && pattern.endsWith(')')) + pattern = pattern.slice(1, -1); + return pattern; +} +function normalizeRegexString(regexString: string): string { + let pattern: string = trimRegexString(regexString); + if (pattern.includes('|')) pattern = `(${pattern})`; + return `/^${pattern}$/`; +} + const partSpec = f.store(() => createXmlSpec({ type: pipe( @@ -179,3 +224,5 @@ function parseField( return undefined; } else return field; } + +export const exportsForTests = { trimRegexString, normalizeRegexString }; diff --git a/specifyweb/frontend/js_src/lib/localization/utils/scanUsages.ts b/specifyweb/frontend/js_src/lib/localization/utils/scanUsages.ts index fe37b54f562..7de4b6cacd0 100644 --- a/specifyweb/frontend/js_src/lib/localization/utils/scanUsages.ts +++ b/specifyweb/frontend/js_src/lib/localization/utils/scanUsages.ts @@ -413,6 +413,7 @@ export async function scanUsages( ), 'Value:\n', instances.at(-1)?.originalValue ?? valueString, + '\n', ].join('') ); }) diff --git a/specifyweb/specify/uiformatters.py b/specifyweb/specify/uiformatters.py index 6c73897e473..f24eba0d657 100644 --- a/specifyweb/specify/uiformatters.py +++ b/specifyweb/specify/uiformatters.py @@ -237,7 +237,16 @@ def value_regexp(self) -> str: class RegexField(Field): def value_regexp(self) -> str: - return self.value + pattern = self.value + if pattern.startswith('/'): + pattern = pattern[1:] + if pattern.startswith('^'): + pattern = pattern[1:] + if pattern.endswith('/'): + pattern = pattern[:-1] + if pattern.endswith('$'): + pattern = pattern[:-1] + return pattern class AlphaField(Field): def value_regexp(self) -> str: From 61ef5fd03880873e58aa8334c9be53cdb765acf9 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 13 Jul 2024 08:28:15 -0700 Subject: [PATCH 11/33] fix(FieldFormatters): correctly resolve formatter in editor preview --- .../AttachmentsBulkImport/__tests__/utils.test.ts | 4 ++-- .../components/FieldFormatters/FieldFormatter.tsx | 15 ++++++++++++--- .../utils/parser/__tests__/definitions.test.ts | 8 ++++---- .../js_src/lib/utils/parser/definitions.ts | 4 ++-- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts index 5b111d6f55c..4f732a2d772 100644 --- a/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts +++ b/specifyweb/frontend/js_src/lib/components/AttachmentsBulkImport/__tests__/utils.test.ts @@ -1,7 +1,7 @@ import type { LocalizedString } from 'typesafe-i18n'; import { requireContext } from '../../../tests/helpers'; -import { formatterToParser } from '../../../utils/parser/definitions'; +import { fieldFormatterToParser } from '../../../utils/parser/definitions'; import type { IR, RA } from '../../../utils/types'; import { localized } from '../../../utils/types'; import { tables } from '../../DataModel/tables'; @@ -110,7 +110,7 @@ describe('file names resolution test', () => { : syncFieldFormat( field, value.toString(), - formatterToParser(field, formatter), + fieldFormatterToParser(field, formatter), undefined, true ); diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx index 62d75cab76d..71eeb0edb78 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx @@ -1,10 +1,12 @@ import React from 'react'; -import { useParser } from '../../hooks/resource'; import { formsText } from '../../localization/forms'; import { resourcesText } from '../../localization/resources'; import { schemaText } from '../../localization/schema'; -import { getValidationAttributes } from '../../utils/parser/definitions'; +import { + fieldFormatterToParser, + getValidationAttributes, +} from '../../utils/parser/definitions'; import type { GetSet, RA } from '../../utils/types'; import { ErrorMessage } from '../Atoms'; import { Input, Label } from '../Atoms/Form'; @@ -164,7 +166,13 @@ function FieldFormatterPreviewField({ () => resolvedFormatter?.parse(value) !== undefined, [value, resolvedFormatter] ); - const parser = useParser(field); + const parser = React.useMemo( + () => + field === undefined || resolvedFormatter === undefined + ? { type: 'text' as const } + : fieldFormatterToParser(field, resolvedFormatter), + [field, resolvedFormatter] + ); const validationAttributes = getValidationAttributes(parser); return resolvedFormatter === undefined ? null : ( @@ -173,6 +181,7 @@ function FieldFormatterPreviewField({ isConforming ? '' : resourcesText.nonConformingInline() }`} { ...parserFromType('java.lang.String'), required: false, ...removeKey( - formatterToParser(field, uiFormatter), + fieldFormatterToParser(field, uiFormatter), 'formatters', 'parser', 'validators' @@ -238,7 +238,7 @@ describe('formatterToParser', () => { validators, parser: parserFunction, ...parser - } = formatterToParser({}, uiFormatter); + } = fieldFormatterToParser({}, uiFormatter); expect(parser).toEqual({ pattern: uiFormatter.regex, title, @@ -270,7 +270,7 @@ describe('formatterToParser', () => { userPreferences.set('form', 'preferences', 'autoNumbering', { CollectionObject: [], }); - expect(formatterToParser(field, uiFormatter).value).toBeUndefined(); + expect(fieldFormatterToParser(field, uiFormatter).value).toBeUndefined(); }); }); diff --git a/specifyweb/frontend/js_src/lib/utils/parser/definitions.ts b/specifyweb/frontend/js_src/lib/utils/parser/definitions.ts index 83e2c01b7a2..f612701d5d3 100644 --- a/specifyweb/frontend/js_src/lib/utils/parser/definitions.ts +++ b/specifyweb/frontend/js_src/lib/utils/parser/definitions.ts @@ -302,7 +302,7 @@ export function resolveParser( ? undefined : (fullField as LiteralField).whiteSpaceSensitive, ...(typeof formatter === 'object' - ? formatterToParser(field, formatter) + ? fieldFormatterToParser(field, formatter) : {}), }); } @@ -367,7 +367,7 @@ function resolveDate( return callback(...(values as RA)); } -export function formatterToParser( +export function fieldFormatterToParser( field: Partial, formatter: UiFormatter ): Parser { From 1bdece9848e4ca1bcdae08d98bcad3a3adbdd257 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 13 Jul 2024 08:44:29 -0700 Subject: [PATCH 12/33] fix(FieldFormatters): don't expose internal autoNumbering state in UI --- .../components/FieldFormatters/FieldFormatter.tsx | 15 +-------------- .../lib/components/FieldFormatters/List.tsx | 1 - .../js_src/lib/components/FieldFormatters/spec.ts | 9 ++++++--- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx index 71eeb0edb78..40a1943ae50 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { formsText } from '../../localization/forms'; import { resourcesText } from '../../localization/resources'; import { schemaText } from '../../localization/schema'; import { @@ -10,7 +9,6 @@ import { import type { GetSet, RA } from '../../utils/types'; import { ErrorMessage } from '../Atoms'; import { Input, Label } from '../Atoms/Form'; -import { ReadOnlyContext } from '../Core/Contexts'; import type { AnySchema } from '../DataModel/helperTypes'; import type { SpecifyResource } from '../DataModel/legacyTypes'; import type { LiteralField } from '../DataModel/specifyField'; @@ -29,20 +27,9 @@ export function FieldFormatterElement({ }: { readonly item: GetSet; }): JSX.Element { - const [fieldFormatter, setFieldFormatter] = item; - const isReadOnly = React.useContext(ReadOnlyContext); + const [fieldFormatter] = item; return ( <> - - - setFieldFormatter({ ...fieldFormatter, autoNumber }) - } - /> - {formsText.autoNumbering()} - {fieldFormatter.external === undefined && typeof fieldFormatter.table === 'object' ? ( diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx index a050d57b641..5c497d18cfd 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/List.tsx @@ -62,7 +62,6 @@ export function FieldFormattersList(): JSX.Element { isDefault: currentItems.length === 0, legacyType: undefined, legacyPartialDate: undefined, - autoNumber: false, external: undefined, parts: [], raw: { diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts index a03ac359d05..af006add80d 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts @@ -21,7 +21,6 @@ export const fieldFormattersSpec = f.store(() => ({ javaClass, rawAutoNumber, ...formatter }) => ({ ...formatter, table: getTable(javaClass ?? ''), - autoNumber: rawAutoNumber !== undefined, raw: { javaClass, legacyAutoNumber: rawAutoNumber, @@ -29,7 +28,6 @@ export const fieldFormattersSpec = f.store(() => }), ({ table, - autoNumber, raw: { javaClass, legacyAutoNumber }, ...formatter }) => ({ @@ -40,7 +38,7 @@ export const fieldFormattersSpec = f.store(() => (getTable(javaClass ?? '') === undefined ? javaClass : undefined), - rawAutoNumber: autoNumber + rawAutoNumber: formatter.parts.some(isAutoNumbering) ? legacyAutoNumber ?? inferLegacyAutoNumber(table, formatter.parts) : undefined, @@ -84,6 +82,11 @@ function inferLegacyAutoNumber( } else return 'edu.ku.brc.af.core.db.AutoNumberGeneric'; } +const isAutoNumbering = (part: { + readonly autoIncrement: boolean; + readonly byYear: boolean; +}): boolean => part.autoIncrement || part.byYear; + export type FieldFormatter = SpecToJson< ReturnType >['fieldFormatters'][number]; From a118d308b5828e91d302d7451390a42170b26d0e Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 13 Jul 2024 15:49:32 +0000 Subject: [PATCH 13/33] Lint code with ESLint and Prettier Triggered by b4736f8e5a8d44c7db10e34e8e867de7142dffe6 on branch refs/heads/field-editor --- .../js_src/lib/components/FormMeta/CarryForward.tsx | 10 +++++----- .../frontend/js_src/lib/components/Forms/Save.tsx | 3 +-- .../lib/utils/parser/__tests__/definitions.test.ts | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/CarryForward.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/CarryForward.tsx index 445594a3861..675486695b2 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/CarryForward.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/CarryForward.tsx @@ -204,11 +204,11 @@ export const tableValidForBulkClone = (table: SpecifyTable): boolean => !( tables.CollectionObject.strictGetLiteralField('catalogNumber') .getUiFormatter() - ?.fields.some( - (field) => - field.type === 'regex' || - field.type === 'alphanumeric' || - (field.type === 'numeric' && !field.canAutonumber()) + ?.parts.some( + (parts) => + parts.type === 'regex' || + parts.type === 'alphanumeric' || + (parts.type === 'numeric' && !parts.canAutonumber()) ) ?? false ); diff --git a/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx b/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx index c7f2f14ddad..f20450395de 100644 --- a/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx +++ b/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx @@ -272,7 +272,6 @@ export function SaveButton({ tables.CollectionObject.strictGetLiteralField( 'catalogNumber' ).getUiFormatter()!; - const wildCard = formatter.valueOrWild(); const clonePromises = Array.from( { length: carryForwardAmount }, @@ -283,7 +282,7 @@ export function SaveButton({ ); clonedResource.set( 'catalogNumber', - wildCard as never + formatter.defaultValue as never ); return clonedResource; } diff --git a/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts b/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts index a40183509ab..c98947a8759 100644 --- a/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts +++ b/specifyweb/frontend/js_src/lib/utils/parser/__tests__/definitions.test.ts @@ -18,8 +18,8 @@ import { removeKey } from '../../utils'; import type { Parser } from '../definitions'; import { browserifyRegex, - formatter, fieldFormatterToParser, + formatter, getValidationAttributes, lengthToRegex, mergeParsers, From 7978624c2955ed79880a155f5b1d6a3fb8a1516b Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sun, 21 Jul 2024 11:46:28 -0700 Subject: [PATCH 14/33] fix(FieldFormatters): resolve minor UX issues Fixes https://github.com/specify/specify7/pull/5075#pullrequestreview-2189080837 --- .../lib/components/FieldFormatters/Parts.tsx | 4 ++-- .../frontend/js_src/lib/localization/resources.ts | 14 +------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx index 86c0558236e..4a6661f8e8e 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx @@ -159,7 +159,7 @@ function Part({ aria-label={commonText.size()} disabled={enforcePlaceholderSize} isReadOnly={isReadOnly} - min={0} + min={1} required value={part.size} onValueChange={(size): void => @@ -173,7 +173,7 @@ function Part({ Date: Mon, 29 Jul 2024 17:27:48 -0700 Subject: [PATCH 15/33] fix(FieldFormatters): fix schema config indexes being off Fixes https://github.com/specify/specify7/pull/5075#pullrequestreview-2194509397 --- .../FieldFormatters/FieldFormatter.tsx | 2 +- .../__snapshots__/index.test.ts.snap | 24 +++++++++++++++++++ .../lib/components/FieldFormatters/index.ts | 13 ++++++---- .../lib/components/SchemaConfig/Format.tsx | 6 ++--- .../lib/components/SchemaConfig/schemaData.ts | 2 ++ 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx index 40a1943ae50..ddf760e6927 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx @@ -95,7 +95,7 @@ function FieldFormatterPreview({ readonly fieldFormatter: FieldFormatter; }): JSX.Element | null { const resolvedFormatter = React.useMemo( - () => resolveFieldFormatter(fieldFormatter), + () => resolveFieldFormatter(fieldFormatter, 0), [fieldFormatter] ); const doFormatting = React.useCallback( diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap index ac849aacaca..126c6ca2946 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap @@ -5,6 +5,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "AccessionNumber": UiFormatter { "field": "[literalField Accession.accessionNumber]", "isSystem": true, + "originalIndex": 0, "parts": [ YearPart { "autoIncrement": false, @@ -53,6 +54,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "AccessionNumberByYear": UiFormatter { "field": "[literalField Accession.accessionNumber]", "isSystem": true, + "originalIndex": 1, "parts": [ YearPart { "autoIncrement": false, @@ -101,6 +103,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "AccessionStringFormatter": UiFormatter { "field": "[literalField Accession.accessionNumber]", "isSystem": true, + "originalIndex": 2, "parts": [ AlphaNumberPart { "autoIncrement": false, @@ -117,6 +120,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "CatalogNumber": UiFormatter { "field": "[literalField CollectionObject.catalogNumber]", "isSystem": false, + "originalIndex": 3, "parts": [ YearPart { "autoIncrement": false, @@ -149,6 +153,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "CatalogNumberAlphaNumByYear": UiFormatter { "field": "[literalField CollectionObject.catalogNumber]", "isSystem": false, + "originalIndex": 5, "parts": [ YearPart { "autoIncrement": false, @@ -181,6 +186,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "CatalogNumberNumeric": CatalogNumberNumeric { "field": "[literalField CollectionObject.catalogNumber]", "isSystem": true, + "originalIndex": 0, "parts": [ CatalogNumberNumericField { "autoIncrement": true, @@ -197,6 +203,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "CatalogNumberNumericRegex": UiFormatter { "field": "[literalField CollectionObject.catalogNumber]", "isSystem": false, + "originalIndex": 4, "parts": [ RegexPart { "autoIncrement": false, @@ -213,6 +220,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "Date": UiFormatter { "field": undefined, "isSystem": true, + "originalIndex": 8, "parts": [], "table": undefined, "title": "Date", @@ -220,6 +228,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "DeaccessionNumber": UiFormatter { "field": "[literalField Deaccession.deaccessionNumber]", "isSystem": true, + "originalIndex": 9, "parts": [ YearPart { "autoIncrement": false, @@ -268,6 +277,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "GiftNumber": UiFormatter { "field": "[literalField Gift.giftNumber]", "isSystem": true, + "originalIndex": 10, "parts": [ YearPart { "autoIncrement": false, @@ -300,6 +310,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "InfoRequestNumber": UiFormatter { "field": "[literalField InfoRequest.infoReqNumber]", "isSystem": true, + "originalIndex": 11, "parts": [ YearPart { "autoIncrement": false, @@ -332,6 +343,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "KUITeach": CatalogNumberNumeric { "field": "[literalField CollectionObject.catalogNumber]", "isSystem": true, + "originalIndex": 0, "parts": [ CatalogNumberNumericField { "autoIncrement": true, @@ -348,6 +360,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "LoanNumber": UiFormatter { "field": "[literalField Loan.loanNumber]", "isSystem": true, + "originalIndex": 13, "parts": [ YearPart { "autoIncrement": false, @@ -380,6 +393,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "NumericBigDecimal": UiFormatter { "field": undefined, "isSystem": true, + "originalIndex": 14, "parts": [ NumericPart { "autoIncrement": false, @@ -396,6 +410,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "NumericByte": UiFormatter { "field": undefined, "isSystem": true, + "originalIndex": 15, "parts": [ NumericPart { "autoIncrement": false, @@ -412,6 +427,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "NumericDouble": UiFormatter { "field": undefined, "isSystem": true, + "originalIndex": 16, "parts": [ NumericPart { "autoIncrement": false, @@ -428,6 +444,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "NumericFloat": UiFormatter { "field": undefined, "isSystem": true, + "originalIndex": 17, "parts": [ NumericPart { "autoIncrement": false, @@ -444,6 +461,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "NumericInteger": UiFormatter { "field": undefined, "isSystem": true, + "originalIndex": 18, "parts": [ NumericPart { "autoIncrement": false, @@ -460,6 +478,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "NumericLong": UiFormatter { "field": undefined, "isSystem": true, + "originalIndex": 19, "parts": [ NumericPart { "autoIncrement": false, @@ -476,6 +495,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "NumericShort": UiFormatter { "field": undefined, "isSystem": true, + "originalIndex": 20, "parts": [ NumericPart { "autoIncrement": false, @@ -492,6 +512,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "PartialDate": UiFormatter { "field": undefined, "isSystem": false, + "originalIndex": 21, "parts": [], "table": undefined, "title": "PartialDate", @@ -499,6 +520,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "PartialDateMonth": UiFormatter { "field": undefined, "isSystem": false, + "originalIndex": 22, "parts": [], "table": undefined, "title": "PartialDateMonth", @@ -506,6 +528,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "PartialDateYear": UiFormatter { "field": undefined, "isSystem": false, + "originalIndex": 23, "parts": [], "table": undefined, "title": "PartialDateYear", @@ -513,6 +536,7 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` "SearchDate": UiFormatter { "field": undefined, "isSystem": true, + "originalIndex": 24, "parts": [], "table": undefined, "title": "SearchDate", diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index 1e8c2dbfbc5..24880d35e8d 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -29,8 +29,8 @@ export const fetchContext = Promise.all([ uiFormatters = Object.fromEntries( filterArray( xmlToSpec(formatters, fieldFormattersSpec()).fieldFormatters.map( - (formatter) => { - const resolvedFormatter = resolveFieldFormatter(formatter); + (formatter, index) => { + const resolvedFormatter = resolveFieldFormatter(formatter, index); return resolvedFormatter === undefined ? undefined : [formatter.name, resolvedFormatter]; @@ -44,7 +44,8 @@ export const getUiFormatters = (): typeof uiFormatters => uiFormatters ?? error('Tried to access UI formatters before fetching them'); export function resolveFieldFormatter( - formatter: FieldFormatter + formatter: FieldFormatter, + index: number ): UiFormatter | undefined { if (typeof formatter.external === 'string') { return parseJavaClassName(formatter.external) === @@ -65,7 +66,8 @@ export function resolveFieldFormatter( formatter.title || formatter.name, parts, formatter.table, - formatter.field + formatter.field, + index ); } } @@ -78,7 +80,8 @@ export class UiFormatter { public readonly parts: RA, public readonly table: SpecifyTable | undefined, // The field which this formatter is formatting - public readonly field: LiteralField | undefined + public readonly field: LiteralField | undefined, + public readonly originalIndex = 0 ) {} public get defaultValue(): string { diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx index 903ff94c6a5..26bf0f7aefb 100644 --- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx +++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx @@ -363,9 +363,9 @@ function FieldFormatterEditing({ readonly value: string | null; readonly schemaData: SchemaData; }): JSX.Element | null { - const index = schemaData.uiFormatters - .filter((formatter) => formatter.tableName === table.name) - .findIndex(({ name }) => name === value); + const index = schemaData.uiFormatters.find( + ({ name }) => name === value + )?.index; const resourceId = appResourceIds.UIFormatters; const navigate = useNavigate(); if (resourceId === undefined) return null; diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts b/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts index dc71b52b337..42120bd4698 100644 --- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts +++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/schemaData.ts @@ -47,6 +47,7 @@ type SimpleFieldFormatter = { readonly value: string; readonly field: LiteralField | undefined; readonly tableName: keyof Tables | undefined; + readonly index: number; }; export const fetchSchemaData = async (): Promise => @@ -71,6 +72,7 @@ export const fetchSchemaData = async (): Promise => value: formatter.defaultValue, field: formatter.field, tableName: formatter.table?.name, + index: formatter.originalIndex, })) .filter(({ value }) => value) ), From f7a6b3ecc3502806282eeafe5e33458893786677 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Wed, 27 Nov 2024 10:24:12 -0800 Subject: [PATCH 16/33] fix(FieldFormatters): don't trigger "Save" without user changes Fixes https://github.com/specify/specify7/pull/5075#pullrequestreview-2219847360 I should have known better than to use `useEffect` for this. Moved the logic to a more appropriate lace. --- .../lib/components/FieldFormatters/Parts.tsx | 42 ++++--------------- .../lib/components/FieldFormatters/index.ts | 2 +- .../lib/components/FieldFormatters/spec.ts | 21 ++++++++++ 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx index 4a6661f8e8e..2985dddf611 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx @@ -15,6 +15,7 @@ import { icons } from '../Atoms/Icons'; import { ReadOnlyContext } from '../Core/Contexts'; import { fieldFormatterLocalization, fieldFormatterTypeMapper } from '.'; import type { FieldFormatter, FieldFormatterPart } from './spec'; +import { fieldFormatterTypesWithForcedSize } from './spec'; export function FieldFormatterParts({ fieldFormatter: [fieldFormatter, setFieldFormatter], @@ -98,40 +99,9 @@ function Part({ }): JSX.Element { const isReadOnly = React.useContext(ReadOnlyContext); - React.useEffect(() => { - if (part.type === 'year') - handleChange({ - ...part, - size: 4, - placeholder: fieldFormatterTypeMapper.year.placeholder, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [part.type]); - - React.useEffect(() => { - if (part.type === 'numeric') - handleChange({ - ...part, - placeholder: fieldFormatterTypeMapper.numeric.buildPlaceholder( - part.size ?? 1 - ), - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [part.size, part.type]); - - const enforcePlaceholderSize = - part.type === 'constant' || - part.type === 'separator' || - part.type === 'year'; - - React.useEffect(() => { - if (enforcePlaceholderSize) - handleChange({ - ...part, - size: part.placeholder.length, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [part.placeholder, enforcePlaceholderSize]); + const enforcePlaceholderSize = fieldFormatterTypesWithForcedSize.has( + part.type as 'constant' + ); return ( @@ -166,6 +136,10 @@ function Part({ handleChange({ ...part, size, + placeholder: + part.type === 'numeric' + ? fieldFormatterTypeMapper.numeric.buildPlaceholder(size) + : part.placeholder, }) } /> diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index 24880d35e8d..def5e373178 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -227,7 +227,7 @@ class NumericPart extends Part { return localized(`\\d{${this.size}}`); } - public static buildPlaceholder(size: number): LocalizedString { + public static buildPlaceholder(size = 1): LocalizedString { return localized(''.padStart(size, '#')); } } diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts index af006add80d..98fff2f444a 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts @@ -135,6 +135,10 @@ const formatterSpec = f.store(() => placeholder: part.type === 'regex' ? localized(trimRegexString(part.placeholder)) + : part.type === 'year' + ? fieldFormatterTypeMapper.year.placeholder + : part.type === 'numeric' + ? fieldFormatterTypeMapper.numeric.buildPlaceholder(part.size) : part.placeholder, }), (part) => ({ @@ -144,6 +148,17 @@ const formatterSpec = f.store(() => ? localized(normalizeRegexString(part.placeholder)) : part.placeholder, }) + ), + syncer( + (part) => ({ + ...part, + size: fieldFormatterTypesWithForcedSize.has( + part.type as 'constant' + ) + ? part.placeholder.length + : part.size, + }), + (part) => part ) ) ) @@ -151,6 +166,12 @@ const formatterSpec = f.store(() => }) ); +export const fieldFormatterTypesWithForcedSize = new Set([ + 'constant', + 'separator', + 'year', +] as const); + /** * Specify 6 expects the regex pattern to start with "/^" and end with "$/" * because it parts each field part individually. From 7ac3dedb0528341920127d813ffefb2ceecdc479 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Wed, 27 Nov 2024 09:50:34 -0800 Subject: [PATCH 17/33] docs(localization): fix __init__.py file name typo --- specifyweb/frontend/js_src/lib/localization/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/localization/README.md b/specifyweb/frontend/js_src/lib/localization/README.md index 6297d1abc86..104a010090d 100644 --- a/specifyweb/frontend/js_src/lib/localization/README.md +++ b/specifyweb/frontend/js_src/lib/localization/README.md @@ -35,7 +35,7 @@ `'es'` is the Weblate language code -8. Open [/specifyweb/settings/**init**.py](/specifyweb/settings/__init__.py) +8. Open [`/specifyweb/settings/__init__.py`](/specifyweb/settings/__init__.py) 9. Add newly created language to `LANGUAGES` array. 10. Push the changes to `production` branch (weblate is setup to only look at that branch). From 341915db58b3b39814a0c9ff82db4748a75a8659 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 14 Dec 2024 09:14:15 -0800 Subject: [PATCH 18/33] refactor(FieldFormatters): deduplicate formatter normalization logic --- .../lib/components/FieldFormatters/Parts.tsx | 43 +++++++------- .../lib/components/FieldFormatters/spec.ts | 57 ++++++++----------- .../lib/components/SchemaConfig/Format.tsx | 4 +- .../js_src/lib/localization/schema.ts | 2 +- 4 files changed, 52 insertions(+), 54 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx index 2985dddf611..78f7d698c78 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx @@ -13,9 +13,12 @@ import { className } from '../Atoms/className'; import { Input, Label, Select } from '../Atoms/Form'; import { icons } from '../Atoms/Icons'; import { ReadOnlyContext } from '../Core/Contexts'; -import { fieldFormatterLocalization, fieldFormatterTypeMapper } from '.'; +import { fieldFormatterLocalization } from '.'; import type { FieldFormatter, FieldFormatterPart } from './spec'; -import { fieldFormatterTypesWithForcedSize } from './spec'; +import { + fieldFormatterTypesWithForcedSize, + normalizeFieldFormatterPart, +} from './spec'; export function FieldFormatterParts({ fieldFormatter: [fieldFormatter, setFieldFormatter], @@ -111,10 +114,12 @@ function Part({ disabled={isReadOnly} value={part.type ?? 'constant'} onValueChange={(newType): void => - handleChange({ - ...part, - type: newType as keyof typeof fieldFormatterLocalization, - }) + handleChange( + normalizeFieldFormatterPart({ + ...part, + type: newType as keyof typeof fieldFormatterLocalization, + }) + ) } > {Object.entries(fieldFormatterLocalization).map(([type, label]) => ( @@ -133,14 +138,12 @@ function Part({ required value={part.size} onValueChange={(size): void => - handleChange({ - ...part, - size, - placeholder: - part.type === 'numeric' - ? fieldFormatterTypeMapper.numeric.buildPlaceholder(size) - : part.placeholder, - }) + handleChange( + normalizeFieldFormatterPart({ + ...part, + size, + }) + ) } /> @@ -156,11 +159,13 @@ function Part({ : part.placeholder } onValueChange={(placeholder): void => - handleChange({ - ...part, - [part.type === 'regex' ? 'regexPlaceholder' : 'placeholder']: - placeholder, - }) + handleChange( + normalizeFieldFormatterPart({ + ...part, + [part.type === 'regex' ? 'regexPlaceholder' : 'placeholder']: + placeholder, + }) + ) } /> diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts index 98fff2f444a..9aaaea6efc7 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts @@ -129,43 +129,36 @@ const formatterSpec = f.store(() => syncers.map( pipe( syncers.object(partSpec()), - syncer( - (part) => ({ - ...part, - placeholder: - part.type === 'regex' - ? localized(trimRegexString(part.placeholder)) - : part.type === 'year' - ? fieldFormatterTypeMapper.year.placeholder - : part.type === 'numeric' - ? fieldFormatterTypeMapper.numeric.buildPlaceholder(part.size) - : part.placeholder, - }), - (part) => ({ - ...part, - placeholder: - part.type === 'regex' - ? localized(normalizeRegexString(part.placeholder)) - : part.placeholder, - }) - ), - syncer( - (part) => ({ - ...part, - size: fieldFormatterTypesWithForcedSize.has( - part.type as 'constant' - ) - ? part.placeholder.length - : part.size, - }), - (part) => part - ) + syncer(normalizeFieldFormatterPart, (part) => ({ + ...part, + placeholder: + part.type === 'regex' + ? localized(normalizeRegexString(part.placeholder)) + : part.placeholder, + })) ) ) ), }) ); +export function normalizeFieldFormatterPart( + part: SpecToJson> +): SpecToJson> { + const placeholder = + part.type === 'regex' + ? localized(trimRegexString(part.placeholder)) + : part.type === 'year' + ? fieldFormatterTypeMapper.year.placeholder + : part.type === 'numeric' + ? fieldFormatterTypeMapper.numeric.buildPlaceholder(part.size) + : part.placeholder; + const size = fieldFormatterTypesWithForcedSize.has(part.type as 'constant') + ? placeholder.length + : part.size; + return { ...part, placeholder, size }; +} + export const fieldFormatterTypesWithForcedSize = new Set([ 'constant', 'separator', @@ -174,7 +167,7 @@ export const fieldFormatterTypesWithForcedSize = new Set([ /** * Specify 6 expects the regex pattern to start with "/^" and end with "$/" - * because it parts each field part individually. + * because it parses each field part individually. * In Specify 7, we construct a combined regex that parts all field parts at * once. * Thus we do not want the "^" and "$" to be part of the pattern as far as diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx index 26bf0f7aefb..954551f9742 100644 --- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx +++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Format.tsx @@ -88,7 +88,7 @@ export function SchemaConfigFormat({ [`${ field === undefined ? '' - : schemaText.uiFormattersForField({ fieldLabel: field.label }) + : schemaText.fieldFormattersForField({ fieldLabel: field.label }) }`]: formattersForField .map( ({ name, isSystem, value }) => @@ -105,7 +105,7 @@ export function SchemaConfigFormat({ ] as const ) .sort(sortFunction((value) => value[1])), - [resourcesText.uiFormatters()]: otherFormatters + [resourcesText.fieldFormatters()]: otherFormatters .map( ({ name, isSystem, value }) => [ diff --git a/specifyweb/frontend/js_src/lib/localization/schema.ts b/specifyweb/frontend/js_src/lib/localization/schema.ts index 0836fa2df7a..0dd3b6ff0b6 100644 --- a/specifyweb/frontend/js_src/lib/localization/schema.ts +++ b/specifyweb/frontend/js_src/lib/localization/schema.ts @@ -209,7 +209,7 @@ export const schemaText = createDictionary({ 'uk-ua': 'Формат поля', 'de-ch': 'Feldformat', }, - uiFormattersForField: { + fieldFormattersForField: { 'en-us': 'Field Formatters for {fieldLabel:string}', }, formatted: { From 93eefcad6bed395c4f6e4d0efc73766182586269 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 14 Dec 2024 09:59:07 -0800 Subject: [PATCH 19/33] refactor(FieldFormatters): remove duplidate regex normalization logic --- .../js_src/lib/components/FieldFormatters/index.ts | 9 ++------- .../js_src/lib/components/FieldFormatters/spec.ts | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index def5e373178..c419863af10 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -19,7 +19,7 @@ import { error } from '../Errors/assert'; import { load } from '../InitialContext'; import { xmlToSpec } from '../Syncer/xmlUtils'; import type { FieldFormatter, FieldFormatterPart } from './spec'; -import { fieldFormattersSpec } from './spec'; +import { fieldFormattersSpec, trimRegexString } from './spec'; let uiFormatters: IR; export const fetchContext = Promise.all([ @@ -262,16 +262,11 @@ class RegexPart extends Part { public readonly type = 'regex'; public get regex(): LocalizedString { - let pattern: string = this.placeholder; /* * In UiFormatter.getRegex() we are adding ^ and $ as necessary, so trim * them if they were present here */ - if (pattern.startsWith('/')) pattern = pattern.slice(1); - if (pattern.startsWith('^')) pattern = pattern.slice(1); - if (pattern.endsWith('/')) pattern = pattern.slice(0, -1); - if (pattern.endsWith('$')) pattern = pattern.slice(0, -1); - return pattern as LocalizedString; + return trimRegexString(this.placeholder) as LocalizedString; } } diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts index 9aaaea6efc7..028455f853a 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/spec.ts @@ -174,7 +174,7 @@ export const fieldFormatterTypesWithForcedSize = new Set([ * Specify 7 front-end is concerned, but we want it to be part of the pattern * in the .xml to work with Specify 6. */ -function trimRegexString(regexString: string): string { +export function trimRegexString(regexString: string): string { let pattern = regexString; if (pattern.startsWith('/')) pattern = pattern.slice(1); if (pattern.startsWith('^')) pattern = pattern.slice(1); From d2dcc58a55be39365e772a7c8a05ffecace0a110 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 14 Dec 2024 09:59:29 -0800 Subject: [PATCH 20/33] fix(FieldFormatters): prevent CustomElementSelect not closing on click --- .../js_src/lib/components/FieldFormatters/FieldFormatter.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx index ddf760e6927..47dfc8c7620 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx @@ -8,6 +8,7 @@ import { } from '../../utils/parser/definitions'; import type { GetSet, RA } from '../../utils/types'; import { ErrorMessage } from '../Atoms'; +import { className } from '../Atoms/className'; import { Input, Label } from '../Atoms/Form'; import type { AnySchema } from '../DataModel/helperTypes'; import type { SpecifyResource } from '../DataModel/legacyTypes'; @@ -53,7 +54,7 @@ function FieldPicker({ [fieldFormatter.field] ); return fieldFormatter.table === undefined ? null : ( - +
    {schemaText.field()} - +
    ); } From c4318aa082333ff6ad144b3b294c12d735c0b949 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 14 Dec 2024 10:02:23 -0800 Subject: [PATCH 21/33] fix(FieldFormatters): set max field size at 9999 --- .../frontend/js_src/lib/components/FieldFormatters/Parts.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx index 78f7d698c78..e74dc66ad55 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx @@ -134,6 +134,7 @@ function Part({ aria-label={commonText.size()} disabled={enforcePlaceholderSize} isReadOnly={isReadOnly} + max={maxSize} min={1} required value={part.size} @@ -226,6 +227,8 @@ function Part({ ); } +const maxSize = 9999; + function RegexField({ value, onChange: handleChange, From 52365e15303f0d2f59df6b238119cefda396e431 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sat, 14 Dec 2024 10:16:45 -0800 Subject: [PATCH 22/33] revert(localization): delete unused string --- specifyweb/frontend/js_src/lib/localization/resources.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index b42e44fb698..ff72f5f2734 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -892,7 +892,4 @@ export const resourcesText = createDictionary({ pattern: { 'en-us': 'Pattern', }, - parentCogSameAsChild: { - 'en-us': 'A Collection Object Group cannot be a parent to itself', - }, } as const); From b7d3cb0c8dd1c5a65564001a7e9a18def4983a18 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Wed, 25 Dec 2024 15:03:08 -0800 Subject: [PATCH 23/33] fix(FieldFormatter): resolve reported issues Fixes https://github.com/specify/specify7/pull/5075#pullrequestreview-2506791959 --- .../js_src/lib/components/Atoms/Form.tsx | 1 + .../FieldFormatters/FieldFormatter.tsx | 17 +++++++--- .../lib/components/FieldFormatters/Parts.tsx | 6 ++-- .../lib/components/QueryBuilder/Line.tsx | 8 +++-- .../lib/components/SchemaConfig/Format.tsx | 31 ++++++++++--------- .../SchemaConfig/UniquenessRuleScope.tsx | 6 ++-- .../components/WbPlanView/LineComponents.tsx | 4 +-- .../WbPlanView/MapperComponents.tsx | 4 +-- .../lib/components/WbPlanView/navigator.ts | 4 +-- .../js_src/lib/localization/resources.ts | 20 ++++++------ 10 files changed, 58 insertions(+), 43 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/Atoms/Form.tsx b/specifyweb/frontend/js_src/lib/components/Atoms/Form.tsx index cf0f6294094..1bd5edbfb99 100644 --- a/specifyweb/frontend/js_src/lib/components/Atoms/Form.tsx +++ b/specifyweb/frontend/js_src/lib/components/Atoms/Form.tsx @@ -257,6 +257,7 @@ export const Input = { props.onChange?.(event); }, readOnly: isReadOnly, + ...withPreventWheel(props.onWheel), } ), Float: wrap< diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx index 47dfc8c7620..87627115e0f 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/FieldFormatter.tsx @@ -12,7 +12,8 @@ import { className } from '../Atoms/className'; import { Input, Label } from '../Atoms/Form'; import type { AnySchema } from '../DataModel/helperTypes'; import type { SpecifyResource } from '../DataModel/legacyTypes'; -import type { LiteralField } from '../DataModel/specifyField'; +import type { LiteralField, Relationship } from '../DataModel/specifyField'; +import { genericTables } from '../DataModel/tables'; import { softError } from '../Errors/assert'; import { ResourceMapping } from '../Formatters/Components'; import { ResourcePreview } from '../Formatters/Preview'; @@ -84,9 +85,17 @@ function FieldPicker({ const excludeNonLiteral = (mappingData: MappingLineData): MappingLineData => ({ ...mappingData, fieldsData: Object.fromEntries( - Object.entries(mappingData.fieldsData).filter( - ([_name, fieldData]) => fieldData.tableName === undefined - ) + Object.entries(mappingData.fieldsData).filter(([name, fieldData]) => { + if (fieldData.tableName !== undefined) return false; + const field: LiteralField | Relationship | undefined = + mappingData.tableName === undefined + ? undefined + : genericTables[mappingData.tableName].field[name]; + if (field === undefined) return false; + return ( + !field.isReadOnly && !field.isVirtual && !field.overrides.isReadOnly + ); + }) ), }); diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx index e74dc66ad55..84d2d7cf6f2 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx @@ -49,7 +49,7 @@ export function FieldFormatterParts({ {resourcesText.type()} {commonText.size()} - {resourcesText.hint()} + {resourcesText.value()} @@ -150,7 +150,7 @@ function Part({ handleFormatted( name, + // Select first option if there is such typeof values === 'object' - ? (Object.values(values)[0][0][0]! ?? null) + ? (Object.values(values).at(0)?.at(0)?.at(0) ?? + Object.values(values).at(1)?.at(0)?.at(0) ?? + null) : null ) } @@ -312,6 +315,9 @@ function PickListEditing({ ); } +const overlayPrefix = '/specify/overlay/resources/app-resource/'; +const fullScreenPrefix = '/specify/resources/app-resource/'; + function WebLinkEditing({ value, schemaData, @@ -322,32 +328,29 @@ function WebLinkEditing({ const index = schemaData.webLinks.find(({ name }) => name === value)?.index; const resourceId = appResourceIds.WebLinks; const navigate = useNavigate(); + return typeof resourceId === 'number' ? ( <> {typeof index === 'number' && ( { event.preventDefault(); - navigate( - `/specify/overlay/resources/app-resource/${resourceId}/web-link/${index}/` - ); + navigate(`${overlayPrefix}${resourceId}/web-link/${index}`); }} /> )} { event.preventDefault(); - navigate( - `/specify/overlay/resources/app-resource/${resourceId}/web-link/` - ); + navigate(`${overlayPrefix}${resourceId}/web-link`); }} /> @@ -370,29 +373,29 @@ function FieldFormatterEditing({ const navigate = useNavigate(); if (resourceId === undefined) return null; - const baseUrl = `/specify/resources/app-resource/${resourceId}/field-formatters/${table.name}/`; + const commonUrl = `${resourceId}/field-formatters/${table.name}`; return ( <> {typeof index === 'number' && ( { event.preventDefault(); - navigate(`${baseUrl}${index}/`); + navigate(`${overlayPrefix}${commonUrl}/${index}`); }} /> )} { event.preventDefault(); - navigate(baseUrl); + navigate(`${overlayPrefix}${commonUrl}`); }} /> diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/UniquenessRuleScope.tsx b/specifyweb/frontend/js_src/lib/components/SchemaConfig/UniquenessRuleScope.tsx index e37f7e78178..2eadb80dd17 100644 --- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/UniquenessRuleScope.tsx +++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/UniquenessRuleScope.tsx @@ -13,7 +13,7 @@ import type { SpecifyTable } from '../DataModel/specifyTable'; import { strictGetTable } from '../DataModel/tables'; import type { Tables } from '../DataModel/types'; import type { UniquenessRule } from '../DataModel/uniquenessRules'; -import type { HtmlGeneratorFieldData } from '../WbPlanView/LineComponents'; +import type { MapperComponentData } from '../WbPlanView/LineComponents'; import { getMappingLineProps } from '../WbPlanView/LineComponents'; import { MappingView } from '../WbPlanView/MapperComponents'; import type { MappingLineData } from '../WbPlanView/navigator'; @@ -35,7 +35,7 @@ export function UniquenessRuleScope({ : rule.scopes[0].split(djangoLookupSeparator) ); - const databaseScopeData: Readonly> = { + const databaseScopeData: Readonly> = { database: { isDefault: true, isEnabled: true, @@ -46,7 +46,7 @@ export function UniquenessRuleScope({ const getValidScopeRelationships = ( table: SpecifyTable - ): Readonly> => + ): Readonly> => Object.fromEntries( table.relationships .filter( diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/LineComponents.tsx b/specifyweb/frontend/js_src/lib/components/WbPlanView/LineComponents.tsx index 248a8f36e37..40075539a39 100644 --- a/specifyweb/frontend/js_src/lib/components/WbPlanView/LineComponents.tsx +++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/LineComponents.tsx @@ -28,7 +28,7 @@ import { import type { AutoMapperSuggestion } from './Mapper'; import type { MappingLineData } from './navigator'; -export type HtmlGeneratorFieldData = { +export type MapperComponentData = { readonly optionLabel: JSX.Element | string; readonly title?: LocalizedString; readonly isEnabled?: boolean; @@ -49,7 +49,7 @@ type MappingLineBaseProps = { }; export type MappingElementProps = { - readonly fieldsData: IR; + readonly fieldsData: IR; } & ( | Omit | (Omit & { diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/MapperComponents.tsx b/specifyweb/frontend/js_src/lib/components/WbPlanView/MapperComponents.tsx index 80f0044c554..82ff6749b18 100644 --- a/specifyweb/frontend/js_src/lib/components/WbPlanView/MapperComponents.tsx +++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/MapperComponents.tsx @@ -19,7 +19,7 @@ import { TableIcon } from '../Molecules/TableIcon'; import { userPreferences } from '../Preferences/userPreferences'; import { ButtonWithConfirmation } from './Components'; import type { - HtmlGeneratorFieldData, + MapperComponentData, MappingElementProps, } from './LineComponents'; import { MappingPathComponent } from './LineComponents'; @@ -230,7 +230,7 @@ export function mappingOptionsMenu({ readonly onChangeMatchBehaviour: (matchBehavior: MatchBehaviors) => void; readonly onToggleAllowNulls: (allowNull: boolean) => void; readonly onChangeDefaultValue: (defaultValue: string | null) => void; -}): IR { +}): IR { return { matchBehavior: { optionLabel: ( diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/navigator.ts b/specifyweb/frontend/js_src/lib/components/WbPlanView/navigator.ts index 1dd5a3291a1..4c8ad3a5d9d 100644 --- a/specifyweb/frontend/js_src/lib/components/WbPlanView/navigator.ts +++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/navigator.ts @@ -20,7 +20,7 @@ import { getTreeDefinitions, isTreeTable } from '../InitialContext/treeRanks'; import { hasTablePermission, hasTreeAccess } from '../Permissions/helpers'; import type { CustomSelectSubtype } from './CustomSelectElement'; import type { - HtmlGeneratorFieldData, + MapperComponentData, MappingElementProps, } from './LineComponents'; import type { MappingPath } from './Mapper'; @@ -284,7 +284,7 @@ export function getMappingLineData({ const commitInstanceData = ( customSelectSubtype: CustomSelectSubtype, table: SpecifyTable, - fieldsData: RA + fieldsData: RA ): void => void internalState.mappingLineData.push({ customSelectSubtype, diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index ff72f5f2734..e4174d726b3 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -155,9 +155,9 @@ export const resourcesText = createDictionary({ }, fieldFormattersDescription: { 'en-us': ` - Field formatter controls how data for a specific table field is - shown in query results, exports, and on the form. It determines - autonumbering and individual parts that make up the field. + The “Field Format” controls how data for a specific table field is + displayed in query results, exports, and forms. It manages autonumbering + and the composition of various parts that define the field. `, }, dataObjectFormatters: { @@ -855,13 +855,13 @@ export const resourcesText = createDictionary({ nonConformingInline: { 'en-us': '(non-conforming)', }, - hint: { - 'en-us': 'Hint', - 'de-ch': 'Hinweis', - 'es-es': 'Sugerencia', - 'fr-fr': 'Indice', - 'ru-ru': 'Подсказка', - 'uk-ua': 'Підказка', + value: { + 'en-us': 'Value', + 'de-ch': 'Wert', + 'es-es': 'Valor', + 'fr-fr': 'Valeur', + 'ru-ru': 'Значение', + 'uk-ua': 'Значення', }, constant: { 'en-us': 'Constant', From 3d4b5113a38df4d5ad3fb9a78ad8b7c4df6a0c6d Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sun, 19 Jan 2025 19:56:04 -0800 Subject: [PATCH 24/33] fix(FieldFormatters): clear stale regex validation error --- .../frontend/js_src/lib/components/FieldFormatters/Parts.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx index 84d2d7cf6f2..f9e1753b866 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx @@ -156,7 +156,7 @@ function Part({ required value={ part.type === 'regex' - ? part.regexPlaceholder ?? '' + ? (part.regexPlaceholder ?? '') : part.placeholder } onValueChange={(placeholder): void => @@ -251,6 +251,7 @@ function RegexField({ // eslint-disable-next-line require-unicode-regexp void new RegExp(target.value); handleChange(target.value as LocalizedString); + target.setCustomValidity(''); } catch (error: unknown) { target.setCustomValidity(String(error)); target.reportValidity(); From b95c145a07a6322d539d5683afca9dda2afd178b Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sun, 19 Jan 2025 20:07:12 -0800 Subject: [PATCH 25/33] fix(FieldFormatters): disable auto-numering in UI where not allowed by back-end --- .../frontend/js_src/lib/components/DataModel/specifyTable.ts | 2 +- .../frontend/js_src/lib/components/FieldFormatters/Parts.tsx | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/specifyTable.ts b/specifyweb/frontend/js_src/lib/components/DataModel/specifyTable.ts index 4b957207d1c..5a818256f6b 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/specifyTable.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/specifyTable.ts @@ -458,7 +458,7 @@ export class SpecifyTable { * * I.e, table can be scoped to collection using a "collectionMemberId" field * (which is not a relationship - sad). Back-end looks at that relationship - * for scoping inconsistenly. Front-end does not look at all. + * for scoping inconsistently. Front-end does not look at all. */ public getScopingRelationship(): Relationship | undefined { this.scopingRelationship ??= diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx index f9e1753b866..634d135b81d 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/Parts.tsx @@ -13,6 +13,7 @@ import { className } from '../Atoms/className'; import { Input, Label, Select } from '../Atoms/Form'; import { icons } from '../Atoms/Icons'; import { ReadOnlyContext } from '../Core/Contexts'; +import type { SpecifyTable } from '../DataModel/specifyTable'; import { fieldFormatterLocalization } from '.'; import type { FieldFormatter, FieldFormatterPart } from './spec'; import { @@ -62,6 +63,7 @@ export function FieldFormatterParts({ part, (part): void => setParts(replaceItem(parts, index, part)), ]} + table={fieldFormatter.table} onRemove={(): void => setParts(removeItem(parts, index))} /> ))} @@ -96,9 +98,11 @@ export function FieldFormatterParts({ function Part({ part: [part, handleChange], onRemove: handleRemove, + table, }: { readonly part: GetSet; readonly onRemove: () => void; + readonly table: SpecifyTable | undefined; }): JSX.Element { const isReadOnly = React.useContext(ReadOnlyContext); @@ -175,6 +179,7 @@ function Part({ handleChange({ From c8acc261f884e0acdca4d08cf241c2bee41f8b59 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Sun, 26 Jan 2025 16:53:25 -0800 Subject: [PATCH 26/33] fix(FieldFormatters): force-wrap long preview lines --- .../frontend/js_src/lib/components/Formatters/Preview.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx b/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx index 7c5ceac76a6..f6ee996461f 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx +++ b/specifyweb/frontend/js_src/lib/components/Formatters/Preview.tsx @@ -97,7 +97,9 @@ export function useResourcePreview( setResources(removeItem(resources, index)), }} /> - {output} + + {output} +
    ); }) From 37ca347b4918629e23bf3526fdd3214bacd8fa53 Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Mon, 27 Jan 2025 01:00:45 +0000 Subject: [PATCH 27/33] Lint code with ESLint and Prettier Triggered by c8acc261f884e0acdca4d08cf241c2bee41f8b59 on branch refs/heads/field-editor --- .../js_src/lib/components/RouterCommands/SwitchCollection.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/RouterCommands/SwitchCollection.tsx b/specifyweb/frontend/js_src/lib/components/RouterCommands/SwitchCollection.tsx index aeb54a2407e..ef201ec09d8 100644 --- a/specifyweb/frontend/js_src/lib/components/RouterCommands/SwitchCollection.tsx +++ b/specifyweb/frontend/js_src/lib/components/RouterCommands/SwitchCollection.tsx @@ -42,8 +42,8 @@ export function SwitchCollectionCommand(): null { body: collectionId!.toString(), errorMode: 'dismissible', }) - .then(clearAllCache) - .then(() => globalThis.location.replace(nextUrl)), + .then(clearAllCache) + .then(() => globalThis.location.replace(nextUrl)), [collectionId, nextUrl] ), true From 7ae4caf289f72d60534793f84a7bdc2c792fc9b1 Mon Sep 17 00:00:00 2001 From: alesan99 Date: Mon, 29 Sep 2025 09:17:35 -0500 Subject: [PATCH 28/33] fix(FieldFormatters): fix merge conflicts --- .../lib/components/FieldFormatters/index.ts | 40 ++----------------- .../lib/components/Forms/BulkCarryForward.tsx | 10 ++--- .../QueryBuilder/QueryLineFilters.tsx | 4 +- 3 files changed, 10 insertions(+), 44 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts index d78236268a8..2726092a534 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/index.ts @@ -28,44 +28,12 @@ export const fetchContext = Promise.all([ ]).then(([formatters]) => { uiFormatters = Object.fromEntries( filterArray( -<<<<<<< HEAD xmlToSpec(formatters, fieldFormattersSpec()).fieldFormatters.map( (formatter, index) => { const resolvedFormatter = resolveFieldFormatter(formatter, index); return resolvedFormatter === undefined ? undefined : [formatter.name, resolvedFormatter]; -======= - xmlToSpec(formatters, fieldFormattersSpec()).formatters.map( - (formatter) => { - let resolvedFormatter; - if (typeof formatter.external === 'string') { - if ( - parseJavaClassName(formatter.external) === - 'CatalogNumberUIFieldFormatter' - ) - resolvedFormatter = new CatalogNumberNumeric(); - else return undefined; - } else { - const fields = filterArray( - formatter.fields.map((field) => - typeof field.type === 'string' - ? new formatterTypeMapper[field.type](field) - : undefined - ) - ); - resolvedFormatter = new UiFormatter( - formatter.isSystem, - formatter.title ?? formatter.name, - fields, - formatter.table, - formatter.field, - formatter.name - ); - } - - return [formatter.name, resolvedFormatter]; ->>>>>>> origin/main } ) ) @@ -99,6 +67,7 @@ export function resolveFieldFormatter( parts, formatter.table, formatter.field, + formatter.name, index ); } @@ -113,11 +82,8 @@ export class UiFormatter { public readonly table: SpecifyTable | undefined, // The field which this formatter is formatting public readonly field: LiteralField | undefined, -<<<<<<< HEAD + public readonly name: string, public readonly originalIndex = 0 -======= - public readonly name: string ->>>>>>> origin/main ) {} public get defaultValue(): string { @@ -161,7 +127,7 @@ export class UiFormatter { } public canAutoIncrement(): boolean { - return this.fields.some((field) => field.autoIncrement); + return this.parts.some((part) => part.autoIncrement); } public format(value: string): LocalizedString | undefined { diff --git a/specifyweb/frontend/js_src/lib/components/Forms/BulkCarryForward.tsx b/specifyweb/frontend/js_src/lib/components/Forms/BulkCarryForward.tsx index 1dafe541205..c3530490d66 100644 --- a/specifyweb/frontend/js_src/lib/components/Forms/BulkCarryForward.tsx +++ b/specifyweb/frontend/js_src/lib/components/Forms/BulkCarryForward.tsx @@ -4,7 +4,7 @@ import { commonText } from '../../localization/common'; import { formsText } from '../../localization/forms'; import { ajax } from '../../utils/ajax'; import { - formatterToParser, + fieldFormatterToParser, getValidationAttributes, } from '../../utils/parser/definitions'; import type { RA } from '../../utils/types'; @@ -78,7 +78,7 @@ function useBulkCarryForwardRange( const formatter = field.getUiFormatter(resource); const canAutoNumberFormatter = formatter?.canAutoIncrement() ?? false; const parser = - formatter === undefined ? undefined : formatterToParser(field, formatter); + formatter === undefined ? undefined : fieldFormatterToParser(field, formatter); const [carryForwardRangeEnd, setCarryForwardRangeEnd] = React.useState(''); @@ -173,7 +173,7 @@ function useBulkCarryForwardRange( aria-label={formsText.bulkCarryForwardRangeStart()} className="!w-fit" isReadOnly - placeholder={formatter.valueOrWild()} + placeholder={formatter.defaultValue} value={resource.get('catalogNumber') ?? ''} width={field.datamodelDefinition.length} /> @@ -181,7 +181,7 @@ function useBulkCarryForwardRange( aria-label={formsText.bulkCarryForwardRangeEnd()} className="!w-fit" {...getValidationAttributes(parser)} - placeholder={formatter.valueOrWild()} + placeholder={formatter.defaultValue} value={carryForwardRangeEnd} onValueChange={(value): void => setCarryForwardRangeEnd(value)} /> @@ -225,7 +225,7 @@ function useBulkCarryForwardCount( const clones = await Promise.all( Array.from({ length: carryForwardAmount }, async () => { const clonedResource = await resource.clone(false, true); - clonedResource.set(field.name, formatter.valueOrWild() as never); + clonedResource.set(field.name, formatter.defaultValue as never); return clonedResource; }) ); diff --git a/specifyweb/frontend/js_src/lib/components/QueryBuilder/QueryLineFilters.tsx b/specifyweb/frontend/js_src/lib/components/QueryBuilder/QueryLineFilters.tsx index 1ac01cd3197..a5a3475ff24 100644 --- a/specifyweb/frontend/js_src/lib/components/QueryBuilder/QueryLineFilters.tsx +++ b/specifyweb/frontend/js_src/lib/components/QueryBuilder/QueryLineFilters.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { commonText } from '../../localization/common'; -import { formatterToParser } from '../../utils/parser/definitions'; +import { fieldFormatterToParser } from '../../utils/parser/definitions'; import type { RA } from '../../utils/types'; import { Select } from '../Atoms/Form'; import { genericTables } from '../DataModel/tables'; @@ -145,7 +145,7 @@ function QueryLineFilterWrapper({ const parser = (terminatingField === undefined || fieldFormatter === undefined ? undefined - : formatterToParser(terminatingField, fieldFormatter)) ?? + : fieldFormatterToParser(terminatingField, fieldFormatter)) ?? fieldMeta.parser; return ( From 24b6e9a1d0e95562398e58120094a9f39fb6c40e Mon Sep 17 00:00:00 2001 From: alesan99 Date: Mon, 29 Sep 2025 09:37:05 -0500 Subject: [PATCH 29/33] fix(FieldFormatters): update tests --- .../__snapshots__/index.test.ts.snap | 424 +++++++++--------- .../lib/components/Formatters/Fields.tsx | 4 +- 2 files changed, 226 insertions(+), 202 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap index a99a79fd9d3..83890545b8f 100644 --- a/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap +++ b/specifyweb/frontend/js_src/lib/components/FieldFormatters/__tests__/__snapshots__/index.test.ts.snap @@ -4,542 +4,566 @@ exports[`field formatters are fetched and parsed correctly 1`] = ` { "AccessionNumber": UiFormatter { "field": "[literalField Accession.accessionNumber]", - "fields": [ - YearField { + "isSystem": true, + "name": "AccessionNumber", + "originalIndex": 0, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - AlphaNumberField { + AlphaNumberPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "AA", + "regexPlaceholder": undefined, "size": 2, - "type": "alphanumeric", - "value": "AA", + "type": "alpha", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, - "name": "AccessionNumber", "table": "[table Accession]", "title": "AccessionNumber", }, "AccessionNumberByYear": UiFormatter { "field": "[literalField Accession.accessionNumber]", - "fields": [ - YearField { + "isSystem": true, + "name": "AccessionNumberByYear", + "originalIndex": 1, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - AlphaNumberField { + AlphaNumberPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "AA", + "regexPlaceholder": undefined, "size": 2, - "type": "alphanumeric", - "value": "AA", + "type": "alpha", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - AlphaNumberField { + AlphaNumberPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "AAA", + "regexPlaceholder": undefined, "size": 3, - "type": "alphanumeric", - "value": "AAA", + "type": "alpha", }, ], - "isSystem": true, - "name": "AccessionNumberByYear", "table": "[table Accession]", "title": "AccessionNumberByYear", }, "AccessionStringFormatter": UiFormatter { "field": "[literalField Accession.accessionNumber]", - "fields": [ - AlphaNumberField { + "isSystem": true, + "name": "AccessionStringFormatter", + "originalIndex": 2, + "parts": [ + AlphaNumberPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "AAAAAAAAAA", + "regexPlaceholder": undefined, "size": 10, - "type": "alphanumeric", - "value": "AAAAAAAAAA", + "type": "alpha", }, ], - "isSystem": true, - "name": "AccessionStringFormatter", "table": "[table Accession]", "title": "AccessionStringFormatter", }, "CatalogNumber": UiFormatter { "field": "[literalField CollectionObject.catalogNumber]", - "fields": [ - YearField { + "isSystem": false, + "name": "CatalogNumber", + "originalIndex": 3, + "parts": [ + YearPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "######", + "regexPlaceholder": undefined, "size": 6, "type": "numeric", - "value": "######", }, ], - "isSystem": false, - "name": "CatalogNumber", "table": "[table CollectionObject]", "title": "CatalogNumber", }, "CatalogNumberAlphaNumByYear": UiFormatter { "field": "[literalField CollectionObject.catalogNumber]", - "fields": [ - YearField { + "isSystem": false, + "name": "CatalogNumberAlphaNumByYear", + "originalIndex": 5, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "######", + "regexPlaceholder": undefined, "size": 6, "type": "numeric", - "value": "######", }, ], - "isSystem": false, - "name": "CatalogNumberAlphaNumByYear", "table": "[table CollectionObject]", "title": "CatalogNumberAlphaNumByYear", }, "CatalogNumberNumeric": CatalogNumberNumeric { "field": "[literalField CollectionObject.catalogNumber]", - "fields": [ + "isSystem": true, + "name": "CatalogNumberNumeric", + "originalIndex": 0, + "parts": [ CatalogNumberNumericField { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "#########", + "regexPlaceholder": undefined, "size": 9, "type": "numeric", - "value": "#########", }, ], - "isSystem": true, - "name": "CatalogNumberNumeric", "table": "[table CollectionObject]", "title": "Catalog Number Numeric", }, "CatalogNumberNumericRegex": UiFormatter { "field": "[literalField CollectionObject.catalogNumber]", - "fields": [ - RegexField { + "isSystem": false, + "name": "CatalogNumberNumericRegex", + "originalIndex": 4, + "parts": [ + RegexPart { "autoIncrement": false, "byYear": false, - "pattern": "####[-A]", + "placeholder": "[0-9]{4}(-[A-Z])?", + "regexPlaceholder": "####[-A]", "size": 1, "type": "regex", - "value": "[0-9]{4}(-[A-Z])?", }, ], - "isSystem": false, - "name": "CatalogNumberNumericRegex", "table": "[table CollectionObject]", "title": "CatalogNumberNumericRegex", }, "Date": UiFormatter { "field": undefined, - "fields": [], "isSystem": true, "name": "Date", + "originalIndex": 8, + "parts": [], "table": undefined, "title": "Date", }, "DeaccessionNumber": UiFormatter { "field": "[literalField Deaccession.deaccessionNumber]", - "fields": [ - YearField { + "isSystem": true, + "name": "DeaccessionNumber", + "originalIndex": 9, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - AlphaNumberField { + AlphaNumberPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "AA", + "regexPlaceholder": undefined, "size": 2, - "type": "alphanumeric", - "value": "AA", + "type": "alpha", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, - "name": "DeaccessionNumber", "table": "[table Deaccession]", "title": "DeaccessionNumber", }, "GiftNumber": UiFormatter { "field": "[literalField Gift.giftNumber]", - "fields": [ - YearField { + "isSystem": true, + "name": "GiftNumber", + "originalIndex": 10, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, - "name": "GiftNumber", "table": "[table Gift]", "title": "GiftNumber", }, "InfoRequestNumber": UiFormatter { "field": "[literalField InfoRequest.infoReqNumber]", - "fields": [ - YearField { + "isSystem": true, + "name": "InfoRequestNumber", + "originalIndex": 11, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, - "name": "InfoRequestNumber", "table": "[table InfoRequest]", "title": "InfoRequestNumber", }, "KUITeach": CatalogNumberNumeric { "field": "[literalField CollectionObject.catalogNumber]", - "fields": [ + "isSystem": true, + "name": "CatalogNumberNumeric", + "originalIndex": 0, + "parts": [ CatalogNumberNumericField { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "#########", + "regexPlaceholder": undefined, "size": 9, "type": "numeric", - "value": "#########", }, ], - "isSystem": true, - "name": "CatalogNumberNumeric", "table": "[table CollectionObject]", "title": "Catalog Number Numeric", }, "LoanNumber": UiFormatter { "field": "[literalField Loan.loanNumber]", - "fields": [ - YearField { + "isSystem": true, + "name": "LoanNumber", + "originalIndex": 13, + "parts": [ + YearPart { "autoIncrement": false, "byYear": true, - "pattern": undefined, + "placeholder": "YEAR", + "regexPlaceholder": undefined, "size": 4, "type": "year", - "value": "YEAR", }, - SeparatorField { + SeparatorPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "-", + "regexPlaceholder": undefined, "size": 1, - "type": "separator", - "value": "-", + "type": "constant", }, - NumericField { + NumericPart { "autoIncrement": true, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, - "name": "LoanNumber", "table": "[table Loan]", "title": "LoanNumber", }, "NumericBigDecimal": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "name": "NumericBigDecimal", + "originalIndex": 14, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "###############", + "regexPlaceholder": undefined, "size": 15, "type": "numeric", - "value": "###############", }, ], - "isSystem": true, - "name": "NumericBigDecimal", "table": undefined, "title": "NumericBigDecimal", }, "NumericByte": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "name": "NumericByte", + "originalIndex": 15, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "###", + "regexPlaceholder": undefined, "size": 3, "type": "numeric", - "value": "###", }, ], - "isSystem": true, - "name": "NumericByte", "table": undefined, "title": "NumericByte", }, "NumericDouble": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "name": "NumericDouble", + "originalIndex": 16, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "##########", + "regexPlaceholder": undefined, "size": 10, "type": "numeric", - "value": "##########", }, ], - "isSystem": true, - "name": "NumericDouble", "table": undefined, "title": "NumericDouble", }, "NumericFloat": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "name": "NumericFloat", + "originalIndex": 17, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "##########", + "regexPlaceholder": undefined, "size": 10, "type": "numeric", - "value": "##########", }, ], - "isSystem": true, - "name": "NumericFloat", "table": undefined, "title": "NumericFloat", }, "NumericInteger": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "name": "NumericInteger", + "originalIndex": 18, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "##########", + "regexPlaceholder": undefined, "size": 10, "type": "numeric", - "value": "##########", }, ], - "isSystem": true, - "name": "NumericInteger", "table": undefined, "title": "NumericInteger", }, "NumericLong": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "name": "NumericLong", + "originalIndex": 19, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "##########", + "regexPlaceholder": undefined, "size": 10, "type": "numeric", - "value": "##########", }, ], - "isSystem": true, - "name": "NumericLong", "table": undefined, "title": "NumericLong", }, "NumericShort": UiFormatter { "field": undefined, - "fields": [ - NumericField { + "isSystem": true, + "name": "NumericShort", + "originalIndex": 20, + "parts": [ + NumericPart { "autoIncrement": false, "byYear": false, - "pattern": undefined, + "placeholder": "#####", + "regexPlaceholder": undefined, "size": 5, "type": "numeric", - "value": "#####", }, ], - "isSystem": true, - "name": "NumericShort", "table": undefined, "title": "NumericShort", }, "PartialDate": UiFormatter { "field": undefined, - "fields": [], "isSystem": false, "name": "PartialDate", + "originalIndex": 21, + "parts": [], "table": undefined, "title": "PartialDate", }, "PartialDateMonth": UiFormatter { "field": undefined, - "fields": [], "isSystem": false, "name": "PartialDateMonth", + "originalIndex": 22, + "parts": [], "table": undefined, "title": "PartialDateMonth", }, "PartialDateYear": UiFormatter { "field": undefined, - "fields": [], "isSystem": false, "name": "PartialDateYear", + "originalIndex": 23, + "parts": [], "table": undefined, "title": "PartialDateYear", }, "SearchDate": UiFormatter { "field": undefined, - "fields": [], "isSystem": true, "name": "SearchDate", + "originalIndex": 24, + "parts": [], "table": undefined, "title": "SearchDate", }, } -`; \ No newline at end of file +`; diff --git a/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx b/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx index f131d1ebc4a..acb6b41f016 100644 --- a/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx +++ b/specifyweb/frontend/js_src/lib/components/Formatters/Fields.tsx @@ -19,7 +19,7 @@ import type { CustomSelectElementPropsOpen, } from '../WbPlanView/CustomSelectElement'; import { CustomSelectElement } from '../WbPlanView/CustomSelectElement'; -import type { HtmlGeneratorFieldData } from '../WbPlanView/LineComponents'; +import type { MapperComponentData } from '../WbPlanView/LineComponents'; import { relationshipIsToMany } from '../WbPlanView/mappingHelpers'; import { FormattersPickList, @@ -368,7 +368,7 @@ function fieldOptionsMenu({ readonly isReadOnly: boolean; readonly columnOptions: FormatterFieldOptions; readonly onToggleTrimZeros: (trimZeros: boolean) => void; -}): IR { +}): IR { return { trimZeros: { optionLabel: ( From bd0ed80af15b6ce386822ee91abdd6e287d79dfa Mon Sep 17 00:00:00 2001 From: Grant Fitzsimmons <37256050+grantfitzsimmons@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:30:14 +0000 Subject: [PATCH 30/33] Lint code with ESLint and Prettier Triggered by 6fa1871f032af4a74e49a0ee8faa6300038c0a28 on branch refs/heads/field-editor --- .../lib/components/Forms/BulkCarryForward.tsx | 4 +- .../js_src/lib/localization/backEnd.ts | 886 ++--- .../js_src/lib/localization/common.ts | 1282 +++---- .../js_src/lib/localization/preferences.ts | 3302 ++++++++--------- .../frontend/js_src/lib/localization/query.ts | 1508 ++++---- .../js_src/lib/localization/resources.ts | 1536 ++++---- 6 files changed, 4260 insertions(+), 4258 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/Forms/BulkCarryForward.tsx b/specifyweb/frontend/js_src/lib/components/Forms/BulkCarryForward.tsx index 02af8838ef1..c9eb46590bb 100644 --- a/specifyweb/frontend/js_src/lib/components/Forms/BulkCarryForward.tsx +++ b/specifyweb/frontend/js_src/lib/components/Forms/BulkCarryForward.tsx @@ -79,7 +79,9 @@ function useBulkCarryForwardRange( const formatter = field.getUiFormatter(resource); const canAutoNumberFormatter = formatter?.canAutoIncrement() ?? false; const parser = - formatter === undefined ? undefined : fieldFormatterToParser(field, formatter); + formatter === undefined + ? undefined + : fieldFormatterToParser(field, formatter); const [carryForwardRangeEnd, setCarryForwardRangeEnd] = React.useState(''); diff --git a/specifyweb/frontend/js_src/lib/localization/backEnd.ts b/specifyweb/frontend/js_src/lib/localization/backEnd.ts index 03527164f1a..38156d0bdd7 100644 --- a/specifyweb/frontend/js_src/lib/localization/backEnd.ts +++ b/specifyweb/frontend/js_src/lib/localization/backEnd.ts @@ -5,624 +5,624 @@ * @module */ -import { createDictionary } from "./utils"; +import { createDictionary } from './utils'; // Refer to "Guidelines for Programmers" in ./README.md before editing this file export const backEndText = createDictionary({ failedParsingBoolean: { - "en-us": 'value "{value:string}" not resolvable to True or False', - "ru-ru": 'значение "{value:string}" не разрешается to True or False', - "es-es": + 'en-us': 'value "{value:string}" not resolvable to True or False', + 'ru-ru': 'значение "{value:string}" не разрешается to True or False', + 'es-es': 'el valor "{value:string}" no se puede resolver en Verdadero o Falso', - "fr-fr": 'Valeur "{value:string}" non résolvable en Vrai ou Faux', - "uk-ua": 'значення "{value:string}" не являється "True" або "False"', - "de-ch": - "Wert „{value:string}“ kann nicht in „Wahr“ oder „Falsch“ aufgelöst werden", - "pt-br": + 'fr-fr': 'Valeur "{value:string}" non résolvable en Vrai ou Faux', + 'uk-ua': 'значення "{value:string}" не являється "True" або "False"', + 'de-ch': + 'Wert „{value:string}“ kann nicht in „Wahr“ oder „Falsch“ aufgelöst werden', + 'pt-br': 'valor "{value:string}" não pode ser resolvido como Verdadeiro ou Falso', }, failedParsingDecimal: { - "en-us": 'value "{value:string}" is not a valid decimal value', - "ru-ru": 'значение "{value:string}" не является допустимым чеслом', - "es-es": 'El valor "{value:string}" no es un valor decimal válido', - "fr-fr": 'valeur "{value:string}" n\'est pas une valeur décimale valide', - "uk-ua": 'значення "{value:string}" не є дійсним цілим числом', - "de-ch": '"{value:string}" ist kein gültiger Dezimalwert', - "pt-br": 'o valor "{value:string}" não é um valor decimal válido', + 'en-us': 'value "{value:string}" is not a valid decimal value', + 'ru-ru': 'значение "{value:string}" не является допустимым чеслом', + 'es-es': 'El valor "{value:string}" no es un valor decimal válido', + 'fr-fr': 'valeur "{value:string}" n\'est pas une valeur décimale valide', + 'uk-ua': 'значення "{value:string}" не є дійсним цілим числом', + 'de-ch': '"{value:string}" ist kein gültiger Dezimalwert', + 'pt-br': 'o valor "{value:string}" não é um valor decimal válido', }, failedParsingFloat: { - "en-us": 'value "{value:string}" is not a valid floating point value', - "ru-ru": + 'en-us': 'value "{value:string}" is not a valid floating point value', + 'ru-ru': 'значение "{value:string}" не является допустимым числом с плавающей точкой', - "es-es": 'El valor "{value:string}" no es un valor de coma flotante válido', - "fr-fr": + 'es-es': 'El valor "{value:string}" no es un valor de coma flotante válido', + 'fr-fr': 'valeur "{value:string}" n\'est pas une valeur à virgule flottante valide', - "uk-ua": 'значення "{value:string}" не є раціональним числом', - "de-ch": '"{value:string}" ist kein gültiger Gleitkommawert', - "pt-br": + 'uk-ua': 'значення "{value:string}" не є раціональним числом', + 'de-ch': '"{value:string}" ist kein gültiger Gleitkommawert', + 'pt-br': 'o valor "{value:string}" não é um valor de ponto flutuante válido', }, failedParsingPickList: { - "en-us": - "{value:string} is not a legal value in this picklist field.\n\nClick on the arrow to choose among available options.", - "ru-ru": - "{value:string} не является допустимым значением в этом списке.\n\nНажмите на стрелку, чтобы выбрать один из доступных вариантов.", - "es-es": - "{value:string} no es un valor legal en este campo de lista de selección.\n\nHaga clic en la flecha para elegir entre las opciones disponibles.", - "fr-fr": + 'en-us': + '{value:string} is not a legal value in this picklist field.\n\nClick on the arrow to choose among available options.', + 'ru-ru': + '{value:string} не является допустимым значением в этом списке.\n\nНажмите на стрелку, чтобы выбрать один из доступных вариантов.', + 'es-es': + '{value:string} no es un valor legal en este campo de lista de selección.\n\nHaga clic en la flecha para elegir entre las opciones disponibles.', + 'fr-fr': "{value:string} n'est pas une valeur acceptée dans ce champ de liste de sélection.\n\nCliquez sur la flèche pour choisir parmi les options disponibles.", - "uk-ua": - "{value:string} не є допустимим значенням у цьому полі списку вибору.\n\nНатисніть на стрілку, щоб вибрати серед доступних варіантів.", - "de-ch": - "{value:string} ist in diesem Auswahllistenfeld kein zulässiger Wert.\n\nKlicken Sie auf den Pfeil, um zwischen den verfügbaren Optionen zu wählen.", - "pt-br": - "{value:string} não é um valor válido neste campo da lista de opções.\n\nClique na seta para escolher entre as opções disponíveis.", + 'uk-ua': + '{value:string} не є допустимим значенням у цьому полі списку вибору.\n\nНатисніть на стрілку, щоб вибрати серед доступних варіантів.', + 'de-ch': + '{value:string} ist in diesem Auswahllistenfeld kein zulässiger Wert.\n\nKlicken Sie auf den Pfeil, um zwischen den verfügbaren Optionen zu wählen.', + 'pt-br': + '{value:string} não é um valor válido neste campo da lista de opções.\n\nClique na seta para escolher entre as opções disponíveis.', }, failedParsingAgentType: { comment: ` Example: bad agent type: "ab". Expected one of "Person", "Group" or "Organization" `, - "en-us": + 'en-us': 'bad {agentTypeField:string}: "{badType:string}". Expected one of {validTypes:string}', - "ru-ru": + 'ru-ru': 'неверный {agentTypeField:string}: "{badType:string}". Ожидается один из {validTypes:string}', - "es-es": + 'es-es': 'malo {agentTypeField:string}: "{badType:string}". Se esperaba uno de {validTypes:string}', - "fr-fr": + 'fr-fr': '{agentTypeField:string} non valide: "{badType:string}". L\'un des {validTypes:string} est attendu', - "uk-ua": + 'uk-ua': 'поганий {agentTypeField:string}: "{badType:string}". Очікується один із {validTypes:string}', - "de-ch": + 'de-ch': 'fehlerhaft {agentTypeField:string}: "{badType:string}". Erwartet wurde einer von {validTypes:string}', - "pt-br": + 'pt-br': 'ruim {agentTypeField:string}: "{badType:string}". Esperava-se um de {validTypes:string}', }, pickListValueTooLong: { - "en-us": - "value from {pickListTable:string} {pickList:string} longer than the max of {maxLength:number|formatted} for field", - "ru-ru": - "значение из {pickListTable:string} {pickList:string} длиннее максимального значения {maxLength:number|formatted} для поля", - "es-es": - "valor de {pickListTable:string} {pickList:string} más largo que el máximo de {maxLength:number|formatted} para el campo", - "fr-fr": - "valeur de {pickListTable:string} {pickList:string} est plus longue que le maximum de {maxLength:number|formatted} pour le champ", - "uk-ua": - "значення з {pickListTable:string} {pickList:string} довше, ніж максимальне значення {maxLength:number|formatted} для поля", - "de-ch": - "Wert von {pickListTable:string} {pickList:string} länger als das Maximum von {maxLength:number|formatted} für Feld", - "pt-br": - "valor de {pickListTable:string} {pickList:string} maior que o máximo de {maxLength:number|formatted} para o campo", + 'en-us': + 'value from {pickListTable:string} {pickList:string} longer than the max of {maxLength:number|formatted} for field', + 'ru-ru': + 'значение из {pickListTable:string} {pickList:string} длиннее максимального значения {maxLength:number|formatted} для поля', + 'es-es': + 'valor de {pickListTable:string} {pickList:string} más largo que el máximo de {maxLength:number|formatted} para el campo', + 'fr-fr': + 'valeur de {pickListTable:string} {pickList:string} est plus longue que le maximum de {maxLength:number|formatted} pour le champ', + 'uk-ua': + 'значення з {pickListTable:string} {pickList:string} довше, ніж максимальне значення {maxLength:number|formatted} для поля', + 'de-ch': + 'Wert von {pickListTable:string} {pickList:string} länger als das Maximum von {maxLength:number|formatted} für Feld', + 'pt-br': + 'valor de {pickListTable:string} {pickList:string} maior que o máximo de {maxLength:number|formatted} para o campo', }, valueTooLong: { - "en-us": - "value must not have length greater than {maxLength:number|formatted}", - "ru-ru": "значение не должно быть длиннее {maxLength:number|formatted}", - "es-es": - "el valor no debe tener una longitud mayor que {maxLength:number|formatted}", - "fr-fr": - "la valeur ne doit pas avoir une longueur supérieure à {maxLength:number|formatted}", - "uk-ua": - "довжина значення не повинна перевищувати {maxLength:number|formatted}", - "de-ch": - "Der Wert darf nicht länger als {maxLength:number|formatted} sein.", - "pt-br": - "o valor não deve ter comprimento maior que {maxLength:number|formatted}", + 'en-us': + 'value must not have length greater than {maxLength:number|formatted}', + 'ru-ru': 'значение не должно быть длиннее {maxLength:number|formatted}', + 'es-es': + 'el valor no debe tener una longitud mayor que {maxLength:number|formatted}', + 'fr-fr': + 'la valeur ne doit pas avoir une longueur supérieure à {maxLength:number|formatted}', + 'uk-ua': + 'довжина значення не повинна перевищувати {maxLength:number|formatted}', + 'de-ch': + 'Der Wert darf nicht länger als {maxLength:number|formatted} sein.', + 'pt-br': + 'o valor não deve ter comprimento maior que {maxLength:number|formatted}', }, invalidYear: { - "en-us": "date value must contain four digit year: {value:string}", - "ru-ru": - "значение даты должно содержать четырехзначный год: {value:string}", - "es-es": - "el valor de fecha debe contener el año de cuatro dígitos: {value:string}", - "fr-fr": + 'en-us': 'date value must contain four digit year: {value:string}', + 'ru-ru': + 'значение даты должно содержать четырехзначный год: {value:string}', + 'es-es': + 'el valor de fecha debe contener el año de cuatro dígitos: {value:string}', + 'fr-fr': "la valeur de la date doit contenir quatre chiffres pour l'année {value:string}", - "uk-ua": "дата має містити чотири цифри року: {value:string}", - "de-ch": - "Datumswert muss vierstellige Jahreszahlen enthalten: {value:string}", - "pt-br": - "o valor da data deve conter o ano de quatro dígitos: {value:string}", + 'uk-ua': 'дата має містити чотири цифри року: {value:string}', + 'de-ch': + 'Datumswert muss vierstellige Jahreszahlen enthalten: {value:string}', + 'pt-br': + 'o valor da data deve conter o ano de quatro dígitos: {value:string}', }, badDateFormat: { - "en-us": "bad date value: {value:string}. expected: {format:string}", - "ru-ru": - "неверное значение даты: {value:string}. ожидается: {format:string}", - "es-es": - "valor de fecha incorrecto: {value:string}. se esperaba: {format:string}", - "fr-fr": - "valeur de date invalide: {value:string}. {format:string} est attendu", - "uk-ua": - "неправильне значення дати: {value:string}. очікуваний формат: {format:string}", - "de-ch": "Ungültiger Datumswert: {value:string}. Erwartet: {format:string}", - "pt-br": - "valor de data inválido: {value:string}. esperado: {format:string}", + 'en-us': 'bad date value: {value:string}. expected: {format:string}', + 'ru-ru': + 'неверное значение даты: {value:string}. ожидается: {format:string}', + 'es-es': + 'valor de fecha incorrecto: {value:string}. se esperaba: {format:string}', + 'fr-fr': + 'valeur de date invalide: {value:string}. {format:string} est attendu', + 'uk-ua': + 'неправильне значення дати: {value:string}. очікуваний формат: {format:string}', + 'de-ch': 'Ungültiger Datumswert: {value:string}. Erwartet: {format:string}', + 'pt-br': + 'valor de data inválido: {value:string}. esperado: {format:string}', }, coordinateBadFormat: { - "en-us": "bad latitude or longitude value: {value:string}", - "ru-ru": "неверное значение широты или долготы: {value:string}", - "es-es": "valor de latitud o longitud incorrecto: {value:string}", - "fr-fr": 'Nœud [X0X] "[X25X]" vers le parent synonymisé "[X67X]"', - "uk-ua": "неправильне значення широти або довготи: {value:string}", - "de-ch": "falscher Breiten- oder Längengradwert: {value:string}", - "pt-br": "valor incorreto de latitude ou longitude: {value:string}", + 'en-us': 'bad latitude or longitude value: {value:string}', + 'ru-ru': 'неверное значение широты или долготы: {value:string}', + 'es-es': 'valor de latitud o longitud incorrecto: {value:string}', + 'fr-fr': 'Nœud [X0X] "[X25X]" vers le parent synonymisé "[X67X]"', + 'uk-ua': 'неправильне значення широти або довготи: {value:string}', + 'de-ch': 'falscher Breiten- oder Längengradwert: {value:string}', + 'pt-br': 'valor incorreto de latitude ou longitude: {value:string}', }, latitudeOutOfRange: { - "en-us": "latitude must be between -90 and 90. Actual: {value:string}", - "ru-ru": "широта должна быть между -90 и 90. Фактически: {value:string}", - "es-es": "la latitud debe estar entre -90 y 90. Actual: {value:string}", - "fr-fr": - "la latitude doit être comprise entre -90 et 90. Réel : {value:string}", - "uk-ua": "широта має бути між -90 і 90. Поточна: {value:string}", - "de-ch": - "Der Breitengrad muss zwischen -90 und 90 liegen. Tatsächlich: {value:string}", - "pt-br": "a latitude deve estar entre -90 e 90. Real: {value:string}", + 'en-us': 'latitude must be between -90 and 90. Actual: {value:string}', + 'ru-ru': 'широта должна быть между -90 и 90. Фактически: {value:string}', + 'es-es': 'la latitud debe estar entre -90 y 90. Actual: {value:string}', + 'fr-fr': + 'la latitude doit être comprise entre -90 et 90. Réel : {value:string}', + 'uk-ua': 'широта має бути між -90 і 90. Поточна: {value:string}', + 'de-ch': + 'Der Breitengrad muss zwischen -90 und 90 liegen. Tatsächlich: {value:string}', + 'pt-br': 'a latitude deve estar entre -90 e 90. Real: {value:string}', }, longitudeOutOfRange: { - "en-us": "longitude must be between -180 and 180. Actual: {value:string}", - "ru-ru": "долгота должна быть между -180 и 180. Фактически: {value:string}", - "es-es": "la longitud debe estar entre -180 y 180. Actual: {value:string}", - "fr-fr": - "la longitude doit être comprise entre -180 et 180. Réel : {value:string}", - "uk-ua": "довгота має бути між -180 і 180. Поточна: {value:string}", - "de-ch": - "Längengrad muss zwischen -180 und 180 liegen. Tatsächlich: {value:string}", - "pt-br": "a longitude deve estar entre -180 e 180. Real: {value:string}", + 'en-us': 'longitude must be between -180 and 180. Actual: {value:string}', + 'ru-ru': 'долгота должна быть между -180 и 180. Фактически: {value:string}', + 'es-es': 'la longitud debe estar entre -180 y 180. Actual: {value:string}', + 'fr-fr': + 'la longitude doit être comprise entre -180 et 180. Réel : {value:string}', + 'uk-ua': 'довгота має бути між -180 і 180. Поточна: {value:string}', + 'de-ch': + 'Längengrad muss zwischen -180 und 180 liegen. Tatsächlich: {value:string}', + 'pt-br': 'a longitude deve estar entre -180 e 180. Real: {value:string}', }, formatMismatch: { - "en-us": "value {value:string} does not match formatter {formatter:string}", - "de-ch": - "Wert {value:string} stimmt nicht mit Formatierer {formatter:string} überein", - "es-es": - "El valor {value:string} no coincide con el formateador {formatter:string}", - "fr-fr": - "la valeur {value:string} ne correspond pas au formateur {formatter:string}", - "ru-ru": - "значение {value:string} не соответствует форматеру {formatter:string}", - "uk-ua": "значення {value:string} не відповідає формату {formatter:string}", - "pt-br": - "valor {value:string} não corresponde ao formatador {formatter:string}", + 'en-us': 'value {value:string} does not match formatter {formatter:string}', + 'de-ch': + 'Wert {value:string} stimmt nicht mit Formatierer {formatter:string} überein', + 'es-es': + 'El valor {value:string} no coincide con el formateador {formatter:string}', + 'fr-fr': + 'la valeur {value:string} ne correspond pas au formateur {formatter:string}', + 'ru-ru': + 'значение {value:string} не соответствует форматеру {formatter:string}', + 'uk-ua': 'значення {value:string} не відповідає формату {formatter:string}', + 'pt-br': + 'valor {value:string} não corresponde ao formatador {formatter:string}', }, invalidPartialRecord: { - "en-us": "this field must be empty if {column:string} is empty", - "ru-ru": "это поле должно быть пустым, если {column:string} пусто", - "es-es": "este campo debe estar vacío si {column:string} está vacío", - "fr-fr": "ce champ doit être vide si {column:string} est vide", - "uk-ua": "це поле має бути порожнім, якщо {column:string} є порожнім", - "de-ch": "dieses Feld muss leer sein, wenn {column:string} leer ist", - "pt-br": "este campo deve estar vazio se {column:string} estiver vazio", + 'en-us': 'this field must be empty if {column:string} is empty', + 'ru-ru': 'это поле должно быть пустым, если {column:string} пусто', + 'es-es': 'este campo debe estar vacío si {column:string} está vacío', + 'fr-fr': 'ce champ doit être vide si {column:string} est vide', + 'uk-ua': 'це поле має бути порожнім, якщо {column:string} є порожнім', + 'de-ch': 'dieses Feld muss leer sein, wenn {column:string} leer ist', + 'pt-br': 'este campo deve estar vazio se {column:string} estiver vazio', }, fieldRequiredByUploadPlan: { - "en-us": "field is required by upload plan mapping", - "ru-ru": "поле обязательно для загрузки плана", - "es-es": "el campo es obligatorio para la asignación del plan de mapeo", - "fr-fr": "le champ est requis par le mappage du plan de téléchargement", - "uk-ua": "це поле є обов’язковим (згідно з визначенням)", - "de-ch": "Das Feld ist für die Upload-Planzuordnung erforderlich", - "pt-br": "campo é obrigatório para mapeamento do plano de upload", + 'en-us': 'field is required by upload plan mapping', + 'ru-ru': 'поле обязательно для загрузки плана', + 'es-es': 'el campo es obligatorio para la asignación del plan de mapeo', + 'fr-fr': 'le champ est requis par le mappage du plan de téléchargement', + 'uk-ua': 'це поле є обов’язковим (згідно з визначенням)', + 'de-ch': 'Das Feld ist für die Upload-Planzuordnung erforderlich', + 'pt-br': 'campo é obrigatório para mapeamento do plano de upload', }, invalidTreeStructure: { - "en-us": 'There are multiple "Uploaded" placeholder values in the tree!', - "ru-ru": 'В дереве есть несколько веток с именем "Uploaded"!', - "es-es": + 'en-us': 'There are multiple "Uploaded" placeholder values in the tree!', + 'ru-ru': 'В дереве есть несколько веток с именем "Uploaded"!', + 'es-es': '¡Hay varios valores de marcador de posición "Subidos" en el árbol!', - "fr-fr": + 'fr-fr': "Il existe plusieurs valeurs d'espace réservé « Téléchargé » dans l'arborescence !", - "uk-ua": 'У дереві є кілька вузлів з назвою "Uploaded"!', - "de-ch": "Es gibt mehrere „Hochgeladene“ Platzhalterwerte im Baum!", - "pt-br": 'Há vários valores de espaço reservado "Carregado" na árvore!', + 'uk-ua': 'У дереві є кілька вузлів з назвою "Uploaded"!', + 'de-ch': 'Es gibt mehrere „Hochgeladene“ Platzhalterwerte im Baum!', + 'pt-br': 'Há vários valores de espaço reservado "Carregado" na árvore!', }, missingRequiredTreeParent: { - "en-us": + 'en-us': 'Missing or unmapped required tree parent rank value for "{names:string}".', - "ru-ru": + 'ru-ru': 'Отсутствует или не сопоставлено необходимое значение родительского ранга для дерева "{names:string}".', - "es-es": + 'es-es': 'Falta o no está asignado el valor requerido del rango del padre en el árbol para "{names:string}".', - "fr-fr": + 'fr-fr': "Valeur de classement parent de l'arborescence requise manquante ou non mappée pour « {names:string} ».", - "uk-ua": + 'uk-ua': 'Відсутнє або не зіставлене необхідне значення батьківського рангу дерева для "{names:string}".', - "de-ch": - "Fehlender oder nicht zugeordneter erforderlicher Rangwert des übergeordneten Baums für „{names:string}“.", - "pt-br": + 'de-ch': + 'Fehlender oder nicht zugeordneter erforderlicher Rangwert des übergeordneten Baums für „{names:string}“.', + 'pt-br': 'Valor de classificação da árvore pai necessário ausente ou não mapeado para "{names:string}".', }, showTraceback: { - "en-us": "Show Traceback", - "es-es": "Mostrar seguimiento", - "fr-fr": "Afficher le traçage", - "ru-ru": "Показать трассировку", - "uk-ua": "Показати помилку", - "de-ch": "Traceback anzeigen", - "pt-br": "Mostrar rastreamento", + 'en-us': 'Show Traceback', + 'es-es': 'Mostrar seguimiento', + 'fr-fr': 'Afficher le traçage', + 'ru-ru': 'Показать трассировку', + 'uk-ua': 'Показати помилку', + 'de-ch': 'Traceback anzeigen', + 'pt-br': 'Mostrar rastreamento', }, fieldNotUnique: { - "en-us": "{tableName:string} must have unique {fieldName:string}", - "es-es": "{tableName:string} debe tener un {fieldName:string} único", - "fr-fr": 'Type de collection inattendu "[X31X]". "[X71X]" attendu', - "ru-ru": "{tableName:string} должно иметь уникальное {fieldName:string}", - "uk-ua": "{tableName:string} має мати унікальний {fieldName:string}", - "de-ch": "{tableName:string} muss eindeutig sein {fieldName:string}", - "pt-br": "{tableName:string} deve ter {fieldName:string} exclusivo", + 'en-us': '{tableName:string} must have unique {fieldName:string}', + 'es-es': '{tableName:string} debe tener un {fieldName:string} único', + 'fr-fr': 'Type de collection inattendu "[X31X]". "[X71X]" attendu', + 'ru-ru': '{tableName:string} должно иметь уникальное {fieldName:string}', + 'uk-ua': '{tableName:string} має мати унікальний {fieldName:string}', + 'de-ch': '{tableName:string} muss eindeutig sein {fieldName:string}', + 'pt-br': '{tableName:string} deve ter {fieldName:string} exclusivo', }, childFieldNotUnique: { - "en-us": - "{tableName:string} must have unique {fieldName:string} in {parentField:string}", - "es-es": - "{tableName:string} debe tener un {fieldName:string} único en {parentField:string}", - "fr-fr": - "{tableName:string} doit avoir un {fieldName:string} unique dans {parentField:string}", - "ru-ru": - "{tableName:string} должно иметь уникальное {fieldName:string} в {parentField:string}", - "uk-ua": + 'en-us': + '{tableName:string} must have unique {fieldName:string} in {parentField:string}', + 'es-es': + '{tableName:string} debe tener un {fieldName:string} único en {parentField:string}', + 'fr-fr': + '{tableName:string} doit avoir un {fieldName:string} unique dans {parentField:string}', + 'ru-ru': + '{tableName:string} должно иметь уникальное {fieldName:string} в {parentField:string}', + 'uk-ua': '{tableName:string} повинен мати унікальний "{fieldName:string}" у "{parentField:string}"', - "de-ch": - "{tableName:string} muss eindeutiges {fieldName:string} in {parentField:string} haben", - "pt-br": - "{tableName:string} deve ter {fieldName:string} exclusivo em {parentField:string}", + 'de-ch': + '{tableName:string} muss eindeutiges {fieldName:string} in {parentField:string} haben', + 'pt-br': + '{tableName:string} deve ter {fieldName:string} exclusivo em {parentField:string}', }, deletingTreeRoot: { - "en-us": "Can not delete root level tree definition item", - "es-es": - "No se puede eliminar la definición del elemento de nivel raíz del árbol", - "fr-fr": + 'en-us': 'Can not delete root level tree definition item', + 'es-es': + 'No se puede eliminar la definición del elemento de nivel raíz del árbol', + 'fr-fr': "Impossible de supprimer l'élément de définition de l'arborescence au niveau racine", - "ru-ru": "Невозможно удалить элемент определения дерева корневого уровня.", - "uk-ua": "Неможливо видалити корінь дерева", - "de-ch": - "Das Baumdefinitionselement auf Stammebene kann nicht gelöscht werden", - "pt-br": - "Não é possível excluir o item de definição da árvore de nível raiz", + 'ru-ru': 'Невозможно удалить элемент определения дерева корневого уровня.', + 'uk-ua': 'Неможливо видалити корінь дерева', + 'de-ch': + 'Das Baumdefinitionselement auf Stammebene kann nicht gelöscht werden', + 'pt-br': + 'Não é possível excluir o item de definição da árvore de nível raiz', }, nodeParentInvalidRank: { - "en-us": "Tree node's parent has rank greater than itself", - "es-es": "El padre de un nodo del árbol tiene un rango mayor que él mismo", - "fr-fr": "Le parent du nœud d'arbre a un rang supérieur à lui-même", - "ru-ru": "Родительский узел дерева имеет ранг выше, чем он сам.", - "uk-ua": "Батько вузла дерева має ранг, вищий за нього самого", - "de-ch": - "Der übergeordnete Knoten des Baumknotens hat einen höheren Rang als er selbst", - "pt-br": "O nó pai da árvore tem classificação maior que ele mesmo", + 'en-us': "Tree node's parent has rank greater than itself", + 'es-es': 'El padre de un nodo del árbol tiene un rango mayor que él mismo', + 'fr-fr': "Le parent du nœud d'arbre a un rang supérieur à lui-même", + 'ru-ru': 'Родительский узел дерева имеет ранг выше, чем он сам.', + 'uk-ua': 'Батько вузла дерева має ранг, вищий за нього самого', + 'de-ch': + 'Der übergeordnete Knoten des Baumknotens hat einen höheren Rang als er selbst', + 'pt-br': 'O nó pai da árvore tem classificação maior que ele mesmo', }, nodeChildrenInvalidRank: { - "en-us": "Tree node's rank is greater than some of its children", - "es-es": - "El rango de un nodo del árbol es mayor que el de alguno de sus hijos", - "fr-fr": + 'en-us': "Tree node's rank is greater than some of its children", + 'es-es': + 'El rango de un nodo del árbol es mayor que el de alguno de sus hijos', + 'fr-fr': "Le rang du nœud d'arbre est supérieur à celui de certains de ses enfants", - "ru-ru": "Ранг узла дерева больше, чем у некоторых его дочерних узлов.", - "uk-ua": "Ранг вузла дерева більший, ніж у деяких його дочірніх вузлів", - "de-ch": - "Der Rang des Baumknotens ist höher als der einiger seiner untergeordneten Knoten", - "pt-br": - "A classificação do nó da árvore é maior que a de alguns de seus filhos", + 'ru-ru': 'Ранг узла дерева больше, чем у некоторых его дочерних узлов.', + 'uk-ua': 'Ранг вузла дерева більший, ніж у деяких його дочірніх вузлів', + 'de-ch': + 'Der Rang des Baumknotens ist höher als der einiger seiner untergeordneten Knoten', + 'pt-br': + 'A classificação do nó da árvore é maior que a de alguns de seus filhos', }, nodeOperationToSynonymizedParent: { - "en-us": + 'en-us': '{operation:string} node "{nodeName:string}" to synonymized parent "{parentName:string}"', - "es-es": + 'es-es': '{operation:string} nodo "{nodeName:string}" al padre sinonimizado "{parentName:string}"', - "fr-fr": - "{operation:string} nœud « {nodeName:string} » vers le parent synonymisé « {parentName:string} »", - "uk-ua": + 'fr-fr': + '{operation:string} nœud « {nodeName:string} » vers le parent synonymisé « {parentName:string} »', + 'uk-ua': '{operation:string} вузол "{nodeName:string}" до синонімічного батьківського елемента "{parentName:string}"', - "de-ch": + 'de-ch': '{operation:string} Knoten "{nodeName:string}" zum synonymisierten übergeordneten Knoten "{parentName:string}"', - "ru-ru": + 'ru-ru': '{operation:string} узел "{nodeName:string}" к синонимизированному родительскому элементу "{parentName:string}"', - "pt-br": + 'pt-br': '{operation:string} nó "{nodeName:string}" para pai sinonimizado "{parentName:string}"', }, nodeSynonymizeToSynonymized: { - "en-us": + 'en-us': 'Synonymizing "{nodeName:string}" to synonymized node "{intoName:string}"', - "es-es": + 'es-es': 'Sinonimizando "{nodeName:string}" al nodo sinonimizado "{intoName:string}"', - "fr-fr": + 'fr-fr': 'Synonymisation de "{nodeName:string}" en nœud synonymisé "{intoName:string}"', - "ru-ru": - "Синонимизация «{nodeName:string}» в синонимизированный узел «{intoName:string}»", - "uk-ua": + 'ru-ru': + 'Синонимизация «{nodeName:string}» в синонимизированный узел «{intoName:string}»', + 'uk-ua': 'Синонімізація "{nodeName:string}" до синоніма "{intoName:string}"', - "de-ch": - "Synonymisierung von „{nodeName:string}“ zum synonymisierten Knoten „{intoName:string}“", - "pt-br": + 'de-ch': + 'Synonymisierung von „{nodeName:string}“ zum synonymisierten Knoten „{intoName:string}“', + 'pt-br': 'Sinonimizando "{nodeName:string}" para o nó sinonimizado "{intoName:string}"', }, nodeSynonimizeWithChildren: { - "en-us": 'Synonymizing node "{nodeName:string}" which has children', - "es-es": 'Sinonimizando el nodo "{nodeName:string}" que tiene hijos', - "fr-fr": 'Type de collection inattendu "[X31X]". "[X71X]" attendu', - "ru-ru": - "Синонимизация узла «{nodeName:string}», имеющего дочерние элементы", - "uk-ua": 'Синонімується вузол "{nodeName:string}", який має дітей', - "de-ch": - "Synonymisierender Knoten „{nodeName:string}“, der untergeordnete Knoten hat", - "pt-br": 'Sinonímia do nó "{nodeName:string}" que tem filhos', + 'en-us': 'Synonymizing node "{nodeName:string}" which has children', + 'es-es': 'Sinonimizando el nodo "{nodeName:string}" que tiene hijos', + 'fr-fr': 'Type de collection inattendu "[X31X]". "[X71X]" attendu', + 'ru-ru': + 'Синонимизация узла «{nodeName:string}», имеющего дочерние элементы', + 'uk-ua': 'Синонімується вузол "{nodeName:string}", який має дітей', + 'de-ch': + 'Synonymisierender Knoten „{nodeName:string}“, der untergeordnete Knoten hat', + 'pt-br': 'Sinonímia do nó "{nodeName:string}" que tem filhos', }, badTreeStructureInvalidRanks: { - "en-us": - "Bad Tree Structure: Found {badRanks:number|formatted} cases where node rank is not greater than its parent", - "es-es": - "Estructura de árbol incorrecta: se encontraron {badRanks:number|formatted} casos en los que el rango del nodo no es mayor que el de su padre", - "fr-fr": + 'en-us': + 'Bad Tree Structure: Found {badRanks:number|formatted} cases where node rank is not greater than its parent', + 'es-es': + 'Estructura de árbol incorrecta: se encontraron {badRanks:number|formatted} casos en los que el rango del nodo no es mayor que el de su padre', + 'fr-fr': "Mauvaise structure d'arborescence : cas {badRanks:number|formatted} trouvés où le rang du nœud n'est pas supérieur à celui de son parent", - "ru-ru": - "Плохая структура дерева: обнаружено {badRanks:number|formatted} случаев, когда ранг узла не превышает ранг его родителя.", - "uk-ua": - "Погана структура дерева: знайдено {badRanks:number|formatted} випадків, коли ранг вузла не перевищує його батьківського рівня", - "de-ch": - "Fehlerhafte Baumstruktur: {badRanks:number|formatted} Fälle gefunden, in denen der Knotenrang nicht größer ist als der des übergeordneten Knotens", - "pt-br": - "Estrutura de árvore ruim: foram encontrados {badRanks:number|formatted} casos em que a classificação do nó não é maior que a do seu pai", + 'ru-ru': + 'Плохая структура дерева: обнаружено {badRanks:number|formatted} случаев, когда ранг узла не превышает ранг его родителя.', + 'uk-ua': + 'Погана структура дерева: знайдено {badRanks:number|formatted} випадків, коли ранг вузла не перевищує його батьківського рівня', + 'de-ch': + 'Fehlerhafte Baumstruktur: {badRanks:number|formatted} Fälle gefunden, in denen der Knotenrang nicht größer ist als der des übergeordneten Knotens', + 'pt-br': + 'Estrutura de árvore ruim: foram encontrados {badRanks:number|formatted} casos em que a classificação do nó não é maior que a do seu pai', }, invalidNodeType: { - "en-us": + 'en-us': 'Unexpected type of node "{node:string}" during {operation:string}. Expected "{nodeModel:string}"', - "es-es": + 'es-es': 'Tipo de nodo inesperado "{node:string}" durante {operation:string}. Se esperaba "{nodeModel:string}"', - "fr-fr": + 'fr-fr': 'Type inattendu de nœud "{node:string}" pendant {operation:string}. "{nodeModel:string}" attendu', - "ru-ru": - "Неожиданный тип узла «{node:string}» во время {operation:string}. Ожидалось «{nodeModel:string}».", - "uk-ua": + 'ru-ru': + 'Неожиданный тип узла «{node:string}» во время {operation:string}. Ожидалось «{nodeModel:string}».', + 'uk-ua': 'Неочікуваний тип вузла "{node:string}" під час {operation:string}. Очікується "{nodeModel:string}"', - "de-ch": - "Unerwarteter Knotentyp „{node:string}“ während {operation:string}. Erwartet „{nodeModel:string}“", - "pt-br": + 'de-ch': + 'Unerwarteter Knotentyp „{node:string}“ während {operation:string}. Erwartet „{nodeModel:string}“', + 'pt-br': 'Tipo inesperado de nó "{node:string}" durante {operation:string}. Esperado "{nodeModel:string}"', }, operationAcrossTrees: { - "en-us": "{operation:string} across trees", - "de-ch": "{operation:string} über Bäume", - "es-es": "{operation:string} a través de los árboles", - "fr-fr": "{operation:string} à travers les arbres", - "ru-ru": "{operation:string} через деревья", - "uk-ua": "{operation:string} поміж деревами", - "pt-br": "{operation:string} através das árvores", + 'en-us': '{operation:string} across trees', + 'de-ch': '{operation:string} über Bäume', + 'es-es': '{operation:string} a través de los árboles', + 'fr-fr': '{operation:string} à travers les arbres', + 'ru-ru': '{operation:string} через деревья', + 'uk-ua': '{operation:string} поміж деревами', + 'pt-br': '{operation:string} através das árvores', }, limitReachedDeterminingAccepted: { - "en-us": - "Could not find accepted taxon for synonymized taxon with ID of {taxonId:number}", - "es-es": - "No se pudo encontrar el taxón válido para el taxón sinonimizado con ID {taxonId:number}", - "fr-fr": + 'en-us': + 'Could not find accepted taxon for synonymized taxon with ID of {taxonId:number}', + 'es-es': + 'No se pudo encontrar el taxón válido para el taxón sinonimizado con ID {taxonId:number}', + 'fr-fr': "Impossible de trouver un taxon accepté pour le taxon synonymisé avec l'ID {taxonId:number}", - "ru-ru": - "Не удалось найти принятый таксон для синонимизированного таксона с идентификатором {taxonId:number}", - "uk-ua": - "Не вдалося знайти прийнятий таксон для синоніма (ІД: {taxonId:number})", - "de-ch": - "Für das synonymisierte Taxon mit der ID {taxonId:number} konnte kein akzeptiertes Taxon gefunden werden.", - "pt-br": - "Não foi possível encontrar o táxon aceito para o táxon sinonimizado com ID de {taxonId:number}", + 'ru-ru': + 'Не удалось найти принятый таксон для синонимизированного таксона с идентификатором {taxonId:number}', + 'uk-ua': + 'Не вдалося знайти прийнятий таксон для синоніма (ІД: {taxonId:number})', + 'de-ch': + 'Für das synonymisierte Taxon mit der ID {taxonId:number} konnte kein akzeptiertes Taxon gefunden werden.', + 'pt-br': + 'Não foi possível encontrar o táxon aceito para o táxon sinonimizado com ID de {taxonId:number}', }, resourceInPermissionRegistry: { - "en-us": "Resource {resource:string} already in Permissions registry", - "es-es": "El recurso {resource:string} ya está en el registro de permisos", - "fr-fr": - "Ressource {resource:string} déjà dans le registre des autorisations", - "ru-ru": "Ресурс {resource:string} уже в реестре разрешений", - "uk-ua": "Ресурс {resource:string} уже є в реєстрі дозволів", - "de-ch": - "Ressource {resource:string} bereits in der Berechtigungsregistrierung", - "pt-br": "Recurso {resource:string} já no registro de permissões", + 'en-us': 'Resource {resource:string} already in Permissions registry', + 'es-es': 'El recurso {resource:string} ya está en el registro de permisos', + 'fr-fr': + 'Ressource {resource:string} déjà dans le registre des autorisations', + 'ru-ru': 'Ресурс {resource:string} уже в реестре разрешений', + 'uk-ua': 'Ресурс {resource:string} уже є в реєстрі дозволів', + 'de-ch': + 'Ressource {resource:string} bereits in der Berechtigungsregistrierung', + 'pt-br': 'Recurso {resource:string} já no registro de permissões', }, actorIsNotSpecifyUser: { comment: 'Agent "Abc" is not a Specify User', - "en-us": - "{agentTable:string} {actor:string} is not a {specifyUserTable:string}", - "es-es": - "{agentTable:string} {actor:string} no es un {specifyUserTable:string}", - "fr-fr": + 'en-us': + '{agentTable:string} {actor:string} is not a {specifyUserTable:string}', + 'es-es': + '{agentTable:string} {actor:string} no es un {specifyUserTable:string}', + 'fr-fr': "{agentTable:string} {actor:string} n'est pas un {specifyUserTable:string}", - "ru-ru": - "{agentTable:string} {actor:string} не является {specifyUserTable:string}", - "uk-ua": - "{agentTable:string} {actor:string} не є {specifyUserTable:string}", - "de-ch": - "{agentTable:string} {actor:string} ist kein {specifyUserTable:string}", - "pt-br": - "{agentTable:string} {actor:string} não é um {specifyUserTable:string}", + 'ru-ru': + '{agentTable:string} {actor:string} не является {specifyUserTable:string}', + 'uk-ua': + '{agentTable:string} {actor:string} не є {specifyUserTable:string}', + 'de-ch': + '{agentTable:string} {actor:string} ist kein {specifyUserTable:string}', + 'pt-br': + '{agentTable:string} {actor:string} não é um {specifyUserTable:string}', }, unexpectedCollectionType: { - "en-us": + 'en-us': 'Unexpected type of collection "{unexpectedTypeName:string}". Expected "{collectionName:string}"', - "es-es": + 'es-es': 'Tipo de colección "{unexpectedTypeName:string}" inesperado. Se esperaba "{collectionName:string}"', - "fr-fr": + 'fr-fr': 'Type de collection inattendu "{unexpectedTypeName:string}". "{collectionName:string}" attendu', - "ru-ru": - "Неожиданный тип коллекции «{unexpectedTypeName:string}». Ожидалось «{collectionName:string}».", - "uk-ua": + 'ru-ru': + 'Неожиданный тип коллекции «{unexpectedTypeName:string}». Ожидалось «{collectionName:string}».', + 'uk-ua': 'Неочікуваний тип колекції "{unexpectedTypeName:string}". Очікується "{collectionName:string}"', - "de-ch": - "Unerwarteter Typ der Sammlung „{unexpectedTypeName:string}“. Erwartet „{collectionName:string}“", - "pt-br": + 'de-ch': + 'Unerwarteter Typ der Sammlung „{unexpectedTypeName:string}“. Erwartet „{collectionName:string}“', + 'pt-br': 'Tipo inesperado de coleta "{unexpectedTypeName:string}". Esperado "{collectionName:string}"', }, invalidReportMimetype: { - "en-us": + 'en-us': 'Can not create report: {mimeTypeField:string} is not one of "jrxml/label" or "jrxml/report"', - "es-es": + 'es-es': 'No se puede crear el informe: {mimeTypeField:string} no es uno de "jrxml/label" o "jrxml/report"', - "fr-fr": + 'fr-fr': 'Impossible de créer un rapport : {mimeTypeField:string} n\'est pas l\'un des "jrxml/label" ou "jrxml/report"', - "ru-ru": - "Невозможно создать отчёт: {mimeTypeField:string} не является ни «jrxml/label», ни «jrxml/report».", - "uk-ua": + 'ru-ru': + 'Невозможно создать отчёт: {mimeTypeField:string} не является ни «jrxml/label», ни «jrxml/report».', + 'uk-ua': 'Не вдається створити звіт: {mimeTypeField:string} має бути "jrxml/label" або "jrxml/report"', - "de-ch": - "Bericht kann nicht erstellt werden: {mimeTypeField:string} ist weder „jrxml/label“ noch „jrxml/report“", - "pt-br": + 'de-ch': + 'Bericht kann nicht erstellt werden: {mimeTypeField:string} ist weder „jrxml/label“ noch „jrxml/report“', + 'pt-br': 'Não é possível criar o relatório: {mimeTypeField:string} não é um dos "jrxml/label" ou "jrxml/report"', }, fieldNotRelationship: { - "en-us": "Field {field:string} is not a Relationship", - "es-es": "El campo {field:string} no es una relación", - "fr-fr": "Le champ {field:string} n'est pas une relation", - "ru-ru": "Поле {field:string} не является отношением", - "uk-ua": "Поле {field:string} не є зв’язком", - "de-ch": "Feld {field:string} ist keine Beziehung", - "pt-br": "Campo {field:string} não é um relacionamento", + 'en-us': 'Field {field:string} is not a Relationship', + 'es-es': 'El campo {field:string} no es una relación', + 'fr-fr': "Le champ {field:string} n'est pas une relation", + 'ru-ru': 'Поле {field:string} не является отношением', + 'uk-ua': 'Поле {field:string} не є зв’язком', + 'de-ch': 'Feld {field:string} ist keine Beziehung', + 'pt-br': 'Campo {field:string} não é um relacionamento', }, unexpectedTableId: { - "en-us": + 'en-us': 'Unexpected table id "{tableId:string}" in request. Expected "{expectedTableId:string}"', - "es-es": + 'es-es': 'ID de tabla inesperado "{tableId:string}" en la solicitud. Se esperaba "{expectedTableId:string}"', - "fr-fr": + 'fr-fr': 'ID de table inattendu "{tableId:string}" dans la demande. "{expectedTableId:string}" attendu', - "ru-ru": - "Неожиданный идентификатор таблицы «{tableId:string}» в запросе. Ожидается «{expectedTableId:string}».", - "uk-ua": + 'ru-ru': + 'Неожиданный идентификатор таблицы «{tableId:string}» в запросе. Ожидается «{expectedTableId:string}».', + 'uk-ua': 'Неочікуваний ІД таблиці "{tableId:string}" у запиті. Очікується "{expectedTableId:string}"', - "de-ch": - "Unerwartete Tabellen-ID „{tableId:string}“ in der Anfrage. Erwartet „{expectedTableId:string}“", - "pt-br": + 'de-ch': + 'Unerwartete Tabellen-ID „{tableId:string}“ in der Anfrage. Erwartet „{expectedTableId:string}“', + 'pt-br': 'ID de tabela inesperado "{tableId:string}" na solicitação. Esperado "{expectedTableId:string}"', }, noCollectionInQuery: { - "en-us": "No Collection found in Query for table {table:string}", - "es-es": - "No se encontró ninguna colección en la consulta de la tabla {table:string}", - "fr-fr": - "Aucune collection trouvée dans la requête pour la table {table:string}", - "ru-ru": - "В запросе для таблицы {table:string} не найдено ни одной коллекции", - "uk-ua": "У запиті для таблиці {table:string} колекція не знайдена", - "de-ch": "Keine Sammlung in Abfrage für Tabelle {table:string} gefunden", - "pt-br": - "Nenhuma coleção encontrada na consulta para a tabela {table:string}", + 'en-us': 'No Collection found in Query for table {table:string}', + 'es-es': + 'No se encontró ninguna colección en la consulta de la tabla {table:string}', + 'fr-fr': + 'Aucune collection trouvée dans la requête pour la table {table:string}', + 'ru-ru': + 'В запросе для таблицы {table:string} не найдено ни одной коллекции', + 'uk-ua': 'У запиті для таблиці {table:string} колекція не знайдена', + 'de-ch': 'Keine Sammlung in Abfrage für Tabelle {table:string} gefunden', + 'pt-br': + 'Nenhuma coleção encontrada na consulta para a tabela {table:string}', }, invalidDatePart: { - "en-us": + 'en-us': 'Invalid date part "{datePart:string}". Expected one of {validDateParts:string}', - "es-es": + 'es-es': 'Parte de la fecha no válida "{datePart:string}". Se esperaba {validDateParts:string}', - "fr-fr": + 'fr-fr': 'Partie de date "{datePart:string}" non valide. Attendu l\'un des {validDateParts:string}', - "ru-ru": - "Неверная часть даты «{datePart:string}». Ожидается {validDateParts:string}.", - "uk-ua": + 'ru-ru': + 'Неверная часть даты «{datePart:string}». Ожидается {validDateParts:string}.', + 'uk-ua': 'Недійсна частина дати "{datePart:string}". Очікується один із {validDateParts:string}', - "de-ch": + 'de-ch': 'Ungültiger Datumsteil "{datePart:string}". Erwartet wurde einer von {validDateParts:string}', - "pt-br": + 'pt-br': 'Parte da data inválida "{datePart:string}". Esperava-se uma de {validDateParts:string}', }, invalidUploadStatus: { - "en-us": + 'en-us': 'Invalid status "{uploadStatus:string}" for {operation:string}. Expected {expectedUploadStatus:string}', - "es-es": + 'es-es': 'Estado no válido "{uploadStatus:string}" para {operation:string}. Se esperaba {expectedUploadStatus:string}', - "fr-fr": + 'fr-fr': 'Statut non valide "{uploadStatus:string}" pour {operation:string}. Attendu {expectedUploadStatus:string}', - "ru-ru": - "Недопустимый статус «{uploadStatus:string}» для {operation:string}. Ожидается {expectedUploadStatus:string}.", - "uk-ua": + 'ru-ru': + 'Недопустимый статус «{uploadStatus:string}» для {operation:string}. Ожидается {expectedUploadStatus:string}.', + 'uk-ua': 'Недійсний статус "{uploadStatus:string}" для {operation:string}. Очікується {expectedUploadStatus:string}', - "de-ch": + 'de-ch': 'Ungültiger Status "{uploadStatus:string}" für {operation:string}. Erwartet: {expectedUploadStatus:string}', - "pt-br": + 'pt-br': 'Status inválido "{uploadStatus:string}" para {operation:string}. Esperado {expectedUploadStatus:string}', }, datasetAlreadyUploaded: { - "en-us": "Dataset already uploaded", - "es-es": "Conjunto de datos ya subido", - "fr-fr": "Ensemble de données déjà téléchargé", - "ru-ru": "Набор данных уже загружен", - "uk-ua": "Таблиця уже завантажена", - "de-ch": "Datensatz bereits hochgeladen", - "pt-br": "Conjunto de dados já carregado", + 'en-us': 'Dataset already uploaded', + 'es-es': 'Conjunto de datos ya subido', + 'fr-fr': 'Ensemble de données déjà téléchargé', + 'ru-ru': 'Набор данных уже загружен', + 'uk-ua': 'Таблиця уже завантажена', + 'de-ch': 'Datensatz bereits hochgeladen', + 'pt-br': 'Conjunto de dados já carregado', }, scopeChangeDetected: { - "en-us": - "Scope change detected in this row. It is recommended to delete this row from the dataset", - "de-ch": - "In dieser Zeile wurde eine Bereichsänderung erkannt. Es wird empfohlen, diese Zeile aus dem Datensatz zu löschen.", - "es-es": - "Se detectó un cambio de alcance en esta fila. Se recomienda eliminarla del conjunto de datos.", - "fr-fr": + 'en-us': + 'Scope change detected in this row. It is recommended to delete this row from the dataset', + 'de-ch': + 'In dieser Zeile wurde eine Bereichsänderung erkannt. Es wird empfohlen, diese Zeile aus dem Datensatz zu löschen.', + 'es-es': + 'Se detectó un cambio de alcance en esta fila. Se recomienda eliminarla del conjunto de datos.', + 'fr-fr': "Modification de portée détectée dans cette ligne. Il est recommandé de supprimer cette ligne de l'ensemble de données.", - "pt-br": - "Alteração de escopo detectada nesta linha. Recomenda-se excluir esta linha do conjunto de dados.", - "ru-ru": - "В этой строке обнаружено изменение области действия. Рекомендуется удалить эту строку из набора данных.", - "uk-ua": - "У цьому рядку виявлено зміну області застосування. Рекомендується видалити цей рядок з набору даних", + 'pt-br': + 'Alteração de escopo detectada nesta linha. Recomenda-se excluir esta linha do conjunto de dados.', + 'ru-ru': + 'В этой строке обнаружено изменение области действия. Рекомендуется удалить эту строку из набора данных.', + 'uk-ua': + 'У цьому рядку виявлено зміну області застосування. Рекомендується видалити цей рядок з набору даних', }, multipleTreeDefsInRow: { - "en-us": "Multiple tree definitions in row", - "de-ch": "Mehrere Baumdefinitionen in einer Zeile", - "es-es": "Varias definiciones de árboles en fila", - "fr-fr": "Plusieurs définitions d'arbres dans la ligne", - "pt-br": "Várias definições de árvores em linha", - "ru-ru": "Несколько определений деревьев в строке", - "uk-ua": "Кілька визначень дерев у рядку", + 'en-us': 'Multiple tree definitions in row', + 'de-ch': 'Mehrere Baumdefinitionen in einer Zeile', + 'es-es': 'Varias definiciones de árboles en fila', + 'fr-fr': "Plusieurs définitions d'arbres dans la ligne", + 'pt-br': 'Várias definições de árvores em linha', + 'ru-ru': 'Несколько определений деревьев в строке', + 'uk-ua': 'Кілька визначень дерев у рядку', }, invalidCotype: { - "en-us": "Invalid type for selected tree rank(s)", - "de-ch": "Ungültiger Typ für ausgewählte(n) Baumrang(e)", - "es-es": "Tipo no válido para el rango de árbol seleccionado", - "fr-fr": "Type non valide pour les rangs d'arbres sélectionnés", - "pt-br": "Tipo inválido para classificação de árvore selecionada(s)", - "ru-ru": "Недопустимый тип для выбранного ранга дерева", - "uk-ua": "Недійсний тип для вибраного(их) рангу(ів) дерева", + 'en-us': 'Invalid type for selected tree rank(s)', + 'de-ch': 'Ungültiger Typ für ausgewählte(n) Baumrang(e)', + 'es-es': 'Tipo no válido para el rango de árbol seleccionado', + 'fr-fr': "Type non valide pour les rangs d'arbres sélectionnés", + 'pt-br': 'Tipo inválido para classificação de árvore selecionada(s)', + 'ru-ru': 'Недопустимый тип для выбранного ранга дерева', + 'uk-ua': 'Недійсний тип для вибраного(их) рангу(ів) дерева', }, invalidComponentType: { - "en-us": "Invalid {componentType: string} for selected tree rank(s)", - "de-ch": - "Ungültiger {componentType: string} für ausgewählte(n) Baumrang(e)", - "es-es": - "{componentType: string} no válido para los rangos de árbol seleccionados", - "fr-fr": + 'en-us': 'Invalid {componentType: string} for selected tree rank(s)', + 'de-ch': + 'Ungültiger {componentType: string} für ausgewählte(n) Baumrang(e)', + 'es-es': + '{componentType: string} no válido para los rangos de árbol seleccionados', + 'fr-fr': "{componentType: string} non valide pour le(s) rang(s) d'arbre sélectionné(s)", - "pt-br": - "{componentType: string} inválido para a(s) classificação(ões) da árvore selecionada(s)", - "ru-ru": "Недопустимый {componentType: string} для выбранных рангов дерева", - "uk-ua": - "Недійсний {componentType: string} для вибраного(их) рангу(ів) дерева", + 'pt-br': + '{componentType: string} inválido para a(s) classificação(ões) da árvore selecionada(s)', + 'ru-ru': 'Недопустимый {componentType: string} для выбранных рангов дерева', + 'uk-ua': + 'Недійсний {componentType: string} для вибраного(их) рангу(ів) дерева', }, attachmentNotFound: { - "en-us": - "One or more attachments do not exist. They may have been deleted.", - "de-ch": - "Ein oder mehrere Anhänge sind nicht vorhanden. Sie wurden möglicherweise gelöscht.", - "es-es": - "Uno o más archivos adjuntos no existen. Es posible que se hayan eliminado.", - "fr-fr": + 'en-us': + 'One or more attachments do not exist. They may have been deleted.', + 'de-ch': + 'Ein oder mehrere Anhänge sind nicht vorhanden. Sie wurden möglicherweise gelöscht.', + 'es-es': + 'Uno o más archivos adjuntos no existen. Es posible que se hayan eliminado.', + 'fr-fr': "Une ou plusieurs pièces jointes n'existent pas. Elles ont peut-être été supprimées.", - "pt-br": "Um ou mais anexos não existem. Eles podem ter sido excluídos.", - "ru-ru": - "Одно или несколько вложений отсутствуют. Возможно, они были удалены.", - "uk-ua": - "Один або декілька вкладень не існують. Можливо, їх було видалено.", + 'pt-br': 'Um ou mais anexos não existem. Eles podem ter sido excluídos.', + 'ru-ru': + 'Одно или несколько вложений отсутствуют. Возможно, они были удалены.', + 'uk-ua': + 'Один або декілька вкладень не існують. Можливо, їх було видалено.', }, tableDoesNotSupportAttachments: { - "en-us": "The attachment's destination table does not support attachments.", - "de-ch": "Die Zieltabelle des Anhangs unterstützt keine Anhänge.", - "es-es": - "La tabla de destino del archivo adjunto no admite archivos adjuntos.", - "fr-fr": - "La table de destination de la pièce jointe ne prend pas en charge les pièces jointes.", - "pt-br": "A tabela de destino do anexo não suporta anexos.", - "ru-ru": "Таблица назначения вложений не поддерживает вложения.", - "uk-ua": "Таблиця призначення вкладення не підтримує вкладення.", + 'en-us': "The attachment's destination table does not support attachments.", + 'de-ch': 'Die Zieltabelle des Anhangs unterstützt keine Anhänge.', + 'es-es': + 'La tabla de destino del archivo adjunto no admite archivos adjuntos.', + 'fr-fr': + 'La table de destination de la pièce jointe ne prend pas en charge les pièces jointes.', + 'pt-br': 'A tabela de destino do anexo não suporta anexos.', + 'ru-ru': 'Таблица назначения вложений не поддерживает вложения.', + 'uk-ua': 'Таблиця призначення вкладення не підтримує вкладення.', }, attachmentAlreadyLinked: { - "en-us": - "One or more attachments are already associated with an uploaded record.", - "de-ch": - "Einem hochgeladenen Datensatz sind bereits ein oder mehrere Anhänge zugeordnet.", - "es-es": - "Uno o más archivos adjuntos ya están asociados a un registro cargado.", - "fr-fr": - "Une ou plusieurs pièces jointes sont déjà associées à un enregistrement téléchargé.", - "pt-br": "Um ou mais anexos já estão associados a um registro carregado.", - "ru-ru": "С загруженной записью уже связано одно или несколько вложений.", - "uk-ua": - "Один або декілька вкладень вже пов’язані із завантаженим записом.", + 'en-us': + 'One or more attachments are already associated with an uploaded record.', + 'de-ch': + 'Einem hochgeladenen Datensatz sind bereits ein oder mehrere Anhänge zugeordnet.', + 'es-es': + 'Uno o más archivos adjuntos ya están asociados a un registro cargado.', + 'fr-fr': + 'Une ou plusieurs pièces jointes sont déjà associées à un enregistrement téléchargé.', + 'pt-br': 'Um ou mais anexos já estão associados a um registro carregado.', + 'ru-ru': 'С загруженной записью уже связано одно или несколько вложений.', + 'uk-ua': + 'Один або декілька вкладень вже пов’язані із завантаженим записом.', }, } as const); diff --git a/specifyweb/frontend/js_src/lib/localization/common.ts b/specifyweb/frontend/js_src/lib/localization/common.ts index d5d90e11be2..0ce84cb8bb5 100644 --- a/specifyweb/frontend/js_src/lib/localization/common.ts +++ b/specifyweb/frontend/js_src/lib/localization/common.ts @@ -4,7 +4,7 @@ * @module */ -import { createDictionary } from "./utils"; +import { createDictionary } from './utils'; // Refer to "Guidelines for Programmers" in ./README.md before editing this file @@ -14,835 +14,835 @@ export const commonText = createDictionary({ This is an example of how to provide comments. Comments are visible to translators. `, - "en-us": "Specify 7", - "ru-ru": "Укажите 7", - "es-es": "Specify 7", - "fr-fr": "Specify 7", - "uk-ua": "Вкажіть 7", - "de-ch": "Specify 7", - "pt-br": "Specify 7", + 'en-us': 'Specify 7', + 'ru-ru': 'Укажите 7', + 'es-es': 'Specify 7', + 'fr-fr': 'Specify 7', + 'uk-ua': 'Вкажіть 7', + 'de-ch': 'Specify 7', + 'pt-br': 'Specify 7', }, no: { - "en-us": "No", - "ru-ru": "Нет", - "es-es": "No", - "fr-fr": "Non", - "uk-ua": "Немає", - "de-ch": "Nein", - "pt-br": "Não", + 'en-us': 'No', + 'ru-ru': 'Нет', + 'es-es': 'No', + 'fr-fr': 'Non', + 'uk-ua': 'Немає', + 'de-ch': 'Nein', + 'pt-br': 'Não', }, cancel: { - "en-us": "Cancel", - "ru-ru": "Отмена", - "es-es": "Cancelar", - "fr-fr": "Annuler", - "uk-ua": "Скасувати", - "de-ch": "Abbrechen", - "pt-br": "Cancelar", + 'en-us': 'Cancel', + 'ru-ru': 'Отмена', + 'es-es': 'Cancelar', + 'fr-fr': 'Annuler', + 'uk-ua': 'Скасувати', + 'de-ch': 'Abbrechen', + 'pt-br': 'Cancelar', }, back: { - "en-us": "Back", - "ru-ru": "Назад", - "es-es": "Atrás", - "fr-fr": "Dos", - "uk-ua": "Назад", - "de-ch": "Zurück", - "pt-br": "Voltar", + 'en-us': 'Back', + 'ru-ru': 'Назад', + 'es-es': 'Atrás', + 'fr-fr': 'Dos', + 'uk-ua': 'Назад', + 'de-ch': 'Zurück', + 'pt-br': 'Voltar', }, skip: { - "en-us": "Skip", - "ru-ru": "Пропускать", - "es-es": "Omitir", - "fr-fr": "Sauter", - "uk-ua": "Пропустити", - "de-ch": "Überspringen", - "pt-br": "Pular", + 'en-us': 'Skip', + 'ru-ru': 'Пропускать', + 'es-es': 'Omitir', + 'fr-fr': 'Sauter', + 'uk-ua': 'Пропустити', + 'de-ch': 'Überspringen', + 'pt-br': 'Pular', }, create: { - "en-us": "Create", - "ru-ru": "Создать", - "es-es": "Crear", - "fr-fr": "Créer", - "uk-ua": "Створити", - "de-ch": "Erstellen", - "pt-br": "Criar", + 'en-us': 'Create', + 'ru-ru': 'Создать', + 'es-es': 'Crear', + 'fr-fr': 'Créer', + 'uk-ua': 'Створити', + 'de-ch': 'Erstellen', + 'pt-br': 'Criar', }, close: { - "en-us": "Close", - "ru-ru": "Закрыть", - "es-es": "Cerrar", - "fr-fr": "Fermer", - "uk-ua": "Закрити", - "de-ch": "Schliessen", - "pt-br": "Fechar", + 'en-us': 'Close', + 'ru-ru': 'Закрыть', + 'es-es': 'Cerrar', + 'fr-fr': 'Fermer', + 'uk-ua': 'Закрити', + 'de-ch': 'Schliessen', + 'pt-br': 'Fechar', }, apply: { - "en-us": "Apply", - "ru-ru": "Применить", - "es-es": "Aplicar", - "fr-fr": "Appliquer", - "uk-ua": "Застосувати", - "de-ch": "Anwenden", - "pt-br": "Aplicar", + 'en-us': 'Apply', + 'ru-ru': 'Применить', + 'es-es': 'Aplicar', + 'fr-fr': 'Appliquer', + 'uk-ua': 'Застосувати', + 'de-ch': 'Anwenden', + 'pt-br': 'Aplicar', }, applyAll: { - "en-us": "Apply All", - "ru-ru": "Применить все", - "es-es": "Aplicar todo", - "fr-fr": "Appliquer à tous", - "uk-ua": "Застосувати все", - "de-ch": "Alle Anwenden", - "pt-br": "Aplicar tudo", + 'en-us': 'Apply All', + 'ru-ru': 'Применить все', + 'es-es': 'Aplicar todo', + 'fr-fr': 'Appliquer à tous', + 'uk-ua': 'Застосувати все', + 'de-ch': 'Alle Anwenden', + 'pt-br': 'Aplicar tudo', }, clearAll: { - "en-us": "Clear all", - "ru-ru": "Очистить все", - "es-es": "Borrar todo", - "fr-fr": "Tout effacer", - "uk-ua": "Очистити все", - "de-ch": "Alles löschen", - "pt-br": "Limpar tudo", + 'en-us': 'Clear all', + 'ru-ru': 'Очистить все', + 'es-es': 'Borrar todo', + 'fr-fr': 'Tout effacer', + 'uk-ua': 'Очистити все', + 'de-ch': 'Alles löschen', + 'pt-br': 'Limpar tudo', }, deleteUnmapped: { - "en-us": "Delete unmapped columns", - "de-ch": "Nicht zugeordnete Spalten löschen", - "es-es": "Eliminar columnas no asignadas", - "fr-fr": "Supprimer les colonnes non mappées", - "pt-br": "Excluir colunas não mapeadas", - "ru-ru": "Удаление неотображенных столбцов", - "uk-ua": "Видалити незіставлені стовпці", + 'en-us': 'Delete unmapped columns', + 'de-ch': 'Nicht zugeordnete Spalten löschen', + 'es-es': 'Eliminar columnas no asignadas', + 'fr-fr': 'Supprimer les colonnes non mappées', + 'pt-br': 'Excluir colunas não mapeadas', + 'ru-ru': 'Удаление неотображенных столбцов', + 'uk-ua': 'Видалити незіставлені стовпці', }, save: { - "en-us": "Save", - "ru-ru": "Сохранить", - "es-es": "Guardar", - "fr-fr": "Sauvegarder", - "uk-ua": "зберегти", - "de-ch": "Speichern", - "pt-br": "Salvar", + 'en-us': 'Save', + 'ru-ru': 'Сохранить', + 'es-es': 'Guardar', + 'fr-fr': 'Sauvegarder', + 'uk-ua': 'зберегти', + 'de-ch': 'Speichern', + 'pt-br': 'Salvar', }, add: { - "en-us": "Add", - "ru-ru": "Добавить", - "es-es": "Añadir", - "fr-fr": "Ajouter", - "uk-ua": "додати", - "de-ch": "Hinzufügen", - "pt-br": "Adicionar", + 'en-us': 'Add', + 'ru-ru': 'Добавить', + 'es-es': 'Añadir', + 'fr-fr': 'Ajouter', + 'uk-ua': 'додати', + 'de-ch': 'Hinzufügen', + 'pt-br': 'Adicionar', }, open: { - "en-us": "Open", - "ru-ru": "Открыть", - "es-es": "Abrir", - "fr-fr": "Ouvrir", - "uk-ua": "ВІДЧИНЕНО", - "de-ch": "Öffnen", - "pt-br": "Abrir", + 'en-us': 'Open', + 'ru-ru': 'Открыть', + 'es-es': 'Abrir', + 'fr-fr': 'Ouvrir', + 'uk-ua': 'ВІДЧИНЕНО', + 'de-ch': 'Öffnen', + 'pt-br': 'Abrir', }, delete: { - "en-us": "Delete", - "es-es": "Eliminar", - "fr-fr": "Supprimer", - "uk-ua": "Видалити", - "de-ch": "Löschen", - "ru-ru": "Удалить", - "pt-br": "Excluir", + 'en-us': 'Delete', + 'es-es': 'Eliminar', + 'fr-fr': 'Supprimer', + 'uk-ua': 'Видалити', + 'de-ch': 'Löschen', + 'ru-ru': 'Удалить', + 'pt-br': 'Excluir', }, next: { - "en-us": "Next", - "ru-ru": "Следующий", - "es-es": "Siguiente", - "fr-fr": "Suivant", - "uk-ua": "Далі", - "de-ch": "Weiter", - "pt-br": "Próximo", + 'en-us': 'Next', + 'ru-ru': 'Следующий', + 'es-es': 'Siguiente', + 'fr-fr': 'Suivant', + 'uk-ua': 'Далі', + 'de-ch': 'Weiter', + 'pt-br': 'Próximo', }, previous: { - "en-us": "Previous", - "ru-ru": "Предыдущий", - "es-es": "Anterior", - "fr-fr": "Précédent", - "uk-ua": "Попередній", - "de-ch": "Zurück", - "pt-br": "Anterior", + 'en-us': 'Previous', + 'ru-ru': 'Предыдущий', + 'es-es': 'Anterior', + 'fr-fr': 'Précédent', + 'uk-ua': 'Попередній', + 'de-ch': 'Zurück', + 'pt-br': 'Anterior', }, tool: { - "en-us": "Tool", - "ru-ru": "Инструмент", - "es-es": "Herramienta", - "fr-fr": "Outil", - "uk-ua": "Інструмент", - "de-ch": "Tool", - "pt-br": "Ferramenta", + 'en-us': 'Tool', + 'ru-ru': 'Инструмент', + 'es-es': 'Herramienta', + 'fr-fr': 'Outil', + 'uk-ua': 'Інструмент', + 'de-ch': 'Tool', + 'pt-br': 'Ferramenta', }, tools: { - "en-us": "Tools", - "ru-ru": "Инструменты", - "es-es": "Herramientas", - "fr-fr": "Outils", - "uk-ua": "Інструменти", - "de-ch": "Tools", - "pt-br": "Ferramentas", + 'en-us': 'Tools', + 'ru-ru': 'Инструменты', + 'es-es': 'Herramientas', + 'fr-fr': 'Outils', + 'uk-ua': 'Інструменти', + 'de-ch': 'Tools', + 'pt-br': 'Ferramentas', }, loading: { - "en-us": "Loading…", - "ru-ru": "Загрузка…", - "es-es": "Cargando…", - "fr-fr": "Chargement…", - "uk-ua": "Завантажуємо…", - "de-ch": "Laden …", - "pt-br": "Carregando…", + 'en-us': 'Loading…', + 'ru-ru': 'Загрузка…', + 'es-es': 'Cargando…', + 'fr-fr': 'Chargement…', + 'uk-ua': 'Завантажуємо…', + 'de-ch': 'Laden …', + 'pt-br': 'Carregando…', }, uploaded: { - "en-us": "Uploaded", - "ru-ru": "Загружено", - "es-es": "Subido", - "fr-fr": "Téléchargé", - "uk-ua": "Завантажено", - "de-ch": "Hochgeladen", - "pt-br": "Enviado", + 'en-us': 'Uploaded', + 'ru-ru': 'Загружено', + 'es-es': 'Subido', + 'fr-fr': 'Téléchargé', + 'uk-ua': 'Завантажено', + 'de-ch': 'Hochgeladen', + 'pt-br': 'Enviado', }, remove: { - "en-us": "Remove", - "ru-ru": "Удалить", - "es-es": "Eliminar", - "fr-fr": "Retirer", - "uk-ua": "видалити", - "de-ch": "Entfernen", - "pt-br": "Remover", + 'en-us': 'Remove', + 'ru-ru': 'Удалить', + 'es-es': 'Eliminar', + 'fr-fr': 'Retirer', + 'uk-ua': 'видалити', + 'de-ch': 'Entfernen', + 'pt-br': 'Remover', }, search: { - "en-us": "Search", - "ru-ru": "Поиск", - "es-es": "Buscar", - "fr-fr": "Recherche", - "uk-ua": "Пошук", - "de-ch": "Suche", - "pt-br": "Procurar", + 'en-us': 'Search', + 'ru-ru': 'Поиск', + 'es-es': 'Buscar', + 'fr-fr': 'Recherche', + 'uk-ua': 'Пошук', + 'de-ch': 'Suche', + 'pt-br': 'Procurar', }, noResults: { - "en-us": "No Results", - "ru-ru": "Нет результатов", - "es-es": "Sin resultados", - "fr-fr": "Aucun résultat", - "uk-ua": "Немає результатів", - "de-ch": "Keine Resultate", - "pt-br": "Nenhum resultado", + 'en-us': 'No Results', + 'ru-ru': 'Нет результатов', + 'es-es': 'Sin resultados', + 'fr-fr': 'Aucun résultat', + 'uk-ua': 'Немає результатів', + 'de-ch': 'Keine Resultate', + 'pt-br': 'Nenhum resultado', }, notApplicable: { - "en-us": "N/A", - "ru-ru": "Н/Д", - "es-es": "N/D", - "fr-fr": "N/A", - "uk-ua": "N/A", - "de-ch": "N/A", - "pt-br": "N / D", + 'en-us': 'N/A', + 'ru-ru': 'Н/Д', + 'es-es': 'N/D', + 'fr-fr': 'N/A', + 'uk-ua': 'N/A', + 'de-ch': 'N/A', + 'pt-br': 'N / D', }, new: { - "en-us": "New", - "ru-ru": "Новый", - "es-es": "Nuevo", - "fr-fr": "Nouveau", - "uk-ua": "новий", - "de-ch": "Neu", - "pt-br": "Novo", + 'en-us': 'New', + 'ru-ru': 'Новый', + 'es-es': 'Nuevo', + 'fr-fr': 'Nouveau', + 'uk-ua': 'новий', + 'de-ch': 'Neu', + 'pt-br': 'Novo', }, edit: { - "en-us": "Edit", - "ru-ru": "Редактировать", - "es-es": "Editar", - "fr-fr": "Modifier", - "uk-ua": "Редагувати", - "de-ch": "Bearbeiten", - "pt-br": "Editar", + 'en-us': 'Edit', + 'ru-ru': 'Редактировать', + 'es-es': 'Editar', + 'fr-fr': 'Modifier', + 'uk-ua': 'Редагувати', + 'de-ch': 'Bearbeiten', + 'pt-br': 'Editar', }, ignore: { - "en-us": "Ignore", - "ru-ru": "Игнорировать", - "es-es": "Ignorar", - "fr-fr": "Ignorer", - "uk-ua": "Ігнорувати", - "de-ch": "Ignorieren", - "pt-br": "Ignorar", + 'en-us': 'Ignore', + 'ru-ru': 'Игнорировать', + 'es-es': 'Ignorar', + 'fr-fr': 'Ignorer', + 'uk-ua': 'Ігнорувати', + 'de-ch': 'Ignorieren', + 'pt-br': 'Ignorar', }, proceed: { - "en-us": "Proceed", - "ru-ru": "Продолжить", - "es-es": "Proceder", - "fr-fr": "Procéder", - "uk-ua": "Продовжуйте", - "de-ch": "Fortfahren", - "pt-br": "Prosseguir", + 'en-us': 'Proceed', + 'ru-ru': 'Продолжить', + 'es-es': 'Proceder', + 'fr-fr': 'Procéder', + 'uk-ua': 'Продовжуйте', + 'de-ch': 'Fortfahren', + 'pt-br': 'Prosseguir', }, start: { - comment: "Noun", - "en-us": "Start", - "ru-ru": "Начать", - "es-es": "Empezar", - "fr-fr": "Commencer", - "uk-ua": "старт", - "de-ch": "Start", - "pt-br": "Começar", + comment: 'Noun', + 'en-us': 'Start', + 'ru-ru': 'Начать', + 'es-es': 'Empezar', + 'fr-fr': 'Commencer', + 'uk-ua': 'старт', + 'de-ch': 'Start', + 'pt-br': 'Começar', }, end: { - comment: "Noun", - "en-us": "End", - "ru-ru": "Конец", - "es-es": "Fin", - "fr-fr": "Fin", - "uk-ua": "Кінець", - "de-ch": "Ende", - "pt-br": "Fim", + comment: 'Noun', + 'en-us': 'End', + 'ru-ru': 'Конец', + 'es-es': 'Fin', + 'fr-fr': 'Fin', + 'uk-ua': 'Кінець', + 'de-ch': 'Ende', + 'pt-br': 'Fim', }, update: { - comment: "Verb", - "en-us": "Update", - "ru-ru": "Обновить", - "es-es": "Actualizar", - "fr-fr": "Mise à jour", - "uk-ua": "оновлення", - "de-ch": "Aktualisieren", - "pt-br": "Atualizar", + comment: 'Verb', + 'en-us': 'Update', + 'ru-ru': 'Обновить', + 'es-es': 'Actualizar', + 'fr-fr': 'Mise à jour', + 'uk-ua': 'оновлення', + 'de-ch': 'Aktualisieren', + 'pt-br': 'Atualizar', }, fullDate: { - "en-us": "Full Date", - "es-es": "Fecha completa", - "de-ch": "Vollständiges Datum", - "fr-fr": "Date complète", - "ru-ru": "Полная дата", - "uk-ua": "Розгорнути все", - "pt-br": "Data completa", + 'en-us': 'Full Date', + 'es-es': 'Fecha completa', + 'de-ch': 'Vollständiges Datum', + 'fr-fr': 'Date complète', + 'ru-ru': 'Полная дата', + 'uk-ua': 'Розгорнути все', + 'pt-br': 'Data completa', }, view: { - comment: "Verb", - "en-us": "View", - "ru-ru": "Вид", - "es-es": "Ver", - "fr-fr": "Voir", - "uk-ua": "Переглянути", - "de-ch": "Ansicht", - "pt-br": "Visualizar", + comment: 'Verb', + 'en-us': 'View', + 'ru-ru': 'Вид', + 'es-es': 'Ver', + 'fr-fr': 'Voir', + 'uk-ua': 'Переглянути', + 'de-ch': 'Ansicht', + 'pt-br': 'Visualizar', }, opensInNewTab: { - comment: "Used in a hover-over message for links that open in new tab", - "en-us": "(opens in a new tab)", - "ru-ru": "(открывается в новой вкладке)", - "es-es": "(se abre en una pestaña nueva)", - "uk-ua": "(відкривається в новій вкладці)", - "de-ch": "(Öffnet sich in einer neuen Registerkarte)", - "fr-fr": "(s'ouvre dans un nouvel onglet)", - "pt-br": "(abre em uma nova aba)", + comment: 'Used in a hover-over message for links that open in new tab', + 'en-us': '(opens in a new tab)', + 'ru-ru': '(открывается в новой вкладке)', + 'es-es': '(se abre en una pestaña nueva)', + 'uk-ua': '(відкривається в новій вкладці)', + 'de-ch': '(Öffnet sich in einer neuen Registerkarte)', + 'fr-fr': "(s'ouvre dans un nouvel onglet)", + 'pt-br': '(abre em uma nova aba)', }, openInNewTab: { - comment: "Used in a button that opens a link in a new tab", - "en-us": "Open in New Tab", - "ru-ru": "Открыть в новой вкладке", - "es-es": "Abrir en una pestaña nueva", - "fr-fr": "Ouvrir dans un nouvel onglet", - "uk-ua": "Відкрити в новій вкладці", - "de-ch": "Öffnet sich in einer neuen Registerkarte", - "pt-br": "Abrir em nova aba", + comment: 'Used in a button that opens a link in a new tab', + 'en-us': 'Open in New Tab', + 'ru-ru': 'Открыть в новой вкладке', + 'es-es': 'Abrir en una pestaña nueva', + 'fr-fr': 'Ouvrir dans un nouvel onglet', + 'uk-ua': 'Відкрити в новій вкладці', + 'de-ch': 'Öffnet sich in einer neuen Registerkarte', + 'pt-br': 'Abrir em nova aba', }, goToHomepage: { - "en-us": "Go to Home Page", - "ru-ru": "Перейти на домашнюю страницу", - "es-es": "Ir a la página de inicio", - "fr-fr": "Accéder à la page d'accueil", - "uk-ua": "Перейдіть на домашню сторінку", - "de-ch": "Zur Startseite gehen", - "pt-br": "Ir para a página inicial", + 'en-us': 'Go to Home Page', + 'ru-ru': 'Перейти на домашнюю страницу', + 'es-es': 'Ir a la página de inicio', + 'fr-fr': "Accéder à la page d'accueil", + 'uk-ua': 'Перейдіть на домашню сторінку', + 'de-ch': 'Zur Startseite gehen', + 'pt-br': 'Ir para a página inicial', }, actions: { - "en-us": "Actions", - "ru-ru": "Действия", - "es-es": "Comportamientos", - "fr-fr": "Actes", - "uk-ua": "Дії", - "de-ch": "Aktionen", - "pt-br": "Ações", + 'en-us': 'Actions', + 'ru-ru': 'Действия', + 'es-es': 'Comportamientos', + 'fr-fr': 'Actes', + 'uk-ua': 'Дії', + 'de-ch': 'Aktionen', + 'pt-br': 'Ações', }, chooseCollection: { - "en-us": "Choose Collection", - "ru-ru": "Выбрать коллекцию", - "es-es": "Elegir Colección", - "fr-fr": "Choisir une collection", - "uk-ua": "Виберіть колекцію", - "de-ch": "Sammlung auswählen", - "pt-br": "Escolha a coleção", + 'en-us': 'Choose Collection', + 'ru-ru': 'Выбрать коллекцию', + 'es-es': 'Elegir Colección', + 'fr-fr': 'Choisir une collection', + 'uk-ua': 'Виберіть колекцію', + 'de-ch': 'Sammlung auswählen', + 'pt-br': 'Escolha a coleção', }, ascending: { comment: 'As in "Ascending sort"', - "en-us": "Ascending", - "ru-ru": "По возрастанию", - "es-es": "Ascendente", - "fr-fr": "Ascendant", - "uk-ua": "Висхідний", - "de-ch": "Aufsteigend", - "pt-br": "Ascendente", + 'en-us': 'Ascending', + 'ru-ru': 'По возрастанию', + 'es-es': 'Ascendente', + 'fr-fr': 'Ascendant', + 'uk-ua': 'Висхідний', + 'de-ch': 'Aufsteigend', + 'pt-br': 'Ascendente', }, descending: { comment: 'As in "Descending sort"', - "en-us": "Descending", - "ru-ru": "По убыванию", - "es-es": "Descendente", - "fr-fr": "Descendant", - "uk-ua": "Спускається", - "de-ch": "Absteigend", - "pt-br": "Descendente", + 'en-us': 'Descending', + 'ru-ru': 'По убыванию', + 'es-es': 'Descendente', + 'fr-fr': 'Descendant', + 'uk-ua': 'Спускається', + 'de-ch': 'Absteigend', + 'pt-br': 'Descendente', }, recordSets: { - "en-us": "Record Sets", - "ru-ru": "Наборы записей", - "es-es": "Conjuntos de registros", - "fr-fr": "Ensembles de records", - "uk-ua": "Набори рекордів", - "de-ch": "Satzgruppen", - "pt-br": "Conjuntos de recordes", + 'en-us': 'Record Sets', + 'ru-ru': 'Наборы записей', + 'es-es': 'Conjuntos de registros', + 'fr-fr': 'Ensembles de records', + 'uk-ua': 'Набори рекордів', + 'de-ch': 'Satzgruppen', + 'pt-br': 'Conjuntos de recordes', }, recordCount: { - "en-us": "Record Count", - "ru-ru": "Количество записей", - "es-es": "Número de registros", - "fr-fr": "Nombre d'enregistrements", - "uk-ua": "Підрахунок записів", - "de-ch": "Anzahl der Datensätze", - "pt-br": "Contagem de recordes", + 'en-us': 'Record Count', + 'ru-ru': 'Количество записей', + 'es-es': 'Número de registros', + 'fr-fr': "Nombre d'enregistrements", + 'uk-ua': 'Підрахунок записів', + 'de-ch': 'Anzahl der Datensätze', + 'pt-br': 'Contagem de recordes', }, size: { - "en-us": "Size", - "ru-ru": "Размер", - "es-es": "Tamaño", - "fr-fr": "Taille", - "uk-ua": "Розмір", - "de-ch": "Grösse", - "pt-br": "Tamanho", + 'en-us': 'Size', + 'ru-ru': 'Размер', + 'es-es': 'Tamaño', + 'fr-fr': 'Taille', + 'uk-ua': 'Розмір', + 'de-ch': 'Grösse', + 'pt-br': 'Tamanho', }, running: { - "en-us": "Running…", - "ru-ru": "Бег…", - "es-es": "Ejecutando…", - "fr-fr": "En cours d'exécution…", - "uk-ua": "Виконується…", - "de-ch": "In Arbeit …", - "pt-br": "Correndo…", + 'en-us': 'Running…', + 'ru-ru': 'Бег…', + 'es-es': 'Ejecutando…', + 'fr-fr': "En cours d'exécution…", + 'uk-ua': 'Виконується…', + 'de-ch': 'In Arbeit …', + 'pt-br': 'Correndo…', }, noMatches: { - "en-us": "No Matches", - "ru-ru": "Нет совпадений", - "es-es": "No hay coincidencias", - "fr-fr": "Aucune correspondance", - "uk-ua": "Немає збігів", - "de-ch": "Keine Treffer", - "pt-br": "Nenhuma correspondência", + 'en-us': 'No Matches', + 'ru-ru': 'Нет совпадений', + 'es-es': 'No hay coincidencias', + 'fr-fr': 'Aucune correspondance', + 'uk-ua': 'Немає збігів', + 'de-ch': 'Keine Treffer', + 'pt-br': 'Nenhuma correspondência', }, searchQuery: { - "en-us": "Search Query", - "ru-ru": "Поисковый запрос", - "es-es": "Consulta de busqueda", - "fr-fr": "Requête de recherche", - "uk-ua": "Пошуковий запит", - "de-ch": "Suchabfrage", - "pt-br": "Consulta de pesquisa", + 'en-us': 'Search Query', + 'ru-ru': 'Поисковый запрос', + 'es-es': 'Consulta de busqueda', + 'fr-fr': 'Requête de recherche', + 'uk-ua': 'Пошуковий запит', + 'de-ch': 'Suchabfrage', + 'pt-br': 'Consulta de pesquisa', }, unknown: { - "en-us": "Unknown", - "ru-ru": "Неизвестный", - "es-es": "Desconocido", - "fr-fr": "Inconnu", - "uk-ua": "Невідомий", - "de-ch": "Unbekannt", - "pt-br": "Desconhecido", + 'en-us': 'Unknown', + 'ru-ru': 'Неизвестный', + 'es-es': 'Desconocido', + 'fr-fr': 'Inconnu', + 'uk-ua': 'Невідомий', + 'de-ch': 'Unbekannt', + 'pt-br': 'Desconhecido', }, language: { - "en-us": "Language", - "ru-ru": "Язык", - "es-es": "Idioma", - "fr-fr": "Langue", - "uk-ua": "Мова", - "de-ch": "Sprache", - "pt-br": "Linguagem", + 'en-us': 'Language', + 'ru-ru': 'Язык', + 'es-es': 'Idioma', + 'fr-fr': 'Langue', + 'uk-ua': 'Мова', + 'de-ch': 'Sprache', + 'pt-br': 'Linguagem', }, country: { - "en-us": "Country", - "ru-ru": "Страна", - "es-es": "País", - "fr-fr": "Pays", - "uk-ua": "Країна", - "de-ch": "Land", - "pt-br": "País", + 'en-us': 'Country', + 'ru-ru': 'Страна', + 'es-es': 'País', + 'fr-fr': 'Pays', + 'uk-ua': 'Країна', + 'de-ch': 'Land', + 'pt-br': 'País', }, viewRecord: { - "en-us": "View Record", - "ru-ru": "Просмотреть запись", - "es-es": "Ver registro", - "fr-fr": "Voir l'enregistrement", - "uk-ua": "Переглянути запис", - "de-ch": "Datensatz anzeigen", - "pt-br": "Ver registro", + 'en-us': 'View Record', + 'ru-ru': 'Просмотреть запись', + 'es-es': 'Ver registro', + 'fr-fr': "Voir l'enregistrement", + 'uk-ua': 'Переглянути запис', + 'de-ch': 'Datensatz anzeigen', + 'pt-br': 'Ver registro', }, nullInline: { - "en-us": "(null)", - "ru-ru": "(нулевой)", - "es-es": "(nulo)", - "fr-fr": "(nul)", - "uk-ua": "(нуль)", - "de-ch": "(null)", - "pt-br": "(nulo)", + 'en-us': '(null)', + 'ru-ru': '(нулевой)', + 'es-es': '(nulo)', + 'fr-fr': '(nul)', + 'uk-ua': '(нуль)', + 'de-ch': '(null)', + 'pt-br': '(nulo)', }, filePickerMessage: { - comment: "Generic. Could refer to any file", - "en-us": "Choose a file or drag it here", - "ru-ru": "Выберите файл или перетащите его сюда", - "es-es": "Elija un archivo o arrástrelo aquí", - "fr-fr": "Choisissez un fichier ou faites-le glisser ici", - "uk-ua": "Виберіть файл або перетягніть його сюди", - "de-ch": "Wählen eine Datei oder ziehen sie hierhin", - "pt-br": "Selecione um arquivo ou arraste-o aqui", + comment: 'Generic. Could refer to any file', + 'en-us': 'Choose a file or drag it here', + 'ru-ru': 'Выберите файл или перетащите его сюда', + 'es-es': 'Elija un archivo o arrástrelo aquí', + 'fr-fr': 'Choisissez un fichier ou faites-le glisser ici', + 'uk-ua': 'Виберіть файл або перетягніть його сюди', + 'de-ch': 'Wählen eine Datei oder ziehen sie hierhin', + 'pt-br': 'Selecione um arquivo ou arraste-o aqui', }, selectedFileName: { - "en-us": "Selected file", - "ru-ru": "Выбранный файл", - "es-es": "Fichero seleccionado", - "fr-fr": "Fichier sélectionné", - "uk-ua": "Вибраний файл", - "de-ch": "Gewählte Datei", - "pt-br": "Arquivo selecionado", + 'en-us': 'Selected file', + 'ru-ru': 'Выбранный файл', + 'es-es': 'Fichero seleccionado', + 'fr-fr': 'Fichier sélectionné', + 'uk-ua': 'Вибраний файл', + 'de-ch': 'Gewählte Datei', + 'pt-br': 'Arquivo selecionado', }, all: { - "en-us": "All", - "ru-ru": "Все", - "es-es": "Todo", - "fr-fr": "Tous", - "uk-ua": "всі", - "de-ch": "Alle", - "pt-br": "Todos", + 'en-us': 'All', + 'ru-ru': 'Все', + 'es-es': 'Todo', + 'fr-fr': 'Tous', + 'uk-ua': 'всі', + 'de-ch': 'Alle', + 'pt-br': 'Todos', }, unused: { - "en-us": "Unused", - "ru-ru": "Неиспользованный", - "es-es": "Sin usar", - "fr-fr": "Inutilisé", - "uk-ua": "Невикористаний", - "de-ch": "Unbenutzt", - "pt-br": "Não utilizado", + 'en-us': 'Unused', + 'ru-ru': 'Неиспользованный', + 'es-es': 'Sin usar', + 'fr-fr': 'Inutilisé', + 'uk-ua': 'Невикористаний', + 'de-ch': 'Unbenutzt', + 'pt-br': 'Não utilizado', }, ordinal: { - "en-us": "Ordinal", - "ru-ru": "Порядковый", - "es-es": "Ordinal", - "fr-fr": "Ordinal", - "uk-ua": "Порядковий", - "de-ch": "Reihenfolge", - "pt-br": "Ordinal", + 'en-us': 'Ordinal', + 'ru-ru': 'Порядковый', + 'es-es': 'Ordinal', + 'fr-fr': 'Ordinal', + 'uk-ua': 'Порядковий', + 'de-ch': 'Reihenfolge', + 'pt-br': 'Ordinal', }, export: { - "en-us": "Export", - "ru-ru": "Экспорт", - "es-es": "Exportar", - "fr-fr": "Exporter", - "uk-ua": "Експорт", - "de-ch": "Export", - "pt-br": "Exportar", + 'en-us': 'Export', + 'ru-ru': 'Экспорт', + 'es-es': 'Exportar', + 'fr-fr': 'Exporter', + 'uk-ua': 'Експорт', + 'de-ch': 'Export', + 'pt-br': 'Exportar', }, import: { - "en-us": "Import", - "ru-ru": "Импорт", - "es-es": "Importar", - "fr-fr": "Importer", - "uk-ua": "Масове вирішення", - "de-ch": "Import", - "pt-br": "Importar", + 'en-us': 'Import', + 'ru-ru': 'Импорт', + 'es-es': 'Importar', + 'fr-fr': 'Importer', + 'uk-ua': 'Масове вирішення', + 'de-ch': 'Import', + 'pt-br': 'Importar', }, dismiss: { - "en-us": "Dismiss", - "ru-ru": "Увольнять", - "es-es": "Descartar", - "fr-fr": "Rejeter", - "uk-ua": "Відхилити", - "de-ch": "Ablehnen", - "pt-br": "Liberar", + 'en-us': 'Dismiss', + 'ru-ru': 'Увольнять', + 'es-es': 'Descartar', + 'fr-fr': 'Rejeter', + 'uk-ua': 'Відхилити', + 'de-ch': 'Ablehnen', + 'pt-br': 'Liberar', }, id: { - "en-us": "ID", - "ru-ru": "ИДЕНТИФИКАТОР", - "es-es": "ID", - "fr-fr": "IDENTIFIANT", - "uk-ua": "ID", - "de-ch": "ID", - "pt-br": "EU IA", + 'en-us': 'ID', + 'ru-ru': 'ИДЕНТИФИКАТОР', + 'es-es': 'ID', + 'fr-fr': 'IDENTIFIANT', + 'uk-ua': 'ID', + 'de-ch': 'ID', + 'pt-br': 'EU IA', }, filter: { - "en-us": "Filter", - "ru-ru": "Фильтр", - "es-es": "Filtrar", - "fr-fr": "Filtre", - "uk-ua": "фільтр", - "de-ch": "Filter", - "pt-br": "Filtro", + 'en-us': 'Filter', + 'ru-ru': 'Фильтр', + 'es-es': 'Filtrar', + 'fr-fr': 'Filtre', + 'uk-ua': 'фільтр', + 'de-ch': 'Filter', + 'pt-br': 'Filtro', }, results: { - "en-us": "Results", - "ru-ru": "Результаты", - "es-es": "Resultados", - "fr-fr": "Résultats", - "uk-ua": "Результати", - "de-ch": "Resultate", - "pt-br": "Resultados", + 'en-us': 'Results', + 'ru-ru': 'Результаты', + 'es-es': 'Resultados', + 'fr-fr': 'Résultats', + 'uk-ua': 'Результати', + 'de-ch': 'Resultate', + 'pt-br': 'Resultados', }, downloadErrorMessage: { - "en-us": "Download Error Message", - "ru-ru": "Сообщение об ошибке загрузки", - "es-es": "Mensaje de error de descarga", - "fr-fr": "Télécharger le message d'erreur", - "uk-ua": "Завантажити повідомлення про помилку", - "de-ch": "Fehlermeldung herunterladen", - "pt-br": "Mensagem de erro de download", + 'en-us': 'Download Error Message', + 'ru-ru': 'Сообщение об ошибке загрузки', + 'es-es': 'Mensaje de error de descarga', + 'fr-fr': "Télécharger le message d'erreur", + 'uk-ua': 'Завантажити повідомлення про помилку', + 'de-ch': 'Fehlermeldung herunterladen', + 'pt-br': 'Mensagem de erro de download', }, copied: { - "en-us": "Copied!", - "ru-ru": "Скопировано!", - "es-es": "¡Copiado!", - "fr-fr": "Copié!", - "uk-ua": "Скопійовано!", - "de-ch": "Wurde kopiert!", - "pt-br": "Copiado!", + 'en-us': 'Copied!', + 'ru-ru': 'Скопировано!', + 'es-es': '¡Copiado!', + 'fr-fr': 'Copié!', + 'uk-ua': 'Скопійовано!', + 'de-ch': 'Wurde kopiert!', + 'pt-br': 'Copiado!', }, copyToClipboard: { - "en-us": "Copy to clipboard", - "ru-ru": "Копировать в буфер обмена", - "es-es": "Copiar al portapapeles", - "fr-fr": "Copier dans le presse-papiers", - "uk-ua": "Копіювати в буфер обміну", - "de-ch": "In Zwischenablage kopieren", - "pt-br": "Copiar para a área de transferência", + 'en-us': 'Copy to clipboard', + 'ru-ru': 'Копировать в буфер обмена', + 'es-es': 'Copiar al portapapeles', + 'fr-fr': 'Copier dans le presse-papiers', + 'uk-ua': 'Копіювати в буфер обміну', + 'de-ch': 'In Zwischenablage kopieren', + 'pt-br': 'Copiar para a área de transferência', }, selected: { - "en-us": "Selected", - "ru-ru": "Выбранный", - "es-es": "Seleccionado", - "fr-fr": "Choisi", - "uk-ua": "Вибране", - "de-ch": "Ausgewählt", - "pt-br": "Selecionado", + 'en-us': 'Selected', + 'ru-ru': 'Выбранный', + 'es-es': 'Seleccionado', + 'fr-fr': 'Choisi', + 'uk-ua': 'Вибране', + 'de-ch': 'Ausgewählt', + 'pt-br': 'Selecionado', }, expand: { - "en-us": "Expand", - "ru-ru": "Расширять", - "es-es": "Desplegar", - "fr-fr": "Développer", - "uk-ua": "Розгорнути", - "de-ch": "Aufklappen", - "pt-br": "Expandir", + 'en-us': 'Expand', + 'ru-ru': 'Расширять', + 'es-es': 'Desplegar', + 'fr-fr': 'Développer', + 'uk-ua': 'Розгорнути', + 'de-ch': 'Aufklappen', + 'pt-br': 'Expandir', }, expandAll: { - "en-us": "Expand All", - "ru-ru": "Развернуть все", - "es-es": "Desplegar todo", - "fr-fr": "Développer tout", - "uk-ua": "Розгорнути все", - "de-ch": "Alle aufklappen", - "pt-br": "Expandir tudo", + 'en-us': 'Expand All', + 'ru-ru': 'Развернуть все', + 'es-es': 'Desplegar todo', + 'fr-fr': 'Développer tout', + 'uk-ua': 'Розгорнути все', + 'de-ch': 'Alle aufklappen', + 'pt-br': 'Expandir tudo', }, collapse: { - "en-us": "Collapse", - "es-es": "Contraer", - "fr-fr": "Effondrement", - "ru-ru": "Крах", - "uk-ua": "Згорнути", - "de-ch": "Zuklappen", - "pt-br": "Colapso", + 'en-us': 'Collapse', + 'es-es': 'Contraer', + 'fr-fr': 'Effondrement', + 'ru-ru': 'Крах', + 'uk-ua': 'Згорнути', + 'de-ch': 'Zuklappen', + 'pt-br': 'Colapso', }, collapseAll: { - "en-us": "Collapse All", - "ru-ru": "Свернуть все", - "es-es": "Contraer todo", - "fr-fr": "Réduire tout", - "uk-ua": "Закрити все", - "de-ch": "Alle zuklappen", - "pt-br": "Recolher tudo", + 'en-us': 'Collapse All', + 'ru-ru': 'Свернуть все', + 'es-es': 'Contraer todo', + 'fr-fr': 'Réduire tout', + 'uk-ua': 'Закрити все', + 'de-ch': 'Alle zuklappen', + 'pt-br': 'Recolher tudo', }, reset: { - "en-us": "Reset", - "ru-ru": "Перезагрузить", - "es-es": "Reiniciar", - "fr-fr": "Réinitialiser", - "uk-ua": "Скинути", - "de-ch": "Zurücksetzen", - "pt-br": "Reiniciar", + 'en-us': 'Reset', + 'ru-ru': 'Перезагрузить', + 'es-es': 'Reiniciar', + 'fr-fr': 'Réinitialiser', + 'uk-ua': 'Скинути', + 'de-ch': 'Zurücksetzen', + 'pt-br': 'Reiniciar', }, select: { - "en-us": "Select", - "ru-ru": "Выбирать", - "es-es": "Seleccionar", - "fr-fr": "Sélectionner", - "uk-ua": "Виберіть", - "de-ch": "Auswählen", - "pt-br": "Selecione", + 'en-us': 'Select', + 'ru-ru': 'Выбирать', + 'es-es': 'Seleccionar', + 'fr-fr': 'Sélectionner', + 'uk-ua': 'Виберіть', + 'de-ch': 'Auswählen', + 'pt-br': 'Selecione', }, none: { - "en-us": "None", - "ru-ru": "Никто", - "es-es": "Ninguno", - "fr-fr": "Aucun", - "uk-ua": "Жодного", - "de-ch": "Keine", - "pt-br": "Nenhum", + 'en-us': 'None', + 'ru-ru': 'Никто', + 'es-es': 'Ninguno', + 'fr-fr': 'Aucun', + 'uk-ua': 'Жодного', + 'de-ch': 'Keine', + 'pt-br': 'Nenhum', }, noneAvailable: { - "en-us": "None available", - "ru-ru": "Нет доступных", - "es-es": "Ninguno disponible", - "fr-fr": "Aucun disponible", - "uk-ua": "Немає доступних", - "de-ch": "Keine Verfügbar", - "pt-br": "Nenhum disponível", + 'en-us': 'None available', + 'ru-ru': 'Нет доступных', + 'es-es': 'Ninguno disponible', + 'fr-fr': 'Aucun disponible', + 'uk-ua': 'Немає доступних', + 'de-ch': 'Keine Verfügbar', + 'pt-br': 'Nenhum disponível', }, countLine: { - comment: "Example usage: Record Sets (1,234)", - "en-us": "{resource:string} ({count:number|formatted})", - "ru-ru": "{resource:string} ({count:number|formatted})", - "es-es": "{resource:string} ({count:number|formatted})", - "fr-fr": "{resource:string} ({count:number|formatted})", - "uk-ua": "{resource:string}({count:number|formatted})", - "de-ch": "{resource:string} ({count:number|formatted})", - "pt-br": "{resource:string} ({count:number|formatted})", + comment: 'Example usage: Record Sets (1,234)', + 'en-us': '{resource:string} ({count:number|formatted})', + 'ru-ru': '{resource:string} ({count:number|formatted})', + 'es-es': '{resource:string} ({count:number|formatted})', + 'fr-fr': '{resource:string} ({count:number|formatted})', + 'uk-ua': '{resource:string}({count:number|formatted})', + 'de-ch': '{resource:string} ({count:number|formatted})', + 'pt-br': '{resource:string} ({count:number|formatted})', }, jsxCountLine: { - comment: "Example usage: Record Sets (1,234)", - "en-us": "{resource:string} ({count:number|formatted})", - "ru-ru": "{resource:string} ({count:number|formatted})", - "es-es": "{resource:string} ({count:number|formatted})", - "fr-fr": "{resource:string} ({count:number|formatted})", - "uk-ua": "{resource:string} ({count:number|formatted})'", - "de-ch": "{resource:string} ({count:number|formatted})", - "pt-br": "{resource:string} ({count:number|formatted})", + comment: 'Example usage: Record Sets (1,234)', + 'en-us': '{resource:string} ({count:number|formatted})', + 'ru-ru': '{resource:string} ({count:number|formatted})', + 'es-es': '{resource:string} ({count:number|formatted})', + 'fr-fr': '{resource:string} ({count:number|formatted})', + 'uk-ua': "{resource:string} ({count:number|formatted})'", + 'de-ch': '{resource:string} ({count:number|formatted})', + 'pt-br': '{resource:string} ({count:number|formatted})', }, colonHeader: { comment: ` Example usage: "Choose collection:". Used only if there is nothing else on this line after the colon heading `, - "en-us": "{header:string}:", - "ru-ru": "{header:string}:", - "es-es": "{header:string}:", - "fr-fr": "{header:string}:", - "uk-ua": "{header:string}':", - "de-ch": "{header:string}:", - "pt-br": "{header:string}:", + 'en-us': '{header:string}:', + 'ru-ru': '{header:string}:', + 'es-es': '{header:string}:', + 'fr-fr': '{header:string}:', + 'uk-ua': "{header:string}':", + 'de-ch': '{header:string}:', + 'pt-br': '{header:string}:', }, colonLine: { comment: ` Example usage: "Created by: Full Name" OR "Record Set: Record Set Name" `, - "en-us": "{label:string}: {value:string}", - "ru-ru": "{label:string}: {value:string}", - "es-es": "{label:string}: {value:string}", - "fr-fr": "{label:string}: {value:string}", - "uk-ua": "{label:string}: {value:string}'", - "de-ch": "{label:string}: {value:string}", - "pt-br": "{label:string}: {value:string}", + 'en-us': '{label:string}: {value:string}', + 'ru-ru': '{label:string}: {value:string}', + 'es-es': '{label:string}: {value:string}', + 'fr-fr': '{label:string}: {value:string}', + 'uk-ua': "{label:string}: {value:string}'", + 'de-ch': '{label:string}: {value:string}', + 'pt-br': '{label:string}: {value:string}', }, jsxColonLine: { comment: ` Example usage: "Created by: Full Name" OR "Record Set: Record Set Name" `, - "en-us": "{label:string}: ", - "ru-ru": "{label:string}: ", - "es-es": "{label:string}: ", - "fr-fr": "{label:string}: ", - "uk-ua": "{label:string}: ", - "de-ch": "{label:string}: ", - "pt-br": "{label:string}: ", + 'en-us': '{label:string}: ', + 'ru-ru': '{label:string}: ', + 'es-es': '{label:string}: ', + 'fr-fr': '{label:string}: ', + 'uk-ua': '{label:string}: ', + 'de-ch': '{label:string}: ', + 'pt-br': '{label:string}: ', }, bulkSelect: { - "en-us": "Bulk Select", - "es-es": "Selección masiva", - "de-ch": "Mehrfachauswahl", - "fr-fr": "Sélection en vrac", - "ru-ru": "Массовый выбор", - "uk-ua": "Масовий вибір", - "pt-br": "Seleção em massa", + 'en-us': 'Bulk Select', + 'es-es': 'Selección masiva', + 'de-ch': 'Mehrfachauswahl', + 'fr-fr': 'Sélection en vrac', + 'ru-ru': 'Массовый выбор', + 'uk-ua': 'Масовий вибір', + 'pt-br': 'Seleção em massa', }, bulkReturn: { - "en-us": "Bulk Return", - "de-ch": "Massenrücksendung", - "es-es": "Devolución masiva", - "fr-fr": "Retour en vrac", - "ru-ru": "Массовый возврат", - "uk-ua": "Масове повернення", - "pt-br": "Devolução em massa", + 'en-us': 'Bulk Return', + 'de-ch': 'Massenrücksendung', + 'es-es': 'Devolución masiva', + 'fr-fr': 'Retour en vrac', + 'ru-ru': 'Массовый возврат', + 'uk-ua': 'Масове повернення', + 'pt-br': 'Devolução em massa', }, bulkResolve: { - "en-us": "Bulk Resolve", - "de-ch": "Massenauflösung", - "es-es": "Resolución masiva", - "fr-fr": "Résolution en masse", - "ru-ru": "Массовое решение", - "uk-ua": "Масове вирішення", - "pt-br": "Resolução em massa", + 'en-us': 'Bulk Resolve', + 'de-ch': 'Massenauflösung', + 'es-es': 'Resolución masiva', + 'fr-fr': 'Résolution en masse', + 'ru-ru': 'Массовое решение', + 'uk-ua': 'Масове вирішення', + 'pt-br': 'Resolução em massa', }, timeRemaining: { - "en-us": "Time remaining", - "es-es": "Tiempo restante", - "fr-fr": "Temps restant", - "ru-ru": "Оставшееся время", - "uk-ua": "Час, що залишився", - "de-ch": "Noch verbleibende Zeit", - "pt-br": "Tempo restante", + 'en-us': 'Time remaining', + 'es-es': 'Tiempo restante', + 'fr-fr': 'Temps restant', + 'ru-ru': 'Оставшееся время', + 'uk-ua': 'Час, що залишився', + 'de-ch': 'Noch verbleibende Zeit', + 'pt-br': 'Tempo restante', }, unlimited: { - "en-us": "Unlimited", - "de-ch": "Unbegrenzt", - "es-es": "Ilimitado", - "fr-fr": "Illimité", - "ru-ru": "Безлимитный", - "uk-ua": "Необмежений", - "pt-br": "Ilimitado", + 'en-us': 'Unlimited', + 'de-ch': 'Unbegrenzt', + 'es-es': 'Ilimitado', + 'fr-fr': 'Illimité', + 'ru-ru': 'Безлимитный', + 'uk-ua': 'Необмежений', + 'pt-br': 'Ilimitado', }, change: { - comment: "Verb", - "en-us": "Change", - "de-ch": "Ändern", - "es-es": "Cambiar", - "fr-fr": "Changement", - "ru-ru": "Изменять", - "uk-ua": "Зміна", - "pt-br": "Mudar", + comment: 'Verb', + 'en-us': 'Change', + 'de-ch': 'Ändern', + 'es-es': 'Cambiar', + 'fr-fr': 'Changement', + 'ru-ru': 'Изменять', + 'uk-ua': 'Зміна', + 'pt-br': 'Mudar', }, dontShowAgain: { - "en-us": "Don't show this again", - "es-es": "No volver a mostrar esto", - "fr-fr": "Ne plus afficher ceci", - "ru-ru": "Больше не показывать", - "uk-ua": "Не показувати це знову", - "de-ch": "Dies nicht mehr anzeigen", - "pt-br": "Não mostrar isso novamente", + 'en-us': "Don't show this again", + 'es-es': 'No volver a mostrar esto', + 'fr-fr': 'Ne plus afficher ceci', + 'ru-ru': 'Больше не показывать', + 'uk-ua': 'Не показувати це знову', + 'de-ch': 'Dies nicht mehr anzeigen', + 'pt-br': 'Não mostrar isso novamente', }, multipleFilePickerMessage: { - "en-us": "Choose files or drag them here", - "de-ch": "Wählen Sie Dateien aus oder ziehen Sie sie hierher", - "es-es": "Seleccione los archivos o arrástrelos hasta aquí", - "fr-fr": "Choisissez des fichiers ou faites-les glisser ici", - "ru-ru": "Выберите файлы или перетащите их сюда", - "uk-ua": "Виберіть файли або перетягніть їх сюди", - "pt-br": "Selecione os arquivos ou arraste-os para cá", + 'en-us': 'Choose files or drag them here', + 'de-ch': 'Wählen Sie Dateien aus oder ziehen Sie sie hierher', + 'es-es': 'Seleccione los archivos o arrástrelos hasta aquí', + 'fr-fr': 'Choisissez des fichiers ou faites-les glisser ici', + 'ru-ru': 'Выберите файлы или перетащите их сюда', + 'uk-ua': 'Виберіть файли або перетягніть їх сюди', + 'pt-br': 'Selecione os arquivos ou arraste-os para cá', }, zoom: { - "en-us": "Zoom", - "fr-fr": "Zoom", - "de-ch": "Zoom", - "es-es": "Zoom", - "pt-br": "Ampliação", - "ru-ru": "Увеличить", - "uk-ua": "Збільшити масштаб", + 'en-us': 'Zoom', + 'fr-fr': 'Zoom', + 'de-ch': 'Zoom', + 'es-es': 'Zoom', + 'pt-br': 'Ampliação', + 'ru-ru': 'Увеличить', + 'uk-ua': 'Збільшити масштаб', }, unzoom: { - "en-us": "Unzoom", - "de-ch": "Zoomen aufheben", - "es-es": "Desenfocar", - "fr-fr": "Dézoom", - "pt-br": "Desacelerar", - "ru-ru": "Убрать масштаб", - "uk-ua": "Зменшити масштаб", + 'en-us': 'Unzoom', + 'de-ch': 'Zoomen aufheben', + 'es-es': 'Desenfocar', + 'fr-fr': 'Dézoom', + 'pt-br': 'Desacelerar', + 'ru-ru': 'Убрать масштаб', + 'uk-ua': 'Зменшити масштаб', }, } as const); diff --git a/specifyweb/frontend/js_src/lib/localization/preferences.ts b/specifyweb/frontend/js_src/lib/localization/preferences.ts index d7fc0a26396..0a802fd5cd7 100644 --- a/specifyweb/frontend/js_src/lib/localization/preferences.ts +++ b/specifyweb/frontend/js_src/lib/localization/preferences.ts @@ -4,2103 +4,2103 @@ * @module */ -import { createDictionary } from "./utils"; +import { createDictionary } from './utils'; // Refer to "Guidelines for Programmers" in ./README.md before editing this file 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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + 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", + 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", + '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.", + '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", + '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": + '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).", + '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", + '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", + '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", + '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", + '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", + '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", + '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": + '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": + '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.", + '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)", + '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", + '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", + '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", + '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", + '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", + '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", + '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)", + '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)", + '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)", + '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)", + '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", + '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", + '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)", + '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)", + '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", + '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.", + '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", + '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", + '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", + '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", + '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", + '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": + '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.", + '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", + '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", + '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", + '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", + '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": + '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": + '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.", + '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": + '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.", + '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", + '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", + '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)", + '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)", + '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.", + '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)", + '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.", + '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)", + '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)", + '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).", + '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).", + '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": + '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.", + '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", + '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": + '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.", + '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", + '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", + '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.", + '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", + '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", + '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", + '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", + '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", + '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", + '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:", + '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", + '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", + '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": + '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.", + '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": + '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.", + '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": + '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.", + '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", + '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", + '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", + '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", + '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", + '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)", + '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)", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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.", + '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", + '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", + '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", + '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.", + '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", + '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", + '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", + '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": + '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.", + '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", + '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", + '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"', + '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"', + '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", + '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", + '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": + '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.", + '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": + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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": "Показати межі навколо підвидів", + '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", + '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", + '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", + '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.", + '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", + '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", + '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.", + '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": + '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", + '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", + '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": "Сортувати за полем", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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": + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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.", + '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.", + '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", + 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", + '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", + '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", + '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", + '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", + '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", + '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", + '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": + 'en-us': 'Show an "*" in the tab title when there are unsaved changes in the current tab.', - "es-es": + 'es-es': 'Mostrar un "*" en el título de la pestaña cuando haya cambios sin guardar en la pestaña actual.', - "fr-fr": + '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": + '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": + '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.", + '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}", + '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": + '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.", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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.", + '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", + '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", + '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", + '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.", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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", + '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": "Показати оператори порівняння для текстових полів", + '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": + '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": - "Дозволяє застосовувати до текстових полів такі фільтри: «Більше ніж», «Менше ніж», «Більше або дорівнює» та «Менше або дорівнює»", + '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", + '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", + '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", + '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", + '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", + '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", + '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": + '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": - "Увімкнути успадкування основного каталожного номера його порожнім братам і сестрам.", + '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": + '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": - "Увімкнути успадкування батьківського каталожного номера його порожнім дочірнім елементам.", + '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 exclusivo nas tabelas Componente e CO", - "ru-ru": - "Поле «Номер каталога» должно быть уникальным в таблицах «Компонент» и «CO».", - "uk-ua": - "Поле «Номер у каталозі» має бути унікальним у таблицях «Компонент» та «CO».", + '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); diff --git a/specifyweb/frontend/js_src/lib/localization/query.ts b/specifyweb/frontend/js_src/lib/localization/query.ts index 3fe1f816ad0..6cb86bf80f1 100644 --- a/specifyweb/frontend/js_src/lib/localization/query.ts +++ b/specifyweb/frontend/js_src/lib/localization/query.ts @@ -4,774 +4,774 @@ * @module */ -import { createDictionary } from "./utils"; +import { createDictionary } from './utils'; // Refer to "Guidelines for Programmers" in ./README.md before editing this file export const queryText = createDictionary({ query: { - "en-us": "Query", - "ru-ru": "Запрос", - "es-es": "Consulta", - "fr-fr": "Requête", - "uk-ua": "Запит", - "de-ch": "Abfrage", - "pt-br": "Consulta", + 'en-us': 'Query', + 'ru-ru': 'Запрос', + 'es-es': 'Consulta', + 'fr-fr': 'Requête', + 'uk-ua': 'Запит', + 'de-ch': 'Abfrage', + 'pt-br': 'Consulta', }, queries: { - "en-us": "Queries", - "ru-ru": "Запросы", - "es-es": "Consultas", - "fr-fr": "Requêtes", - "uk-ua": "Запити", - "de-ch": "Abfragen", - "pt-br": "Consultas", + 'en-us': 'Queries', + 'ru-ru': 'Запросы', + 'es-es': 'Consultas', + 'fr-fr': 'Requêtes', + 'uk-ua': 'Запити', + 'de-ch': 'Abfragen', + 'pt-br': 'Consultas', }, queryBuilder: { - "en-us": "Query Builder", - "ru-ru": "Конструктор запросов", - "es-es": "Generador de consultas", - "fr-fr": "Générateur de requêtes", - "uk-ua": "Конструктор запитів", - "de-ch": "Query Builder", - "pt-br": "Construtor de consultas", + 'en-us': 'Query Builder', + 'ru-ru': 'Конструктор запросов', + 'es-es': 'Generador de consultas', + 'fr-fr': 'Générateur de requêtes', + 'uk-ua': 'Конструктор запитів', + 'de-ch': 'Query Builder', + 'pt-br': 'Construtor de consultas', }, newQueryName: { - "en-us": "New Query", - "es-es": "Nueva consulta", - "uk-ua": "Новий запит", - "de-ch": "Neue Abfrage", - "fr-fr": "Nouvelle requête", - "ru-ru": "Новый запрос", - "pt-br": "Nova consulta", + 'en-us': 'New Query', + 'es-es': 'Nueva consulta', + 'uk-ua': 'Новий запит', + 'de-ch': 'Neue Abfrage', + 'fr-fr': 'Nouvelle requête', + 'ru-ru': 'Новый запрос', + 'pt-br': 'Nova consulta', }, searchFields: { comment: ` Used in a Query Combo Box's hover-over message to show which fields are being searched on `, - "en-us": "Searched fields", - "ru-ru": "Поля поиска", - "es-es": "Campos buscados", - "fr-fr": "Champs recherchés", - "uk-ua": "Пошукові поля", - "de-ch": "Durchsuchte Felder", - "pt-br": "Campos pesquisados", + 'en-us': 'Searched fields', + 'ru-ru': 'Поля поиска', + 'es-es': 'Campos buscados', + 'fr-fr': 'Champs recherchés', + 'uk-ua': 'Пошукові поля', + 'de-ch': 'Durchsuchte Felder', + 'pt-br': 'Campos pesquisados', }, any: { - "en-us": "Any", - "ru-ru": "Любой", - "es-es": "Cualquiera", - "fr-fr": "N'importe lequel", - "uk-ua": "Будь-який", - "de-ch": "Beliebig", - "pt-br": "Qualquer", + 'en-us': 'Any', + 'ru-ru': 'Любой', + 'es-es': 'Cualquiera', + 'fr-fr': "N'importe lequel", + 'uk-ua': 'Будь-який', + 'de-ch': 'Beliebig', + 'pt-br': 'Qualquer', }, startValue: { - "en-us": "Start Value", - "ru-ru": "Начальное значение", - "es-es": "Valor inicial", - "fr-fr": "Valeur de départ", - "uk-ua": "Початкове значення", - "de-ch": "Startwert", - "pt-br": "Valor inicial", + 'en-us': 'Start Value', + 'ru-ru': 'Начальное значение', + 'es-es': 'Valor inicial', + 'fr-fr': 'Valeur de départ', + 'uk-ua': 'Початкове значення', + 'de-ch': 'Startwert', + 'pt-br': 'Valor inicial', }, endValue: { - "en-us": "End Value", - "ru-ru": "Конечное значение", - "es-es": "Valor final", - "fr-fr": "Valeur finale", - "uk-ua": "Кінцеве значення", - "de-ch": "Endwert", - "pt-br": "Valor final", + 'en-us': 'End Value', + 'ru-ru': 'Конечное значение', + 'es-es': 'Valor final', + 'fr-fr': 'Valeur finale', + 'uk-ua': 'Кінцеве значення', + 'de-ch': 'Endwert', + 'pt-br': 'Valor final', }, saveQuery: { - "en-us": "Save Query", - "ru-ru": "Сохранить запрос", - "es-es": "Guardar consulta", - "fr-fr": "Enregistrer la requête", - "uk-ua": "Зберегти запит", - "de-ch": "Abfrage speichern", - "pt-br": "Salvar consulta", + 'en-us': 'Save Query', + 'ru-ru': 'Сохранить запрос', + 'es-es': 'Guardar consulta', + 'fr-fr': 'Enregistrer la requête', + 'uk-ua': 'Зберегти запит', + 'de-ch': 'Abfrage speichern', + 'pt-br': 'Salvar consulta', }, saveClonedQuery: { - "en-us": "Save query as...", - "ru-ru": "Сохранить запрос как...", - "es-es": "Guardar consulta como...", - "fr-fr": "Enregistrer la requête sous...", - "uk-ua": "Зберегти запит як...", - "de-ch": "Abfrage speichern unter...", - "pt-br": "Salvar consulta como...", + 'en-us': 'Save query as...', + 'ru-ru': 'Сохранить запрос как...', + 'es-es': 'Guardar consulta como...', + 'fr-fr': 'Enregistrer la requête sous...', + 'uk-ua': 'Зберегти запит як...', + 'de-ch': 'Abfrage speichern unter...', + 'pt-br': 'Salvar consulta como...', }, saveClonedQueryDescription: { - "en-us": - "The query will be saved with a new name leaving the current query unchanged.", - "ru-ru": - "Запрос будет сохранен под новым именем, текущий запрос останется без изменений.", - "es-es": - "La consulta se guardará con un nuevo nombre dejando la consulta actual sin cambios.", - "fr-fr": - "La requête sera enregistrée avec un nouveau nom, laissant la requête actuelle inchangée.", - "uk-ua": - "Запит буде збережено з новою назвою, а поточний запит залишиться без змін.", - "de-ch": - "Die Abfrage wird unter einem neuen Namen gespeichert, die aktuelle Abfrage bleibt unverändert.", - "pt-br": - "A consulta será salva com um novo nome, deixando a consulta atual inalterada.", + 'en-us': + 'The query will be saved with a new name leaving the current query unchanged.', + 'ru-ru': + 'Запрос будет сохранен под новым именем, текущий запрос останется без изменений.', + 'es-es': + 'La consulta se guardará con un nuevo nombre dejando la consulta actual sin cambios.', + 'fr-fr': + 'La requête sera enregistrée avec un nouveau nom, laissant la requête actuelle inchangée.', + 'uk-ua': + 'Запит буде збережено з новою назвою, а поточний запит залишиться без змін.', + 'de-ch': + 'Die Abfrage wird unter einem neuen Namen gespeichert, die aktuelle Abfrage bleibt unverändert.', + 'pt-br': + 'A consulta será salva com um novo nome, deixando a consulta atual inalterada.', }, queryDeleteIncomplete: { - "en-us": "Query definition contains incomplete fields", - "ru-ru": "Определение запроса содержит неполные поля", - "es-es": "La definición de consulta contiene campos incompletos", - "fr-fr": "La définition de la requête contient des champs incomplets", - "uk-ua": "Визначення запиту містить незаповнені поля", - "de-ch": "Abfragedefinition enthält unvollständige Felder", - "pt-br": "A definição da consulta contém campos incompletos", + 'en-us': 'Query definition contains incomplete fields', + 'ru-ru': 'Определение запроса содержит неполные поля', + 'es-es': 'La definición de consulta contiene campos incompletos', + 'fr-fr': 'La définition de la requête contient des champs incomplets', + 'uk-ua': 'Визначення запиту містить незаповнені поля', + 'de-ch': 'Abfragedefinition enthält unvollständige Felder', + 'pt-br': 'A definição da consulta contém campos incompletos', }, queryDeleteIncompleteDescription: { - "en-us": - "There are uncompleted fields in the query definition. Do you want to remove them?", - "ru-ru": - "В определении запроса есть незаполненные поля. Хотите их удалить?", - "es-es": - "Hay campos sin completar en la definición de la consulta. ¿Desea eliminarlos?", - "fr-fr": - "Il y a des champs incomplets dans la définition de la requête. Voulez-vous les supprimer ?", - "uk-ua": "У визначенні запиту є незаповнені поля. Ви хочете видалити їх?", - "de-ch": - "Die Abfragedefinition enthält unvollständige Felder. Möchten Sie diese entfernen?", - "pt-br": - "Há campos incompletos na definição da consulta. Deseja removê-los?", + 'en-us': + 'There are uncompleted fields in the query definition. Do you want to remove them?', + 'ru-ru': + 'В определении запроса есть незаполненные поля. Хотите их удалить?', + 'es-es': + 'Hay campos sin completar en la definición de la consulta. ¿Desea eliminarlos?', + 'fr-fr': + 'Il y a des champs incomplets dans la définition de la requête. Voulez-vous les supprimer ?', + 'uk-ua': 'У визначенні запиту є незаповнені поля. Ви хочете видалити їх?', + 'de-ch': + 'Die Abfragedefinition enthält unvollständige Felder. Möchten Sie diese entfernen?', + 'pt-br': + 'Há campos incompletos na definição da consulta. Deseja removê-los?', }, queryUnloadProtect: { - "en-us": "The new or modified query definition has not been saved", - "ru-ru": "Новое или измененное определение запроса не было сохранено.", - "es-es": "La definición de consulta nueva o modificada no se ha guardado", - "fr-fr": + 'en-us': 'The new or modified query definition has not been saved', + 'ru-ru': 'Новое или измененное определение запроса не было сохранено.', + 'es-es': 'La definición de consulta nueva o modificada no se ha guardado', + 'fr-fr': "La définition de requête nouvelle ou modifiée n'a pas été enregistrée", - "uk-ua": "Нове або змінене визначення запиту не було збережено", - "de-ch": - "Die neue oder geänderte Abfragedefinition wurde nicht gespeichert", - "pt-br": "A definição de consulta nova ou modificada não foi salva", + 'uk-ua': 'Нове або змінене визначення запиту не було збережено', + 'de-ch': + 'Die neue oder geänderte Abfragedefinition wurde nicht gespeichert', + 'pt-br': 'A definição de consulta nova ou modificada não foi salva', }, recordSetToQuery: { - comment: "Example: Creating a Record Set from Query", - "en-us": "Creating a {recordSetTable:string} from Query", - "ru-ru": "Создание {recordSetTable:string} из запроса", - "es-es": "Creando un {recordSetTable:string} a partir de una consulta", - "fr-fr": "Création d'un {recordSetTable:string} à partir d'une requête", - "uk-ua": "Створення {recordSetTable:string} із запиту", - "de-ch": "Erstellen eines {recordSetTable:string} aus einer Abfrage", - "pt-br": "Criando um {recordSetTable:string} a partir da consulta", + comment: 'Example: Creating a Record Set from Query', + 'en-us': 'Creating a {recordSetTable:string} from Query', + 'ru-ru': 'Создание {recordSetTable:string} из запроса', + 'es-es': 'Creando un {recordSetTable:string} a partir de una consulta', + 'fr-fr': "Création d'un {recordSetTable:string} à partir d'une requête", + 'uk-ua': 'Створення {recordSetTable:string} із запиту', + 'de-ch': 'Erstellen eines {recordSetTable:string} aus einer Abfrage', + 'pt-br': 'Criando um {recordSetTable:string} a partir da consulta', }, recordSetToQueryDescription: { - "en-us": "Generating {recordSetTable:string}...", - "ru-ru": "Генерация {recordSetTable:string}...", - "es-es": "Generando {recordSetTable:string}...", - "fr-fr": "Génération de {recordSetTable:string}...", - "uk-ua": "Створення {recordSetTable:string}...", - "de-ch": "{recordSetTable:string} wird generiert...", - "pt-br": "Gerando {recordSetTable:string}...", + 'en-us': 'Generating {recordSetTable:string}...', + 'ru-ru': 'Генерация {recordSetTable:string}...', + 'es-es': 'Generando {recordSetTable:string}...', + 'fr-fr': 'Génération de {recordSetTable:string}...', + 'uk-ua': 'Створення {recordSetTable:string}...', + 'de-ch': '{recordSetTable:string} wird generiert...', + 'pt-br': 'Gerando {recordSetTable:string}...', }, recordSetCreated: { - "en-us": "{recordSetTable:string} Created", - "ru-ru": "{recordSetTable:string} Создано", - "es-es": "{recordSetTable:string} Creado", - "fr-fr": "{recordSetTable:string} Créé", - "uk-ua": "{recordSetTable:string} Створено", - "de-ch": "{recordSetTable:string} Erstellt", - "pt-br": "{recordSetTable:string} Criado", + 'en-us': '{recordSetTable:string} Created', + 'ru-ru': '{recordSetTable:string} Создано', + 'es-es': '{recordSetTable:string} Creado', + 'fr-fr': '{recordSetTable:string} Créé', + 'uk-ua': '{recordSetTable:string} Створено', + 'de-ch': '{recordSetTable:string} Erstellt', + 'pt-br': '{recordSetTable:string} Criado', }, missingCoordinatesForKml: { - "en-us": "Unable to export to KML", - "ru-ru": "Невозможно экспортировать в KML", - "es-es": "No se puede exportar a KML", - "fr-fr": "Impossible d'exporter vers KML", - "uk-ua": "Не вдалося експортувати в KML", - "de-ch": "Export in KML nicht möglich", - "pt-br": "Não é possível exportar para KML", + 'en-us': 'Unable to export to KML', + 'ru-ru': 'Невозможно экспортировать в KML', + 'es-es': 'No se puede exportar a KML', + 'fr-fr': "Impossible d'exporter vers KML", + 'uk-ua': 'Не вдалося експортувати в KML', + 'de-ch': 'Export in KML nicht möglich', + 'pt-br': 'Não é possível exportar para KML', }, missingCoordinatesForKmlDescription: { - "en-us": "Please add latitude and longitude fields to the query.", - "ru-ru": "Пожалуйста, добавьте в запрос поля широты и долготы.", - "es-es": "Agregue campos de latitud y longitud a la consulta.", - "fr-fr": - "Veuillez ajouter les champs de latitude et de longitude à la requête.", - "uk-ua": "Будь ласка, додайте поля широти та довготи до запиту.", - "de-ch": "Bitte fügen Sie der Abfrage Breiten- und Längengradfelder hinzu.", - "pt-br": "Adicione campos de latitude e longitude à consulta.", + 'en-us': 'Please add latitude and longitude fields to the query.', + 'ru-ru': 'Пожалуйста, добавьте в запрос поля широты и долготы.', + 'es-es': 'Agregue campos de latitud y longitud a la consulta.', + 'fr-fr': + 'Veuillez ajouter les champs de latitude et de longitude à la requête.', + 'uk-ua': 'Будь ласка, додайте поля широти та довготи до запиту.', + 'de-ch': 'Bitte fügen Sie der Abfrage Breiten- und Längengradfelder hinzu.', + 'pt-br': 'Adicione campos de latitude e longitude à consulta.', }, queryExportStarted: { - "en-us": "Export File Being Created", - "ru-ru": "Создается экспортный файл", - "es-es": "Creando archivo de exportación", - "fr-fr": "Fichier d'exportation en cours de création", - "uk-ua": "Експортний файл створюється", - "de-ch": "Exportdatei wird erstellt", - "pt-br": "Arquivo de exportação sendo criado", + 'en-us': 'Export File Being Created', + 'ru-ru': 'Создается экспортный файл', + 'es-es': 'Creando archivo de exportación', + 'fr-fr': "Fichier d'exportation en cours de création", + 'uk-ua': 'Експортний файл створюється', + 'de-ch': 'Exportdatei wird erstellt', + 'pt-br': 'Arquivo de exportação sendo criado', }, queryExportStartedDescription: { - "en-us": - "A notification will appear when the export file is complete and ready for download.", - "es-es": - "Aparecerá una notificación cuando el archivo de exportación esté completo y listo para descargar.", - "uk-ua": - "Коли файл експорту буде завершено та готовий до завантаження, з’явиться сповіщення.", - "de-ch": - "Wenn die Exportdatei vollständig ist und zum Download bereit steht, wird eine Benachrichtigung angezeigt.", - "fr-fr": + 'en-us': + 'A notification will appear when the export file is complete and ready for download.', + 'es-es': + 'Aparecerá una notificación cuando el archivo de exportación esté completo y listo para descargar.', + 'uk-ua': + 'Коли файл експорту буде завершено та готовий до завантаження, з’явиться сповіщення.', + 'de-ch': + 'Wenn die Exportdatei vollständig ist und zum Download bereit steht, wird eine Benachrichtigung angezeigt.', + 'fr-fr': "Une notification apparaîtra lorsque le fichier d'exportation sera terminé et prêt à être téléchargé.", - "ru-ru": - "Когда файл экспорта будет завершен и готов к загрузке, появится уведомление.", - "pt-br": - "Uma notificação aparecerá quando o arquivo de exportação estiver concluído e pronto para download.", + 'ru-ru': + 'Когда файл экспорта будет завершен и готов к загрузке, появится уведомление.', + 'pt-br': + 'Uma notificação aparecerá quando o arquivo de exportação estiver concluído e pronto para download.', }, invalidPicklistValue: { - comment: "Used when selected pick list value is not one of allowed values", - "en-us": "{value:string} (current, invalid value)", - "ru-ru": "{value:string} (текущее, недопустимое значение)", - "es-es": "{value:string} (valor actual, no válido)", - "fr-fr": "{value:string} (valeur actuelle, non valide)", - "uk-ua": "{value:string} (поточне, недійсне значення)", - "de-ch": "{value:string} (aktueller, ungültiger Wert)", - "pt-br": "{value:string} (valor atual, inválido)", + comment: 'Used when selected pick list value is not one of allowed values', + 'en-us': '{value:string} (current, invalid value)', + 'ru-ru': '{value:string} (текущее, недопустимое значение)', + 'es-es': '{value:string} (valor actual, no válido)', + 'fr-fr': '{value:string} (valeur actuelle, non valide)', + 'uk-ua': '{value:string} (поточне, недійсне значення)', + 'de-ch': '{value:string} (aktueller, ungültiger Wert)', + 'pt-br': '{value:string} (valor atual, inválido)', }, queryRecordSetTitle: { - comment: "Used in query builder header when querying on record set", - "en-us": + comment: 'Used in query builder header when querying on record set', + 'en-us': 'Query: "{queryName:string}" on {recordSetTable:string}: "{recordSetName:string}"', - "ru-ru": + 'ru-ru': 'Запрос: "{queryName:string}" на {recordSetTable:string}: "{recordSetName:string}"', - "es-es": + 'es-es': 'Consulta: "{queryName:string}" en {recordSetTable:string}: "{recordSetName:string}"', - "fr-fr": - "Requête : « {queryName:string} » sur {recordSetTable:string} : « {recordSetName:string} »", - "uk-ua": + 'fr-fr': + 'Requête : « {queryName:string} » sur {recordSetTable:string} : « {recordSetName:string} »', + 'uk-ua': 'Запит: "{queryName:string}" на {recordSetTable:string}: "{recordSetName:string}"', - "de-ch": + 'de-ch': 'Abfrage: "{queryName:string}" auf {recordSetTable:string}: "{recordSetName:string}"', - "pt-br": + 'pt-br': 'Consulta: "{queryName:string}" em {recordSetTable:string}: "{recordSetName:string}"', }, treeQueryName: { - comment: "Used in query builder header when querying on tree node usages", - "en-us": '{tableName:string} using "{nodeFullName:string}"', - "ru-ru": "{tableName:string} используя «{nodeFullName:string}»", - "es-es": '{tableName:string} usando "{nodeFullName:string}"', - "fr-fr": "{tableName:string} en utilisant « {nodeFullName:string} »", - "uk-ua": '{tableName:string} за допомогою "{nodeFullName:string}"', - "de-ch": '{tableName:string} mit "{nodeFullName:string}"', - "pt-br": '{tableName:string} usando "{nodeFullName:string}"', + comment: 'Used in query builder header when querying on tree node usages', + 'en-us': '{tableName:string} using "{nodeFullName:string}"', + 'ru-ru': '{tableName:string} используя «{nodeFullName:string}»', + 'es-es': '{tableName:string} usando "{nodeFullName:string}"', + 'fr-fr': '{tableName:string} en utilisant « {nodeFullName:string} »', + 'uk-ua': '{tableName:string} за допомогою "{nodeFullName:string}"', + 'de-ch': '{tableName:string} mit "{nodeFullName:string}"', + 'pt-br': '{tableName:string} usando "{nodeFullName:string}"', }, newButtonDescription: { - "en-us": "Add New Field", - "ru-ru": "Добавить новое поле", - "es-es": "Agregar nuevo campo", - "fr-fr": "Ajouter un nouveau champ", - "uk-ua": "Додати нове поле", - "de-ch": "Neues Feld hinzufügen", - "pt-br": "Adicionar novo campo", + 'en-us': 'Add New Field', + 'ru-ru': 'Добавить новое поле', + 'es-es': 'Agregar nuevo campo', + 'fr-fr': 'Ajouter un nouveau champ', + 'uk-ua': 'Додати нове поле', + 'de-ch': 'Neues Feld hinzufügen', + 'pt-br': 'Adicionar novo campo', }, countOnly: { - comment: "Verb", - "en-us": "Count", - "ru-ru": "Считать", - "es-es": "Conteo", - "fr-fr": "Compter", - "uk-ua": "Рахувати", - "de-ch": "Zählen", - "pt-br": "Contar", + comment: 'Verb', + 'en-us': 'Count', + 'ru-ru': 'Считать', + 'es-es': 'Conteo', + 'fr-fr': 'Compter', + 'uk-ua': 'Рахувати', + 'de-ch': 'Zählen', + 'pt-br': 'Contar', }, distinct: { - "en-us": "Distinct", - "ru-ru": "Отчетливый", - "es-es": "Distinto", - "fr-fr": "Distinct", - "uk-ua": "Виразний", - "de-ch": "Unterscheidbar", - "pt-br": "Distinto", + 'en-us': 'Distinct', + 'ru-ru': 'Отчетливый', + 'es-es': 'Distinto', + 'fr-fr': 'Distinct', + 'uk-ua': 'Виразний', + 'de-ch': 'Unterscheidbar', + 'pt-br': 'Distinto', }, series: { - "en-us": "Series", - "de-ch": "Serie", - "es-es": "Serie", - "fr-fr": "Série", - "pt-br": "Série", - "ru-ru": "Ряд", - "uk-ua": "Серія", + 'en-us': 'Series', + 'de-ch': 'Serie', + 'es-es': 'Serie', + 'fr-fr': 'Série', + 'pt-br': 'Série', + 'ru-ru': 'Ряд', + 'uk-ua': 'Серія', }, createCsv: { - "en-us": "Create CSV", - "ru-ru": "Создать CSV-файл", - "es-es": "Crear CSV", - "fr-fr": "Créer un fichier CSV", - "uk-ua": "Створити CSV", - "de-ch": "CSV erstellen", - "pt-br": "Criar CSV", + 'en-us': 'Create CSV', + 'ru-ru': 'Создать CSV-файл', + 'es-es': 'Crear CSV', + 'fr-fr': 'Créer un fichier CSV', + 'uk-ua': 'Створити CSV', + 'de-ch': 'CSV erstellen', + 'pt-br': 'Criar CSV', }, createKml: { - "en-us": "Create KML", - "ru-ru": "Создать KML", - "es-es": "Crear KML", - "fr-fr": "Créer un fichier KML", - "uk-ua": "Створіть KML", - "de-ch": "KML erstellen", - "pt-br": "Criar KML", + 'en-us': 'Create KML', + 'ru-ru': 'Создать KML', + 'es-es': 'Crear KML', + 'fr-fr': 'Créer un fichier KML', + 'uk-ua': 'Створіть KML', + 'de-ch': 'KML erstellen', + 'pt-br': 'Criar KML', }, createRecordSet: { - "en-us": "Create {recordSetTable:string}", - "ru-ru": "Создать {recordSetTable:string}", - "es-es": "Crear {recordSetTable:string}", - "fr-fr": "Créer {recordSetTable:string}", - "uk-ua": "Створити {recordSetTable:string}", - "de-ch": "Erstellen {recordSetTable:string}", - "pt-br": "Criar {recordSetTable:string}", + 'en-us': 'Create {recordSetTable:string}', + 'ru-ru': 'Создать {recordSetTable:string}', + 'es-es': 'Crear {recordSetTable:string}', + 'fr-fr': 'Créer {recordSetTable:string}', + 'uk-ua': 'Створити {recordSetTable:string}', + 'de-ch': 'Erstellen {recordSetTable:string}', + 'pt-br': 'Criar {recordSetTable:string}', }, saveAs: { - "en-us": "Save As", - "es-es": "Guardar como", - "uk-ua": "Зберегти як", - "de-ch": "Speichern unter", - "fr-fr": "Enregistrer sous", - "ru-ru": "Сохранить как", - "pt-br": "Salvar como", + 'en-us': 'Save As', + 'es-es': 'Guardar como', + 'uk-ua': 'Зберегти як', + 'de-ch': 'Speichern unter', + 'fr-fr': 'Enregistrer sous', + 'ru-ru': 'Сохранить как', + 'pt-br': 'Salvar como', }, anyRank: { - "en-us": "(any rank)", - "ru-ru": "(любой ранг)", - "es-es": "(cualquier rango)", - "fr-fr": "(n'importe quel rang)", - "uk-ua": "(будь-який ранг)", - "de-ch": "(jeder Rang)", - "pt-br": "(qualquer classificação)", + 'en-us': '(any rank)', + 'ru-ru': '(любой ранг)', + 'es-es': '(cualquier rango)', + 'fr-fr': "(n'importe quel rang)", + 'uk-ua': '(будь-який ранг)', + 'de-ch': '(jeder Rang)', + 'pt-br': '(qualquer classificação)', }, anyTree: { - "en-us": "(any tree)", - "de-ch": "(jeder Baum)", - "es-es": "(cualquier árbol)", - "fr-fr": "(n'importe quel arbre)", - "pt-br": "(qualquer árvore)", - "ru-ru": "(любое дерево)", - "uk-ua": "(будь-яке дерево)", + 'en-us': '(any tree)', + 'de-ch': '(jeder Baum)', + 'es-es': '(cualquier árbol)', + 'fr-fr': "(n'importe quel arbre)", + 'pt-br': '(qualquer árvore)', + 'ru-ru': '(любое дерево)', + 'uk-ua': '(будь-яке дерево)', }, moveUp: { - comment: "As in move it up", - "en-us": "Move Up", - "ru-ru": "Двигаться вверх", - "es-es": "Mover hacia arriba", - "fr-fr": "Monter", - "uk-ua": "Рухатися вгору", - "de-ch": "Nach oben", - "pt-br": "Mover para cima", + comment: 'As in move it up', + 'en-us': 'Move Up', + 'ru-ru': 'Двигаться вверх', + 'es-es': 'Mover hacia arriba', + 'fr-fr': 'Monter', + 'uk-ua': 'Рухатися вгору', + 'de-ch': 'Nach oben', + 'pt-br': 'Mover para cima', }, moveDown: { - comment: "As in move it down", - "en-us": "Move Down", - "ru-ru": "Двигаться вниз", - "es-es": "Mover hacia abajo", - "fr-fr": "Descendre", - "uk-ua": "Рухатися вниз", - "de-ch": "Nach unten", - "pt-br": "Mover para baixo", + comment: 'As in move it down', + 'en-us': 'Move Down', + 'ru-ru': 'Двигаться вниз', + 'es-es': 'Mover hacia abajo', + 'fr-fr': 'Descendre', + 'uk-ua': 'Рухатися вниз', + 'de-ch': 'Nach unten', + 'pt-br': 'Mover para baixo', }, sort: { - "en-us": "Sort", - "ru-ru": "Сортировать", - "es-es": "Ordenar", - "fr-fr": "Trier", - "uk-ua": "Сортувати", - "de-ch": "Sortieren", - "pt-br": "Organizar", + 'en-us': 'Sort', + 'ru-ru': 'Сортировать', + 'es-es': 'Ordenar', + 'fr-fr': 'Trier', + 'uk-ua': 'Сортувати', + 'de-ch': 'Sortieren', + 'pt-br': 'Organizar', }, ascendingSort: { - "en-us": "Ascending Sort", - "ru-ru": "Сортировка по возрастанию", - "es-es": "Orden ascendente", - "fr-fr": "Tri croissant", - "uk-ua": "Сортування за зростанням", - "de-ch": "Aufsteigende Sortierung", - "pt-br": "Classificação crescente", + 'en-us': 'Ascending Sort', + 'ru-ru': 'Сортировка по возрастанию', + 'es-es': 'Orden ascendente', + 'fr-fr': 'Tri croissant', + 'uk-ua': 'Сортування за зростанням', + 'de-ch': 'Aufsteigende Sortierung', + 'pt-br': 'Classificação crescente', }, descendingSort: { - "en-us": "Descending Sort", - "ru-ru": "Сортировка по убыванию", - "es-es": "Orden descendente", - "fr-fr": "Tri décroissant", - "uk-ua": "Сортування за спаданням", - "de-ch": "Absteigende Sortierung", - "pt-br": "Classificação decrescente", + 'en-us': 'Descending Sort', + 'ru-ru': 'Сортировка по убыванию', + 'es-es': 'Orden descendente', + 'fr-fr': 'Tri décroissant', + 'uk-ua': 'Сортування за спаданням', + 'de-ch': 'Absteigende Sortierung', + 'pt-br': 'Classificação decrescente', }, negate: { - comment: "as in negate query condition", - "en-us": "Negate", - "ru-ru": "Отрицать", - "es-es": "Negar", - "fr-fr": "Nier", - "uk-ua": "Заперечувати", - "de-ch": "Negieren", - "pt-br": "Negar", + comment: 'as in negate query condition', + 'en-us': 'Negate', + 'ru-ru': 'Отрицать', + 'es-es': 'Negar', + 'fr-fr': 'Nier', + 'uk-ua': 'Заперечувати', + 'de-ch': 'Negieren', + 'pt-br': 'Negar', }, showButtonDescription: { - "en-us": "Show in results", - "es-es": "Mostrar en resultados", - "uk-ua": "Показати в результатах", - "de-ch": "In Ergebnissen anzeigen", - "fr-fr": "Afficher dans les résultats", - "ru-ru": "Показать в результатах", - "pt-br": "Mostrar nos resultados", + 'en-us': 'Show in results', + 'es-es': 'Mostrar en resultados', + 'uk-ua': 'Показати в результатах', + 'de-ch': 'In Ergebnissen anzeigen', + 'fr-fr': 'Afficher dans les résultats', + 'ru-ru': 'Показать в результатах', + 'pt-br': 'Mostrar nos resultados', }, aggregatedInline: { - "en-us": "(aggregated)", - "ru-ru": "(агрегированные)", - "es-es": "(agregado)", - "fr-fr": "(agrégés)", - "uk-ua": "(узагальнено)", - "de-ch": "(aggregiert)", - "pt-br": "(agregado)", + 'en-us': '(aggregated)', + 'ru-ru': '(агрегированные)', + 'es-es': '(agregado)', + 'fr-fr': '(agrégés)', + 'uk-ua': '(узагальнено)', + 'de-ch': '(aggregiert)', + 'pt-br': '(agregado)', }, formattedInline: { - "en-us": "(formatted)", - "ru-ru": "(отформатировано)", - "es-es": "(formateado)", - "fr-fr": "(formaté)", - "uk-ua": "(відформатований)", - "de-ch": "(formatiert)", - "pt-br": "(formatado)", + 'en-us': '(formatted)', + 'ru-ru': '(отформатировано)', + 'es-es': '(formateado)', + 'fr-fr': '(formaté)', + 'uk-ua': '(відформатований)', + 'de-ch': '(formatiert)', + 'pt-br': '(formatado)', }, like: { - "en-us": "Like", - "ru-ru": "Нравиться", - "es-es": "Como", - "fr-fr": "Comme", - "uk-ua": "Люблю", - "de-ch": "Wie", - "pt-br": "Como", + 'en-us': 'Like', + 'ru-ru': 'Нравиться', + 'es-es': 'Como', + 'fr-fr': 'Comme', + 'uk-ua': 'Люблю', + 'de-ch': 'Wie', + 'pt-br': 'Como', }, likeDescription: { comment: 'Explains the use of special symbols for the "like" query filter', - "en-us": + 'en-us': 'Use "%" to match any number of characters.\n\nUse "_" to match a single character', - "ru-ru": - "Используйте «%» для обозначения любого количества символов.\n\nИспользуйте «_» для обозначения одного символа.", - "es-es": + 'ru-ru': + 'Используйте «%» для обозначения любого количества символов.\n\nИспользуйте «_» для обозначения одного символа.', + 'es-es': 'Usar "%" para hacer coincidir cualquier número de caracteres.\n\nUsar "_" para hacer coincidir un solo carácter', - "fr-fr": + 'fr-fr': "Utilisez « % » pour correspondre à n'importe quel nombre de caractères.\n\nUtilisez « _ » pour correspondre à un seul caractère.", - "uk-ua": + 'uk-ua': 'Використовуйте "%", щоб відповідати будь-якій кількості символів.\n\nВикористовуйте "_", щоб відповідати одному символу', - "de-ch": - "Verwenden Sie „%“, um eine beliebige Anzahl von Zeichen abzugleichen.\n\nVerwenden Sie „_“, um ein einzelnes Zeichen abzugleichen", - "pt-br": + 'de-ch': + 'Verwenden Sie „%“, um eine beliebige Anzahl von Zeichen abzugleichen.\n\nVerwenden Sie „_“, um ein einzelnes Zeichen abzugleichen', + 'pt-br': 'Use "%" para corresponder a qualquer número de caracteres.\n\nUse "_" para corresponder a um único caractere.', }, equal: { - "en-us": "Equal", - "ru-ru": "Равный", - "es-es": "Igual", - "fr-fr": "Égal", - "uk-ua": "Рівні", - "de-ch": "Gleich", - "pt-br": "Igual", + 'en-us': 'Equal', + 'ru-ru': 'Равный', + 'es-es': 'Igual', + 'fr-fr': 'Égal', + 'uk-ua': 'Рівні', + 'de-ch': 'Gleich', + 'pt-br': 'Igual', }, greaterThan: { - "en-us": "Greater than", - "ru-ru": "Больше чем", - "es-es": "Mayor que", - "fr-fr": "Plus grand que", - "uk-ua": "Більш чим", - "de-ch": "Größer als", - "pt-br": "Maior que", + 'en-us': 'Greater than', + 'ru-ru': 'Больше чем', + 'es-es': 'Mayor que', + 'fr-fr': 'Plus grand que', + 'uk-ua': 'Більш чим', + 'de-ch': 'Größer als', + 'pt-br': 'Maior que', }, lessThan: { - "en-us": "Less than", - "ru-ru": "Меньше, чем", - "es-es": "Menor que", - "fr-fr": "Moins que", - "uk-ua": "Менше ніж", - "de-ch": "Weniger als", - "pt-br": "Menor que", + 'en-us': 'Less than', + 'ru-ru': 'Меньше, чем', + 'es-es': 'Menor que', + 'fr-fr': 'Moins que', + 'uk-ua': 'Менше ніж', + 'de-ch': 'Weniger als', + 'pt-br': 'Menor que', }, greaterOrEqualTo: { - "en-us": "Greater or Equal to", - "ru-ru": "Больше или равно", - "es-es": "Mayor o igual a", - "fr-fr": "Supérieur ou égal à", - "uk-ua": "Більше або дорівнює", - "de-ch": "Größer oder gleich", - "pt-br": "Maior ou igual a", + 'en-us': 'Greater or Equal to', + 'ru-ru': 'Больше или равно', + 'es-es': 'Mayor o igual a', + 'fr-fr': 'Supérieur ou égal à', + 'uk-ua': 'Більше або дорівнює', + 'de-ch': 'Größer oder gleich', + 'pt-br': 'Maior ou igual a', }, lessOrEqualTo: { - "en-us": "Less or Equal to", - "ru-ru": "Меньше или равно", - "es-es": "Menor o igual a", - "fr-fr": "Inférieur ou égal à", - "uk-ua": "Менше або дорівнює", - "de-ch": "Kleiner oder gleich", - "pt-br": "Menor ou igual a", + 'en-us': 'Less or Equal to', + 'ru-ru': 'Меньше или равно', + 'es-es': 'Menor o igual a', + 'fr-fr': 'Inférieur ou égal à', + 'uk-ua': 'Менше або дорівнює', + 'de-ch': 'Kleiner oder gleich', + 'pt-br': 'Menor ou igual a', }, true: { - "en-us": "True", - "ru-ru": "Истинный", - "es-es": "Verdadero", - "fr-fr": "Vrai", - "uk-ua": "правда", - "de-ch": "WAHR", - "pt-br": "Verdadeiro", + 'en-us': 'True', + 'ru-ru': 'Истинный', + 'es-es': 'Verdadero', + 'fr-fr': 'Vrai', + 'uk-ua': 'правда', + 'de-ch': 'WAHR', + 'pt-br': 'Verdadeiro', }, false: { - "en-us": "False", - "ru-ru": "ЛОЖЬ", - "es-es": "Falso", - "fr-fr": "FAUX", - "uk-ua": "помилковий", - "de-ch": "FALSCH", - "pt-br": "Falso", + 'en-us': 'False', + 'ru-ru': 'ЛОЖЬ', + 'es-es': 'Falso', + 'fr-fr': 'FAUX', + 'uk-ua': 'помилковий', + 'de-ch': 'FALSCH', + 'pt-br': 'Falso', }, trueOrNull: { - "en-us": "True or Empty", - "ru-ru": "Истина или Пусто", - "es-es": "Verdadero o vacío", - "fr-fr": "Vrai ou vide", - "uk-ua": "True або Empty", - "de-ch": "Wahr oder leer", - "pt-br": "Verdadeiro ou Vazio", + 'en-us': 'True or Empty', + 'ru-ru': 'Истина или Пусто', + 'es-es': 'Verdadero o vacío', + 'fr-fr': 'Vrai ou vide', + 'uk-ua': 'True або Empty', + 'de-ch': 'Wahr oder leer', + 'pt-br': 'Verdadeiro ou Vazio', }, falseOrNull: { - "en-us": "False or Empty", - "ru-ru": "Ложь или пусто", - "es-es": "Falso o vacío", - "fr-fr": "Faux ou vide", - "uk-ua": "False або Empty", - "de-ch": "Falsch oder leer", - "pt-br": "Falso ou Vazio", + 'en-us': 'False or Empty', + 'ru-ru': 'Ложь или пусто', + 'es-es': 'Falso o vacío', + 'fr-fr': 'Faux ou vide', + 'uk-ua': 'False або Empty', + 'de-ch': 'Falsch oder leer', + 'pt-br': 'Falso ou Vazio', }, between: { - "en-us": "Between", - "ru-ru": "Между", - "es-es": "Entre", - "fr-fr": "Entre", - "uk-ua": "Між", - "de-ch": "Zwischen", - "pt-br": "Entre", + 'en-us': 'Between', + 'ru-ru': 'Между', + 'es-es': 'Entre', + 'fr-fr': 'Entre', + 'uk-ua': 'Між', + 'de-ch': 'Zwischen', + 'pt-br': 'Entre', }, in: { - "en-us": "In", - "ru-ru": "В", - "es-es": "En", - "fr-fr": "Dans", - "uk-ua": "в", - "de-ch": "In", - "pt-br": "Em", + 'en-us': 'In', + 'ru-ru': 'В', + 'es-es': 'En', + 'fr-fr': 'Dans', + 'uk-ua': 'в', + 'de-ch': 'In', + 'pt-br': 'Em', }, inDescription: { - "en-us": "A comma-separated list of values", - "ru-ru": "Список значений, разделенных запятыми", - "es-es": "Una lista de valores separados por comas", - "fr-fr": "Une liste de valeurs séparées par des virgules", - "uk-ua": "Список значень, розділених комами", - "de-ch": "Eine durch Kommas getrennte Liste von Werten", - "pt-br": "Uma lista de valores separados por vírgulas", + 'en-us': 'A comma-separated list of values', + 'ru-ru': 'Список значений, разделенных запятыми', + 'es-es': 'Una lista de valores separados por comas', + 'fr-fr': 'Une liste de valeurs séparées par des virgules', + 'uk-ua': 'Список значень, розділених комами', + 'de-ch': 'Eine durch Kommas getrennte Liste von Werten', + 'pt-br': 'Uma lista de valores separados por vírgulas', }, contains: { - "en-us": "Contains", - "ru-ru": "Содержит", - "es-es": "Contiene", - "fr-fr": "Contient", - "uk-ua": "Містить", - "de-ch": "Enthält", - "pt-br": "Contém", + 'en-us': 'Contains', + 'ru-ru': 'Содержит', + 'es-es': 'Contiene', + 'fr-fr': 'Contient', + 'uk-ua': 'Містить', + 'de-ch': 'Enthält', + 'pt-br': 'Contém', }, empty: { - "en-us": "Empty", - "ru-ru": "Пустой", - "es-es": "Vacío", - "fr-fr": "Vide", - "uk-ua": "Порожній", - "de-ch": "Leer", - "pt-br": "Vazio", + 'en-us': 'Empty', + 'ru-ru': 'Пустой', + 'es-es': 'Vacío', + 'fr-fr': 'Vide', + 'uk-ua': 'Порожній', + 'de-ch': 'Leer', + 'pt-br': 'Vazio', }, and: { - "en-us": "and", - "ru-ru": "и", - "es-es": "y", - "fr-fr": "et", - "uk-ua": "і", - "de-ch": "Und", - "pt-br": "e", + 'en-us': 'and', + 'ru-ru': 'и', + 'es-es': 'y', + 'fr-fr': 'et', + 'uk-ua': 'і', + 'de-ch': 'Und', + 'pt-br': 'e', }, startsWith: { - "en-us": "Starts With", - "ru-ru": "Начинается с", - "es-es": "Comienza con", - "fr-fr": "Commence par", - "uk-ua": "Починається з", - "de-ch": "Beginnt mit", - "pt-br": "Começa com", + 'en-us': 'Starts With', + 'ru-ru': 'Начинается с', + 'es-es': 'Comienza con', + 'fr-fr': 'Commence par', + 'uk-ua': 'Починається з', + 'de-ch': 'Beginnt mit', + 'pt-br': 'Começa com', }, endsWith: { - "en-us": "Ends With", - "de-ch": "Endet mit", - "es-es": "Termina con", - "fr-fr": "Se termine par", - "pt-br": "Termina com", - "ru-ru": "Заканчивается с", - "uk-ua": "Закінчується на", + 'en-us': 'Ends With', + 'de-ch': 'Endet mit', + 'es-es': 'Termina con', + 'fr-fr': 'Se termine par', + 'pt-br': 'Termina com', + 'ru-ru': 'Заканчивается с', + 'uk-ua': 'Закінчується на', }, or: { - "en-us": "or", - "ru-ru": "или", - "es-es": "o", - "fr-fr": "ou", - "uk-ua": "або", - "de-ch": "oder", - "pt-br": "ou", + 'en-us': 'or', + 'ru-ru': 'или', + 'es-es': 'o', + 'fr-fr': 'ou', + 'uk-ua': 'або', + 'de-ch': 'oder', + 'pt-br': 'ou', }, yes: { - "en-us": "Yes", - "ru-ru": "Да", - "es-es": "Sí", - "fr-fr": "Oui", - "uk-ua": "Так", - "de-ch": "Ja", - "pt-br": "Sim", + 'en-us': 'Yes', + 'ru-ru': 'Да', + 'es-es': 'Sí', + 'fr-fr': 'Oui', + 'uk-ua': 'Так', + 'de-ch': 'Ja', + 'pt-br': 'Sim', }, queryResults: { - "en-us": "Query Results", - "ru-ru": "Результаты запроса", - "es-es": "Resultados de la consulta", - "fr-fr": "Résultats de la requête", - "uk-ua": "Результати запиту", - "de-ch": "Abfrageergebnisse", - "pt-br": "Resultados da consulta", + 'en-us': 'Query Results', + 'ru-ru': 'Результаты запроса', + 'es-es': 'Resultados de la consulta', + 'fr-fr': 'Résultats de la requête', + 'uk-ua': 'Результати запиту', + 'de-ch': 'Abfrageergebnisse', + 'pt-br': 'Resultados da consulta', }, browseInForms: { - "en-us": "Browse in Forms", - "ru-ru": "Просмотр в формах", - "es-es": "Navegar en formularios", - "fr-fr": "Parcourir les formulaires", - "uk-ua": "Перегляд у Формах", - "de-ch": "In Formularen blättern", - "pt-br": "Navegar em Formulários", + 'en-us': 'Browse in Forms', + 'ru-ru': 'Просмотр в формах', + 'es-es': 'Navegar en formularios', + 'fr-fr': 'Parcourir les formulaires', + 'uk-ua': 'Перегляд у Формах', + 'de-ch': 'In Formularen blättern', + 'pt-br': 'Navegar em Formulários', }, configureQueryTables: { - "en-us": "Configure visible query tables", - "ru-ru": "Настроить видимые таблицы запросов", - "es-es": "Configurar tablas de consulta visibles", - "fr-fr": "Configurer les tables de requête visibles", - "uk-ua": "Налаштувати видимі таблиці запитів", - "de-ch": "Konfigurieren sichtbarer Abfragetabellen", - "pt-br": "Configurar tabelas de consulta visíveis", + 'en-us': 'Configure visible query tables', + 'ru-ru': 'Настроить видимые таблицы запросов', + 'es-es': 'Configurar tablas de consulta visibles', + 'fr-fr': 'Configurer les tables de requête visibles', + 'uk-ua': 'Налаштувати видимі таблиці запитів', + 'de-ch': 'Konfigurieren sichtbarer Abfragetabellen', + 'pt-br': 'Configurar tabelas de consulta visíveis', }, exportQueryForDwca: { - "en-us": "Export query for DwCA definition", - "ru-ru": "Экспорт запроса на определение DwCA", - "es-es": "Consulta de exportación para la definición de DwCA", - "fr-fr": "Requête d'exportation pour la définition DwCA", - "uk-ua": "Експорт запиту для визначення DwCA", - "de-ch": "Exportabfrage für DwCA-Definition", - "pt-br": "Consulta de exportação para definição DwCA", + 'en-us': 'Export query for DwCA definition', + 'ru-ru': 'Экспорт запроса на определение DwCA', + 'es-es': 'Consulta de exportación para la definición de DwCA', + 'fr-fr': "Requête d'exportation pour la définition DwCA", + 'uk-ua': 'Експорт запиту для визначення DwCA', + 'de-ch': 'Exportabfrage für DwCA-Definition', + 'pt-br': 'Consulta de exportação para definição DwCA', }, exportQueryAsReport: { - "en-us": "Define report based on query", - "ru-ru": "Определить отчет на основе запроса", - "es-es": "Definir informe basado en consulta", - "fr-fr": "Définir un rapport basé sur une requête", - "uk-ua": "Визначити звіт на основі запиту", - "de-ch": "Definieren Sie den Bericht basierend auf der Abfrage", - "pt-br": "Definir relatório com base na consulta", + 'en-us': 'Define report based on query', + 'ru-ru': 'Определить отчет на основе запроса', + 'es-es': 'Definir informe basado en consulta', + 'fr-fr': 'Définir un rapport basé sur une requête', + 'uk-ua': 'Визначити звіт на основі запиту', + 'de-ch': 'Definieren Sie den Bericht basierend auf der Abfrage', + 'pt-br': 'Definir relatório com base na consulta', }, exportQueryAsLabel: { - "en-us": "Define label based on query", - "ru-ru": "Определить метку на основе запроса", - "es-es": "Definir etiqueta según consulta", - "fr-fr": "Définir l'étiquette en fonction de la requête", - "uk-ua": "Визначте мітку на основі запиту", - "de-ch": "Definieren Sie das Label basierend auf der Abfrage", - "pt-br": "Definir rótulo com base na consulta", + 'en-us': 'Define label based on query', + 'ru-ru': 'Определить метку на основе запроса', + 'es-es': 'Definir etiqueta según consulta', + 'fr-fr': "Définir l'étiquette en fonction de la requête", + 'uk-ua': 'Визначте мітку на основі запиту', + 'de-ch': 'Definieren Sie das Label basierend auf der Abfrage', + 'pt-br': 'Definir rótulo com base na consulta', }, treeMerge: { - comment: "Audit Log Action Type", - "en-us": "Tree Merge", - "ru-ru": "Слияние деревьев", - "es-es": "Fusión de árboles", - "fr-fr": "Fusion d'arbres", - "uk-ua": "Об'єднання дерев", - "de-ch": "Baumzusammenführung", - "pt-br": "Mesclagem de Árvores", + comment: 'Audit Log Action Type', + 'en-us': 'Tree Merge', + 'ru-ru': 'Слияние деревьев', + 'es-es': 'Fusión de árboles', + 'fr-fr': "Fusion d'arbres", + 'uk-ua': "Об'єднання дерев", + 'de-ch': 'Baumzusammenführung', + 'pt-br': 'Mesclagem de Árvores', }, treeMove: { - comment: "Audit Log Action Type", - "en-us": "Tree Move", - "ru-ru": "Перемещение дерева", - "es-es": "Movimiento de árbol", - "fr-fr": "Déplacement d'arbre", - "uk-ua": "Переміщення дерева", - "de-ch": "Baum verschieben", - "pt-br": "Movimentação de árvores", + comment: 'Audit Log Action Type', + 'en-us': 'Tree Move', + 'ru-ru': 'Перемещение дерева', + 'es-es': 'Movimiento de árbol', + 'fr-fr': "Déplacement d'arbre", + 'uk-ua': 'Переміщення дерева', + 'de-ch': 'Baum verschieben', + 'pt-br': 'Movimentação de árvores', }, treeSynonymize: { - comment: "Audit Log Action Type", - "en-us": "Tree Synonymize", - "ru-ru": "Дерево Синонимизировать", - "es-es": "Árbol Sinónimos", - "fr-fr": "Synonyme d'arbre", - "uk-ua": "Синонімізувати дерево", - "de-ch": "Baum synonymisieren", - "pt-br": "Árvore Sinonímia", + comment: 'Audit Log Action Type', + 'en-us': 'Tree Synonymize', + 'ru-ru': 'Дерево Синонимизировать', + 'es-es': 'Árbol Sinónimos', + 'fr-fr': "Synonyme d'arbre", + 'uk-ua': 'Синонімізувати дерево', + 'de-ch': 'Baum synonymisieren', + 'pt-br': 'Árvore Sinonímia', }, treeDesynonymize: { - comment: "Audit Log Action Type", - "en-us": "Tree Desynonymize", - "ru-ru": "Десинонимизация дерева", - "es-es": "Desinonimizar árboles", - "fr-fr": "Arbre désynonymisé", - "uk-ua": "Десинонімізація дерева", - "de-ch": "Baum desynonymisieren", - "pt-br": "Árvore Dessinonimizar", + comment: 'Audit Log Action Type', + 'en-us': 'Tree Desynonymize', + 'ru-ru': 'Десинонимизация дерева', + 'es-es': 'Desinonimizar árboles', + 'fr-fr': 'Arbre désynonymisé', + 'uk-ua': 'Десинонімізація дерева', + 'de-ch': 'Baum desynonymisieren', + 'pt-br': 'Árvore Dessinonimizar', }, treeBulkMove: { - comment: "Audit Log Action Type", - "en-us": "Tree Bulk Move", - "de-ch": "Massenbewegung von Bäumen", - "es-es": "Movimiento masivo de árboles", - "fr-fr": "Déplacement d'arbres en vrac", - "ru-ru": "Массовая перевозка деревьев", - "uk-ua": "Масове переміщення дерева", - "pt-br": "Mudança de árvores em massa", + comment: 'Audit Log Action Type', + 'en-us': 'Tree Bulk Move', + 'de-ch': 'Massenbewegung von Bäumen', + 'es-es': 'Movimiento masivo de árboles', + 'fr-fr': "Déplacement d'arbres en vrac", + 'ru-ru': 'Массовая перевозка деревьев', + 'uk-ua': 'Масове переміщення дерева', + 'pt-br': 'Mudança de árvores em massa', }, tooLongErrorMessage: { - "en-us": - "Field value is too long. Max allowed length is {maxLength:number|formatted}", - "ru-ru": - "Значение поля слишком длинное. Максимально допустимая длина: {maxLength:number|formatted}.", - "es-es": - "El valor del campo es demasiado largo. La longitud máxima permitida es {maxLength:number|formatted}.", - "fr-fr": - "La valeur du champ est trop longue. La longueur maximale autorisée est {maxLength:number|formatted}.", - "uk-ua": - "Значення поля задовге. Максимальна дозволена довжина {maxLength:number|formatted}", - "de-ch": - "Der Feldwert ist zu lang. Die maximal zulässige Länge beträgt {maxLength:number|formatted}", - "pt-br": - "O valor do campo é muito longo. O comprimento máximo permitido é {maxLength:number|formatted}", + 'en-us': + 'Field value is too long. Max allowed length is {maxLength:number|formatted}', + 'ru-ru': + 'Значение поля слишком длинное. Максимально допустимая длина: {maxLength:number|formatted}.', + 'es-es': + 'El valor del campo es demasiado largo. La longitud máxima permitida es {maxLength:number|formatted}.', + 'fr-fr': + 'La valeur du champ est trop longue. La longueur maximale autorisée est {maxLength:number|formatted}.', + 'uk-ua': + 'Значення поля задовге. Максимальна дозволена довжина {maxLength:number|formatted}', + 'de-ch': + 'Der Feldwert ist zu lang. Die maximal zulässige Länge beträgt {maxLength:number|formatted}', + 'pt-br': + 'O valor do campo é muito longo. O comprimento máximo permitido é {maxLength:number|formatted}', }, future: { - "en-us": "in the future", - "de-ch": "in der Zukunft", - "es-es": "en el futuro", - "fr-fr": "à l'avenir", - "ru-ru": "в будущем", - "uk-ua": "в майбутньому", - "pt-br": "no futuro", + 'en-us': 'in the future', + 'de-ch': 'in der Zukunft', + 'es-es': 'en el futuro', + 'fr-fr': "à l'avenir", + 'ru-ru': 'в будущем', + 'uk-ua': 'в майбутньому', + 'pt-br': 'no futuro', }, past: { - "en-us": "in the past", - "de-ch": "in der Vergangenheit", - "es-es": "en el pasado", - "fr-fr": "dans le passé", - "ru-ru": "в прошлом", - "uk-ua": "в минулому", - "pt-br": "no passado", + 'en-us': 'in the past', + 'de-ch': 'in der Vergangenheit', + 'es-es': 'en el pasado', + 'fr-fr': 'dans le passé', + 'ru-ru': 'в прошлом', + 'uk-ua': 'в минулому', + 'pt-br': 'no passado', }, days: { 'en-us': 'Days', @@ -822,159 +822,159 @@ export const queryText = createDictionary({ Used in query builder lines, will be shown as a number followed by a period of time (ie: day, month or week) then a direction (past or future) `, - "en-us": - "{size:number} {type:string} {direction:string}", - "de-ch": - "{size:number} {type:string} {direction:string}", - "es-es": - "{size:number} {type:string} {direction:string}", - "fr-fr": - "{size:number} {type:string} {direction:string}", - "ru-ru": - "{size:number} {type:string} {direction:string}", - "uk-ua": - "{size:number} {type:string} {direction:string}", - "pt-br": - "{size:number} {type:string} {direction:string}", + 'en-us': + '{size:number} {type:string} {direction:string}', + 'de-ch': + '{size:number} {type:string} {direction:string}', + 'es-es': + '{size:number} {type:string} {direction:string}', + 'fr-fr': + '{size:number} {type:string} {direction:string}', + 'ru-ru': + '{size:number} {type:string} {direction:string}', + 'uk-ua': + '{size:number} {type:string} {direction:string}', + 'pt-br': + '{size:number} {type:string} {direction:string}', }, importHiddenFields: { - "en-us": "The following fields are hidden in the query you imported:", - "es-es": "Los siguientes campos están ocultos en la consulta que importó:", - "fr-fr": - "Les champs suivants sont masqués dans la requête que vous avez importée :", - "ru-ru": "В импортированном вами запросе скрыты следующие поля:", - "uk-ua": "В імпортованому вами запиті приховано такі поля:", - "de-ch": - "Die folgenden Felder sind in der von Ihnen importierten Abfrage ausgeblendet:", - "pt-br": "Os seguintes campos estão ocultos na consulta que você importou:", + 'en-us': 'The following fields are hidden in the query you imported:', + 'es-es': 'Los siguientes campos están ocultos en la consulta que importó:', + 'fr-fr': + 'Les champs suivants sont masqués dans la requête que vous avez importée :', + 'ru-ru': 'В импортированном вами запросе скрыты следующие поля:', + 'uk-ua': 'В імпортованому вами запиті приховано такі поля:', + 'de-ch': + 'Die folgenden Felder sind in der von Ihnen importierten Abfrage ausgeblendet:', + 'pt-br': 'Os seguintes campos estão ocultos na consulta que você importou:', }, importNoReadPermission: { - "en-us": - "The query you imported contains tables you do not have read access to:", - "es-es": - "La consulta que importó contiene tablas a las que no tiene acceso de lectura:", - "fr-fr": - "La requête que vous avez importée contient des tables auxquelles vous n’avez pas accès en lecture :", - "ru-ru": - "Импортированный вами запрос содержит таблицы, к которым у вас нет доступа на чтение:", - "uk-ua": - "Запит, який ви імпортували, містить таблиці, до яких ви не маєте доступу на читання:", - "de-ch": - "Die von Ihnen importierte Abfrage enthält Tabellen, auf die Sie keinen Lesezugriff haben:", - "pt-br": - "A consulta que você importou contém tabelas às quais você não tem acesso de leitura:", + 'en-us': + 'The query you imported contains tables you do not have read access to:', + 'es-es': + 'La consulta que importó contiene tablas a las que no tiene acceso de lectura:', + 'fr-fr': + 'La requête que vous avez importée contient des tables auxquelles vous n’avez pas accès en lecture :', + 'ru-ru': + 'Импортированный вами запрос содержит таблицы, к которым у вас нет доступа на чтение:', + 'uk-ua': + 'Запит, який ви імпортували, містить таблиці, до яких ви не маєте доступу на читання:', + 'de-ch': + 'Die von Ihnen importierte Abfrage enthält Tabellen, auf die Sie keinen Lesezugriff haben:', + 'pt-br': + 'A consulta que você importou contém tabelas às quais você não tem acesso de leitura:', }, noReadPermission: { - "en-us": "No read permission", - "es-es": "Sin permiso de lectura", - "fr-fr": "Aucune autorisation de lecture", - "ru-ru": "Нет разрешения на чтение", - "uk-ua": "Немає дозволу на читання", - "de-ch": "Keine Leseberechtigung", - "pt-br": "Sem permissão de leitura", + 'en-us': 'No read permission', + 'es-es': 'Sin permiso de lectura', + 'fr-fr': 'Aucune autorisation de lecture', + 'ru-ru': 'Нет разрешения на чтение', + 'uk-ua': 'Немає дозволу на читання', + 'de-ch': 'Keine Leseberechtigung', + 'pt-br': 'Sem permissão de leitura', }, switchToRelative: { - "en-us": "Switch to relative", - "de-ch": "Wechseln zu relativ", - "es-es": "Cambiar a relativo", - "fr-fr": "Passer au relatif", - "ru-ru": "Переключиться на относительный", - "uk-ua": "Перейти до відносного", - "pt-br": "Mudar para relativo", + 'en-us': 'Switch to relative', + 'de-ch': 'Wechseln zu relativ', + 'es-es': 'Cambiar a relativo', + 'fr-fr': 'Passer au relatif', + 'ru-ru': 'Переключиться на относительный', + 'uk-ua': 'Перейти до відносного', + 'pt-br': 'Mudar para relativo', }, switchToAbsolute: { - "en-us": "Switch to absolute", - "de-ch": "Wechseln Sie zu absolut", - "es-es": "Cambiar a absoluto", - "fr-fr": "Passer à l'absolu", - "ru-ru": "Переключиться на абсолютный", - "uk-ua": "Перейти до відносного", - "pt-br": "Mudar para absoluto", + 'en-us': 'Switch to absolute', + 'de-ch': 'Wechseln Sie zu absolut', + 'es-es': 'Cambiar a absoluto', + 'fr-fr': "Passer à l'absolu", + 'ru-ru': 'Переключиться на абсолютный', + 'uk-ua': 'Перейти до відносного', + 'pt-br': 'Mudar para absoluto', }, scrollToEditor: { - "en-us": "Scroll to editor", - "de-ch": "Zum Editor scrollen", - "es-es": "Desplazarse al editor", - "uk-ua": "Перейдіть до редактора", - "fr-fr": "Faites défiler jusqu'à l'éditeur", - "ru-ru": "Прокрутите до редактора", - "pt-br": "Vá até o editor", + 'en-us': 'Scroll to editor', + 'de-ch': 'Zum Editor scrollen', + 'es-es': 'Desplazarse al editor', + 'uk-ua': 'Перейдіть до редактора', + 'fr-fr': "Faites défiler jusqu'à l'éditeur", + 'ru-ru': 'Прокрутите до редактора', + 'pt-br': 'Vá até o editor', }, viewRecords: { - "en-us": "View records", - "de-ch": "Datensätze anzeigen", - "es-es": "Ver registros", - "fr-fr": "Afficher les enregistrements", - "ru-ru": "Просмотреть записи", - "uk-ua": "Переглянути записи", - "pt-br": "Ver registros", + 'en-us': 'View records', + 'de-ch': 'Datensätze anzeigen', + 'es-es': 'Ver registros', + 'fr-fr': 'Afficher les enregistrements', + 'ru-ru': 'Просмотреть записи', + 'uk-ua': 'Переглянути записи', + 'pt-br': 'Ver registros', }, chooseFormatter: { - "en-us": "Choose formatter", - "de-ch": "Formatierer auswählen", - "es-es": "Elija el formateador", - "fr-fr": "Choisir le formateur", - "ru-ru": "Выбрать форматировщик", - "uk-ua": "Виберіть форматер", - "pt-br": "Escolha o formatador", + 'en-us': 'Choose formatter', + 'de-ch': 'Formatierer auswählen', + 'es-es': 'Elija el formateador', + 'fr-fr': 'Choisir le formateur', + 'ru-ru': 'Выбрать форматировщик', + 'uk-ua': 'Виберіть форматер', + 'pt-br': 'Escolha o formatador', }, range: { - "en-us": "Range", - "de-ch": "Reichweite", - "es-es": "Rango", - "fr-fr": "Gamme", - "pt-br": "Faixa", - "ru-ru": "Диапазон", - "uk-ua": "Діапазон", + 'en-us': 'Range', + 'de-ch': 'Reichweite', + 'es-es': 'Rango', + 'fr-fr': 'Gamme', + 'pt-br': 'Faixa', + 'ru-ru': 'Диапазон', + 'uk-ua': 'Діапазон', }, strict: { - "en-us": "Strict", - "de-ch": "Strikt", - "es-es": "Estricto", - "fr-fr": "Strict", - "pt-br": "Estrito", - "ru-ru": "Строгий", - "uk-ua": "Суворий", + 'en-us': 'Strict', + 'de-ch': 'Strikt', + 'es-es': 'Estricto', + 'fr-fr': 'Strict', + 'pt-br': 'Estrito', + 'ru-ru': 'Строгий', + 'uk-ua': 'Суворий', }, nonStrict: { - "en-us": "Non strict", - "de-ch": "Nicht streng", - "es-es": "No estricto", - "fr-fr": "Non strict", - "pt-br": "Não rigoroso", - "ru-ru": "Нестрогий", - "uk-ua": "Не суворий", + 'en-us': 'Non strict', + 'de-ch': 'Nicht streng', + 'es-es': 'No estricto', + 'fr-fr': 'Non strict', + 'pt-br': 'Não rigoroso', + 'ru-ru': 'Нестрогий', + 'uk-ua': 'Не суворий', }, catalogNumberInheritance: { - "en-us": "Catalog Number Inheritance", - "de-ch": "Katalognummernvererbung", - "es-es": "Herencia del número de catálogo", - "fr-fr": "Héritage du numéro de catalogue", - "pt-br": "Herança de números de catálogo", - "ru-ru": "Наследование каталожного номера", - "uk-ua": "Успадкування каталожних номерів", + 'en-us': 'Catalog Number Inheritance', + 'de-ch': 'Katalognummernvererbung', + 'es-es': 'Herencia del número de catálogo', + 'fr-fr': 'Héritage du numéro de catalogue', + 'pt-br': 'Herança de números de catálogo', + '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": "Успадкування батьківського об'єкта колекції за номером каталогу", + '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": "Eindeutigkeit der Katalognummer in Komponenten- und CO-Tabellen", - "es-es": - "Unicidad del número de catálogo en las tablas de componentes y CO", - "fr-fr": - "Unicité des numéros de catalogue entre les tableaux de composants et de CO", - "pt-br": - "Exclusividade do número de catálogo nas tabelas de componentes e CO", - "ru-ru": "Уникальность каталожного номера в таблицах компонентов и CO", - "uk-ua": "Унікальність каталожних номерів у таблицях компонентів та CO", + 'en-us': 'Catalog Number Uniqueness Across Component And CO tables', + 'de-ch': 'Eindeutigkeit der Katalognummer in Komponenten- und CO-Tabellen', + 'es-es': + 'Unicidad del número de catálogo en las tablas de componentes y CO', + 'fr-fr': + 'Unicité des numéros de catalogue entre les tableaux de composants et de CO', + 'pt-br': + 'Exclusividade do número de catálogo nas tabelas de componentes e CO', + 'ru-ru': 'Уникальность каталожного номера в таблицах компонентов и CO', + 'uk-ua': 'Унікальність каталожних номерів у таблицях компонентів та CO', }, formatInputAs: { comment: ` @@ -984,52 +984,52 @@ export const queryText = createDictionary({ Example: Format As: Ichthyology Example: Format As: Rock, Mineral `, - "en-us": "Format As: {commaSeparatedFormats:string}", - "de-ch": "Formatieren als: {commaSeparatedFormats:string}", - "es-es": "Formato como: {commaSeparatedFormats:string}", - "fr-fr": "Formater comme : {commaSeparatedFormats:string}", - "pt-br": "Formato como: {commaSeparatedFormats:string}", - "ru-ru": "Форматировать как: {commaSeparatedFormats:string}", - "uk-ua": "Форматувати як: {commaSeparatedFormats:string}", + 'en-us': 'Format As: {commaSeparatedFormats:string}', + 'de-ch': 'Formatieren als: {commaSeparatedFormats:string}', + 'es-es': 'Formato como: {commaSeparatedFormats:string}', + 'fr-fr': 'Formater comme : {commaSeparatedFormats:string}', + 'pt-br': 'Formato como: {commaSeparatedFormats:string}', + 'ru-ru': 'Форматировать как: {commaSeparatedFormats:string}', + 'uk-ua': 'Форматувати як: {commaSeparatedFormats:string}', }, unsavedChangesInQuery: { - "en-us": "Query has unsaved changes", - "de-ch": "Die Abfrage enthält nicht gespeicherte Änderungen", - "es-es": "La consulta tiene cambios sin guardar", - "fr-fr": "La requête comporte des modifications non enregistrées", - "pt-br": "A consulta possui alterações não salvas", - "ru-ru": "Запрос имеет несохраненные изменения", - "uk-ua": "Запит містить незбережені зміни", + 'en-us': 'Query has unsaved changes', + 'de-ch': 'Die Abfrage enthält nicht gespeicherte Änderungen', + 'es-es': 'La consulta tiene cambios sin guardar', + 'fr-fr': 'La requête comporte des modifications non enregistrées', + 'pt-br': 'A consulta possui alterações não salvas', + 'ru-ru': 'Запрос имеет несохраненные изменения', + 'uk-ua': 'Запит містить незбережені зміни', }, unsavedChangesInQueryDescription: { - "en-us": "Please save the query before running Batch Edit", - "de-ch": - "Bitte speichern Sie die Abfrage, bevor Sie die Stapelbearbeitung ausführen", - "es-es": "Guarde la consulta antes de ejecutar la edición por lotes", - "fr-fr": + 'en-us': 'Please save the query before running Batch Edit', + 'de-ch': + 'Bitte speichern Sie die Abfrage, bevor Sie die Stapelbearbeitung ausführen', + 'es-es': 'Guarde la consulta antes de ejecutar la edición por lotes', + 'fr-fr': "Veuillez enregistrer la requête avant d'exécuter l'édition par lots", - "pt-br": "Salve a consulta antes de executar a edição em lote", - "ru-ru": - "Пожалуйста, сохраните запрос перед запуском пакетного редактирования.", - "uk-ua": "Будь ласка, збережіть запит перед запуском пакетного редагування", + 'pt-br': 'Salve a consulta antes de executar a edição em lote', + 'ru-ru': + 'Пожалуйста, сохраните запрос перед запуском пакетного редактирования.', + 'uk-ua': 'Будь ласка, збережіть запит перед запуском пакетного редагування', }, noPreparationsToReturn: { - "en-us": "There are no unresolved items to return", - "ru-ru": "Нет нерешенных вопросов для возврата", - "es-es": "No hay items sin resolver para devolver", - "fr-fr": "Il n'y a aucun article non résolu à retourner", - "uk-ua": "Немає невирішених елементів для повернення", - "de-ch": - "Es gibt keine ungelösten Elemente, die zurückgegeben werden müssen", - "pt-br": "Não há itens não resolvidos para retornar", + 'en-us': 'There are no unresolved items to return', + 'ru-ru': 'Нет нерешенных вопросов для возврата', + 'es-es': 'No hay items sin resolver para devolver', + 'fr-fr': "Il n'y a aucun article non résolu à retourner", + 'uk-ua': 'Немає невирішених елементів для повернення', + 'de-ch': + 'Es gibt keine ungelösten Elemente, die zurückgegeben werden müssen', + 'pt-br': 'Não há itens não resolvidos para retornar', }, itemsReturned: { - "en-us": "Items have been returned", - "ru-ru": "Товары были возвращены", - "es-es": "Los items han sido devueltos", - "fr-fr": "Les articles ont été retournés", - "uk-ua": "Товари повернуто", - "de-ch": "Artikel wurden zurückgegeben", - "pt-br": "Os itens foram devolvidos", + 'en-us': 'Items have been returned', + 'ru-ru': 'Товары были возвращены', + 'es-es': 'Los items han sido devueltos', + 'fr-fr': 'Les articles ont été retournés', + 'uk-ua': 'Товари повернуто', + 'de-ch': 'Artikel wurden zurückgegeben', + 'pt-br': 'Os itens foram devolvidos', }, } as const); diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index 09766d2a6f8..3bb2340c8af 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -4,172 +4,172 @@ * @module */ -import { createDictionary } from "./utils"; +import { createDictionary } from './utils'; // Refer to "Guidelines for Programmers" in ./README.md before editing this file export const resourcesText = createDictionary({ appResources: { - "en-us": "App Resources", - "ru-ru": "Ресурсы приложений", - "es-es": "Recursos de la aplicación", - "fr-fr": "Ressources de l'application", - "uk-ua": "Ресурси програми", - "de-ch": "App Ressourcen", - "pt-br": "Recursos do aplicativo", + 'en-us': 'App Resources', + 'ru-ru': 'Ресурсы приложений', + 'es-es': 'Recursos de la aplicación', + 'fr-fr': "Ressources de l'application", + 'uk-ua': 'Ресурси програми', + 'de-ch': 'App Ressourcen', + 'pt-br': 'Recursos do aplicativo', }, formDefinition: { - "en-us": "Form Definition", - "ru-ru": "Определение формы", - "es-es": "Definición de formulario", - "fr-fr": "Définition du formulaire", - "uk-ua": "Визначення форми", - "de-ch": "Formular Definition", - "pt-br": "Definição de Formulário", + 'en-us': 'Form Definition', + 'ru-ru': 'Определение формы', + 'es-es': 'Definición de formulario', + 'fr-fr': 'Définition du formulaire', + 'uk-ua': 'Визначення форми', + 'de-ch': 'Formular Definition', + 'pt-br': 'Definição de Formulário', }, formDefinitions: { - "en-us": "Form Definition", - "ru-ru": "Определение формы", - "es-es": "Definición de formulario", - "fr-fr": "Définition du formulaire", - "uk-ua": "Визначення форми", - "de-ch": "Formulardefinition", - "pt-br": "Definição de Formulário", + 'en-us': 'Form Definition', + 'ru-ru': 'Определение формы', + 'es-es': 'Definición de formulario', + 'fr-fr': 'Définition du formulaire', + 'uk-ua': 'Визначення форми', + 'de-ch': 'Formulardefinition', + 'pt-br': 'Definição de Formulário', }, newViewDefinition: { - "en-us": "New View Definition", - "de-ch": "Neue Ansichtsdefinition", - "es-es": "Nueva definición de vista", - "fr-fr": "Nouvelle définition de vue", - "pt-br": "Nova Definição de Visualização", - "ru-ru": "Новое определение взгляда", - "uk-ua": "Нове визначення подання", + 'en-us': 'New View Definition', + 'de-ch': 'Neue Ansichtsdefinition', + 'es-es': 'Nueva definición de vista', + 'fr-fr': 'Nouvelle définition de vue', + 'pt-br': 'Nova Definição de Visualização', + 'ru-ru': 'Новое определение взгляда', + 'uk-ua': 'Нове визначення подання', }, loadFile: { - "en-us": "Load File", - "ru-ru": "Загрузить файл", - "es-es": "Cargar archivo", - "fr-fr": "Charger le fichier", - "uk-ua": "Завантажити файл", - "de-ch": "Datei Laden", - "pt-br": "Carregar arquivo", + 'en-us': 'Load File', + 'ru-ru': 'Загрузить файл', + 'es-es': 'Cargar archivo', + 'fr-fr': 'Charger le fichier', + 'uk-ua': 'Завантажити файл', + 'de-ch': 'Datei Laden', + 'pt-br': 'Carregar arquivo', }, globalResources: { - "en-us": "Global Resources", - "ru-ru": "Глобальные ресурсы", - "es-es": "Recursos globales", - "fr-fr": "Ressources mondiales", - "uk-ua": "Глобальні ресурси", - "de-ch": "Globale Ressourcen", - "pt-br": "Recursos Globais", + 'en-us': 'Global Resources', + 'ru-ru': 'Глобальные ресурсы', + 'es-es': 'Recursos globales', + 'fr-fr': 'Ressources mondiales', + 'uk-ua': 'Глобальні ресурси', + 'de-ch': 'Globale Ressourcen', + 'pt-br': 'Recursos Globais', }, disciplineResources: { - "en-us": "Discipline Resources", - "ru-ru": "Дисциплинарные ресурсы", - "es-es": "Recursos de disciplina", - "fr-fr": "Ressources disciplinaires", - "uk-ua": "Дисциплінарні ресурси", - "de-ch": "Disziplin-Ressourcen", - "pt-br": "Recursos de Disciplina", + 'en-us': 'Discipline Resources', + 'ru-ru': 'Дисциплинарные ресурсы', + 'es-es': 'Recursos de disciplina', + 'fr-fr': 'Ressources disciplinaires', + 'uk-ua': 'Дисциплінарні ресурси', + 'de-ch': 'Disziplin-Ressourcen', + 'pt-br': 'Recursos de Disciplina', }, type: { - "en-us": "Type", - "ru-ru": "Тип", - "es-es": "Tipo", - "fr-fr": "Taper", - "uk-ua": "Тип", - "de-ch": "Typ", - "pt-br": "Tipo", + 'en-us': 'Type', + 'ru-ru': 'Тип', + 'es-es': 'Tipo', + 'fr-fr': 'Taper', + 'uk-ua': 'Тип', + 'de-ch': 'Typ', + 'pt-br': 'Tipo', }, userTypes: { - "en-us": "User Types", - "ru-ru": "Типы пользователей", - "es-es": "Tipos de usuarios", - "fr-fr": "Types d'utilisateurs", - "uk-ua": "Типи користувачів", - "de-ch": "Benutzertypen", - "pt-br": "Tipos de Usuário", + 'en-us': 'User Types', + 'ru-ru': 'Типы пользователей', + 'es-es': 'Tipos de usuarios', + 'fr-fr': "Types d'utilisateurs", + 'uk-ua': 'Типи користувачів', + 'de-ch': 'Benutzertypen', + 'pt-br': 'Tipos de Usuário', }, resources: { - "en-us": "Resources", - "ru-ru": "Ресурсы", - "es-es": "Recursos", - "fr-fr": "Ressources", - "uk-ua": "Ресурси", - "de-ch": "Ressourcen", - "pt-br": "Recursos", + 'en-us': 'Resources', + 'ru-ru': 'Ресурсы', + 'es-es': 'Recursos', + 'fr-fr': 'Ressources', + 'uk-ua': 'Ресурси', + 'de-ch': 'Ressourcen', + 'pt-br': 'Recursos', }, subCategories: { - "en-us": "Sub-categories", - "ru-ru": "Подкатегории", - "es-es": "Subcategorías", - "fr-fr": "Sous-catégories", - "uk-ua": "Підкатегорії", - "de-ch": "Unterkategorien", - "pt-br": "Subcategorias", + 'en-us': 'Sub-categories', + 'ru-ru': 'Подкатегории', + 'es-es': 'Subcategorías', + 'fr-fr': 'Sous-catégories', + 'uk-ua': 'Підкатегорії', + 'de-ch': 'Unterkategorien', + 'pt-br': 'Subcategorias', }, addResource: { - "en-us": "Add Resource", - "ru-ru": "Добавить ресурс", - "es-es": "Agregar recurso", - "fr-fr": "Ajouter une ressource", - "uk-ua": "Додати ресурс", - "de-ch": "Ressource hinzufügen", - "pt-br": "Adicionar recurso", + 'en-us': 'Add Resource', + 'ru-ru': 'Добавить ресурс', + 'es-es': 'Agregar recurso', + 'fr-fr': 'Ajouter une ressource', + 'uk-ua': 'Додати ресурс', + 'de-ch': 'Ressource hinzufügen', + 'pt-br': 'Adicionar recurso', }, appResource: { - "en-us": "App Resource", - "ru-ru": "Подкатегории", - "es-es": "Recursos de la aplicación", - "fr-fr": "Ressource d'application", - "uk-ua": "Підкатегорії", - "de-ch": "App Ressource", - "pt-br": "Recurso do aplicativo", + 'en-us': 'App Resource', + 'ru-ru': 'Подкатегории', + 'es-es': 'Recursos de la aplicación', + 'fr-fr': "Ressource d'application", + 'uk-ua': 'Підкатегорії', + 'de-ch': 'App Ressource', + 'pt-br': 'Recurso do aplicativo', }, rssExportFeed: { - "en-us": "RSS Export Feed", - "ru-ru": "RSS-канал экспорта", - "es-es": "Fuente de exportación RSS", - "fr-fr": "Flux d'exportation RSS", - "uk-ua": "Канал експорту RSS", - "de-ch": "RSS-Export-Feed", - "pt-br": "Feed de exportação RSS", + 'en-us': 'RSS Export Feed', + 'ru-ru': 'RSS-канал экспорта', + 'es-es': 'Fuente de exportación RSS', + 'fr-fr': "Flux d'exportation RSS", + 'uk-ua': 'Канал експорту RSS', + 'de-ch': 'RSS-Export-Feed', + 'pt-br': 'Feed de exportação RSS', }, exports: { - "en-us": "Exports", - "de-ch": "Exporte", - "es-es": "Exportaciones", - "fr-fr": "Exportations", - "ru-ru": "Экспорт", - "uk-ua": "Експорт", - "pt-br": "Exportações", + 'en-us': 'Exports', + 'de-ch': 'Exporte', + 'es-es': 'Exportaciones', + 'fr-fr': 'Exportations', + 'ru-ru': 'Экспорт', + 'uk-ua': 'Експорт', + 'pt-br': 'Exportações', }, expressSearchConfig: { - "en-us": "Express Search Config", - "ru-ru": "Конфигурация экспресс-поиска", - "es-es": "Configuración de búsqueda rápida", - "fr-fr": "Configuration de la recherche express", - "uk-ua": "Конфігурація експрес-пошуку", - "de-ch": "Express Suche Konfigurieren", - "pt-br": "Configuração de pesquisa expressa", + 'en-us': 'Express Search Config', + 'ru-ru': 'Конфигурация экспресс-поиска', + 'es-es': 'Configuración de búsqueda rápida', + 'fr-fr': 'Configuration de la recherche express', + 'uk-ua': 'Конфігурація експрес-пошуку', + 'de-ch': 'Express Suche Konfigurieren', + 'pt-br': 'Configuração de pesquisa expressa', }, typeSearches: { - "en-us": "Type Searches", - "de-ch": "Typsuchen", - "es-es": "Búsquedas de tipos", - "fr-fr": "Recherches de type", - "ru-ru": "Тип поиска", - "uk-ua": "Пошук типів", - "pt-br": "Pesquisas de tipo", + 'en-us': 'Type Searches', + 'de-ch': 'Typsuchen', + 'es-es': 'Búsquedas de tipos', + 'fr-fr': 'Recherches de type', + 'ru-ru': 'Тип поиска', + 'uk-ua': 'Пошук типів', + 'pt-br': 'Pesquisas de tipo', }, webLinks: { - "en-us": "Web Links", - "ru-ru": "Веб ссылки", - "es-es": "Enlaces web", - "fr-fr": "Liens Web", - "uk-ua": "Веб-посилання", - "de-ch": "Weblinks", - "pt-br": "Links da Web", + 'en-us': 'Web Links', + 'ru-ru': 'Веб ссылки', + 'es-es': 'Enlaces web', + 'fr-fr': 'Liens Web', + 'uk-ua': 'Веб-посилання', + 'de-ch': 'Weblinks', + 'pt-br': 'Links da Web', }, fieldFormatters: { 'en-us': 'Field Formatters', @@ -188,90 +188,90 @@ export const resourcesText = createDictionary({ `, }, dataObjectFormatters: { - "en-us": "Record Formatters", - "ru-ru": "Форматеры записи", - "es-es": "Formateadores de registros", - "uk-ua": "Форматувальники записів", - "de-ch": "Datensatz-Formatierer", - "fr-fr": "Formateurs d'enregistrements", - "pt-br": "Formatadores de registros", + 'en-us': 'Record Formatters', + 'ru-ru': 'Форматеры записи', + 'es-es': 'Formateadores de registros', + 'uk-ua': 'Форматувальники записів', + 'de-ch': 'Datensatz-Formatierer', + 'fr-fr': "Formateurs d'enregistrements", + 'pt-br': 'Formatadores de registros', }, formatter: { - "en-us": "Table Format", - "de-ch": "Tabellenformat", - "es-es": "Formato de tabla", - "fr-fr": "Format de tableau", - "ru-ru": "Формат таблицы", - "uk-ua": "Формат таблиці", - "pt-br": "Formato de tabela", + 'en-us': 'Table Format', + 'de-ch': 'Tabellenformat', + 'es-es': 'Formato de tabla', + 'fr-fr': 'Format de tableau', + 'ru-ru': 'Формат таблицы', + 'uk-ua': 'Формат таблиці', + 'pt-br': 'Formato de tabela', }, formatterDescription: { - "en-us": - "The “Table Format” controls how data from a specific table is shown in query results, exports, and query combo boxes. It determines the fields to display and their order. Conditional formatting can be configured based on a value in the record.", - "de-ch": - "Das Tabellenformat steuert die Darstellung von Daten aus einer bestimmten Tabelle in Abfrageergebnissen, Exporten und Abfrage-Kombinationsfeldern. Es bestimmt die anzuzeigenden Felder und deren Reihenfolge. Die bedingte Formatierung kann basierend auf einem Wert im Datensatz konfiguriert werden.", - "es-es": + 'en-us': + 'The “Table Format” controls how data from a specific table is shown in query results, exports, and query combo boxes. It determines the fields to display and their order. Conditional formatting can be configured based on a value in the record.', + 'de-ch': + 'Das Tabellenformat steuert die Darstellung von Daten aus einer bestimmten Tabelle in Abfrageergebnissen, Exporten und Abfrage-Kombinationsfeldern. Es bestimmt die anzuzeigenden Felder und deren Reihenfolge. Die bedingte Formatierung kann basierend auf einem Wert im Datensatz konfiguriert werden.', + 'es-es': 'El "Formato de tabla" controla cómo se muestran los datos de una tabla específica en los resultados de consultas, las exportaciones y los cuadros combinados de consultas. Determina los campos que se mostrarán y su orden. El formato condicional se puede configurar según un valor del registro.', - "fr-fr": + 'fr-fr': "Le « Format de table » contrôle l'affichage des données d'une table spécifique dans les résultats de requête, les exportations et les listes déroulantes. Il détermine les champs à afficher et leur ordre. La mise en forme conditionnelle peut être configurée en fonction d'une valeur de l'enregistrement.", - "ru-ru": - "«Формат таблицы» определяет, как данные из определенной таблицы отображаются в результатах запроса, экспорте и полях со списком запроса. Он определяет поля для отображения и их порядок. Условное форматирование можно настроить на основе значения в записи.", - "uk-ua": - "«Формат таблиці» керує тим, як дані з певної таблиці відображаються в результатах запитів, експорті та комбінованих полях запитів. Він визначає поля для відображення та їх порядок. Умовне форматування можна налаштувати на основі значення в записі.", - "pt-br": - "O “Formato da Tabela” controla como os dados de uma tabela específica são exibidos nos resultados da consulta, exportações e caixas de combinação da consulta. Ele determina os campos a serem exibidos e sua ordem. A formatação condicional pode ser configurada com base em um valor no registro.", + 'ru-ru': + '«Формат таблицы» определяет, как данные из определенной таблицы отображаются в результатах запроса, экспорте и полях со списком запроса. Он определяет поля для отображения и их порядок. Условное форматирование можно настроить на основе значения в записи.', + 'uk-ua': + '«Формат таблиці» керує тим, як дані з певної таблиці відображаються в результатах запитів, експорті та комбінованих полях запитів. Він визначає поля для відображення та їх порядок. Умовне форматування можна налаштувати на основі значення в записі.', + 'pt-br': + 'O “Formato da Tabela” controla como os dados de uma tabela específica são exibidos nos resultados da consulta, exportações e caixas de combinação da consulta. Ele determina os campos a serem exibidos e sua ordem. A formatação condicional pode ser configurada com base em um valor no registro.', }, aggregator: { - "en-us": "Table Aggregation", - "de-ch": "Tabellenaggregation", - "es-es": "Agregación de tablas", - "fr-fr": "Agrégation de table", - "ru-ru": "Агрегация таблиц", - "uk-ua": "Агрегація таблиць", - "pt-br": "Agregação de tabelas", + 'en-us': 'Table Aggregation', + 'de-ch': 'Tabellenaggregation', + 'es-es': 'Agregación de tablas', + 'fr-fr': 'Agrégation de table', + 'ru-ru': 'Агрегация таблиц', + 'uk-ua': 'Агрегація таблиць', + 'pt-br': 'Agregação de tabelas', }, aggregatorDescription: { - "en-us": - "The “Table Aggregation” controls how multiple table records are consolidated together into a single text string. The table format, separator, suffix, sort field, and record preview limit are customizable. It can be displayed in query results and table formats.", - "de-ch": - "Die „Tabellenaggregation“ steuert, wie mehrere Tabellendatensätze zu einer einzigen Textzeichenfolge zusammengefasst werden. Tabellenformat, Trennzeichen, Suffix, Sortierfeld und Datensatzvorschaulimit sind anpassbar. Die Anzeige kann in Abfrageergebnissen und Tabellenformaten erfolgen.", - "es-es": + 'en-us': + 'The “Table Aggregation” controls how multiple table records are consolidated together into a single text string. The table format, separator, suffix, sort field, and record preview limit are customizable. It can be displayed in query results and table formats.', + 'de-ch': + 'Die „Tabellenaggregation“ steuert, wie mehrere Tabellendatensätze zu einer einzigen Textzeichenfolge zusammengefasst werden. Tabellenformat, Trennzeichen, Suffix, Sortierfeld und Datensatzvorschaulimit sind anpassbar. Die Anzeige kann in Abfrageergebnissen und Tabellenformaten erfolgen.', + 'es-es': 'La "Agregación de Tablas" controla cómo se consolidan varios registros de tabla en una sola cadena de texto. El formato de tabla, el separador, el sufijo, el campo de ordenación y el límite de vista previa de registros son personalizables. Se pueden mostrar en los resultados de consultas y en los formatos de tabla.', - "fr-fr": + 'fr-fr': "L'« Agrégation de table » contrôle la consolidation de plusieurs enregistrements de table en une seule chaîne de texte. Le format de table, le séparateur, le suffixe, le champ de tri et la limite d'aperçu des enregistrements sont personnalisables. L'affichage peut être effectué dans les résultats de requête et les formats de table.", - "ru-ru": - "«Агрегация таблиц» управляет тем, как несколько записей таблицы объединяются в одну текстовую строку. Формат таблицы, разделитель, суффикс, поле сортировки и ограничение предварительного просмотра записи можно настроить. Его можно отобразить в результатах запроса и в форматах таблиц.", - "uk-ua": - "«Агрегація таблиць» контролює, як кілька записів таблиці об’єднуються разом в один текстовий рядок. Формат таблиці, роздільник, суфікс, поле сортування та ліміт попереднього перегляду запису можна налаштувати. Він може відображатися в результатах запитів і у форматах таблиць.", - "pt-br": + 'ru-ru': + '«Агрегация таблиц» управляет тем, как несколько записей таблицы объединяются в одну текстовую строку. Формат таблицы, разделитель, суффикс, поле сортировки и ограничение предварительного просмотра записи можно настроить. Его можно отобразить в результатах запроса и в форматах таблиц.', + 'uk-ua': + '«Агрегація таблиць» контролює, як кілька записів таблиці об’єднуються разом в один текстовий рядок. Формат таблиці, роздільник, суфікс, поле сортування та ліміт попереднього перегляду запису можна налаштувати. Він може відображатися в результатах запитів і у форматах таблиць.', + 'pt-br': 'A "Agregação de Tabelas" controla como vários registros de tabelas são consolidados em uma única sequência de texto. O formato da tabela, o separador, o sufixo, o campo de classificação e o limite de visualização de registros são personalizáveis. Ele pode ser exibido nos resultados da consulta e nos formatos de tabela.', }, formattedResource: { - "en-us": "Formatted Resource", - "de-ch": "Formatierte Ressource", - "es-es": "Recurso formateado", - "fr-fr": "Ressource formatée", - "ru-ru": "Форматированный ресурс", - "uk-ua": "Відформатований ресурс", - "pt-br": "Recurso formatado", + 'en-us': 'Formatted Resource', + 'de-ch': 'Formatierte Ressource', + 'es-es': 'Recurso formateado', + 'fr-fr': 'Ressource formatée', + 'ru-ru': 'Форматированный ресурс', + 'uk-ua': 'Відформатований ресурс', + 'pt-br': 'Recurso formatado', }, availableFormatters: { - "en-us": "Available Table Formats", - "de-ch": "Verfügbare Tabellenformate", - "es-es": "Formatos de tabla disponibles", - "fr-fr": "Formats de tableau disponibles", - "ru-ru": "Доступные форматы таблиц", - "uk-ua": "Доступні формати таблиць", - "pt-br": "Formatos de tabela disponíveis", + 'en-us': 'Available Table Formats', + 'de-ch': 'Verfügbare Tabellenformate', + 'es-es': 'Formatos de tabla disponibles', + 'fr-fr': 'Formats de tableau disponibles', + 'ru-ru': 'Доступные форматы таблиц', + 'uk-ua': 'Доступні формати таблиць', + 'pt-br': 'Formatos de tabela disponíveis', }, availableAggregators: { - "en-us": "Available Table Aggregations", - "de-ch": "Verfügbare Tabellenaggregationen", - "es-es": "Agregaciones de tablas disponibles", - "fr-fr": "Agrégations de tables disponibles", - "ru-ru": "Доступные агрегаты таблиц", - "uk-ua": "Доступні агрегації таблиць", - "pt-br": "Agregações de tabelas disponíveis", + 'en-us': 'Available Table Aggregations', + 'de-ch': 'Verfügbare Tabellenaggregationen', + 'es-es': 'Agregaciones de tablas disponibles', + 'fr-fr': 'Agrégations de tables disponibles', + 'ru-ru': 'Доступные агрегаты таблиц', + 'uk-ua': 'Доступні агрегації таблиць', + 'pt-br': 'Agregações de tabelas disponíveis', }, availableWebLinks: { 'en-us': 'Available Web Links', @@ -291,744 +291,744 @@ export const resourcesText = createDictionary({ 'uk-ua': 'Доступні форматувальники полів', }, selectDefaultFormatter: { - "en-us": "Please select a default record formatter for this table", - "de-ch": - "Bitte wählen Sie einen Standard-Datensatzformatierer für diese Tabelle", - "es-es": - "Seleccione un formateador de registro predeterminado para esta tabla", - "fr-fr": + 'en-us': 'Please select a default record formatter for this table', + 'de-ch': + 'Bitte wählen Sie einen Standard-Datensatzformatierer für diese Tabelle', + 'es-es': + 'Seleccione un formateador de registro predeterminado para esta tabla', + 'fr-fr': "Veuillez sélectionner un formateur d'enregistrement par défaut pour cette table", - "ru-ru": - "Пожалуйста, выберите форматирование записей по умолчанию для этой таблицы.", - "uk-ua": "Виберіть стандартний формат запису для цієї таблиці", - "pt-br": "Selecione um formatador de registro padrão para esta tabela", + 'ru-ru': + 'Пожалуйста, выберите форматирование записей по умолчанию для этой таблицы.', + 'uk-ua': 'Виберіть стандартний формат запису для цієї таблиці', + 'pt-br': 'Selecione um formatador de registro padrão para esta tabela', }, duplicateFormatters: { - "en-us": "Record formatter names must be unique", - "de-ch": "Datensatzformatierernamen müssen eindeutig sein", - "es-es": "Los nombres de los formateadores de registros deben ser únicos", - "fr-fr": "Les noms des formateurs d'enregistrement doivent être uniques", - "ru-ru": "Имена средств форматирования записей должны быть уникальными.", - "uk-ua": "Імена форматування записів мають бути унікальними", - "pt-br": "Os nomes dos formatadores de registro devem ser exclusivos", + 'en-us': 'Record formatter names must be unique', + 'de-ch': 'Datensatzformatierernamen müssen eindeutig sein', + 'es-es': 'Los nombres de los formateadores de registros deben ser únicos', + 'fr-fr': "Les noms des formateurs d'enregistrement doivent être uniques", + 'ru-ru': 'Имена средств форматирования записей должны быть уникальными.', + 'uk-ua': 'Імена форматування записів мають бути унікальними', + 'pt-br': 'Os nomes dos formatadores de registro devem ser exclusivos', }, dataEntryTables: { - "en-us": "Data Entry Tables", - "ru-ru": "Таблицы ввода данных", - "es-es": "Tablas de entrada de datos", - "fr-fr": "Tableaux de saisie de données", - "uk-ua": "Таблиці введення даних", - "de-ch": "Dateneingabetabellen", - "pt-br": "Tabelas de entrada de dados", + 'en-us': 'Data Entry Tables', + 'ru-ru': 'Таблицы ввода данных', + 'es-es': 'Tablas de entrada de datos', + 'fr-fr': 'Tableaux de saisie de données', + 'uk-ua': 'Таблиці введення даних', + 'de-ch': 'Dateneingabetabellen', + 'pt-br': 'Tabelas de entrada de dados', }, interactionsTables: { - "en-us": "Interactions Tables", - "ru-ru": "Таблицы взаимодействий", - "es-es": "Tablas de interacciones", - "fr-fr": "Tables d'interactions", - "uk-ua": "Таблиці взаємодій", - "de-ch": "Interaktionstabellen", - "pt-br": "Tabelas de Interações", + 'en-us': 'Interactions Tables', + 'ru-ru': 'Таблицы взаимодействий', + 'es-es': 'Tablas de interacciones', + 'fr-fr': "Tables d'interactions", + 'uk-ua': 'Таблиці взаємодій', + 'de-ch': 'Interaktionstabellen', + 'pt-br': 'Tabelas de Interações', }, otherXmlResource: { - "en-us": "Other XML Resource", - "ru-ru": "Выберите тип ресурса", - "es-es": "Otros recursos XML", - "fr-fr": "Autre ressource XML", - "uk-ua": "Інший XML-ресурс", - "de-ch": "Andere XML-Ressource", - "pt-br": "Outro recurso XML", + 'en-us': 'Other XML Resource', + 'ru-ru': 'Выберите тип ресурса', + 'es-es': 'Otros recursos XML', + 'fr-fr': 'Autre ressource XML', + 'uk-ua': 'Інший XML-ресурс', + 'de-ch': 'Andere XML-Ressource', + 'pt-br': 'Outro recurso XML', }, otherJsonResource: { - "en-us": "Other JSON Resource", - "ru-ru": "Другой ресурс JSON", - "es-es": "Otros recursos JSON", - "fr-fr": "Autre ressource JSON", - "uk-ua": "Інший JSON-ресурс", - "de-ch": "Andere JSON-Ressource", - "pt-br": "Outro recurso JSON", + 'en-us': 'Other JSON Resource', + 'ru-ru': 'Другой ресурс JSON', + 'es-es': 'Otros recursos JSON', + 'fr-fr': 'Autre ressource JSON', + 'uk-ua': 'Інший JSON-ресурс', + 'de-ch': 'Andere JSON-Ressource', + 'pt-br': 'Outro recurso JSON', }, otherPropertiesResource: { - "en-us": "Other Properties Resource", - "ru-ru": "Ресурс «Другие свойства»", - "es-es": "Recursos de otras propiedades", - "fr-fr": "Autres propriétés", - "uk-ua": "Ресурс інших властивостей", - "de-ch": "Andere Eigenschaften Ressource", - "pt-br": "Outros recursos de propriedades", + 'en-us': 'Other Properties Resource', + 'ru-ru': 'Ресурс «Другие свойства»', + 'es-es': 'Recursos de otras propiedades', + 'fr-fr': 'Autres propriétés', + 'uk-ua': 'Ресурс інших властивостей', + 'de-ch': 'Andere Eigenschaften Ressource', + 'pt-br': 'Outros recursos de propriedades', }, otherAppResource: { - "en-us": "Other Resource", - "ru-ru": "Другой ресурс", - "es-es": "Otros recursos", - "fr-fr": "Autre ressource", - "uk-ua": "Інший ресурс", - "de-ch": "Andere Ressource", - "pt-br": "Outros recursos", + 'en-us': 'Other Resource', + 'ru-ru': 'Другой ресурс', + 'es-es': 'Otros recursos', + 'fr-fr': 'Autre ressource', + 'uk-ua': 'Інший ресурс', + 'de-ch': 'Andere Ressource', + 'pt-br': 'Outros recursos', }, filters: { - "en-us": "Filters", - "ru-ru": "Фильтры", - "es-es": "Filtros", - "fr-fr": "Filtres", - "uk-ua": "Фільтри", - "de-ch": "Filter", - "pt-br": "Filtros", + 'en-us': 'Filters', + 'ru-ru': 'Фильтры', + 'es-es': 'Filtros', + 'fr-fr': 'Filtres', + 'uk-ua': 'Фільтри', + 'de-ch': 'Filter', + 'pt-br': 'Filtros', }, custom: { - "en-us": "Custom", - "ru-ru": "Обычай", - "es-es": "Costumbre", - "fr-fr": "Coutume", - "uk-ua": "Спеціальні", - "de-ch": "Individuell", - "pt-br": "Personalizado", + 'en-us': 'Custom', + 'ru-ru': 'Обычай', + 'es-es': 'Costumbre', + 'fr-fr': 'Coutume', + 'uk-ua': 'Спеціальні', + 'de-ch': 'Individuell', + 'pt-br': 'Personalizado', }, leafletLayers: { - "en-us": "Leaflet Layers", - "ru-ru": "Слои листовок", - "es-es": "Capas de los folletos", - "fr-fr": "Couches de folioles", - "uk-ua": "Шари листівок", - "de-ch": "Leaflet-Layer", - "pt-br": "Camadas de folhetos", + 'en-us': 'Leaflet Layers', + 'ru-ru': 'Слои листовок', + 'es-es': 'Capas de los folletos', + 'fr-fr': 'Couches de folioles', + 'uk-ua': 'Шари листівок', + 'de-ch': 'Leaflet-Layer', + 'pt-br': 'Camadas de folhetos', }, textEditor: { - "en-us": "Text Editor", - "ru-ru": "Текстовый редактор", - "es-es": "Editor de texto", - "fr-fr": "Éditeur de texte", - "uk-ua": "Текстовий редактор", - "de-ch": "Text-Editor", - "pt-br": "Editor de texto", + 'en-us': 'Text Editor', + 'ru-ru': 'Текстовый редактор', + 'es-es': 'Editor de texto', + 'fr-fr': 'Éditeur de texte', + 'uk-ua': 'Текстовий редактор', + 'de-ch': 'Text-Editor', + 'pt-br': 'Editor de texto', }, xmlEditor: { - "en-us": "XML Editor", - "ru-ru": "XML-редактор", - "es-es": "Editor XML", - "fr-fr": "Éditeur XML", - "uk-ua": "Редактор XML", - "de-ch": "XML-Editor", - "pt-br": "Editor XML", + 'en-us': 'XML Editor', + 'ru-ru': 'XML-редактор', + 'es-es': 'Editor XML', + 'fr-fr': 'Éditeur XML', + 'uk-ua': 'Редактор XML', + 'de-ch': 'XML-Editor', + 'pt-br': 'Editor XML', }, jsonEditor: { - "en-us": "JSON Editor", - "ru-ru": "Редактор JSON", - "es-es": "Editor JSON", - "fr-fr": "Éditeur JSON", - "uk-ua": "Редактор JSON", - "de-ch": "JSON-Editor", - "pt-br": "Editor JSON", + 'en-us': 'JSON Editor', + 'ru-ru': 'Редактор JSON', + 'es-es': 'Editor JSON', + 'fr-fr': 'Éditeur JSON', + 'uk-ua': 'Редактор JSON', + 'de-ch': 'JSON-Editor', + 'pt-br': 'Editor JSON', }, visualEditor: { - "en-us": "Visual Editor", - "ru-ru": "Визуальный редактор", - "es-es": "Editor visual", - "fr-fr": "Éditeur visuel", - "uk-ua": "Візуальний редактор", - "de-ch": "Visueller Editor", - "pt-br": "Editor Visual", + 'en-us': 'Visual Editor', + 'ru-ru': 'Визуальный редактор', + 'es-es': 'Editor visual', + 'fr-fr': 'Éditeur visuel', + 'uk-ua': 'Візуальний редактор', + 'de-ch': 'Visueller Editor', + 'pt-br': 'Editor Visual', }, selectResourceType: { - "en-us": "Select Resource Type", - "ru-ru": "Выберите тип ресурса", - "es-es": "Seleccionar el tipo de recurso", - "fr-fr": "Sélectionner le type de ressource", - "uk-ua": "Виберіть тип ресурсу", - "de-ch": "Ressourcentyp auswählen", - "pt-br": "Selecione o tipo de recurso", + 'en-us': 'Select Resource Type', + 'ru-ru': 'Выберите тип ресурса', + 'es-es': 'Seleccionar el tipo de recurso', + 'fr-fr': 'Sélectionner le type de ressource', + 'uk-ua': 'Виберіть тип ресурсу', + 'de-ch': 'Ressourcentyp auswählen', + 'pt-br': 'Selecione o tipo de recurso', }, globalPreferences: { - "en-us": "Global Preferences", - "ru-ru": "Глобальные настройки", - "es-es": "Preferencias globales", - "fr-fr": "Préférences globales", - "uk-ua": "Глобальні налаштування", - "de-ch": "Globale Einstellungen", - "pt-br": "Preferências globais", + 'en-us': 'Global Preferences', + 'ru-ru': 'Глобальные настройки', + 'es-es': 'Preferencias globales', + 'fr-fr': 'Préférences globales', + 'uk-ua': 'Глобальні налаштування', + 'de-ch': 'Globale Einstellungen', + 'pt-br': 'Preferências globais', }, remotePreferences: { - "en-us": "Remote Preferences", - "ru-ru": "Удаленные настройки", - "es-es": "Preferencias remotas", - "fr-fr": "Préférences à distance", - "uk-ua": "Віддалені параметри", - "de-ch": "Remote-Einstellungen", - "pt-br": "Preferências Remotas", + 'en-us': 'Remote Preferences', + 'ru-ru': 'Удаленные настройки', + 'es-es': 'Preferencias remotas', + 'fr-fr': 'Préférences à distance', + 'uk-ua': 'Віддалені параметри', + 'de-ch': 'Remote-Einstellungen', + 'pt-br': 'Preferências Remotas', }, failedParsingXml: { - "en-us": "Failed to parse XML", - "ru-ru": "Не удалось разобрать XML", - "de-ch": "XML konnte nicht analysiert werden", - "es-es": "No se pudo analizar XML", - "fr-fr": "Échec de l'analyse XML", - "uk-ua": "Не вдалося проаналізувати XML", - "pt-br": "Falha ao analisar XML", + 'en-us': 'Failed to parse XML', + 'ru-ru': 'Не удалось разобрать XML', + 'de-ch': 'XML konnte nicht analysiert werden', + 'es-es': 'No se pudo analizar XML', + 'fr-fr': "Échec de l'analyse XML", + 'uk-ua': 'Не вдалося проаналізувати XML', + 'pt-br': 'Falha ao analisar XML', }, name: { - "en-us": "Name", - "ru-ru": "Имя", - "de-ch": "Name", - "es-es": "Nombre", - "fr-fr": "Nom", - "uk-ua": "Ім'я", - "pt-br": "Nome", + 'en-us': 'Name', + 'ru-ru': 'Имя', + 'de-ch': 'Name', + 'es-es': 'Nombre', + 'fr-fr': 'Nom', + 'uk-ua': "Ім'я", + 'pt-br': 'Nome', }, title: { - "en-us": "Title", - "ru-ru": "Заголовок", - "de-ch": "Titel", - "es-es": "Título", - "fr-fr": "Titre", - "uk-ua": "Назва", - "pt-br": "Título", + 'en-us': 'Title', + 'ru-ru': 'Заголовок', + 'de-ch': 'Titel', + 'es-es': 'Título', + 'fr-fr': 'Titre', + 'uk-ua': 'Назва', + 'pt-br': 'Título', }, default: { - "en-us": "Default", - "ru-ru": "По умолчанию", - "de-ch": "Standard", - "es-es": "Por defecto", - "fr-fr": "Défaut", - "uk-ua": "За замовчуванням", - "pt-br": "Padrão", + 'en-us': 'Default', + 'ru-ru': 'По умолчанию', + 'de-ch': 'Standard', + 'es-es': 'Por defecto', + 'fr-fr': 'Défaut', + 'uk-ua': 'За замовчуванням', + 'pt-br': 'Padrão', }, separator: { - "en-us": "Separator", - "ru-ru": "Разделитель", - "de-ch": "Separator", - "es-es": "Separador", - "fr-fr": "Séparateur", - "uk-ua": "Роздільник", - "pt-br": "Separador", + 'en-us': 'Separator', + 'ru-ru': 'Разделитель', + 'de-ch': 'Separator', + 'es-es': 'Separador', + 'fr-fr': 'Séparateur', + 'uk-ua': 'Роздільник', + 'pt-br': 'Separador', }, suffix: { - "en-us": "Suffix", - "ru-ru": "Суффикс", - "de-ch": "Suffix", - "es-es": "Sufijo", - "fr-fr": "Suffixe", - "uk-ua": "Суфікс", - "pt-br": "Sufixo", + 'en-us': 'Suffix', + 'ru-ru': 'Суффикс', + 'de-ch': 'Suffix', + 'es-es': 'Sufijo', + 'fr-fr': 'Suffixe', + 'uk-ua': 'Суфікс', + 'pt-br': 'Sufixo', }, limit: { - "en-us": "Limit", - "ru-ru": "Лимит", - "de-ch": "Limit", - "es-es": "Límite", - "fr-fr": "Limite", - "uk-ua": "Ліміт", - "pt-br": "Limite", + 'en-us': 'Limit', + 'ru-ru': 'Лимит', + 'de-ch': 'Limit', + 'es-es': 'Límite', + 'fr-fr': 'Limite', + 'uk-ua': 'Ліміт', + 'pt-br': 'Limite', }, defaultInline: { - "en-us": "(default)", - "de-ch": "(Standard)", - "es-es": "(por defecto)", - "fr-fr": "(défaut)", - "ru-ru": "(по умолчанию)", - "uk-ua": "(за умовчанням)", - "pt-br": "(padrão)", + 'en-us': '(default)', + 'de-ch': '(Standard)', + 'es-es': '(por defecto)', + 'fr-fr': '(défaut)', + 'ru-ru': '(по умолчанию)', + 'uk-ua': '(за умовчанням)', + 'pt-br': '(padrão)', }, sortField: { - "en-us": "Sort Field", - "ru-ru": "Сортировать поле", - "de-ch": "Sortierfeld", - "es-es": "Campo de ordenación", - "fr-fr": "Champ de tri", - "uk-ua": "Поле сортування", - "pt-br": "Campo de classificação", + 'en-us': 'Sort Field', + 'ru-ru': 'Сортировать поле', + 'de-ch': 'Sortierfeld', + 'es-es': 'Campo de ordenación', + 'fr-fr': 'Champ de tri', + 'uk-ua': 'Поле сортування', + 'pt-br': 'Campo de classificação', }, preview: { - "en-us": "Preview", - "ru-ru": "Предварительный просмотр", - "de-ch": "Vorschau", - "es-es": "Avance", - "fr-fr": "Aperçu", - "uk-ua": "Попередній перегляд", - "pt-br": "Pré-visualização", + 'en-us': 'Preview', + 'ru-ru': 'Предварительный просмотр', + 'de-ch': 'Vorschau', + 'es-es': 'Avance', + 'fr-fr': 'Aperçu', + 'uk-ua': 'Попередній перегляд', + 'pt-br': 'Pré-visualização', }, previewExplainer: { - "en-us": "Search your collection records to preview the record formatter", - "de-ch": - "Durchsuchen Sie Ihre Sammlungsdatensätze, um eine Vorschau des Datensatzformatierers anzuzeigen", - "es-es": - "Busque en sus registros de colección para obtener una vista previa del formateador de registros", - "fr-fr": + 'en-us': 'Search your collection records to preview the record formatter', + 'de-ch': + 'Durchsuchen Sie Ihre Sammlungsdatensätze, um eine Vorschau des Datensatzformatierers anzuzeigen', + 'es-es': + 'Busque en sus registros de colección para obtener una vista previa del formateador de registros', + 'fr-fr': "Recherchez dans vos enregistrements de collection pour prévisualiser le formateur d'enregistrements", - "ru-ru": - "Выполните поиск в записях своей коллекции, чтобы просмотреть средство форматирования записей.", - "uk-ua": - "Виконайте пошук у своїх записах колекції, щоб переглянути інструмент форматування записів", - "pt-br": - "Pesquise os registros da sua coleção para visualizar o formatador de registros", + 'ru-ru': + 'Выполните поиск в записях своей коллекции, чтобы просмотреть средство форматирования записей.', + 'uk-ua': + 'Виконайте пошук у своїх записах колекції, щоб переглянути інструмент форматування записів', + 'pt-br': + 'Pesquise os registros da sua coleção para visualizar o formatador de registros', }, editorNotAvailable: { - "en-us": "Visual editor is not available for this resource", - "de-ch": "Für diese Ressource ist kein visueller Editor verfügbar", - "es-es": "El editor visual no está disponible para este recurso", - "fr-fr": "L'éditeur visuel n'est pas disponible pour cette ressource", - "ru-ru": "Визуальный редактор недоступен для этого ресурса.", - "uk-ua": "Візуальний редактор недоступний для цього ресурсу", - "pt-br": "O editor visual não está disponível para este recurso", + 'en-us': 'Visual editor is not available for this resource', + 'de-ch': 'Für diese Ressource ist kein visueller Editor verfügbar', + 'es-es': 'El editor visual no está disponible para este recurso', + 'fr-fr': "L'éditeur visuel n'est pas disponible pour cette ressource", + 'ru-ru': 'Визуальный редактор недоступен для этого ресурса.', + 'uk-ua': 'Візуальний редактор недоступний для цього ресурсу', + 'pt-br': 'O editor visual não está disponível para este recurso', }, definition: { - "en-us": "Definition", - "de-ch": "Definition", - "es-es": "Definición", - "fr-fr": "Définition", - "ru-ru": "Определение", - "uk-ua": "Визначення", - "pt-br": "Definição", + 'en-us': 'Definition', + 'de-ch': 'Definition', + 'es-es': 'Definición', + 'fr-fr': 'Définition', + 'ru-ru': 'Определение', + 'uk-ua': 'Визначення', + 'pt-br': 'Definição', }, addDefinition: { - "en-us": "Add definition", - "de-ch": "Definition hinzufügen", - "es-es": "Añadir definición", - "fr-fr": "Ajouter une définition", - "ru-ru": "Добавить определение", - "uk-ua": "Додайте визначення", - "pt-br": "Adicionar definição", + 'en-us': 'Add definition', + 'de-ch': 'Definition hinzufügen', + 'es-es': 'Añadir definición', + 'fr-fr': 'Ajouter une définition', + 'ru-ru': 'Добавить определение', + 'uk-ua': 'Додайте визначення', + 'pt-br': 'Adicionar definição', }, deleteDefinition: { - "en-us": "Delete definition", - "de-ch": "Definition löschen", - "es-es": "Eliminar definición", - "fr-fr": "Supprimer la définition", - "ru-ru": "Удалить определение", - "uk-ua": "Видалити визначення", - "pt-br": "Excluir definição", + 'en-us': 'Delete definition', + 'de-ch': 'Definition löschen', + 'es-es': 'Eliminar definición', + 'fr-fr': 'Supprimer la définition', + 'ru-ru': 'Удалить определение', + 'uk-ua': 'Видалити визначення', + 'pt-br': 'Excluir definição', }, urlPart: { - "en-us": "URL part", - "de-ch": "URL-Teil", - "es-es": "Parte de la URL", - "fr-fr": "partie URL", - "ru-ru": "Другой ресурс JSON", - "uk-ua": "URL-адреса", - "pt-br": "Parte da URL", + 'en-us': 'URL part', + 'de-ch': 'URL-Teil', + 'es-es': 'Parte de la URL', + 'fr-fr': 'partie URL', + 'ru-ru': 'Другой ресурс JSON', + 'uk-ua': 'URL-адреса', + 'pt-br': 'Parte da URL', }, addField: { - "en-us": "Add field", - "de-ch": "Feld hinzufügen", - "es-es": "Agregar campo", - "fr-fr": "Ajouter un champ", - "ru-ru": "Добавить поле", - "uk-ua": "Додати поле", - "pt-br": "Adicionar campo", + 'en-us': 'Add field', + 'de-ch': 'Feld hinzufügen', + 'es-es': 'Agregar campo', + 'fr-fr': 'Ajouter un champ', + 'ru-ru': 'Добавить поле', + 'uk-ua': 'Додати поле', + 'pt-br': 'Adicionar campo', }, thisField: { - "en-us": "This field", - "de-ch": "Dieses Feld", - "es-es": "Este campo", - "fr-fr": "Ce champ", - "ru-ru": "Это поле", - "uk-ua": "Це поле", - "pt-br": "Este campo", + 'en-us': 'This field', + 'de-ch': 'Dieses Feld', + 'es-es': 'Este campo', + 'fr-fr': 'Ce champ', + 'ru-ru': 'Это поле', + 'uk-ua': 'Це поле', + 'pt-br': 'Este campo', }, selectTableFirst: { - "en-us": "Select table first", - "de-ch": "Wählen Sie zuerst die Tabelle aus", - "es-es": "Seleccione la tabla primero", - "fr-fr": "Sélectionnez d'abord la table", - "ru-ru": "Сначала выберите таблицу", - "uk-ua": "Спочатку виберіть таблицю", - "pt-br": "Selecione a tabela primeiro", + 'en-us': 'Select table first', + 'de-ch': 'Wählen Sie zuerst die Tabelle aus', + 'es-es': 'Seleccione la tabla primero', + 'fr-fr': "Sélectionnez d'abord la table", + 'ru-ru': 'Сначала выберите таблицу', + 'uk-ua': 'Спочатку виберіть таблицю', + 'pt-br': 'Selecione a tabela primeiro', }, conditionFieldValue: { - "en-us": "Condition Field Value", - "de-ch": "Bedingungsfeldwert", - "es-es": "Valor del campo de condición", - "fr-fr": "Valeur du champ de condition", - "ru-ru": "Значение поля условия", - "uk-ua": "Умова Значення поля", - "pt-br": "Valor do campo de condição", + 'en-us': 'Condition Field Value', + 'de-ch': 'Bedingungsfeldwert', + 'es-es': 'Valor del campo de condición', + 'fr-fr': 'Valeur du champ de condition', + 'ru-ru': 'Значение поля условия', + 'uk-ua': 'Умова Значення поля', + 'pt-br': 'Valor do campo de condição', }, conditionDescription: { - "en-us": - "This format will be used only if the condition field value equals this condition and is not null.", - "de-ch": - "Dieses Format wird nur verwendet, wenn der Wert des Bedingungsfelds dieser Bedingung entspricht und nicht null ist.", - "es-es": - "Este formato se utilizará solo si el valor del campo de condición es igual a esta condición y no es nulo.", - "fr-fr": + 'en-us': + 'This format will be used only if the condition field value equals this condition and is not null.', + 'de-ch': + 'Dieses Format wird nur verwendet, wenn der Wert des Bedingungsfelds dieser Bedingung entspricht und nicht null ist.', + 'es-es': + 'Este formato se utilizará solo si el valor del campo de condición es igual a esta condición y no es nulo.', + 'fr-fr': "Ce format sera utilisé uniquement si la valeur du champ de condition est égale à cette condition et n'est pas nulle.", - "ru-ru": - "Этот формат будет использоваться только в том случае, если значение поля условия равно этому условию и не равно нулю.", - "uk-ua": - "Цей формат використовуватиметься, лише якщо значення поля умови дорівнює цій умові й не є нульовим.", - "pt-br": - "Este formato será usado somente se o valor do campo de condição for igual a esta condição e não for nulo.", + 'ru-ru': + 'Этот формат будет использоваться только в том случае, если значение поля условия равно этому условию и не равно нулю.', + 'uk-ua': + 'Цей формат використовуватиметься, лише якщо значення поля умови дорівнює цій умові й не є нульовим.', + 'pt-br': + 'Este formato será usado somente se o valor do campo de condição for igual a esta condição e não for nulo.', }, wrongScopeWarning: { - "en-us": + 'en-us': "This resource belongs to a different collection/discipline than the one you are currently in. It's recommended to switch collection before editing this resource", - "de-ch": - "Diese Ressource gehört zu einer anderen Sammlung/Disziplin als der, in der Sie sich gerade befinden. Es wird empfohlen, die Sammlung zu wechseln, bevor Sie diese Ressource bearbeiten.", - "es-es": - "Este recurso pertenece a una colección/disciplina diferente a la que estás actualmente. Se recomienda cambiar de colección antes de editar este recurso.", - "fr-fr": - "Cette ressource appartient à une collection/discipline différente de celle dans laquelle vous vous trouvez actuellement. Il est recommandé de changer de collection avant de modifier cette ressource.", - "ru-ru": - "Этот ресурс принадлежит к другой коллекции/дисциплине, отличной от той, в которой вы сейчас находитесь. Перед редактированием этого ресурса рекомендуется сменить коллекцию.", - "uk-ua": - "Цей ресурс належить до іншої колекції/дисципліни, ніж та, у якій ви зараз перебуваєте. Рекомендовано змінити колекцію перед редагуванням цього ресурсу", - "pt-br": - "Este recurso pertence a uma coleção/disciplina diferente daquela em que você está atualmente. É recomendável trocar de coleção antes de editar este recurso.", + 'de-ch': + 'Diese Ressource gehört zu einer anderen Sammlung/Disziplin als der, in der Sie sich gerade befinden. Es wird empfohlen, die Sammlung zu wechseln, bevor Sie diese Ressource bearbeiten.', + 'es-es': + 'Este recurso pertenece a una colección/disciplina diferente a la que estás actualmente. Se recomienda cambiar de colección antes de editar este recurso.', + 'fr-fr': + 'Cette ressource appartient à une collection/discipline différente de celle dans laquelle vous vous trouvez actuellement. Il est recommandé de changer de collection avant de modifier cette ressource.', + 'ru-ru': + 'Этот ресурс принадлежит к другой коллекции/дисциплине, отличной от той, в которой вы сейчас находитесь. Перед редактированием этого ресурса рекомендуется сменить коллекцию.', + 'uk-ua': + 'Цей ресурс належить до іншої колекції/дисципліни, ніж та, у якій ви зараз перебуваєте. Рекомендовано змінити колекцію перед редагуванням цього ресурсу', + 'pt-br': + 'Este recurso pertence a uma coleção/disciplina diferente daquela em que você está atualmente. É recomendável trocar de coleção antes de editar este recurso.', }, thisFieldName: { - "en-us": "This field name (for preview purposes only)", - "de-ch": "Dieser Feldname (nur für Vorschauzwecke)", - "es-es": "Este nombre de campo (solo para fines de vista previa)", - "fr-fr": "Ce nom de champ (à des fins d'aperçu uniquement)", - "ru-ru": "Имя этого поля (только для целей предварительного просмотра)", - "uk-ua": "Назва цього поля (лише для попереднього перегляду)", - "pt-br": "Este nome de campo (apenas para fins de visualização)", + 'en-us': 'This field name (for preview purposes only)', + 'de-ch': 'Dieser Feldname (nur für Vorschauzwecke)', + 'es-es': 'Este nombre de campo (solo para fines de vista previa)', + 'fr-fr': "Ce nom de champ (à des fins d'aperçu uniquement)", + 'ru-ru': 'Имя этого поля (только для целей предварительного просмотра)', + 'uk-ua': 'Назва цього поля (лише для попереднього перегляду)', + 'pt-br': 'Este nome de campo (apenas para fins de visualização)', }, publishEveryDays: { - "en-us": "Publish every N days", - "de-ch": "Alle N Tage veröffentlichen", - "es-es": "Publicar cada N días", - "fr-fr": "Publier tous les N jours", - "ru-ru": "Публиковать каждые N дней", - "uk-ua": "Публікуйте кожні N днів", - "pt-br": "Publicar a cada N dias", + 'en-us': 'Publish every N days', + 'de-ch': 'Alle N Tage veröffentlichen', + 'es-es': 'Publicar cada N días', + 'fr-fr': 'Publier tous les N jours', + 'ru-ru': 'Публиковать каждые N дней', + 'uk-ua': 'Публікуйте кожні N днів', + 'pt-br': 'Publicar a cada N dias', }, publish: { - "en-us": "Publish", - "de-ch": "Veröffentlichen", - "es-es": "Publicar", - "fr-fr": "Publier", - "ru-ru": "Публиковать", - "uk-ua": "Опублікувати", - "pt-br": "Publicar", + 'en-us': 'Publish', + 'de-ch': 'Veröffentlichen', + 'es-es': 'Publicar', + 'fr-fr': 'Publier', + 'ru-ru': 'Публиковать', + 'uk-ua': 'Опублікувати', + 'pt-br': 'Publicar', }, fileName: { - "en-us": "File name", - "de-ch": "Dateiname", - "es-es": "Nombre del archivo", - "fr-fr": "Nom de fichier", - "ru-ru": "Сначала выберите таблицу", - "uk-ua": "Ім'я файлу", - "pt-br": "Nome do arquivo", + 'en-us': 'File name', + 'de-ch': 'Dateiname', + 'es-es': 'Nombre del archivo', + 'fr-fr': 'Nom de fichier', + 'ru-ru': 'Сначала выберите таблицу', + 'uk-ua': "Ім'я файлу", + 'pt-br': 'Nome do arquivo', }, runAsUser: { - "en-us": "Run as user", - "de-ch": "Als Benutzer ausführen", - "es-es": "Ejecutar como usuario", - "fr-fr": "Exécuter en tant qu'utilisateur", - "ru-ru": "Запуск от имени пользователя", - "uk-ua": "Запуск від імені користувача", - "pt-br": "Executar como usuário", + 'en-us': 'Run as user', + 'de-ch': 'Als Benutzer ausführen', + 'es-es': 'Ejecutar como usuario', + 'fr-fr': "Exécuter en tant qu'utilisateur", + 'ru-ru': 'Запуск от имени пользователя', + 'uk-ua': 'Запуск від імені користувача', + 'pt-br': 'Executar como usuário', }, notifyUser: { - "en-us": "Send completion notification to user", - "de-ch": "Abschlussbenachrichtigung an Benutzer senden", - "es-es": "Enviar notificación de finalización al usuario", - "fr-fr": "Envoyer une notification d'achèvement à l'utilisateur", - "ru-ru": "Отправить уведомление о завершении пользователю", - "uk-ua": "Надіслати сповіщення про завершення користувачеві", - "pt-br": "Enviar notificação de conclusão ao usuário", + 'en-us': 'Send completion notification to user', + 'de-ch': 'Abschlussbenachrichtigung an Benutzer senden', + 'es-es': 'Enviar notificación de finalización al usuario', + 'fr-fr': "Envoyer une notification d'achèvement à l'utilisateur", + 'ru-ru': 'Отправить уведомление о завершении пользователю', + 'uk-ua': 'Надіслати сповіщення про завершення користувачеві', + 'pt-br': 'Enviar notificação de conclusão ao usuário', }, runInCollection: { - "en-us": "Run in collection", - "de-ch": "In der Sammlung ausführen", - "es-es": "Correr en colección", - "fr-fr": "Exécution en collection", - "ru-ru": "Запустить в коллекцию", - "uk-ua": "Запустити в колекції", - "pt-br": "Executar na coleção", + 'en-us': 'Run in collection', + 'de-ch': 'In der Sammlung ausführen', + 'es-es': 'Correr en colección', + 'fr-fr': 'Exécution en collection', + 'ru-ru': 'Запустить в коллекцию', + 'uk-ua': 'Запустити в колекції', + 'pt-br': 'Executar na coleção', }, createNewForm: { - "en-us": "Create a new view definition", - "de-ch": "Erstellen einer neuen Ansichtsdefinition", - "es-es": "Crear una nueva definición de vista", - "fr-fr": "Créer une nouvelle définition de vue", - "ru-ru": "Создайте новое определение представления", - "uk-ua": "Створіть нове визначення подання", - "pt-br": "Criar uma nova definição de visualização", + 'en-us': 'Create a new view definition', + 'de-ch': 'Erstellen einer neuen Ansichtsdefinition', + 'es-es': 'Crear una nueva definición de vista', + 'fr-fr': 'Créer une nouvelle définition de vue', + 'ru-ru': 'Создайте новое определение представления', + 'uk-ua': 'Створіть нове визначення подання', + 'pt-br': 'Criar uma nova definição de visualização', }, copyFromExistingForm: { - "en-us": "Copy existing view definition", - "de-ch": "Vorhandene Ansichtsdefinition kopieren", - "es-es": "Copiar la definición de vista existente", - "fr-fr": "Copier la définition de vue existante", - "ru-ru": "Копировать существующее определение представления", - "uk-ua": "Копіювати існуюче визначення перегляду", - "pt-br": "Copiar definição de visualização existente", + 'en-us': 'Copy existing view definition', + 'de-ch': 'Vorhandene Ansichtsdefinition kopieren', + 'es-es': 'Copiar la definición de vista existente', + 'fr-fr': 'Copier la définition de vue existante', + 'ru-ru': 'Копировать существующее определение представления', + 'uk-ua': 'Копіювати існуюче визначення перегляду', + 'pt-br': 'Copiar definição de visualização existente', }, copyDefaultForm: { - "en-us": "Copy default view definition", - "de-ch": "Standardansichtsdefinition kopieren", - "es-es": "Copiar la definición de vista predeterminada", - "fr-fr": "Copier la définition de la vue par défaut", - "ru-ru": "Форматеры записи", - "uk-ua": "Формати записів", - "pt-br": "Copiar definição de visualização padrão", + 'en-us': 'Copy default view definition', + 'de-ch': 'Standardansichtsdefinition kopieren', + 'es-es': 'Copiar la definición de vista predeterminada', + 'fr-fr': 'Copier la définition de la vue par défaut', + 'ru-ru': 'Форматеры записи', + 'uk-ua': 'Формати записів', + 'pt-br': 'Copiar definição de visualização padrão', }, copyDefaultForms: { - "en-us": "Copy default forms", - "de-ch": "Standardformulare kopieren", - "es-es": "Copiar formularios predeterminados", - "fr-fr": "Copier les formulaires par défaut", - "ru-ru": "Копировать формы по умолчанию", - "uk-ua": "Копіювати стандартні форми", - "pt-br": "Copiar formulários padrão", + 'en-us': 'Copy default forms', + 'de-ch': 'Standardformulare kopieren', + 'es-es': 'Copiar formularios predeterminados', + 'fr-fr': 'Copier les formulaires par défaut', + 'ru-ru': 'Копировать формы по умолчанию', + 'uk-ua': 'Копіювати стандартні форми', + 'pt-br': 'Copiar formulários padrão', }, saveFormFirst: { - "en-us": "Save form first", - "de-ch": "Formular zuerst speichern", - "es-es": "Guarde el formulario primero", - "fr-fr": "Enregistrer d'abord le formulaire", - "ru-ru": "Сначала сохраните форму", - "uk-ua": "Спочатку збережіть форму", - "pt-br": "Salve o formulário primeiro", + 'en-us': 'Save form first', + 'de-ch': 'Formular zuerst speichern', + 'es-es': 'Guarde el formulario primero', + 'fr-fr': "Enregistrer d'abord le formulaire", + 'ru-ru': 'Сначала сохраните форму', + 'uk-ua': 'Спочатку збережіть форму', + 'pt-br': 'Salve o formulário primeiro', }, saveFormFirstDescription: { - "en-us": "You need to save this form before you edit another one", - "de-ch": - "Sie müssen dieses Formular speichern, bevor Sie ein anderes bearbeiten", - "es-es": "Debes guardar este formulario antes de editar otro.", - "fr-fr": + 'en-us': 'You need to save this form before you edit another one', + 'de-ch': + 'Sie müssen dieses Formular speichern, bevor Sie ein anderes bearbeiten', + 'es-es': 'Debes guardar este formulario antes de editar otro.', + 'fr-fr': "Vous devez enregistrer ce formulaire avant d'en modifier un autre.", - "ru-ru": - "Вам необходимо сохранить эту форму, прежде чем редактировать другую.", - "uk-ua": "Вам потрібно зберегти цю форму, перш ніж редагувати іншу", - "pt-br": "Você precisa salvar este formulário antes de editar outro", + 'ru-ru': + 'Вам необходимо сохранить эту форму, прежде чем редактировать другую.', + 'uk-ua': 'Вам потрібно зберегти цю форму, перш ніж редагувати іншу', + 'pt-br': 'Você precisa salvar este formulário antes de editar outro', }, conditionalFormatter: { - "en-us": "Conditional Format", - "de-ch": "Bedingtes Format", - "es-es": "Formato condicional", - "fr-fr": "Format conditionnel", - "ru-ru": "Условный формат", - "uk-ua": "Умовний формат", - "pt-br": "Formato condicional", + 'en-us': 'Conditional Format', + 'de-ch': 'Bedingtes Format', + 'es-es': 'Formato condicional', + 'fr-fr': 'Format conditionnel', + 'ru-ru': 'Условный формат', + 'uk-ua': 'Умовний формат', + 'pt-br': 'Formato condicional', }, customizeFieldFormatters: { - "en-us": "Customize Field Formats", - "de-ch": "Feldformate anpassen", - "es-es": "Personalizar formatos de campo", - "fr-fr": "Personnaliser les formats de champ", - "ru-ru": "Настройте форматы полей", - "uk-ua": "Налаштувати формати полів", - "pt-br": "Personalizar formatos de campo", + 'en-us': 'Customize Field Formats', + 'de-ch': 'Feldformate anpassen', + 'es-es': 'Personalizar formatos de campo', + 'fr-fr': 'Personnaliser les formats de champ', + 'ru-ru': 'Настройте форматы полей', + 'uk-ua': 'Налаштувати формати полів', + 'pt-br': 'Personalizar formatos de campo', }, expandConditionalField: { - "en-us": "Expand Conditional Field", - "de-ch": "Bedingtes Feld erweitern", - "es-es": "Expandir campo condicional", - "fr-fr": "Développer le champ conditionnel", - "ru-ru": "Развернуть условное поле", - "uk-ua": "Розгорнути умовне поле", - "pt-br": "Expandir campo condicional", + 'en-us': 'Expand Conditional Field', + 'de-ch': 'Bedingtes Feld erweitern', + 'es-es': 'Expandir campo condicional', + 'fr-fr': 'Développer le champ conditionnel', + 'ru-ru': 'Развернуть условное поле', + 'uk-ua': 'Розгорнути умовне поле', + 'pt-br': 'Expandir campo condicional', }, collapseConditionalField: { - "en-us": "Collapse Conditional Field", - "de-ch": "Bedingtes Feld reduzieren", - "es-es": "Contraer campo condicional", - "fr-fr": "Champ conditionnel de réduction", - "ru-ru": "Свернуть условное поле", - "uk-ua": "Згорнути умовне поле", - "pt-br": "Recolher campo condicional", + 'en-us': 'Collapse Conditional Field', + 'de-ch': 'Bedingtes Feld reduzieren', + 'es-es': 'Contraer campo condicional', + 'fr-fr': 'Champ conditionnel de réduction', + 'ru-ru': 'Свернуть условное поле', + 'uk-ua': 'Згорнути умовне поле', + 'pt-br': 'Recolher campo condicional', }, conditionalFieldValue: { - "en-us": "Conditional Field Value", - "de-ch": "Bedingter Feldwert", - "es-es": "Valor de campo condicional", - "fr-fr": "Valeur du champ conditionnel", - "ru-ru": "Условное значение поля", - "uk-ua": "Умовне значення поля", - "pt-br": "Valor do campo condicional", + 'en-us': 'Conditional Field Value', + 'de-ch': 'Bedingter Feldwert', + 'es-es': 'Valor de campo condicional', + 'fr-fr': 'Valeur du champ conditionnel', + 'ru-ru': 'Условное значение поля', + 'uk-ua': 'Умовне значення поля', + 'pt-br': 'Valor do campo condicional', }, formatPreview: { - "en-us": "Format Preview", - "de-ch": "Formatvorschau", - "es-es": "Vista previa del formato", - "fr-fr": "Aperçu du format", - "ru-ru": "Предварительный просмотр формата", - "uk-ua": "Попередній перегляд формату", - "pt-br": "Visualização do formato", + 'en-us': 'Format Preview', + 'de-ch': 'Formatvorschau', + 'es-es': 'Vista previa del formato', + 'fr-fr': 'Aperçu du format', + 'ru-ru': 'Предварительный просмотр формата', + 'uk-ua': 'Попередній перегляд формату', + 'pt-br': 'Visualização do formato', }, currentDeterminationRequired: { - "en-us": "A current determination is required.", - "de-ch": "Eine aktuelle Feststellung ist erforderlich.", - "es-es": "Se requiere una determinación actual.", - "fr-fr": "Une détermination actuelle est requise.", - "ru-ru": "Требуется текущее определение.", - "uk-ua": "Потрібне поточне визначення.", - "pt-br": "É necessária uma determinação atual.", + 'en-us': 'A current determination is required.', + 'de-ch': 'Eine aktuelle Feststellung ist erforderlich.', + 'es-es': 'Se requiere una determinación actual.', + 'fr-fr': 'Une détermination actuelle est requise.', + 'ru-ru': 'Требуется текущее определение.', + 'uk-ua': 'Потрібне поточне визначення.', + 'pt-br': 'É necessária uma determinação atual.', }, cogAddedToItself: { - "en-us": "A COG cannot be added to itself.", - "de-ch": "Ein Schwerpunkt kann nicht zu sich selbst addiert werden.", - "es-es": "No se puede agregar un COG a sí mismo.", - "fr-fr": "Un COG ne peut pas être ajouté à lui-même.", - "pt-br": "Um COG não pode ser adicionado a si mesmo.", - "ru-ru": "COG не может быть сложен сам с собой.", - "uk-ua": "COG не можна додати до самого себе.", + 'en-us': 'A COG cannot be added to itself.', + 'de-ch': 'Ein Schwerpunkt kann nicht zu sich selbst addiert werden.', + 'es-es': 'No se puede agregar un COG a sí mismo.', + 'fr-fr': 'Un COG ne peut pas être ajouté à lui-même.', + 'pt-br': 'Um COG não pode ser adicionado a si mesmo.', + 'ru-ru': 'COG не может быть сложен сам с собой.', + 'uk-ua': 'COG не можна додати до самого себе.', }, primaryCogChildRequired: { - "en-us": - "A Consolidated Collection Object Group must have a primary Collection Object child", - "de-ch": - "Eine konsolidierte Sammlungsobjektgruppe muss ein primäres Sammlungsobjekt-Unterobjekt haben", - "es-es": - "Un grupo de objetos de colección consolidada debe tener un objeto de colección secundario principal", - "fr-fr": + 'en-us': + 'A Consolidated Collection Object Group must have a primary Collection Object child', + 'de-ch': + 'Eine konsolidierte Sammlungsobjektgruppe muss ein primäres Sammlungsobjekt-Unterobjekt haben', + 'es-es': + 'Un grupo de objetos de colección consolidada debe tener un objeto de colección secundario principal', + 'fr-fr': "Un groupe d'objets de collection consolidé doit avoir un objet de collection enfant principal", - "pt-br": - "Um Grupo de Objetos de Coleção Consolidada deve ter um Objeto de Coleção primário filho", - "ru-ru": - "Группа объектов консолидированной коллекции должна иметь основной дочерний объект коллекции.", - "uk-ua": + 'pt-br': + 'Um Grupo de Objetos de Coleção Consolidada deve ter um Objeto de Coleção primário filho', + 'ru-ru': + 'Группа объектов консолидированной коллекции должна иметь основной дочерний объект коллекции.', + 'uk-ua': "Консолідована група об'єктів колекції повинна мати дочірній об'єкт первинної колекції", }, deletePrimaryRecord: { - "en-us": "Primary record CO cannot be deleted.", - "de-ch": "Primärdatensatz CO kann nicht gelöscht werden.", - "es-es": "El registro primario CO no se puede eliminar.", - "fr-fr": "L'enregistrement principal CO ne peut pas être supprimé.", - "pt-br": "O registro primário CO não pode ser excluído.", - "ru-ru": "Первичная запись CO не может быть удалена.", - "uk-ua": "Основний запис CO неможливо видалити.", + 'en-us': 'Primary record CO cannot be deleted.', + 'de-ch': 'Primärdatensatz CO kann nicht gelöscht werden.', + 'es-es': 'El registro primario CO no se puede eliminar.', + 'fr-fr': "L'enregistrement principal CO ne peut pas être supprimé.", + 'pt-br': 'O registro primário CO não pode ser excluído.', + 'ru-ru': 'Первичная запись CO не может быть удалена.', + 'uk-ua': 'Основний запис CO неможливо видалити.', }, deleteLoanedPrep: { - "en-us": "A loaned preparation cannot be deleted", - "de-ch": "Eine ausgeliehene Zubereitung kann nicht gelöscht werden", - "es-es": "Una preparación prestada no se puede eliminar", - "fr-fr": "Une préparation prêtée ne peut pas être supprimée", - "pt-br": "Uma preparação emprestada não pode ser excluída", - "ru-ru": "Заимствованный препарат не может быть удален", - "uk-ua": "Позичений препарат не можна видалити", + 'en-us': 'A loaned preparation cannot be deleted', + 'de-ch': 'Eine ausgeliehene Zubereitung kann nicht gelöscht werden', + 'es-es': 'Una preparación prestada no se puede eliminar', + 'fr-fr': 'Une préparation prêtée ne peut pas être supprimée', + 'pt-br': 'Uma preparação emprestada não pode ser excluída', + 'ru-ru': 'Заимствованный препарат не может быть удален', + 'uk-ua': 'Позичений препарат не можна видалити', }, deleteGiftedPrep: { - "en-us": "A gifted preparation cannot be deleted", - "de-ch": "Eine geschenkte Vorbereitung kann nicht gelöscht werden", - "es-es": "Una preparación dotada no se puede eliminar", - "fr-fr": "Une préparation surdouée ne peut pas être supprimée", - "pt-br": "Uma preparação talentosa não pode ser apagada", - "ru-ru": "Подаренный препарат не может быть удален", - "uk-ua": "Подарований препарат не можна видалити", + 'en-us': 'A gifted preparation cannot be deleted', + 'de-ch': 'Eine geschenkte Vorbereitung kann nicht gelöscht werden', + 'es-es': 'Una preparación dotada no se puede eliminar', + 'fr-fr': 'Une préparation surdouée ne peut pas être supprimée', + 'pt-br': 'Uma preparação talentosa não pode ser apagada', + 'ru-ru': 'Подаренный препарат не может быть удален', + 'uk-ua': 'Подарований препарат не можна видалити', }, deleteDisposedPrep: { - "en-us": "A disposed preparation cannot be deleted", - "de-ch": "Eine entsorgte Zubereitung kann nicht gelöscht werden", - "es-es": "Una preparación desechada no se puede eliminar", - "fr-fr": "Une préparation éliminée ne peut pas être supprimée", - "pt-br": "Uma preparação descartada não pode ser excluída", - "ru-ru": "Утилизированный препарат не может быть удален", - "uk-ua": "Викинутий препарат не можна видалити", + 'en-us': 'A disposed preparation cannot be deleted', + 'de-ch': 'Eine entsorgte Zubereitung kann nicht gelöscht werden', + 'es-es': 'Una preparación desechada no se puede eliminar', + 'fr-fr': 'Une préparation éliminée ne peut pas être supprimée', + 'pt-br': 'Uma preparação descartada não pode ser excluída', + 'ru-ru': 'Утилизированный препарат не может быть удален', + 'uk-ua': 'Викинутий препарат не можна видалити', }, deleteExchangeOutPrep: { - "en-us": "A exchanged out preparation cannot be deleted", - "de-ch": "Ein ausgetauschtes Präparat kann nicht gelöscht werden", - "es-es": "No se puede eliminar una preparación intercambiada", - "fr-fr": "Une préparation échangée ne peut pas être supprimée", - "pt-br": "Uma preparação trocada não pode ser excluída", - "ru-ru": "Замененный препарат не может быть удален.", - "uk-ua": "Обмінений препарат не можна видалити", + 'en-us': 'A exchanged out preparation cannot be deleted', + 'de-ch': 'Ein ausgetauschtes Präparat kann nicht gelöscht werden', + 'es-es': 'No se puede eliminar una preparación intercambiada', + 'fr-fr': 'Une préparation échangée ne peut pas être supprimée', + 'pt-br': 'Uma preparação trocada não pode ser excluída', + 'ru-ru': 'Замененный препарат не может быть удален.', + 'uk-ua': 'Обмінений препарат не можна видалити', }, deleteExchangeInPrep: { - "en-us": "A exchanged in preparation cannot be deleted", - "de-ch": - "Ein in Vorbereitung befindlicher Austausch kann nicht gelöscht werden", - "es-es": "No se puede eliminar un intercambio en preparación", - "fr-fr": "Un échange en préparation ne peut pas être supprimé", - "pt-br": "Uma troca em preparação não pode ser excluída", - "ru-ru": "Обмен в стадии подготовки не может быть удален", - "uk-ua": "Обмін, що готується, не можна видалити", + 'en-us': 'A exchanged in preparation cannot be deleted', + 'de-ch': + 'Ein in Vorbereitung befindlicher Austausch kann nicht gelöscht werden', + 'es-es': 'No se puede eliminar un intercambio en preparación', + 'fr-fr': 'Un échange en préparation ne peut pas être supprimé', + 'pt-br': 'Uma troca em preparação não pode ser excluída', + 'ru-ru': 'Обмен в стадии подготовки не может быть удален', + 'uk-ua': 'Обмін, що готується, не можна видалити', }, invalidDeterminationTaxon: { - "en-us": - "Determination does not belong to the taxon tree associated with the Collection Object Type", - "de-ch": - "Die Bestimmung gehört nicht zum Taxonbaum, der mit dem Sammlungsobjekttyp verknüpft ist", - "es-es": - "La determinación no pertenece al árbol taxonómico asociado con el tipo de objeto de colección", - "fr-fr": + 'en-us': + 'Determination does not belong to the taxon tree associated with the Collection Object Type', + 'de-ch': + 'Die Bestimmung gehört nicht zum Taxonbaum, der mit dem Sammlungsobjekttyp verknüpft ist', + 'es-es': + 'La determinación no pertenece al árbol taxonómico asociado con el tipo de objeto de colección', + 'fr-fr': "La détermination n'appartient pas à l'arbre taxonomique associé au type d'objet de collection", - "pt-br": - "A determinação não pertence à árvore de táxons associada ao Tipo de Objeto de Coleção", - "ru-ru": - "Определение не принадлежит к таксонному дереву, связанному с типом объекта коллекции.", - "uk-ua": + 'pt-br': + 'A determinação não pertence à árvore de táxons associada ao Tipo de Objeto de Coleção', + 'ru-ru': + 'Определение не принадлежит к таксонному дереву, связанному с типом объекта коллекции.', + 'uk-ua': "Визначення не належить до дерева таксонів, пов'язаного з типом об'єкта колекції", }, invalidNameTaxon: { - "en-us": - "{taxonName: string} does not belong to the {taxonTableLabel: string} tree associated with {typeName: string}", - "de-ch": - "{taxonName: string} gehört nicht zum {taxonTableLabel: string}-Baum, der mit {typeName: string} verknüpft ist", - "es-es": - "{taxonName: string} no pertenece al árbol {taxonTableLabel: string} asociado con {typeName: string}", - "fr-fr": + 'en-us': + '{taxonName: string} does not belong to the {taxonTableLabel: string} tree associated with {typeName: string}', + 'de-ch': + '{taxonName: string} gehört nicht zum {taxonTableLabel: string}-Baum, der mit {typeName: string} verknüpft ist', + 'es-es': + '{taxonName: string} no pertenece al árbol {taxonTableLabel: string} asociado con {typeName: string}', + 'fr-fr': "{taxonName: string} n'appartient pas à l'arbre {taxonTableLabel: string} associé à {typeName: string}", - "pt-br": - "{taxonName: string} não pertence à árvore {taxonTableLabel: string} associada a {typeName: string}", - "ru-ru": - "{taxonName: string} не принадлежит дереву {taxonTableLabel: string}, связанному с {typeName: string}", - "uk-ua": + 'pt-br': + '{taxonName: string} não pertence à árvore {taxonTableLabel: string} associada a {typeName: string}', + 'ru-ru': + '{taxonName: string} не принадлежит дереву {taxonTableLabel: string}, связанному с {typeName: string}', + 'uk-ua': "{taxonName: string} не належить до дерева {taxonTableLabel: string}, пов'язаного з {typeName: string}", }, catalogNumberAlreadyUsed: { - "en-us": - "{catalogNumberFieldName: string} {catalogNumber: string} is already in use for another Component or Collection Object in this collection.", - "de-ch": - "{catalogNumberFieldName: string} {catalogNumber: string} wird bereits für eine andere Komponente oder ein anderes Sammlungsobjekt in dieser Sammlung verwendet.", - "es-es": - "{catalogNumberFieldName: string} {catalogNumber: string} ya está en uso para otro componente u objeto de colección en esta colección.", - "fr-fr": - "{catalogNumberFieldName: string} {catalogNumber: string} est déjà utilisé pour un autre composant ou objet de collection dans cette collection.", - "pt-br": - "{catalogNumberFieldName: string} {catalogNumber: string} já está em uso para outro Componente ou Objeto de Coleção nesta coleção.", - "ru-ru": - "{catalogNumberFieldName: string} {catalogNumber: string} уже используется для другого компонента или объекта коллекции в этой коллекции.", - "uk-ua": + 'en-us': + '{catalogNumberFieldName: string} {catalogNumber: string} is already in use for another Component or Collection Object in this collection.', + 'de-ch': + '{catalogNumberFieldName: string} {catalogNumber: string} wird bereits für eine andere Komponente oder ein anderes Sammlungsobjekt in dieser Sammlung verwendet.', + 'es-es': + '{catalogNumberFieldName: string} {catalogNumber: string} ya está en uso para otro componente u objeto de colección en esta colección.', + 'fr-fr': + '{catalogNumberFieldName: string} {catalogNumber: string} est déjà utilisé pour un autre composant ou objet de collection dans cette collection.', + 'pt-br': + '{catalogNumberFieldName: string} {catalogNumber: string} já está em uso para outro Componente ou Objeto de Coleção nesta coleção.', + 'ru-ru': + '{catalogNumberFieldName: string} {catalogNumber: string} уже используется для другого компонента или объекта коллекции в этой коллекции.', + 'uk-ua': "{catalogNumberFieldName: string} {catalogNumber: string} вже використовується для іншого компонента або об'єкта колекції в цій колекції.", }, preparationUsedInLoan: { - "en-us": "The preparation is used in a loan.", - "de-ch": "Das Präparat dient der Kreditvergabe.", - "es-es": "La preparación se utiliza en un préstamo.", - "fr-fr": "La préparation est utilisée dans un prêt.", - "pt-br": "A preparação é usada em um empréstimo.", - "ru-ru": "Препарат используется при кредитовании.", - "uk-ua": "Препарат використовується у позиці.", + 'en-us': 'The preparation is used in a loan.', + 'de-ch': 'Das Präparat dient der Kreditvergabe.', + 'es-es': 'La preparación se utiliza en un préstamo.', + 'fr-fr': 'La préparation est utilisée dans un prêt.', + 'pt-br': 'A preparação é usada em um empréstimo.', + 'ru-ru': 'Препарат используется при кредитовании.', + 'uk-ua': 'Препарат використовується у позиці.', }, preparationIsNegative: { - "en-us": "Preparation count cannot be negative", - "de-ch": "Die Anzahl der Vorbereitungen darf nicht negativ sein", - "es-es": "El recuento de preparación no puede ser negativo", - "fr-fr": "Le nombre de préparations ne peut pas être négatif", - "pt-br": "A contagem de preparação não pode ser negativa", - "ru-ru": "Количество приготовлений не может быть отрицательным.", - "uk-ua": "Кількість підготовок не може бути від'ємним значенням", + 'en-us': 'Preparation count cannot be negative', + 'de-ch': 'Die Anzahl der Vorbereitungen darf nicht negativ sein', + 'es-es': 'El recuento de preparación no puede ser negativo', + 'fr-fr': 'Le nombre de préparations ne peut pas être négatif', + 'pt-br': 'A contagem de preparação não pode ser negativa', + 'ru-ru': 'Количество приготовлений не может быть отрицательным.', + 'uk-ua': "Кількість підготовок не може бути від'ємним значенням", }, configureField: { - "en-us": "Configure field", - "de-ch": "Feld konfigurieren", - "es-es": "Configurar campo", - "fr-fr": "Configurer le champ", - "pt-br": "Configurar campo", - "ru-ru": "Настроить поле", - "uk-ua": "Налаштувати поле", + 'en-us': 'Configure field', + 'de-ch': 'Feld konfigurieren', + 'es-es': 'Configurar campo', + 'fr-fr': 'Configurer le champ', + 'pt-br': 'Configurar campo', + 'ru-ru': 'Настроить поле', + 'uk-ua': 'Налаштувати поле', }, trimZeros: { - "en-us": "Trim Leading Zeros", - "de-ch": "Führende Nullen entfernen", - "es-es": "Recortar ceros iniciales", - "fr-fr": "Couper les zéros non significatifs", - "pt-br": "Aparar zeros à esquerda", - "ru-ru": "Удалить начальные нули", - "uk-ua": "Видалити початкові нулі", + 'en-us': 'Trim Leading Zeros', + 'de-ch': 'Führende Nullen entfernen', + 'es-es': 'Recortar ceros iniciales', + 'fr-fr': 'Couper les zéros non significatifs', + 'pt-br': 'Aparar zeros à esquerda', + 'ru-ru': 'Удалить начальные нули', + 'uk-ua': 'Видалити початкові нулі', }, trimZerosDescription: { - "en-us": "Remove leading zeros from numeric values.", - "de-ch": "Entfernen Sie führende Nullen aus numerischen Werten.", - "es-es": "Eliminar los ceros iniciales de los valores numéricos.", - "fr-fr": "Supprimez les zéros non significatifs des valeurs numériques.", - "pt-br": "Remova os zeros à esquerda dos valores numéricos.", - "ru-ru": "Удалить начальные нули из числовых значений.", - "uk-ua": "Видаліть початкові нулі з числових значень.", + 'en-us': 'Remove leading zeros from numeric values.', + 'de-ch': 'Entfernen Sie führende Nullen aus numerischen Werten.', + 'es-es': 'Eliminar los ceros iniciales de los valores numéricos.', + 'fr-fr': 'Supprimez les zéros non significatifs des valeurs numériques.', + 'pt-br': 'Remova os zeros à esquerda dos valores numéricos.', + 'ru-ru': 'Удалить начальные нули из числовых значений.', + 'uk-ua': 'Видаліть початкові нулі з числових значень.', }, formatterPreviewUnavailable: { 'en-us': 'Preview for formatter of this type is not available', From aaeda8942fa76e16078b1dcf361baa9fabb29d09 Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:22:57 -0400 Subject: [PATCH 31/33] Fix: Disable bulk carry for alphanumeric catalogNumber fields --- .../frontend/js_src/lib/components/FormMeta/CarryForward.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/CarryForward.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/CarryForward.tsx index 460dc03c712..8721484d316 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/CarryForward.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/CarryForward.tsx @@ -263,6 +263,7 @@ export const tableValidForBulkClone = ( (parts) => parts.type === 'regex' || parts.type === 'alphanumeric' || + parts.type === 'alpha' || (parts.type === 'numeric' && !parts.canAutonumber()) ) ?? false ); From 628c9bb285c2e666f2d199055574298052f7ca96 Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:43:47 -0400 Subject: [PATCH 32/33] Fix: Error message says 'record formatter' instead of either formatter or field formatter --- .../frontend/js_src/lib/localization/resources.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index 3bb2340c8af..28459cfc8fa 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -291,17 +291,7 @@ export const resourcesText = createDictionary({ 'uk-ua': 'Доступні форматувальники полів', }, selectDefaultFormatter: { - 'en-us': 'Please select a default record formatter for this table', - 'de-ch': - 'Bitte wählen Sie einen Standard-Datensatzformatierer für diese Tabelle', - 'es-es': - 'Seleccione un formateador de registro predeterminado para esta tabla', - 'fr-fr': - "Veuillez sélectionner un formateur d'enregistrement par défaut pour cette table", - 'ru-ru': - 'Пожалуйста, выберите форматирование записей по умолчанию для этой таблицы.', - 'uk-ua': 'Виберіть стандартний формат запису для цієї таблиці', - 'pt-br': 'Selecione um formatador de registro padrão para esta tabela', + 'en-us': 'Please designate one of the formatters as default', }, duplicateFormatters: { 'en-us': 'Record formatter names must be unique', From 68a69738c92f487ff60df7e696816c54e44cafa4 Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Tue, 30 Sep 2025 17:48:51 +0000 Subject: [PATCH 33/33] Lint code with ESLint and Prettier Triggered by 628c9bb285c2e666f2d199055574298052f7ca96 on branch refs/heads/field-editor --- .../frontend/js_src/lib/components/FormSliders/RecordSet.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormSliders/RecordSet.tsx b/specifyweb/frontend/js_src/lib/components/FormSliders/RecordSet.tsx index 42eea4f8a52..bd3995be1fc 100644 --- a/specifyweb/frontend/js_src/lib/components/FormSliders/RecordSet.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormSliders/RecordSet.tsx @@ -384,9 +384,9 @@ function RecordSet({ ids={ids} isDependent={false} isInRecordSet - recordSetId={recordSet.id} isLoading={isLoading} newResource={currentRecord.isNew() ? currentRecord : undefined} + recordSetId={recordSet.id} table={currentRecord.specifyTable} title={ recordSet.isNew()