diff --git a/client/assets/welcome_banner_1.png b/client/assets/welcome_banner_1.png
new file mode 100644
index 000000000..c25cbeee5
Binary files /dev/null and b/client/assets/welcome_banner_1.png differ
diff --git a/client/assets/welcome_banner_2.png b/client/assets/welcome_banner_2.png
new file mode 100644
index 000000000..ae45aef2b
Binary files /dev/null and b/client/assets/welcome_banner_2.png differ
diff --git a/client/assets/welcome_banner_3.png b/client/assets/welcome_banner_3.png
new file mode 100644
index 000000000..b4f9ad17a
Binary files /dev/null and b/client/assets/welcome_banner_3.png differ
diff --git a/client/components/Application/Browser.tsx b/client/components/Application/Browser.tsx
index eb911fea3..ef888c50c 100644
--- a/client/components/Application/Browser.tsx
+++ b/client/components/Application/Browser.tsx
@@ -35,7 +35,10 @@ function DefaultBrowser() {
{t('create-an-issue')}
{' '}
- }} />
+ (if possible) }}
+ />
}
>
diff --git a/client/components/Application/Content.tsx b/client/components/Application/Content.tsx
index 9bc756e45..ceff8229c 100644
--- a/client/components/Application/Content.tsx
+++ b/client/components/Application/Content.tsx
@@ -44,7 +44,10 @@ function FileContent() {
{t('create-an-issue')}
{' '}
- }} />
+ (if possible) }}
+ />
}
>
diff --git a/client/components/Application/Dialogs/Assistant/Assistant.tsx b/client/components/Application/Dialogs/Assistant/Assistant.tsx
index fe4c7a052..0dd27aa9d 100644
--- a/client/components/Application/Dialogs/Assistant/Assistant.tsx
+++ b/client/components/Application/Dialogs/Assistant/Assistant.tsx
@@ -10,12 +10,7 @@ import * as React from 'react'
import { PropsWithChildren } from 'react'
import Markdown from 'react-markdown'
import * as store from './store'
-import { useTranslation } from 'react-i18next'
-
-const DEFAULT_PROMPT = `
-suggest improvements to the names of the columns in the table
-and provide descriptions for each of them
-`
+import { useTranslation, Trans } from 'react-i18next'
export function AssistantDialog() {
const state = store.useState()
@@ -54,7 +49,6 @@ function TermsStepDialog() {
function CredsStepDialog() {
const [key, setKey] = React.useState('')
const { t } = useTranslation()
-
return (
- Click{' '}
-
- here
- {' '}
- to learn how to find your key. You can also check OpenAI terms and policies{' '}
-
- here
-
- .
+
+ here
+
+ ),
+ link2: (
+
+ here
+
+ ),
+ }}
+ />
@@ -96,8 +96,9 @@ function CredsStepDialog() {
}
function PromptStepDialog() {
- const [prompt, setPrompt] = React.useState(DEFAULT_PROMPT)
const { t } = useTranslation()
+ const DEFAULT_PROMPT = t('AI-assistant-default-prompt')
+ const [prompt, setPrompt] = React.useState(DEFAULT_PROMPT)
return (
-
+
-
+
{text}
-
-
+
+
+
+
)
}
+
+function ImageWithText(props: { image: string; text: string; alt: string }) {
+ return (
+
+
+
+
+
+ {props.text}
+
+
+ )
+}
diff --git a/client/components/Application/Sidebar.tsx b/client/components/Application/Sidebar.tsx
index 006602e38..3d5cf9431 100644
--- a/client/components/Application/Sidebar.tsx
+++ b/client/components/Application/Sidebar.tsx
@@ -4,8 +4,10 @@ import LowerMenu from './LowerMenu'
import sidebarLogo from '../../assets/ODE_sidebar_logo.svg'
import Button from '@mui/material/Button'
import * as store from '@client/store'
+import { useTranslation } from 'react-i18next'
export default function Sidebar() {
+ const { t } = useTranslation()
return (
store.openDialog('fileUpload')}
>
- Upload your data
+ {t('upload-your-data')}
diff --git a/client/components/Editors/Base/ListItem.tsx b/client/components/Editors/Base/ListItem.tsx
index 125bdfef7..a1e4837a7 100644
--- a/client/components/Editors/Base/ListItem.tsx
+++ b/client/components/Editors/Base/ListItem.tsx
@@ -3,6 +3,7 @@ import Box from '@mui/material/Box'
import Button from '@mui/material/Button'
import Typography from '@mui/material/Typography'
import { useTheme } from '@mui/material/styles'
+import { useTranslation } from 'react-i18next'
interface EditorListItemProps {
kind: string
@@ -17,6 +18,8 @@ interface EditorListItemProps {
export default function EditorListItem(props: EditorListItemProps) {
const theme = useTheme()
+ const { t } = useTranslation()
+
const RemoveButton = () => {
if (!props.onRemoveClick) return null
@@ -25,14 +28,14 @@ export default function EditorListItem(props: EditorListItemProps) {
size="small"
color="warning"
component="span"
- title={`Remove ${capitalize(props.kind)}`}
+ title={`${t('remove')} ${capitalize(props.kind)}`}
sx={{ marginLeft: 2, textDecoration: 'underline' }}
onClick={(ev) => {
ev.stopPropagation()
props.onRemoveClick?.()
}}
>
- Remove
+ {t('remove')}
)
}
diff --git a/client/components/Editors/Base/Section.tsx b/client/components/Editors/Base/Section.tsx
index ff1d4db1e..19a2573d1 100644
--- a/client/components/Editors/Base/Section.tsx
+++ b/client/components/Editors/Base/Section.tsx
@@ -3,6 +3,7 @@ import Box from '@mui/material/Box'
import Button from '@mui/material/Button'
import Columns from '../../Parts/Grids/Columns'
import HeadingBox from './Heading/Box'
+import { useTranslation } from 'react-i18next'
export interface EditorItemProps {
name?: string
@@ -13,12 +14,13 @@ export interface EditorItemProps {
export default function EditorItem(props: React.PropsWithChildren) {
const BackButton = () => {
if (!props.onBackClick) return null
+ const { t } = useTranslation()
return (
)
}
diff --git a/client/components/Editors/Dialect/Layout.tsx b/client/components/Editors/Dialect/Layout.tsx
index 3e9d319e1..e24094de2 100644
--- a/client/components/Editors/Dialect/Layout.tsx
+++ b/client/components/Editors/Dialect/Layout.tsx
@@ -8,6 +8,7 @@ import DialectSection from './Sections/Dialect'
import FormatSection from './Sections/Format'
import { useStore } from './store'
import * as types from '../../../types'
+import { useTranslation } from 'react-i18next'
export default function Layout() {
const externalMenu = useStore((state) => state.externalMenu)
@@ -23,9 +24,10 @@ function LayoutWithMenu() {
const section = useStore((state) => state.section)
const updateHelp = useStore((state) => state.updateHelp)
const updateState = useStore((state) => state.updateState)
+ const { t } = useTranslation()
const MENU_ITEMS: types.IMenuItem[] = [
- { section: 'dialect', name: 'Dialect' },
- { section: 'dialect/format', name: capitalize(format) || 'Format' },
+ { section: 'dialect', name: t('dialect') },
+ { section: 'dialect/format', name: capitalize(format) || t('format') },
]
return (
diff --git a/client/components/Editors/Dialect/Sections/Dialect.tsx b/client/components/Editors/Dialect/Sections/Dialect.tsx
index 0c55f9bbe..45e24d746 100644
--- a/client/components/Editors/Dialect/Sections/Dialect.tsx
+++ b/client/components/Editors/Dialect/Sections/Dialect.tsx
@@ -9,11 +9,13 @@ import Columns from '../../../Parts/Grids/Columns'
import { useStore } from '../store'
import validator from 'validator'
import * as settings from '../../../../settings'
+import { useTranslation } from 'react-i18next'
export default function General() {
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
- updateHelp('dialect')}>
+ updateHelp('dialect')}>
@@ -40,6 +42,7 @@ function Title() {
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
const [isValid, setIsValid] = React.useState(isValidTitle())
+ const { t } = useTranslation()
function isValidTitle() {
return title ? !validator.isNumeric(title) : true
}
@@ -53,7 +56,7 @@ function Title() {
setIsValid(isValidTitle())
}}
onChange={(value) => updateDescriptor({ title: value || undefined })}
- helperText={!isValid ? 'Title is not valid.' : ''}
+ helperText={!isValid ? t('title-not-valid') : ''}
/>
)
}
@@ -62,9 +65,10 @@ function Description() {
const description = useStore((state) => state.descriptor.description)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('dialect/description')}
onChange={(value) => updateDescriptor({ description: value || undefined })}
@@ -77,9 +81,10 @@ function Format() {
const updateHelp = useStore((state) => state.updateHelp)
const updateState = useStore((state) => state.updateState)
const externalMenu = useStore((state) => state.externalMenu)
+ const { t } = useTranslation()
return (
state.descriptor.commentChar)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('dialect/type/commentChar')}
onChange={(value) => updateDescriptor({ commentChar: value || undefined })}
@@ -107,9 +113,10 @@ function CommentRows() {
const commentRows = useStore((state) => state.descriptor.commentRows)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('dialect/type/commentRows')}
onChange={(value) =>
@@ -125,9 +132,10 @@ function Header() {
const header = useStore((state) => state.descriptor.header)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('dialect/type/header')}
onChange={(value) => updateDescriptor({ header: value ?? undefined })}
@@ -139,9 +147,10 @@ function HeaderRows() {
const headerRows = useStore((state) => state.descriptor.headerRows)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('dialect/type/headerRows')}
onChange={(value) =>
@@ -157,9 +166,10 @@ function HeaderJoin() {
const headerJoin = useStore((state) => state.descriptor.headerJoin)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('dialect/type/headerJoin')}
onChange={(value) => updateDescriptor({ headerJoin: value || undefined })}
@@ -171,9 +181,10 @@ function HeaderCase() {
const headerCase = useStore((state) => state.descriptor.headerCase)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('dialect/type/headerCase')}
onChange={(value) => updateDescriptor({ headerCase: value ?? undefined })}
diff --git a/client/components/Editors/Dialect/Sections/Format/Csv.tsx b/client/components/Editors/Dialect/Sections/Format/Csv.tsx
index 13a322adc..816650b0e 100644
--- a/client/components/Editors/Dialect/Sections/Format/Csv.tsx
+++ b/client/components/Editors/Dialect/Sections/Format/Csv.tsx
@@ -5,6 +5,7 @@ import YesNoField from '../../../../Parts/Fields/YesNo'
import EditorSection from '../../../Base/Section'
import * as settings from '../../../../../settings'
import { useStore, selectors, select } from '../../store'
+import { useTranslation } from 'react-i18next'
export default function General() {
const updateHelp = useStore((state) => state.updateHelp)
@@ -31,9 +32,10 @@ function Delimiter() {
const delimiter = useStore(select(selectors.csv, (csv) => csv.delimiter))
const updateHelp = useStore((state) => state.updateHelp)
const updateCsv = useStore((state) => state.updateCsv)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/delimiter')}
onChange={(delimiter) => updateCsv({ delimiter })}
@@ -45,9 +47,10 @@ function LineTerminator() {
const lineTerminator = useStore(select(selectors.csv, (csv) => csv.lineTerminator))
const updateHelp = useStore((state) => state.updateHelp)
const updateCsv = useStore((state) => state.updateCsv)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/lineTerminator')}
onChange={(lineTerminator) => updateCsv({ lineTerminator })}
@@ -59,9 +62,10 @@ function QuoteChar() {
const quoteChar = useStore(select(selectors.csv, (csv) => csv.quoteChar))
const updateHelp = useStore((state) => state.updateHelp)
const updateCsv = useStore((state) => state.updateCsv)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/quoteChar')}
onChange={(quoteChar) => updateCsv({ quoteChar })}
@@ -73,9 +77,10 @@ function DoubleQuote() {
const doubleQuote = useStore(select(selectors.csv, (csv) => csv.doubleQuote))
const updateHelp = useStore((state) => state.updateHelp)
const updateCsv = useStore((state) => state.updateCsv)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/doubleQuote')}
onChange={(doubleQuote) => updateCsv({ doubleQuote })}
@@ -87,9 +92,10 @@ function EscapeChar() {
const escapeChar = useStore(select(selectors.csv, (csv) => csv.escapeChar))
const updateHelp = useStore((state) => state.updateHelp)
const updateCsv = useStore((state) => state.updateCsv)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/escapeChar')}
onChange={(escapeChar) => updateCsv({ escapeChar })}
@@ -101,9 +107,10 @@ function NullSequence() {
const nullSequence = useStore(select(selectors.csv, (csv) => csv.nullSequence))
const updateHelp = useStore((state) => state.updateHelp)
const updateCsv = useStore((state) => state.updateCsv)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/nullSequence')}
onChange={(nullSequence) => updateCsv({ nullSequence })}
@@ -115,11 +122,12 @@ function SkipInitialSpace() {
const skipInitialSpace = useStore(select(selectors.csv, (csv) => csv.skipInitialSpace))
const updateHelp = useStore((state) => state.updateHelp)
const updateCsv = useStore((state) => state.updateCsv)
+ const { t } = useTranslation()
return (
updateHelp('csv/skipInitialSpace')}
+ onFocus={() => updateHelp('dialect/format/skipInitialSpace')}
onChange={(skipInitialSpace) => updateCsv({ skipInitialSpace })}
/>
)
diff --git a/client/components/Editors/Dialect/Sections/Format/Empty.tsx b/client/components/Editors/Dialect/Sections/Format/Empty.tsx
index 9fcf40107..382ae28bb 100644
--- a/client/components/Editors/Dialect/Sections/Format/Empty.tsx
+++ b/client/components/Editors/Dialect/Sections/Format/Empty.tsx
@@ -3,13 +3,15 @@ import Box from '@mui/material/Box'
import Columns from '../../../../Parts/Grids/Columns'
import EditorSection from '../../../Base/Section'
import { useStore } from '../../store'
+import { useTranslation } from 'react-i18next'
export default function General() {
const format = useStore((state) => state.format)
+ const { t } = useTranslation()
return (
-
+
- No options available for this format
+ {t('no-options-available-for-format')}
)
diff --git a/client/components/Editors/Dialect/Sections/Format/Excel.tsx b/client/components/Editors/Dialect/Sections/Format/Excel.tsx
index 23d52c375..55a1fd515 100644
--- a/client/components/Editors/Dialect/Sections/Format/Excel.tsx
+++ b/client/components/Editors/Dialect/Sections/Format/Excel.tsx
@@ -7,6 +7,7 @@ import EditorSection from '../../../Base/Section'
import * as settings from '../../../../../settings'
import { useStore, selectors, select } from '../../store'
// import validator from 'validator'
+import { useTranslation } from 'react-i18next'
export default function General() {
const updateHelp = useStore((state) => state.updateHelp)
@@ -32,6 +33,7 @@ function Sheet() {
const updateHelp = useStore((state) => state.updateHelp)
const updateExcel = useStore((state) => state.updateExcel)
const [isValid, setIsValid] = React.useState(isValidSheet())
+ const { t } = useTranslation()
function isValidSheet() {
// Sheet can be both string/number for now
// return sheet ? validator.isNumeric(sheet) : true
@@ -40,14 +42,14 @@ function Sheet() {
return (
updateHelp('dialect/format/sheet')}
onBlur={() => {
setIsValid(isValidSheet())
}}
onChange={(value) => updateExcel({ sheet: value || undefined })}
- helperText={!isValid ? 'Sheet is not valid.' : ''}
+ helperText={!isValid ? t('sheet-not-valid') : ''}
/>
)
}
@@ -58,9 +60,10 @@ function FillMergedCells() {
)
const updateHelp = useStore((state) => state.updateHelp)
const updateExcel = useStore((state) => state.updateExcel)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/fillMergedCells')}
onChange={(fillMergedCells) => updateExcel({ fillMergedCells })}
@@ -74,9 +77,10 @@ function PreserveFormatting() {
)
const updateHelp = useStore((state) => state.updateHelp)
const updateExcel = useStore((state) => state.updateExcel)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/preserveFormatting')}
onChange={(preserveFormatting) => updateExcel({ preserveFormatting })}
@@ -90,9 +94,10 @@ function AdjustFloatingPointError() {
)
const updateHelp = useStore((state) => state.updateHelp)
const updateExcel = useStore((state) => state.updateExcel)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/adjustFloatingPointError')}
onChange={(adjustFloatingPointError) => updateExcel({ adjustFloatingPointError })}
@@ -104,9 +109,10 @@ function Stringified() {
const stringified = useStore(select(selectors.excel, (excel) => excel.stringified))
const updateHelp = useStore((state) => state.updateHelp)
const updateExcel = useStore((state) => state.updateExcel)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/stringified')}
onChange={(stringified) => updateExcel({ stringified })}
diff --git a/client/components/Editors/Dialect/Sections/Format/Json.tsx b/client/components/Editors/Dialect/Sections/Format/Json.tsx
index 44a57b203..ad61e0626 100644
--- a/client/components/Editors/Dialect/Sections/Format/Json.tsx
+++ b/client/components/Editors/Dialect/Sections/Format/Json.tsx
@@ -5,6 +5,7 @@ import EditorSection from '../../../Base/Section'
import * as settings from '../../../../../settings'
import { useStore, selectors, select } from '../../store'
import YesNoField from '../../../../Parts/Fields/YesNo'
+import { useTranslation } from 'react-i18next'
export default function General() {
const updateHelp = useStore((state) => state.updateHelp)
@@ -27,9 +28,10 @@ function Keys() {
const keys = useStore(select(selectors.json, (json) => json.keys || ''))
const updateHelp = useStore((state) => state.updateHelp)
const updateJson = useStore((state) => state.updateJson)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/keys')}
onChange={(value) => updateJson({ keys: value ? value.split(',') : undefined })}
@@ -41,9 +43,10 @@ function Keyed() {
const keyed = useStore(select(selectors.json, (json) => json.keyed))
const updateHelp = useStore((state) => state.updateHelp)
const updateJson = useStore((state) => state.updateJson)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/keyed')}
onChange={(keyed) => updateJson({ keyed })}
@@ -55,9 +58,10 @@ function Property() {
const property = useStore(select(selectors.json, (json) => json.property || ''))
const updateHelp = useStore((state) => state.updateHelp)
const updateJson = useStore((state) => state.updateJson)
+ const { t } = useTranslation()
return (
updateHelp('dialect/format/property')}
onChange={(value) => updateJson({ property: value || undefined })}
diff --git a/client/components/Editors/Dialect/help.yaml b/client/components/Editors/Dialect/help.yaml
deleted file mode 100644
index 3e80b824d..000000000
--- a/client/components/Editors/Dialect/help.yaml
+++ /dev/null
@@ -1,143 +0,0 @@
-# Dialect
-
-dialect:
- - Dialect
- - https://framework.frictionlessdata.io/docs/framework/dialect.html
- - File dialect concept give us an ability to manage table header and any details related to specific formats.
-
-dialect/title:
- - Title
- - https://framework.frictionlessdata.io/docs/framework/dialect.html
- - A human-readable title for this dialect.
-
-dialect/description:
- - Description
- - https://framework.frictionlessdata.io/docs/framework/dialect.html
- - A brief description of the dialect.
-
-# Type
-
-dialect/type:
- - Type
- - https://specs.frictionlessdata.io/csv-dialect/
- - CSV Dialect defines a simple format to describe the various dialects of CSV files in a language agnostic manner.
-
-# Type (Table)
-
-dialect/type/header:
- - Header
- - https://framework.frictionlessdata.io/docs/framework/dialect.html#header
- - It's a boolean flag which defaults to True indicating whether the data has a header row or not.
-
-dialect/type/headerRows:
- - Header Rows
- - https://framework.frictionlessdata.io/docs/framework/dialect.html#header-rows
- - It specifies the header row or rows for multiline header.
-
-dialect/type/commentChar:
- - Comment Char
- - https://framework.frictionlessdata.io/docs/framework/dialect.html#comment-char
- - It specifies the char to use to comment the rows.
-
-dialect/type/headerJoin:
- - Header Join
- - https://framework.frictionlessdata.io/docs/framework/dialect.html#header-join
- - It specifies the header rows to combine, if there are multiple header rows.
-
-dialect/type/commentRows:
- - Comment Rows
- - https://framework.frictionlessdata.io/docs/framework/dialect.html#comment-rows
- - It specifies list of rows to ignore.
-
-dialect/type/headerCase:
- - Header Case
- - https://framework.frictionlessdata.io/docs/framework/dialect.html#header-case
- - It specifies case sensitivity mode. Header is case sensitive by default.
-
-# Format
-
-dialect/format:
- - Format
- - https://specs.frictionlessdata.io/csv-dialect/
- - CSV Dialect defines a simple format to describe the various dialects of CSV files in a language agnostic manner.
-
-# Format (Csv)
-
-dialect/format/delimiter:
- - Delimiter
- - https://specs.frictionlessdata.io/csv-dialect/#specification
- - Specifies the character sequence which should separate fields. (default ",")
-
-dialect/format/lineTerminator:
- - Line Terminator
- - https://specs.frictionlessdata.io/csv-dialect/#specification
- - Specifies the line terminator for the csv file while reading/writing. (default "\r\n")
-
-dialect/format/quoteChar:
- - Quote Char
- - https://specs.frictionlessdata.io/csv-dialect/#specification
- - Specifies a one-character string to use as the quoting character. (default '"')
-
-dialect/format/doubleQuote:
- - Double Quote
- - https://specs.frictionlessdata.io/csv-dialect/#specification
- - Controls the handling of quotes inside fields. (default true)
-
-dialect/format/escapeChar:
- - Escape Char
- - https://specs.frictionlessdata.io/csv-dialect/#specification
- - Specifies a one-character string to use for escaping.
-
-dialect/format/nullSequence:
- - Null Sequence
- - https://specs.frictionlessdata.io/csv-dialect/#specification
- - Specifies the null sequence.
-
-dialect/format/skipInitialSpace:
- - Skip Initial Space
- - https://specs.frictionlessdata.io/csv-dialect/#specification
- - Specifies how to interpret whitespace which immediately follows a delimiter. (default false)
-
-# Format (Excel)
-
-dialect/format/sheet:
- - Sheet
- - https://framework.frictionlessdata.io/docs/formats/excel.html?query=excel#configuration
- - Specifies name of the sheet from where to read or write data. (default 1)
-
-dialect/format/fillMergedCells:
- - Fill Merged Cells
- - https://framework.frictionlessdata.io/docs/formats/excel.html?query=excel#configuration
- - Specifies to unmerge and fill all merged cells by the visible value. (default false)
-
-dialect/format/preserveFormatting:
- - Preserve Formatting
- - https://framework.frictionlessdata.io/docs/formats/excel.html?query=excel#configuration
- - Specifies to preserve text formatting for numeric and temporal cells. (default false)
-
-dialect/format/adjustFloatingPointError:
- - Adjust Floating Point Error
- - https://framework.frictionlessdata.io/docs/formats/excel.html?query=excel#configuration
- - Specifies to ajust the Excel behavior regarding floating point numbers.
-
-dialect/format/stringified:
- - Stringified
- - https://framework.frictionlessdata.io/docs/formats/excel.html?query=excel#configuration
- - Specifies to stringify all cell values. (default false)
-
-# Format (Json)
-
-dialect/format/keys:
- - keys
- - https://framework.frictionlessdata.io/docs/formats/json.html?query=json#configuration
- - Specifies the keys/columns to read from the json resource.
-
-dialect/format/keyed:
- - Keyed
- - https://framework.frictionlessdata.io/docs/formats/json.html?query=json#configuration
- - Specifies to return the data as "key:value" pair. (default false)
-
-dialect/format/property:
- - Property
- - https://framework.frictionlessdata.io/docs/formats/json.html?query=json#configuration
- - Specifies the path to the attribute in a json file, if it has nested fields.
diff --git a/client/components/Editors/Dialect/store.ts b/client/components/Editors/Dialect/store.ts
index 28d2ccecf..4e6148d02 100644
--- a/client/components/Editors/Dialect/store.ts
+++ b/client/components/Editors/Dialect/store.ts
@@ -7,11 +7,8 @@ import { createStore } from 'zustand/vanilla'
import { createSelector } from 'reselect'
import { DialectProps } from './index'
import * as settings from '../../../settings'
-import * as helpers from '../../../helpers'
import * as types from '../../../types'
-import help from './help.yaml'
-
-const DEFAULT_HELP_ITEM = helpers.readHelpItem(help, 'dialect')!
+import { t } from 'i18next'
interface State {
format: string
@@ -32,6 +29,8 @@ interface State {
}
export function makeStore(props: DialectProps) {
+ const DEFAULT_HELP_ITEM = t('help-dialect', { returnObjects: true }) as types.IHelpItem
+
return createStore((set, get) => ({
descriptor: props.dialect || cloneDeep(settings.INITIAL_DIALECT),
externalMenu: props.externalMenu,
@@ -43,7 +42,8 @@ export function makeStore(props: DialectProps) {
set({ ...patch })
},
updateHelp: (path) => {
- const helpItem = helpers.readHelpItem(help, path) || DEFAULT_HELP_ITEM
+ let helpItem = t(`help-${path}`, { returnObjects: true }) as types.IHelpItem
+ if (typeof helpItem !== 'object') helpItem = DEFAULT_HELP_ITEM
set({ helpItem })
},
updateDescriptor: (patch) => {
diff --git a/client/components/Editors/Resource/Layout.tsx b/client/components/Editors/Resource/Layout.tsx
index 83ad192bd..b03da50bd 100644
--- a/client/components/Editors/Resource/Layout.tsx
+++ b/client/components/Editors/Resource/Layout.tsx
@@ -13,6 +13,7 @@ import SourcesSection from './Sections/Sources'
import ContributorsSection from './Sections/Contributors'
import { useStore } from './store'
import * as types from '../../../types'
+import { useTranslation } from 'react-i18next'
export default function Layout() {
const externalMenu = useStore((state) => state.externalMenu)
@@ -34,18 +35,19 @@ function LayoutWithMenu() {
const updateState = useStore((state) => state.updateState)
const updateDescriptor = useStore((state) => state.updateDescriptor)
const onFieldSelected = useStore((state) => state.onFieldSelected)
+ const { t } = useTranslation()
const MENU_ITEMS: types.IMenuItem[] = [
- { section: 'resource', name: 'Resource' },
- { section: 'resource/integrity', name: 'Integrity' },
- { section: 'resource/licenses', name: 'Licenses' },
- { section: 'resource/contributors', name: 'Contributors' },
- { section: 'resource/sources', name: 'Sources' },
- { section: 'dialect', name: 'Dialect', disabled: type !== 'table' },
- { section: 'dialect/format', name: capitalize(format) || 'Format' },
- { section: 'schema', name: 'Schema', disabled: type !== 'table' },
- { section: 'schema/fields', name: 'Fields' },
- { section: 'schema/foreignKeys', name: 'Foreign Keys' },
+ { section: 'resource', name: t('resource') },
+ { section: 'resource/integrity', name: t('integrity') },
+ { section: 'resource/licenses', name: t('licenses') },
+ { section: 'resource/contributors', name: t('contributors') },
+ { section: 'resource/sources', name: t('sources') },
+ { section: 'dialect', name: t('dialect'), disabled: type !== 'table' },
+ { section: 'dialect/format', name: capitalize(format) || t('format') },
+ { section: 'schema', name: t('schema'), disabled: type !== 'table' },
+ { section: 'schema/fields', name: t('fields') },
+ { section: 'schema/foreignKeys', name: t('foreign-keys') },
]
// We use memo to avoid nested editors re-rerender
@@ -112,7 +114,7 @@ function LayoutWithoutMenu() {
if (!section) return null
return (
-
+
diff --git a/client/components/Editors/Resource/Sections/Contributors.tsx b/client/components/Editors/Resource/Sections/Contributors.tsx
index a7b020646..fd678ec6f 100644
--- a/client/components/Editors/Resource/Sections/Contributors.tsx
+++ b/client/components/Editors/Resource/Sections/Contributors.tsx
@@ -5,6 +5,7 @@ import EditorItem from '../../Base/Item'
import EditorList from '../../Base/List'
import EditorListItem from '../../Base/ListItem'
import { useStore, selectors, select } from '../store'
+import { useTranslation } from 'react-i18next'
export default function Contributors() {
const index = useStore((state) => state.contributorState.index)
@@ -69,9 +70,10 @@ function Title() {
)
const updateHelp = useStore((state) => state.updateHelp)
const updateContributor = useStore((state) => state.updateContributor)
+ const { t } = useTranslation()
return (
updateHelp('resource/contributors/title')}
onChange={(value) => updateContributor({ title: value })}
@@ -85,9 +87,10 @@ function Email() {
)
const updateHelp = useStore((state) => state.updateHelp)
const updateContributor = useStore((state) => state.updateContributor)
+ const { t } = useTranslation()
return (
updateHelp('resource/contributors/email')}
onChange={(value) => updateContributor({ email: value || undefined })}
@@ -99,9 +102,10 @@ function Path() {
const path = useStore(select(selectors.contributor, (contributor) => contributor.path))
const updateHelp = useStore((state) => state.updateHelp)
const updateContributor = useStore((state) => state.updateContributor)
+ const { t } = useTranslation()
return (
updateHelp('resource/contributors/path')}
onChange={(value) => updateContributor({ path: value || undefined })}
@@ -113,9 +117,10 @@ function Role() {
const role = useStore(select(selectors.contributor, (contributor) => contributor.role))
const updateHelp = useStore((state) => state.updateHelp)
const updateContributor = useStore((state) => state.updateContributor)
+ const { t } = useTranslation()
return (
updateHelp('resource/contributors/role')}
onChange={(value) => updateContributor({ role: value || undefined })}
diff --git a/client/components/Editors/Resource/Sections/Integrity.tsx b/client/components/Editors/Resource/Sections/Integrity.tsx
index 047a7a7ae..d2724bb97 100644
--- a/client/components/Editors/Resource/Sections/Integrity.tsx
+++ b/client/components/Editors/Resource/Sections/Integrity.tsx
@@ -3,12 +3,14 @@ import InputField from '../../../Parts/Fields/Input'
import EditorSection from '../../Base/Section'
import Columns from '../../../Parts/Grids/Columns'
import { useStore } from '../store'
+import { useTranslation } from 'react-i18next'
export default function Integrity() {
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('resource/integrity')}
>
@@ -29,9 +31,10 @@ function Hash() {
const hash = useStore((state) => state.descriptor.hash)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('resource/integrity/hash')}
onChange={(value) => updateDescriptor({ hash: value || undefined })}
@@ -43,9 +46,10 @@ function Bytes() {
const bytes = useStore((state) => state.descriptor.bytes)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('resource/integrity/bytes')}
onChange={(value) => updateDescriptor({ bytes: parseInt(value) || undefined })}
@@ -58,11 +62,12 @@ function Fields() {
const fields = useStore((state) => state.descriptor.fields)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
// Until standards@2 we use a safer check
if (['file', 'text', 'json'].includes(type)) return null
return (
updateHelp('resource/integrity/fields')}
onChange={(value) => updateDescriptor({ fields: parseInt(value) || undefined })}
@@ -75,11 +80,12 @@ function Rows() {
const rows = useStore((state) => state.descriptor.rows)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
// Until standards@2 we use a safer check
if (['file', 'text', 'json'].includes(type)) return null
return (
updateHelp('resource/integrity/rows')}
onChange={(value) => updateDescriptor({ fields: parseInt(value) || undefined })}
diff --git a/client/components/Editors/Resource/Sections/Licenses.tsx b/client/components/Editors/Resource/Sections/Licenses.tsx
index e7e8137eb..4c32a0f89 100644
--- a/client/components/Editors/Resource/Sections/Licenses.tsx
+++ b/client/components/Editors/Resource/Sections/Licenses.tsx
@@ -16,6 +16,7 @@ import FormControl from '@mui/material/FormControl'
import Autocomplete from '@mui/material/Autocomplete'
import { useStore, selectors, select } from '../store'
import validator from 'validator'
+import { useTranslation } from 'react-i18next'
export default function Licenses() {
const index = useStore((state) => state.licenseState.index)
@@ -51,6 +52,7 @@ function LicenseList() {
function LicenseDialog(props: { open: boolean; onClose: () => void }) {
const addLicense = useStore((state) => state.addLicense)
+ const { t } = useTranslation()
const licenses = Object.values(openDefinitionLicenses).map((license) => ({
name: license.id,
@@ -68,7 +70,7 @@ function LicenseDialog(props: { open: boolean; onClose: () => void }) {
return (
-
+ {helpItem ? : null}
)
}
diff --git a/client/components/Editors/Schema/Sections/Fields.tsx b/client/components/Editors/Schema/Sections/Fields.tsx
index bb98656eb..821e09571 100644
--- a/client/components/Editors/Schema/Sections/Fields.tsx
+++ b/client/components/Editors/Schema/Sections/Fields.tsx
@@ -18,6 +18,7 @@ import DateTimePickerField from '../../../Parts/Fields/DateTimePicker'
import TimePickerField from '../../../Parts/Fields/TimePicker'
import validator from 'validator'
import dayjs from 'dayjs'
+import { useTranslation } from 'react-i18next'
export default function Fields() {
const index = useStore((state) => state.fieldState.index)
@@ -100,6 +101,7 @@ function Name() {
const updateField = useStore((state) => state.updateField)
const [value, setValue] = React.useState('')
+ const { t } = useTranslation()
React.useEffect(() => {
setValue(name)
@@ -114,7 +116,7 @@ function Name() {
return (
updateHelp('schema/fields/name')}
@@ -127,9 +129,10 @@ function Type() {
const updateField = useStore((state) => state.updateField)
const updateHelp = useStore((state) => state.updateHelp)
const type = useStore(select(selectors.field, (field) => field.type))
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/type')}
@@ -146,16 +149,17 @@ function Format() {
// TODO: remove any
const FIELD = (settings.FIELDS as any)[type]
const isFree = FIELD.formats.includes('*')
+ const { t } = useTranslation()
return isFree ? (
updateHelp('schema/fields/format')}
onChange={(value) => updateField({ format: value || undefined })}
/>
) : (
updateHelp('schema/field/format')}
@@ -168,9 +172,10 @@ function Title() {
const updateField = useStore((state) => state.updateField)
const updateHelp = useStore((state) => state.updateHelp)
const title = useStore(select(selectors.field, (field) => field.title))
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/title')}
onChange={(value) => updateField({ title: value || undefined })}
@@ -182,9 +187,10 @@ function Description() {
const updateField = useStore((state) => state.updateField)
const updateHelp = useStore((state) => state.updateHelp)
const descriptor = useStore(select(selectors.field, (field) => field.description))
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/description')}
onChange={(value) => updateField({ description: value || undefined })}
@@ -196,9 +202,10 @@ function MissingValues() {
const updateField = useStore((state) => state.updateField)
const updateHelp = useStore((state) => state.updateHelp)
const missingValues = useStore(select(selectors.field, (field) => field.missingValues))
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/missingValues')}
onChange={(value) =>
@@ -212,9 +219,10 @@ function RdfType() {
const updateField = useStore((state) => state.updateField)
const updateHelp = useStore((state) => state.updateHelp)
const rdfType = useStore(select(selectors.field, (field) => field.rdfType))
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/rdfType')}
onChange={(value) => updateField({ rdfType: value || undefined })}
@@ -282,10 +290,11 @@ function ArrayItem() {
const updateField = useStore((state) => state.updateField)
const arrayItem = useStore(select(selectors.field, (field) => field.arrayItem))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/arrayItem')}
onChange={(value) => updateField({ arrayItem: value || undefined })}
@@ -297,9 +306,10 @@ function TrueValues() {
const updateField = useStore((state) => state.updateField)
const trueValues = useStore(select(selectors.field, (field) => field.trueValues))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/trueValues')}
onChange={(value) =>
@@ -313,9 +323,10 @@ function FalseValues() {
const updateField = useStore((state) => state.updateField)
const falseValues = useStore(select(selectors.field, (field) => field.falseValues))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/falseValues')}
onChange={(value) =>
@@ -329,9 +340,10 @@ function BareNumber() {
const updateField = useStore((state) => state.updateField)
const bareNumber = useStore(select(selectors.field, (field) => field.bareNumber))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/bareNumber')}
onChange={(value) =>
@@ -345,9 +357,10 @@ function FloatNumber() {
const updateField = useStore((state) => state.updateField)
const floatNumber = useStore(select(selectors.field, (field) => field.floatNumber))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/floatNumber')}
onChange={(value) => updateField({ floatNumber: value || undefined })}
@@ -359,9 +372,10 @@ function DecimalChar() {
const updateField = useStore((state) => state.updateField)
const decimalChar = useStore(select(selectors.field, (field) => field.decimalChar))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/decimalChar')}
value={decimalChar || settings.DEFAULT_DECIMAL_CHAR}
onChange={(value) => updateField({ decimalChar: value || undefined })}
@@ -373,9 +387,10 @@ function GroupChar() {
const updateField = useStore((state) => state.updateField)
const groupChar = useStore(select(selectors.field, (field) => field.groupChar))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/groupChar')}
value={groupChar || settings.DEFAULT_GROUP_CHAR}
onChange={(value) => updateField({ groupChar: value || undefined })}
@@ -430,10 +445,11 @@ function Required() {
const updateField = useStore((state) => state.updateField)
const constraints = useStore(select(selectors.field, (field) => field.constraints))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/required')}
value={constraints?.required || false}
onChange={(value) => {
@@ -479,9 +495,10 @@ function MinimumDate() {
const updateHelp = useStore((state) => state.updateHelp)
const format = field.format || settings.DEFUALT_DATE_FORMAT
const value = constraints ? dayjs(constraints.minimum, format) : null
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/minimum')}
onChange={(value) => {
@@ -500,9 +517,10 @@ function MaximumDate() {
const updateHelp = useStore((state) => state.updateHelp)
const format = field.format || settings.DEFUALT_DATE_FORMAT
const value = constraints ? dayjs(constraints.maximum, format) : null
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/maximum')}
onChange={(value) => {
@@ -521,16 +539,17 @@ function MinimumDateTime() {
const updateHelp = useStore((state) => state.updateHelp)
const format = field.format || settings.DEFUALT_DATETIME_FORMAT
const value = constraints ? dayjs(constraints.minimum, format) : null
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/minimum')}
onChange={(value) => {
if (!value) return
updateField({ constraints: { ...constraints, minimum: value.format(format) } })
}}
- errorMessage={'Minimum value is not valid'}
+ errorMessage={t('minimum-not-valid')}
/>
)
}
@@ -542,16 +561,17 @@ function MaximumDateTime() {
const updateHelp = useStore((state) => state.updateHelp)
const format = field.format || settings.DEFUALT_DATETIME_FORMAT
const value = constraints ? dayjs(constraints.maximum, format) : null
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/maximum')}
onChange={(value) => {
if (!value) return
updateField({ constraints: { ...constraints, maximum: value.format(format) } })
}}
- errorMessage={'Maximum value is not valid'}
+ errorMessage={t('maximum-not-valid')}
/>
)
}
@@ -563,9 +583,10 @@ function MinimumTime() {
const updateHelp = useStore((state) => state.updateHelp)
const format = field.format || settings.DEFUALT_TIME_FORMAT
const value = constraints ? dayjs(constraints.minimum, format) : null
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/minimum')}
onChange={(value) => {
@@ -577,7 +598,7 @@ function MinimumTime() {
},
})
}}
- errorMessage={'Minimum value is not valid'}
+ errorMessage={t('minimum-not-valid')}
/>
)
}
@@ -589,9 +610,10 @@ function MaximumTime() {
const updateHelp = useStore((state) => state.updateHelp)
const format = field.format || settings.DEFUALT_TIME_FORMAT
const value = constraints ? dayjs(constraints.maximum, format) : null
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/maximum')}
onChange={(value) => {
@@ -603,7 +625,7 @@ function MaximumTime() {
},
})
}}
- errorMessage={'Maximum value is not valid'}
+ errorMessage={t('maximum-not-valid')}
/>
)
}
@@ -613,6 +635,7 @@ function MinimumNumber() {
const constraints = useStore(select(selectors.field, (field) => field.constraints))
const updateHelp = useStore((state) => state.updateHelp)
const [isValid, setIsValid] = React.useState(isValidMinimumNumber())
+ const { t } = useTranslation()
function isValidMinimumNumber() {
if (!constraints) return true
@@ -625,7 +648,7 @@ function MinimumNumber() {
updateHelp('schema/fields/minimum')}
onBlur={() => {
@@ -635,7 +658,7 @@ function MinimumNumber() {
const minimum = value || undefined
updateField({ constraints: { ...constraints, minimum } })
}}
- helperText={!isValid ? 'Minimum value is not valid.' : ''}
+ helperText={!isValid ? t('minimum-not-valid') : ''}
/>
)
}
@@ -645,6 +668,7 @@ function MaximumNumber() {
const constraints = useStore(select(selectors.field, (field) => field.constraints))
const updateHelp = useStore((state) => state.updateHelp)
const [isValid, setIsValid] = React.useState(isValidMaximumNumber())
+ const { t } = useTranslation()
function isValidMaximumNumber() {
if (!constraints) return true
@@ -657,7 +681,7 @@ function MaximumNumber() {
updateHelp('schema/fields/maximum')}
onBlur={() => {
@@ -667,7 +691,7 @@ function MaximumNumber() {
const maximum = value || undefined
updateField({ constraints: { ...constraints, maximum } })
}}
- helperText={!isValid ? 'Maximum value is not valid.' : ''}
+ helperText={!isValid ? t('maximum-not-valid') : ''}
/>
)
}
@@ -676,11 +700,12 @@ function MinLength() {
const updateField = useStore((state) => state.updateField)
const constraints = useStore(select(selectors.field, (field) => field.constraints))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/minLength')}
onChange={(value) => {
@@ -695,11 +720,12 @@ function MaxLength() {
const updateField = useStore((state) => state.updateField)
const constraints = useStore(select(selectors.field, (field) => field.constraints))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/maxLength')}
onChange={(value) => {
@@ -714,11 +740,12 @@ function Pattern() {
const updateField = useStore((state) => state.updateField)
const constraints = useStore(select(selectors.field, (field) => field.constraints))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/fields/pattern')}
onChange={(value) => {
@@ -734,6 +761,7 @@ function Enum() {
const descriptor = useStore((state) => state.descriptor)
const constraints = useStore(select(selectors.field, (field) => field.constraints))
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
const [value, setValue] = React.useState('')
@@ -752,7 +780,7 @@ function Enum() {
return (
updateHelp('schema/fields/enum')}
onChange={handleChange}
diff --git a/client/components/Editors/Schema/Sections/ForeignKeys.tsx b/client/components/Editors/Schema/Sections/ForeignKeys.tsx
index 0bc3854e6..e77976eac 100644
--- a/client/components/Editors/Schema/Sections/ForeignKeys.tsx
+++ b/client/components/Editors/Schema/Sections/ForeignKeys.tsx
@@ -6,6 +6,7 @@ import EditorItem from '../../Base/Item'
import EditorList from '../../Base/List'
import EditorListItem from '../../Base/ListItem'
import { useStore, selectors, select } from '../store'
+import { useTranslation } from 'react-i18next'
export default function ForeignKey() {
const index = useStore((state) => state.foreignKeyState.index)
@@ -64,9 +65,10 @@ function SourceField() {
const fieldNames = useStore(selectors.fieldNames)
const updateForeignKey = useStore((state) => state.updateForeignKey)
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/foreignKey/sourceField')}
@@ -81,9 +83,10 @@ function TargetField() {
)
const updateForeignKey = useStore((state) => state.updateForeignKey)
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
updateHelp('schema/foreignKey/targetField')}
@@ -99,10 +102,11 @@ function TargetResource() {
select(selectors.foreignKey, (foreignKey) => foreignKey.reference)
)
const updateForeignKey = useStore((state) => state.updateForeignKey)
+ const { t } = useTranslation()
return (
updateForeignKey({ reference: { ...reference, resource } })}
/>
diff --git a/client/components/Editors/Schema/Sections/Schema.tsx b/client/components/Editors/Schema/Sections/Schema.tsx
index be4291351..780b21c90 100644
--- a/client/components/Editors/Schema/Sections/Schema.tsx
+++ b/client/components/Editors/Schema/Sections/Schema.tsx
@@ -7,11 +7,13 @@ import EditorSection from '../../Base/Section'
import Columns from '../../../Parts/Grids/Columns'
import { useStore, selectors } from '../store'
import validator from 'validator'
+import { useTranslation } from 'react-i18next'
export default function General() {
const updateHelp = useStore((state) => state.updateHelp)
+ const { t } = useTranslation()
return (
- updateHelp('schema')}>
+ updateHelp('schema')}>
@@ -32,20 +34,21 @@ function Name() {
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
const [isValid, setIsValid] = React.useState(isValidName())
+ const { t } = useTranslation()
function isValidName() {
return name ? validator.isSlug(name) : true
}
return (
updateHelp('schema/name')}
onBlur={() => {
setIsValid(isValidName())
}}
onChange={(value) => updateDescriptor({ name: value || undefined })}
- helperText={!isValid ? 'Name is not valid.' : ''}
+ helperText={!isValid ? t('name-not-valid') : ''}
/>
)
}
@@ -54,9 +57,10 @@ function Title() {
const title = useStore((state) => state.descriptor.title)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('schema/title')}
onChange={(value) => updateDescriptor({ title: value || undefined })}
@@ -68,9 +72,10 @@ function Description() {
const description = useStore((state) => state.descriptor.description)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('schema/description')}
onChange={(value) => updateDescriptor({ description: value || undefined })}
@@ -83,9 +88,10 @@ function PrimaryKey() {
const primaryKey = useStore((state) => state.descriptor.primaryKey)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('schema/primaryKey')}
@@ -99,9 +105,10 @@ function MissingValues() {
const missingValues = useStore((state) => state.descriptor.missingValues)
const updateHelp = useStore((state) => state.updateHelp)
const updateDescriptor = useStore((state) => state.updateDescriptor)
+ const { t } = useTranslation()
return (
updateHelp('schema/missingValues')}
onChange={(value) =>
diff --git a/client/components/Editors/Schema/help.yaml b/client/components/Editors/Schema/help.yaml
deleted file mode 100644
index 2a05585bf..000000000
--- a/client/components/Editors/Schema/help.yaml
+++ /dev/null
@@ -1,160 +0,0 @@
-# Schema
-
-schema:
- - Schema
- - https://specs.frictionlessdata.io/table-schema/
- - Table Schema is a specification for providing a schema for tabular data. It includes the expected data type for each value in a column.
-
-schema/title:
- - Title
- - https://specs.frictionlessdata.io/table-schema/
- - A human-readable title.
-
-schema/description:
- - Description
- - https://specs.frictionlessdata.io/table-schema/
- - A description of the schema. The description MUST be markdown formatted – this also allows for simple plain text as plain text is itself valid markdown.
-
-schema/primaryKey:
- - Primary Key
- - https://specs.frictionlessdata.io/table-schema/#primary-key
- - A primary key is a field or set of fields that uniquely identifies each row in the table.
-
-schema/missingValues:
- - Missing Values
- - https://specs.frictionlessdata.io/table-schema/#missing-values
- - Many datasets arrive with missing data values, either because a value was not collected or it never existed.
-
-# Fields
-
-schema/fields:
- - Fields
- - https://specs.frictionlessdata.io/table-schema/#descriptor
- - Fields MUST be an array where each entry in the array is a field descriptor (as defined below).
-
-schema/fields/name:
- - Name
- - https://specs.frictionlessdata.io/table-schema/#name
- - The field descriptor MUST contain a name property. This property SHOULD correspond to the name of field/column in the data file (if it has a name)
-
-schema/fields/type:
- - Type
- - https://specs.frictionlessdata.io/table-schema/#types-and-formats
- - String indicating the type of this field.
-
-schema/fields/format:
- - Format
- - https://specs.frictionlessdata.io/table-schema/#types-and-formats
- - String indicating the format of this field.
-
-schema/fields/missingValues:
- - Missing Values
- - https://specs.frictionlessdata.io/table-schema/#missing-values
- - Specifies which string values should be treated as null values.
-
-schema/fields/rdfType:
- - RDF Type
- - https://specs.frictionlessdata.io/table-schema/#rich-types
- - Indicates whether the field is of RDF type.
-
-schema/fields/title:
- - Title
- - https://specs.frictionlessdata.io/table-schema/#title
- - A human-readable title.
-
-schema/fields/bareNumber:
- - Bare Number
- - https://specs.frictionlessdata.io/table-schema/#types-and-formats
- - A boolean field with a default of true.
-
-schema/fields/description:
- - Description
- - https://specs.frictionlessdata.io/table-schema/#description
- - A description of the field.
-
-schema/fields/groupChar:
- - Group Char
- - https://specs.frictionlessdata.io/table-schema/#types-and-formats
- - A string whose value is used to group digits within the number.
-
-schema/fields/arrayItem:
- - Array Item
- - https://specs.frictionlessdata.io/table-schema/#array
- - A dictionary that specifies the type and other constraints for the data that will be read in this data type field.
-
-schema/fields/trueValues:
- - True Values
- - https://specs.frictionlessdata.io/table-schema/#boolean
- - Specifies which string values should be treated as true values.
-
-schema/fields/falseValues:
- - False Values
- - https://specs.frictionlessdata.io/table-schema/#boolean
- - Specifies which string values should be treated as false values.
-
-schema/fields/floatNumber:
- - Float Number
- - https://specs.frictionlessdata.io/table-schema/#number
- - It specifies that the value is a float number.
-
-schema/fields/decimalChar:
- - Float Number
- - https://specs.frictionlessdata.io/table-schema/#number
- - It specifies the char to be used as decimal character. The default value is "."
-
-schema/fields/minimum:
- - Float Minimum
- - https://specs.frictionlessdata.io/table-schema/#constraints
- - It specifies a minimum value for a field.
-
-schema/fields/maximum:
- - Float Maximum
- - https://specs.frictionlessdata.io/table-schema/#constraints
- - It specifies a maximum value for a field.
-
-schema/fields/enum:
- - Enum
- - https://specs.frictionlessdata.io/table-schema/#constraints
- - Each cell in this field must exactly match one of the specified values. Please provide comma separated list of values.
-
-schema/fields/required:
- - Required
- - https://specs.frictionlessdata.io/table-schema/#constraints
- - Indicates whether this field cannot be null.
-
-schema/fields/unique:
- - Unique
- - https://specs.frictionlessdata.io/table-schema/#constraints
- - Specifies all the values for that field MUST be unique.
-
-schema/fields/minLength:
- - Min Length
- - https://specs.frictionlessdata.io/table-schema/#constraints
- - An integer that specifies the minimum length of a value.
-
-schema/fields/maxLength:
- - Max Length
- - https://specs.frictionlessdata.io/table-schema/#constraints
- - An integer that specifies the maximum length of a value.
-
-schema/fields/pattern:
- - Max Length
- - https://specs.frictionlessdata.io/table-schema/#constraints
- - A regular expression that can be used to test field values.
-
-# Foreign Keys
-
-schema/foreignKeys:
- - Foreign Keys
- - https://specs.frictionlessdata.io/table-schema/#foreign-keys
- - A foreign key is a reference where values in a field (or fields) on the table described by this Table Schema connect to values a field (or fields) on this or a separate table
-
-schema/foreignKey/sourceField:
- - Source Field
- - https://specs.frictionlessdata.io/table-schema/#foreign-keys
- - Name of the field in this resource that form the source part of the foreign key.
-
-schema/foreignKey/targetField:
- - Target Field
- - https://specs.frictionlessdata.io/table-schema/#foreign-keys
- - Name of the referenced field in destination resource.
diff --git a/client/components/Editors/Schema/store.ts b/client/components/Editors/Schema/store.ts
index 15bfaced2..ed87456b5 100644
--- a/client/components/Editors/Schema/store.ts
+++ b/client/components/Editors/Schema/store.ts
@@ -9,9 +9,7 @@ import { SchemaProps } from './index'
import * as settings from '../../../settings'
import * as helpers from '../../../helpers'
import * as types from '../../../types'
-import help from './help.yaml'
-
-const DEFAULT_HELP_ITEM = helpers.readHelpItem(help, 'schema')!
+import { t } from 'i18next'
interface ISectionState {
query?: string
@@ -48,6 +46,8 @@ interface State {
}
export function makeStore(props: SchemaProps) {
+ const DEFAULT_HELP_ITEM = t('help-schema', { returnObjects: true }) as types.IHelpItem
+
return createStore((set, get) => ({
descriptor: props.schema || cloneDeep(settings.INITIAL_SCHEMA),
externalMenu: props.externalMenu,
@@ -59,7 +59,8 @@ export function makeStore(props: SchemaProps) {
set({ ...patch })
},
updateHelp: (path) => {
- const helpItem = helpers.readHelpItem(help, path) || DEFAULT_HELP_ITEM
+ let helpItem = t(`help-${path}`, { returnObjects: true }) as types.IHelpItem
+ if (typeof helpItem !== 'object') helpItem = DEFAULT_HELP_ITEM
set({ helpItem })
},
updateDescriptor: (patch) => {
diff --git a/client/components/Parts/Cards/Help.tsx b/client/components/Parts/Cards/Help.tsx
index aa9bfa1f6..63527e7df 100644
--- a/client/components/Parts/Cards/Help.tsx
+++ b/client/components/Parts/Cards/Help.tsx
@@ -7,6 +7,7 @@ import CardContent from '@mui/material/CardContent'
import iconInfoImg from '../../../assets/icon_info.png'
import CardActions from '@mui/material/CardActions'
import Button from '@mui/material/Button'
+import { useTranslation } from 'react-i18next'
// TODO: review geometry porps/logic
@@ -26,6 +27,7 @@ export default function HelpCard(props: React.PropsWithChildren)
}
export function HelpCardWithIcon(props: React.PropsWithChildren){
+ const { t } = useTranslation()
return (
){
{props.children}
- {' '}Learn More
+ {' '}{t('learn-more')}
@@ -49,6 +51,7 @@ export function HelpCardWithIcon(props: React.PropsWithChildren){
}
export function HelpCardNoIcon(props: React.PropsWithChildren){
+ const { t } = useTranslation()
return (
){
color="text.primary"
gutterBottom
>
- Help
+ {t('help')}
{props.title}
@@ -73,7 +76,7 @@ export function HelpCardNoIcon(props: React.PropsWithChildren){
diff --git a/client/components/Parts/Trees/File.tsx b/client/components/Parts/Trees/File.tsx
index a19e60d57..0667c9c81 100644
--- a/client/components/Parts/Trees/File.tsx
+++ b/client/components/Parts/Trees/File.tsx
@@ -247,7 +247,7 @@ const StyledTreeItem = styled(
primaryTypographyProps={{
color: (theme) => theme.palette.OKFNRed500.main,
}}
- primary={`${t('delete')} ${fileOrFolder}`} secondary={t('context-menu-delete-description')}
+ primary={`${t('delete-filefolder', { fileOrFolder })}`} secondary={t('context-menu-delete-description')}
/>
diff --git a/localization/i18n.config.js b/localization/i18n.config.js
index e61a92a0d..ca9d013dc 100644
--- a/localization/i18n.config.js
+++ b/localization/i18n.config.js
@@ -11,3 +11,7 @@ i18next
en: { main: locales.en },
}
})
+
+i18next.services.formatter.add('lowercase', (value, lng, options) => {
+ return value.toLowerCase();
+});
\ No newline at end of file
diff --git a/localization/locales/en.json b/localization/locales/en.json
index 7c23cb756..516aa6a46 100644
--- a/localization/locales/en.json
+++ b/localization/locales/en.json
@@ -3,6 +3,7 @@
"detect-errors-generate-report": "Detect errors and generate a report",
"save-download-work": "Save & download your work",
"welcome-to-ODE": "Welcome to the Open Data Editor!",
+ "alt-welcome-screen-image": "Welcome Screen Image",
"welcomebanner-description": "The ODE helps data practitioners with no coding skills to explore tabular data and detect errors in an easier way. Advanced users can also edit metadata and publish their work.
The OKFN team aims to add other data formats in the future.",
"link-check-blog": "Check our blog for updates",
"get-started": "Get Started",
@@ -15,11 +16,12 @@
"ok": "Ok",
"next": "Next",
"back": "Back",
+ "back-to-list": "Back to list",
"alt-open-file-location": "Open File Location Slide",
"open-file-location": "Open File Location",
"upload-your-data": "Upload your data",
"create-an-issue": "create an issue",
- "sharing-contents-if-possible": "sharing the file contents (if possible)",
+ "sharing-contents-if-possible": "sharing the file contents <1>(if possible)1>",
"ODE-supports-CSV-Excel-files" : "The ODE supports Excel & csv files",
"links-online-tables" : "You can also add links to online tables",
"create-folder": "Create folder",
@@ -41,10 +43,10 @@
"error-file-size-exceeds-10mb": "The total size of the files exceeds 10MB. This operation might take some time...",
"checking-errors": "Checking errors",
"rename": "Rename",
- "file": "file",
- "folder": "folder",
+ "file": "File",
+ "folder": "Folder",
"delete": "Delete",
- "open-fileorfolder-location": "Open {{fileOrFolder}} Location",
+ "open-fileorfolder-location": "Open {{fileOrFolder, lowercase}} Location",
"context-menu-delete-description":"Only removes this element from the ODE folder",
"context-menu-openlocation-description": "The ODE folder where this {{fileOrFolder}} exists",
"metadata": "Metadata",
@@ -68,12 +70,12 @@
"ODE-user-guide": "ODE User guide",
"name-new-folder": "Name of the new folder",
"save": "Save",
- "name-new-filefolder": "Name of new {{ fileOrFolder }}",
- "rename-filefolder": "Rename {{ fileOrFolder }}",
+ "name-new-filefolder": "Name of new {{ fileOrFolder, lowercase }}",
+ "rename-filefolder": "Rename {{ fileOrFolder, lowercase }}",
"no": "No",
- "delete-fileFolder": "Delete {{ fileOrFolder }}",
+ "delete-filefolder": "Delete {{ fileOrFolder, lowercase }}",
"are-you-sure-delete-elements": "Are you sure you want to delete these elements?",
- "are-you-sure-delete-filefolder": "Are you sure you want to delete this {{ fileOrFolder }}?",
+ "are-you-sure-delete-filefolder": "Are you sure you want to delete this {{ fileOrFolder, lowercase }}?",
"cancel": "Cancel",
"dialog": "Dialog",
"loading": "Loading",
@@ -91,6 +93,7 @@
"user": "User",
"repo": "Repo",
"email": "Email",
+ "email-not-valid": "Email is not valid.",
"description": "Description",
"author": "Author",
"failed-open-project": "Failed to open the project. Please",
@@ -98,7 +101,603 @@
"accept": "accept",
"enter-openAI-key": "Please enter your OpenAI API key:",
"open-AI-key": "OpenAI API Key",
+ "AI-assistant-default-prompt": "suggest improvements to the names of the columns in the table and provide descriptions for each of them",
"assistant-step-dialog": "If you proceed, the Open Data Editor will only share the names of the columns in your table to suggest improvements to the titles and descriptions associated with them. Do you want to proceed?",
"AI-assistant-enter-prompt": "Please enter your prompt to the AI assistant:",
- "AI-assistant": "AI Assistant"
+ "AI-assistant": "AI Assistant",
+ "alt-image-folder-dialog": "Image Folder Dialog",
+ "alt-icon-upload-file": "Icon-Upload File",
+ "resource": "Resource",
+ "integrity": "Integrity",
+ "licenses": "Licenses",
+ "add-license": "Add license",
+ "contributors": "Contributors",
+ "add-contributor": "Add contributor",
+ "sources": "Sources",
+ "add-source": "Add source",
+ "dialect": "Dialect",
+ "schema": "Schema",
+ "fields": "Fields",
+ "foreign-keys": "Foreign Keys",
+ "add-foreign-key": "Add foreign key",
+ "search": "Search",
+ "name": "Name",
+ "name-not-valid": "Name is not valid.",
+ "type": "Type",
+ "title": "Title",
+ "title-not-valid": "Title is not valid.",
+ "path": "Path",
+ "path-required": "Path is required",
+ "scheme": "Scheme",
+ "format": "Format",
+ "encoding": "Encoding",
+ "media-type": "Media type",
+ "hash": "Hash",
+ "bytes": "Bytes",
+ "rows": "Rows",
+ "select-license": "Select the license",
+ "type-to-search": "Type to search",
+ "remove": "Remove",
+ "role": "Role",
+ "comment-char": "Comment Char",
+ "comment-rows": "Comment Rows",
+ "header": "Header",
+ "header-rows": "Header rows",
+ "header-join": "Header join",
+ "header-case": "Header case",
+ "no-options-available-for-format": "No options available for this format",
+ "unknown": "Unknown",
+ "delimiter": "Delimiter",
+ "line-terminator": "Line Terminator",
+ "quote-char": "Quote Char",
+ "double-quote": "Double Quote",
+ "escape-char": "Escape Char",
+ "null-sequence": "Null Sequence",
+ "skip-initial-space": "Skip Initial Space",
+ "Keys": "Keys",
+ "keyed": "Keyed",
+ "property": "Property",
+ "sheet": "Sheet",
+ "sheet-not-valid": "Sheet is not valid.",
+ "fill-merged-cells": "Fill Merged Cells",
+ "preserve-formatting": "Preserve Formatting",
+ "adjust-floating-point-error" :"Adjust Floating Point Error",
+ "stringified": "Stringified",
+ "primary-key": "Primary Key",
+ "missing-values": "Missing Values",
+ "rdf-type": "RDF Type",
+ "array-item": "Array Item",
+ "true-values": "True Values",
+ "false-values": "False Values",
+ "bare-number": "Bare Number",
+ "float-number": "Float Number",
+ "decimal-char": "Decimal Char",
+ "group-char": "Group Char",
+ "required": "Required",
+ "minimum": "Minimum",
+ "minimum-not-valid": "Minimum value is not valid",
+ "maximum": "Maximum",
+ "maximum-not-valid": "Maximum value is not valid",
+ "min-length": "Min Length",
+ "max-length": "Max Length",
+ "pattern": "Pattern",
+ "enum": "Enum",
+ "source-field": "Source Field",
+ "target-field": "Target Field",
+ "target-resource": "Target Resource",
+ "learn-more": "Learn more",
+ "generating-response": "AI assistant is generating the response.",
+ "prompt-required": "Prompt is required",
+ "api-required": "API key is required",
+ "AI-assistant-find-your-key": "Click here to learn how to find your key. You can also check OpenAI terms and policies here.",
+ "help-resource": {
+ "path": "resource",
+ "title": "Resource",
+ "link" : "https://specs.frictionlessdata.io/data-resource/",
+ "description": "A simple format to describe and package a single data resource such as a individual table or file."
+ },
+ "help-resource/name": {
+ "path": "resource/name",
+ "title": "Name",
+ "link": "https://specs.frictionlessdata.io/data-resource/#name",
+ "description": "A simple name or identifier to be used for this resource. The name should be slugified e.g sales-data."
+ },
+ "help-resource/type": {
+ "path": "resource/type",
+ "title": "Type",
+ "link": "https://specs.frictionlessdata.io/data-resource/#metadata-properties",
+ "description": "Specifies the type of this resource."
+ },
+ "help-resource/title": {
+ "path": "resource/title",
+ "title": "Title",
+ "link": "https://specs.frictionlessdata.io/data-resource/#optional-properties",
+ "description": "A human-readable title or label for this resource e.g. 'Sales Data'."
+ },
+ "help-resource/description": {
+ "path": "resource/description",
+ "title": "Description",
+ "link": "https://specs.frictionlessdata.io/data-resource/#optional-properties",
+ "description": "A description of this resource. The description MUST be markdown formatted – this also allows for simple plain text as plain text is itself valid markdown."
+ },
+ "help-resource/mediaType": {
+ "path": "resource/mediaType",
+ "title": "Media Type",
+ "link": "https://specs.frictionlessdata.io/data-resource/#optional-properties",
+ "description": "Specifies the media type/mime type of this resource e.g 'text/csv', 'application/vnd.ms-excel' etc."
+ },
+ "help-resource/path": {
+ "path": "resource/path",
+ "title": "Path",
+ "link": "https://specs.frictionlessdata.io/data-resource/#path-data-in-files",
+ "description": "Specifies the path of this resource. It MUST either be a URL or a POSIX path."
+ },
+ "help-resource/scheme": {
+ "path": "resource/scheme",
+ "title": "Scheme",
+ "link": "https://specs.frictionlessdata.io/data-resource/#url-or-path",
+ "description": "Specifies the scheme for loading the file (file, http, ...)."
+ },
+ "help-resource/format": {
+ "path": "resource/format",
+ "title": "Format",
+ "link": "https://specs.frictionlessdata.io/data-resource/#optional-properties",
+ "description": "Specifies the standard file extension for this resource e.g. 'csv', 'xls', 'json' etc"
+ },
+ "help-resource/encoding" : {
+ "path": "resource/encoding",
+ "title": "Encoding",
+ "link": "https://specs.frictionlessdata.io/data-resource/#optional-properties",
+ "description": "Specifies the character encoding of this resource e.g. 'UTF-8'. The values should be one of the 'Preferred MIME Names' for a character encoding registered with IANA."
+ },
+ "help-resource/integrity" : {
+ "path": "resource/integrity",
+ "title": "Integrity",
+ "link": "https://specs.frictionlessdata.io/data-resource/#metadata-properties",
+ "description": "Checksum details of this resource."
+ },
+ "help-resource/integrity/hash" : {
+ "path": "resource/integrity/hash",
+ "title": "Hash",
+ "link": "https://specs.frictionlessdata.io/data-resource/#metadata-properties",
+ "description": "The MD5 hash for this resource."
+ },
+ "help-resource/integrity/bytes" : {
+ "path": "resource/integrity/bytes",
+ "title": "Bytes",
+ "link": "https://specs.frictionlessdata.io/data-resource/#metadata-properties",
+ "description": "Size of the resource file in bytes."
+ },
+ "help-resource/integrity/fields" : {
+ "path": "resource/integrity/fields",
+ "title": "Fields",
+ "link": "https://specs.frictionlessdata.io/data-resource/#metadata-properties",
+ "description": "Total fiels in this resource."
+ },
+ "help-resource/integrity/rows" : {
+ "path": "resource/integrity/rows",
+ "title": "Rows",
+ "link": "https://specs.frictionlessdata.io/data-resource/#metadata-properties",
+ "description": "Total rows in this resource."
+ },
+ "help-resource/licenses" : {
+ "path": "resource/licenses",
+ "title": "Licenses",
+ "link": "https://specs.frictionlessdata.io/data-package/#licenses",
+ "description": "The license(s) under which the resource is provided."
+ },
+ "help-resource/licenses/name" : {
+ "path": "resource/licenses/name",
+ "title": "Name",
+ "link": "https://specs.frictionlessdata.io/data-package/#licenses",
+ "description": "The name MUST be an Open Definition license ID e.g. ODC-BY-1.0"
+ },
+ "help-resource/licenses/path" : {
+ "path": "resource/licenses/path",
+ "title": "Path",
+ "link": "https://specs.frictionlessdata.io/data-package/#licenses",
+ "description": "A url-or-path string, that is a fully qualified HTTP address, or a relative POSIX path for this license."
+ },
+ "help-resource/licenses/title" : {
+ "path": "resource/licenses/title",
+ "title": "Title",
+ "link": "https://specs.frictionlessdata.io/data-package/#licenses",
+ "description": "A human-readable title or label for this license e.g. 'Open Data Commons Public Domain Dedication and License v1.0'."
+ },
+ "help-resource/contributors" : {
+ "path": "resource/contributors",
+ "title": "Contributors",
+ "link": "https://specs.frictionlessdata.io/data-package/#contributors",
+ "description": "A name/title of the contributor (name for person, name/title of organization)."
+ },
+ "help-resource/contributors/title" : {
+ "path": "resource/contributors/title",
+ "title": "Title",
+ "link": "https://specs.frictionlessdata.io/data-package/#contributors",
+ "description": "Title of the source (e.g. document or organization name)."
+ },
+ "help-resource/contributors/email" : {
+ "path": "resource/contributors/email",
+ "title": "Email",
+ "link": "https://specs.frictionlessdata.io/data-package/#contributors",
+ "description": "An email address"
+ },
+ "help-resource/contributors/path" : {
+ "path": "resource/contributors/path",
+ "title": "Path",
+ "link": "https://specs.frictionlessdata.io/data-package/#contributors",
+ "description": "A fully qualified http URL pointing to a relevant location online for the contributor."
+ },
+ "help-resource/contributors/role" : {
+ "path": "resource/contributors/role",
+ "title": "Role",
+ "link": "https://specs.frictionlessdata.io/data-package/#contributors",
+ "description": "A string describing the role of the contributor."
+ },
+ "help-resource/sources" : {
+ "path": "resource/sources",
+ "title": "Sources",
+ "link": "https://specs.frictionlessdata.io/data-package/#sources",
+ "description": "Raw sources for the data resource."
+ },
+ "help-resource/sources/title" : {
+ "path": "resource/sources/title",
+ "title": "Title",
+ "link": "https://specs.frictionlessdata.io/data-package/#sources",
+ "description": "Title of the source (e.g. document or organization name)"
+ },
+ "help-resource/sources/path" : {
+ "path": "resource/sources/path",
+ "title": "Path",
+ "link": "https://specs.frictionlessdata.io/data-package/#sources",
+ "description": "A url-or-path string, that is a fully qualified HTTP address, or a relative POSIX path."
+ },
+ "help-resource/sources/email" : {
+ "path": "resource/sources/email",
+ "title": "Email",
+ "link": "https://specs.frictionlessdata.io/data-package/#sources",
+ "description": "An email address."
+ },
+ "help-dialect": {
+ "path": "dialect",
+ "title": "Dialect",
+ "link": "https://framework.frictionlessdata.io/docs/framework/dialect.html",
+ "description": "File dialect concept give us an ability to manage table header and any details related to specific formats."
+ },
+ "help-dialect/title": {
+ "path": "dialect/title",
+ "title": "Title",
+ "link": "https://framework.frictionlessdata.io/docs/framework/dialect.html",
+ "description": "A human-readable title for this dialect."
+ },
+ "help-dialect/description": {
+ "path": "dialect/description",
+ "title": "Description",
+ "link": "https://framework.frictionlessdata.io/docs/framework/dialect.html",
+ "description": "A brief description of the dialect."
+ },
+ "help-dialect/type": {
+ "path": "dialect/type",
+ "title": "Type",
+ "link": "https://specs.frictionlessdata.io/csv-dialect/",
+ "description": "CSV Dialect defines a simple format to describe the various dialects of CSV files in a language agnostic manner."
+ },
+ "help-dialect/type/header": {
+ "path": "dialect/type/header",
+ "title": "Header",
+ "link": "https://framework.frictionlessdata.io/docs/framework/dialect.html#header",
+ "description": "It's a boolean flag which defaults to True indicating whether the data has a header row or not."
+ },
+ "help-dialect/type/headerRows": {
+ "path": "dialect/type/headerRows",
+ "title": "Header Rows",
+ "link": "https://framework.frictionlessdata.io/docs/framework/dialect.html#header-rows",
+ "description": "It specifies the header row or rows for multiline header."
+ },
+ "help-dialect/type/commentChar": {
+ "path": "dialect/type/commentChar",
+ "title": "Comment Char",
+ "link": "https://framework.frictionlessdata.io/docs/framework/dialect.html#comment-char",
+ "description": "It specifies the char to use to comment the rows."
+ },
+ "help-dialect/type/headerJoin": {
+ "path": "dialect/type/headerJoin",
+ "title": "Header Join",
+ "link": "https://framework.frictionlessdata.io/docs/framework/dialect.html#header-join",
+ "description": "It specifies the header rows to combine, if there are multiple header rows."
+ },
+ "help-dialect/type/commentRows": {
+ "path": "dialect/type/commentRows",
+ "title": "Comment Rows",
+ "link": "https://framework.frictionlessdata.io/docs/framework/dialect.html#comment-rows",
+ "description": "It specifies list of rows to ignore."
+ },
+ "help-dialect/type/headerCase": {
+ "path": "dialect/type/headerCase",
+ "title": "Header Case",
+ "link": "https://framework.frictionlessdata.io/docs/framework/dialect.html#header-case",
+ "description": "It specifies case sensitivity mode. Header is case sensitive by default."
+ },
+ "help-dialect/format": {
+ "path": "dialect/format",
+ "title": "Format",
+ "link": "https://specs.frictionlessdata.io/csv-dialect/",
+ "description": "CSV Dialect defines a simple format to describe the various dialects of CSV files in a language agnostic manner."
+ },
+ "help-dialect/format/delimiter": {
+ "path": "dialect/format/delimiter",
+ "title": "Delimiter",
+ "link": "https://specs.frictionlessdata.io/csv-dialect/#specification",
+ "description": "Specifies the character sequence which should separate fields. (default ',')"
+ },
+ "help-dialect/format/lineTerminator": {
+ "path": "dialect/format/lineTerminator",
+ "title": "Line Terminator",
+ "link": "https://specs.frictionlessdata.io/csv-dialect/#specification",
+ "description": "Specifies the line terminator for the csv file while reading/writing. (default '\r\n')"
+ },
+ "help-dialect/format/quoteChar": {
+ "path": "dialect/format/quoteChar",
+ "title": "Quote Char",
+ "link": "https://specs.frictionlessdata.io/csv-dialect/#specification",
+ "description": "Specifies a one-character string to use as the quoting character. (default '\"')"
+ },
+ "help-dialect/format/doubleQuote": {
+ "path": "dialect/format/doubleQuote",
+ "title": "Double Quote",
+ "link": "https://specs.frictionlessdata.io/csv-dialect/#specification",
+ "description": "Controls the handling of quotes inside fields. (default true)"
+ },
+ "help-dialect/format/escapeChar": {
+ "path": "dialect/format/escapeChar",
+ "title": "Escape Char",
+ "link": "https://specs.frictionlessdata.io/csv-dialect/#specification",
+ "description": "Specifies a one-character string to use for escaping."
+ },
+ "help-dialect/format/nullSequence": {
+ "path": "dialect/format/nullSequence",
+ "title": "Null Sequence",
+ "link": "https://specs.frictionlessdata.io/csv-dialect/#specification",
+ "description": "Specifies the null sequence."
+ },
+ "help-dialect/format/skipInitialSpace": {
+ "path": "dialect/format/skipInitialSpace",
+ "title": "Skip Initial Space",
+ "link": "https://specs.frictionlessdata.io/csv-dialect/#specification",
+ "description": "Specifies how to interpret whitespace which immediately follows a delimiter. (default false)"
+ },
+ "help-dialect/format/sheet": {
+ "path": "dialect/format/sheet",
+ "title": "Sheet",
+ "link": "https://framework.frictionlessdata.io/docs/formats/excel.html?query=excel#configuration",
+ "description": "Specifies name of the sheet from where to read or write data. (default 1)"
+ },
+ "help-dialect/format/fillMergedCells": {
+ "path": "dialect/format/fillMergedCells",
+ "title": "Fill Merged Cells",
+ "link": "https://framework.frictionlessdata.io/docs/formats/excel.html?query=excel#configuration",
+ "description": "Specifies to unmerge and fill all merged cells by the visible value. (default false)"
+ },
+ "help-dialect/format/preserveFormatting": {
+ "path": "dialect/format/preserveFormatting",
+ "title": "Preserve Formatting",
+ "link": "https://framework.frictionlessdata.io/docs/formats/excel.html?query=excel#configuration",
+ "description": "Specifies to preserve text formatting for numeric and temporal cells. (default false)"
+ },
+ "help-dialect/format/adjustFloatingPointError": {
+ "path": "dialect/format/adjustFloatingPointError",
+ "title": "Adjust Floating Point Error",
+ "link": "https://framework.frictionlessdata.io/docs/formats/excel.html?query=excel#configuration",
+ "description": "Specifies to ajust the Excel behavior regarding floating point numbers."
+ },
+ "help-dialect/format/stringified": {
+ "path": "dialect/format/stringified",
+ "title": "Stringified",
+ "link": "https://framework.frictionlessdata.io/docs/formats/excel.html?query=excel#configuration",
+ "description": "Specifies to stringify all cell values. (default false)"
+ },
+ "help-dialect/format/keys": {
+ "path": "dialect/format/keys",
+ "title": "keys",
+ "link": "https://framework.frictionlessdata.io/docs/formats/json.html?query=json#configuration",
+ "description": "Specifies the keys/columns to read from the json resource."
+ },
+ "help-dialect/format/keyed": {
+ "path": "dialect/format/keyed",
+ "title": "Keyed",
+ "link": "https://framework.frictionlessdata.io/docs/formats/json.html?query=json#configuration",
+ "description": "Specifies to return the data as 'key:value' pair. (default false)"
+ },
+ "help-dialect/format/property": {
+ "path": "dialect/format/property",
+ "title": "Property",
+ "link": "https://framework.frictionlessdata.io/docs/formats/json.html?query=json#configuration",
+ "description": "Specifies the path to the attribute in a json file, if it has nested fields."
+ },
+ "help-schema": {
+ "path": "schema",
+ "title": "Schema",
+ "link": "https://specs.frictionlessdata.io/table-schema/",
+ "description": "Table Schema is a specification for providing a schema for tabular data. It includes the expected data type for each value in a column."
+ },
+ "help-schema/title": {
+ "path": "schema/title",
+ "title": "Title",
+ "link": "https://specs.frictionlessdata.io/table-schema/",
+ "description": "A human-readable title."
+ },
+ "help-schema/description": {
+ "path": "schema/description",
+ "title": "Description",
+ "link": "https://specs.frictionlessdata.io/table-schema/",
+ "description": "A description of the schema. The description MUST be markdown formatted – this also allows for simple plain text as plain text is itself valid markdown."
+ },
+ "help-schema/primaryKey": {
+ "path": "schema/primaryKey",
+ "title": "Primary Key",
+ "link": "https://specs.frictionlessdata.io/table-schema/#primary-key",
+ "description": "A primary key is a field or set of fields that uniquely identifies each row in the table."
+ },
+ "help-schema/missingValues": {
+ "path": "schema/missingValues",
+ "title": "Missing Values",
+ "link": "https://specs.frictionlessdata.io/table-schema/#missing-values",
+ "description": "Many datasets arrive with missing data values, either because a value was not collected or it never existed."
+ },
+ "help-schema/fields": {
+ "path": "schema/fields",
+ "title": "Fields",
+ "link": "https://specs.frictionlessdata.io/table-schema/#descriptor",
+ "description": "Fields MUST be an array where each entry in the array is a field descriptor (as defined below)."
+ },
+ "help-schema/fields/name": {
+ "path": "schema/fields/name",
+ "title": "Name",
+ "link": "https://specs.frictionlessdata.io/table-schema/#name",
+ "description": "The field descriptor MUST contain a name property. This property SHOULD correspond to the name of field/column in the data file (if it has a name)"
+ },
+ "help-schema/fields/type": {
+ "path": "schema/fields/type",
+ "title": "Type",
+ "link": "https://specs.frictionlessdata.io/table-schema/#types-and-formats",
+ "description": "String indicating the type of this field."
+ },
+ "help-schema/fields/format": {
+ "path": "schema/fields/format",
+ "title": "Format",
+ "link": "https://specs.frictionlessdata.io/table-schema/#types-and-formats",
+ "description": "String indicating the format of this field."
+ },
+ "help-schema/fields/missingValues": {
+ "path": "schema/fields/missingValues",
+ "title": "Missing Values",
+ "link": "https://specs.frictionlessdata.io/table-schema/#missing-values",
+ "description": "Specifies which string values should be treated as null values."
+ },
+ "help-schema/fields/rdfType": {
+ "path": "schema/fields/rdfType",
+ "title": "RDF Type",
+ "link": "https://specs.frictionlessdata.io/table-schema/#rich-types",
+ "description": "Indicates whether the field is of RDF type."
+ },
+ "help-schema/fields/title": {
+ "path": "schema/fields/title",
+ "title": "Title",
+ "link": "https://specs.frictionlessdata.io/table-schema/#title",
+ "description": "A human-readable title."
+ },
+ "help-schema/fields/bareNumber": {
+ "path": "schema/fields/bareNumber",
+ "title": "Bare Number",
+ "link": "https://specs.frictionlessdata.io/table-schema/#types-and-formats",
+ "description": "A boolean field with a default of true."
+ },
+ "help-schema/fields/description": {
+ "path": "schema/fields/description",
+ "title": "Description",
+ "link": "https://specs.frictionlessdata.io/table-schema/#description",
+ "description": "A description of the field."
+ },
+ "help-schema/fields/groupChar": {
+ "path": "schema/fields/groupChar",
+ "title": "Group Char",
+ "link": "https://specs.frictionlessdata.io/table-schema/#types-and-formats",
+ "description": "A string whose value is used to group digits within the number."
+ },
+ "help-schema/fields/arrayItem": {
+ "path": "schema/fields/arrayItem",
+ "title": "Array Item",
+ "link": "https://specs.frictionlessdata.io/table-schema/#array",
+ "description": "A dictionary that specifies the type and other constraints for the data that will be read in this data type field."
+ },
+ "help-schema/fields/trueValues": {
+ "path": "schema/fields/trueValues",
+ "title": "True Values",
+ "link": "https://specs.frictionlessdata.io/table-schema/#boolean",
+ "description": "Specifies which string values should be treated as true values."
+ },
+ "help-schema/fields/falseValues": {
+ "path": "schema/fields/falseValues",
+ "title": "False Values",
+ "link": "https://specs.frictionlessdata.io/table-schema/#boolean",
+ "description": "Specifies which string values should be treated as false values."
+ },
+ "help-schema/fields/floatNumber": {
+ "path": "schema/fields/floatNumber",
+ "title": "Float Number",
+ "link": "https://specs.frictionlessdata.io/table-schema/#number",
+ "description": "It specifies that the value is a float number."
+ },
+ "help-schema/fields/decimalChar": {
+ "path": "schema/fields/decimalChar",
+ "title": "Float Number",
+ "link": "https://specs.frictionlessdata.io/table-schema/#number",
+ "description": "It specifies the char to be used as decimal character. The default value is '.'"
+ },
+ "help-schema/fields/minimum": {
+ "path": "schema/fields/minimum",
+ "title": "Float Minimum",
+ "link": "https://specs.frictionlessdata.io/table-schema/#constraints",
+ "description": "It specifies a minimum value for a field."
+ },
+ "help-schema/fields/maximum": {
+ "path": "schema/fields/maximum",
+ "title": "Float Maximum",
+ "link": "https://specs.frictionlessdata.io/table-schema/#constraints",
+ "description": "It specifies a maximum value for a field."
+ },
+ "help-schema/fields/enum": {
+ "path": "schema/fields/enum",
+ "title": "Enum",
+ "link": "https://specs.frictionlessdata.io/table-schema/#constraints",
+ "description": "Each cell in this field must exactly match one of the specified values. Please provide comma separated list of values."
+ },
+ "help-schema/fields/required": {
+ "path": "schema/fields/required",
+ "title": "Required",
+ "link": "https://specs.frictionlessdata.io/table-schema/#constraints",
+ "description": "Indicates whether this field cannot be null."
+ },
+ "help-schema/fields/unique": {
+ "path": "schema/fields/unique",
+ "title": "Unique",
+ "link": "https://specs.frictionlessdata.io/table-schema/#constraints",
+ "description": "Specifies all the values for that field MUST be unique."
+ },
+ "help-schema/fields/minLength": {
+ "path": "schema/fields/minLength",
+ "title": "Min Length",
+ "link": "https://specs.frictionlessdata.io/table-schema/#constraints",
+ "description": "An integer that specifies the minimum length of a value."
+ },
+ "help-schema/fields/maxLength": {
+ "path": "schema/fields/maxLength",
+ "title": "Max Length",
+ "link": "https://specs.frictionlessdata.io/table-schema/#constraints",
+ "description": "An integer that specifies the maximum length of a value."
+ },
+ "help-schema/fields/pattern": {
+ "path": "schema/fields/pattern",
+ "title": "Target Field",
+ "link": "https://specs.frictionlessdata.io/table-schema/#foreign-keys",
+ "description": "Name of the referenced field in destination resource."
+ },
+ "help-schema/foreignKeys": {
+ "path": "schema/foreignKeys",
+ "title": "Foreign Keys",
+ "link": "https://specs.frictionlessdata.io/table-schema/#foreign-keys",
+ "description": "A foreign key is a reference where values in a field (or fields) on the table described by this Table Schema connect to values a field (or fields) on this or a separate table"
+ },
+ "help-schema/foreignKey/sourceField": {
+ "path": "schema/foreignKey/sourceField",
+ "title": "Source Field",
+ "link": "https://specs.frictionlessdata.io/table-schema/#foreign-keys",
+ "description": "Name of the field in this resource that form the source part of the foreign key."
+ },
+ "help-schema/foreignKey/targetField": {
+ "path": "schema/foreignKey/targetField",
+ "title": "Max Length",
+ "link": "https://specs.frictionlessdata.io/table-schema/#constraints",
+ "description": "A regular expression that can be used to test field values."
+ }
}
\ No newline at end of file