diff --git a/src/component/multilingual/EditLanguageSelector.tsx b/src/component/multilingual/EditLanguageSelector.tsx index fd743a9e..dcc7d3ba 100644 --- a/src/component/multilingual/EditLanguageSelector.tsx +++ b/src/component/multilingual/EditLanguageSelector.tsx @@ -1,10 +1,8 @@ import * as React from "react"; -import ISO6391 from "iso-639-1"; import classNames from "classnames"; // @ts-ignore import { IntelligentTreeSelect } from "intelligent-tree-select"; -import Constants from "../../util/Constants"; -import { getShortLocale } from "../../util/IntlUtil"; +import { getLanguageOptions, Language } from "../../util/IntlUtil"; import { renderLanguages } from "./LanguageSelector"; import { Nav, NavItem, NavLink } from "reactstrap"; import { FaPlusCircle } from "react-icons/fa"; @@ -18,29 +16,6 @@ interface EditLanguageSelectorProps { onRemove: (lang: string) => void; } -interface Language { - code: string; - name: string; - nativeName: string; -} - -function prioritizeLanguages(options: Language[], languages: string[]) { - languages.forEach((lang) => { - const ind = options.findIndex((v) => v.code === lang); - const option = options[ind]; - options.splice(ind, 1); - options.unshift(option); - }); - return options; -} - -const OPTIONS = prioritizeLanguages( - ISO6391.getLanguages(ISO6391.getAllCodes()), - Object.getOwnPropertyNames(Constants.LANG).map((lang) => - getShortLocale(Constants.LANG[lang].locale) - ) -); - const EditLanguageSelector: React.FC = (props) => { const { language, existingLanguages, onSelect, onRemove } = props; const { i18n, formatMessage } = useI18n(); @@ -51,7 +26,7 @@ const EditLanguageSelector: React.FC = (props) => { if (existingLanguages.indexOf(language) === -1) { existingLanguages.push(language); } - const options = OPTIONS.slice(); + const options = getLanguageOptions().slice(); for (const existing of existingLanguages) { const toRemove = options.findIndex((o) => o.code === existing); options.splice(toRemove, 1); diff --git a/src/component/resource/document/Files.tsx b/src/component/resource/document/Files.tsx index f2a43430..11d523f4 100644 --- a/src/component/resource/document/Files.tsx +++ b/src/component/resource/document/Files.tsx @@ -1,7 +1,7 @@ import TermItFile from "../../../model/File"; import File from "../../../model/File"; import Utils from "../../../util/Utils"; -import { ButtonToolbar, Label, Table } from "reactstrap"; +import { Badge, ButtonToolbar, Label, Table } from "reactstrap"; import { useI18n } from "../../hook/useI18n"; interface FilesProps { @@ -20,7 +20,7 @@ const Files = (props: FilesProps) => { -
+
{files.length > 0 ? ( - +
{files.map((v: File) => ( - + - - - - - - - - - - - - - ); - } -} + return ( + + + + + setLabel(e.target.value)} + hint={i18n("required")} + /> + + + + + + + + + + + + + + + + + + + + ); +}; -export default injectIntl(withI18n(CreateFileMetadata)); +export default CreateFileMetadata; diff --git a/src/component/resource/file/LanguageSelector.tsx b/src/component/resource/file/LanguageSelector.tsx new file mode 100644 index 00000000..ea46c853 --- /dev/null +++ b/src/component/resource/file/LanguageSelector.tsx @@ -0,0 +1,33 @@ +import React from "react"; +// @ts-ignore +import { IntelligentTreeSelect } from "intelligent-tree-select"; +import { getLanguageOptions, Language } from "../../../util/IntlUtil"; +import { useI18n } from "../../hook/useI18n"; + +const LanguageSelector: React.FC<{ + onChange: (lang: string) => void; + value: string; +}> = ({ onChange, value }) => { + const options = getLanguageOptions(); + const { i18n } = useI18n(); + return ( + onChange(item.code)} + options={options} + maxHeight={200} + multi={false} + labelKey="nativeName" + valueKey="code" + classNamePrefix="react-select" + simpleTreeData={true} + renderAsTree={false} + showSettings={false} + isClearable={false} + placeholder="" + noResultsText={i18n("search.no-results")} + value={options.find((o) => o.code === value)} + /> + ); +}; + +export default LanguageSelector; diff --git a/src/component/resource/file/__tests__/CreateFileMetadata.test.tsx b/src/component/resource/file/__tests__/CreateFileMetadata.test.tsx index 6fa3e0f8..61fef673 100644 --- a/src/component/resource/file/__tests__/CreateFileMetadata.test.tsx +++ b/src/component/resource/file/__tests__/CreateFileMetadata.test.tsx @@ -1,11 +1,9 @@ import Resource from "../../../../model/Resource"; import Ajax from "../../../../util/Ajax"; -import { - flushPromises, - mountWithIntl, -} from "../../../../__tests__/environment/Environment"; -import { CreateFileMetadata } from "../CreateFileMetadata"; +import { mountWithIntl } from "../../../../__tests__/environment/Environment"; +import CreateFileMetadata from "../CreateFileMetadata"; import { intlFunctions } from "../../../../__tests__/environment/IntlUtil"; +import UploadFile from "../UploadFile"; jest.mock("../../../../util/Ajax", () => { const originalModule = jest.requireActual("../../../../util/Ajax"); @@ -43,9 +41,10 @@ describe("CreateFileMetadata", () => { {...intlFunctions()} /> ); - (wrapper.find(CreateFileMetadata).instance() as CreateFileMetadata).setFile( - file as File - ); + wrapper + .find(UploadFile) + .props() + .setFile(file as File); const labelInput = wrapper.find('input[name="create-resource-label"]'); expect((labelInput.getDOMNode() as HTMLInputElement).value).toEqual( fileName diff --git a/src/i18n/cs.ts b/src/i18n/cs.ts index ebeb3399..4da09f10 100644 --- a/src/i18n/cs.ts +++ b/src/i18n/cs.ts @@ -622,6 +622,7 @@ const cs = { "file.upload.hint": "Maximální velikost souboru: {maxUploadFileSize}. Má-li být soubor použit pro extrakci pojmů do slovníku, musí být ve formátu UTF-8, nebo validní MS Excel.", "file.upload.size.exceeded": "Soubor je příliš velký.", + "file.language": "Jazyk obsahu souboru", "dataset.license": "Licence", "dataset.format": "Formát", @@ -814,6 +815,11 @@ const cs = { 'Neplatný identifikátor: "{uri}", neočekávaný znak "{char}" na pozici {index}.', "error.invalidIdentifier": 'Neplatný identifikátor: "{uri}"', + "error.annotation.file.unsupportedLanguage": + "Služba textové analýza nepodporuje jazyk obsahu souboru.", + "error.annotation.term.unsupportedLanguage": + "Služba textové analýza nepodporuje jazyk definice pojmu.", + "history.label": "Historie změn", "history.loading": "Načítám historii...", "history.empty": "Zaznamenaná historie je prázdná.", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index ea573359..c20e4d70 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -613,6 +613,7 @@ const en = { "file.upload.hint": "Maximum file size: {maxUploadFileSize}. To use the file for term extraction, it must be in UTF-8 or a valid MS Excel file.", "file.upload.size.exceeded": "File is too large.", + "file.language": "File content language", "dataset.license": "License", "dataset.format": "Format", @@ -806,6 +807,11 @@ const en = { 'Invalid identifier: "{uri}", unexpected character "{char}" at {index}.', "error.invalidIdentifier": 'Invalid identifier: "{uri}"', + "error.annotation.file.unsupportedLanguage": + "Text analysis service does not support the language of this file.", + "error.annotation.term.unsupportedLanguage": + "Text analysis service does not support the language of this term's definition.", + "history.label": "Change history", "history.loading": "Loading history...", "history.empty": "The recorded history of this asset is empty.", diff --git a/src/model/File.ts b/src/model/File.ts index 6833cc17..16bad627 100644 --- a/src/model/File.ts +++ b/src/model/File.ts @@ -6,6 +6,7 @@ import VocabularyUtils from "../util/VocabularyUtils"; const ctx = { content: VocabularyUtils.CONTENT, owner: VocabularyUtils.IS_PART_OF_DOCUMENT, + language: VocabularyUtils.DC_LANGUAGE, }; /** @@ -18,18 +19,21 @@ export const OWN_CONTEXT = ctx; export interface FileData extends ResourceData { origin?: string; content?: string; + language?: string; owner?: DocumentData; } export default class File extends Resource implements FileData { public origin: string; public content?: string; + public language?: string; public owner?: DocumentData; constructor(data: FileData) { super(data); this.origin = data.origin ? data.origin : ""; this.content = data.content; + this.language = data.language; this.owner = data.owner; } diff --git a/src/util/IntlUtil.ts b/src/util/IntlUtil.ts index 3c5631dd..f3306787 100644 --- a/src/util/IntlUtil.ts +++ b/src/util/IntlUtil.ts @@ -2,6 +2,7 @@ import Constants from "./Constants"; import IntlData from "../model/IntlData"; import BrowserStorage from "./BrowserStorage"; import Utils from "./Utils"; +import ISO6391 from "iso-639-1"; export function loadInitialLocalizationData(): IntlData { const prefLang = BrowserStorage.get(Constants.STORAGE_LANG_KEY); @@ -88,3 +89,38 @@ export function removeTranslation( } }); } + +/** + * Type representing language data in an asset language selector. + */ +export interface Language { + code: string; + name: string; + nativeName: string; +} + +function prioritizeLanguages(options: Language[], languages: string[]) { + languages.forEach((lang) => { + const ind = options.findIndex((v) => v.code === lang); + const option = options[ind]; + options.splice(ind, 1); + options.unshift(option); + }); + return options; +} + +const LANGUAGE_OPTIONS = prioritizeLanguages( + ISO6391.getLanguages(ISO6391.getAllCodes()), + Object.getOwnPropertyNames(Constants.LANG).map((lang) => + getShortLocale(Constants.LANG[lang].locale) + ) +); + +/** + * Gets a list of all possible languages. + * + * The languages are retrieved using the iso-639-1 JS library. + */ +export function getLanguageOptions(): Language[] { + return LANGUAGE_OPTIONS; +}
{v.label} + {v.language && ( + + {v.language} + + )} + {v.label} + {props.itemActions(v)} diff --git a/src/component/resource/file/CreateFileMetadata.tsx b/src/component/resource/file/CreateFileMetadata.tsx index cbefd0d1..daf7de26 100644 --- a/src/component/resource/file/CreateFileMetadata.tsx +++ b/src/component/resource/file/CreateFileMetadata.tsx @@ -1,101 +1,105 @@ -import * as React from "react"; -import { injectIntl } from "react-intl"; -import withI18n, { HasI18n } from "../../hoc/withI18n"; -import { Button, ButtonToolbar, Col, Form, Row } from "reactstrap"; +import React from "react"; +import { + Button, + ButtonToolbar, + Col, + Form, + FormGroup, + Label, + Row, +} from "reactstrap"; import UploadFile from "./UploadFile"; import TermItFile from "../../../model/File"; import CustomInput from "../../misc/CustomInput"; -import { AssetData } from "../../../model/Asset"; +import { useI18n } from "../../hook/useI18n"; +import { useSelector } from "react-redux"; +import TermItState from "../../../model/TermItState"; +import LanguageSelector from "./LanguageSelector"; -interface CreateFileMetadataProps extends HasI18n { +interface CreateFileMetadataProps { onCreate: (termItFile: TermItFile, file: File) => any; onCancel: () => void; } -interface CreateFileMetadataState extends AssetData { - iri: string; - label: string; - file?: File; - dragActive: boolean; -} - -export class CreateFileMetadata extends React.Component< - CreateFileMetadataProps, - CreateFileMetadataState -> { - constructor(props: CreateFileMetadataProps) { - super(props); - this.state = { - iri: "", - label: "", - file: undefined, - dragActive: false, - }; - } +const CreateFileMetadata: React.FC = ({ + onCreate, + onCancel, +}) => { + const { i18n } = useI18n(); + const [label, setLabel] = React.useState(""); + const [file, setFile] = React.useState(); + const lang = useSelector( + (state: TermItState) => state.configuration.language + ); + const [language, setLanguage] = React.useState(lang); - protected onLabelChange = (e: React.ChangeEvent): void => { - const label = e.currentTarget.value; - this.setState({ label }); + const onFileSelected = (file: File) => { + setFile(file); + setLabel(file.name); }; - - public onCreate = () => { - const { file, dragActive, ...data } = this.state; + const onSubmit = () => { if (file) { - this.props.onCreate(new TermItFile(data), file); + onCreate( + new TermItFile({ + iri: "", + label, + language, + }), + file + ); } }; - - public setFile = (file: File) => { - this.setState({ file, label: file.name, dragActive: false }); - }; - - public cannotSubmit = () => { - return !this.state.file || this.state.label.trim().length === 0; + const cannotSubmit = () => { + return !file || label.trim().length === 0; }; - public render() { - const i18n = this.props.i18n; - - return ( -
- - -