From eab4fc9f81b2aed6db2a798288628f4b3af66b60 Mon Sep 17 00:00:00 2001 From: Oleksii Savran Date: Mon, 22 Oct 2018 14:48:34 +0300 Subject: [PATCH] I18n translated ContentHeader translated ContentHeader (fixed page title) fixed ContentHeader translated crops Translated partners added i18n for vocabulary module added i18n for datasets module added i18n for descriptor lists module added i18n for descriptors module added i18n for several common components added missing translations, added build script fix after rebase --- i18n/generateI18n.ts | 18 + locales/en/common.json | 111 +- locales/en/translations.json | 1093 +++++++++++++++-- package-lock.json | 227 ++-- package.json | 8 +- src/actions/userProfile.ts | 4 +- src/crops/translations.json | 37 + src/crops/ui/BrowsePage.tsx | 2 +- src/crops/ui/EditPage.tsx | 18 +- src/crops/ui/c/CropForm.tsx | 14 +- src/crops/ui/c/CropSelector.tsx | 2 +- src/datasets/routes.ts | 8 +- src/datasets/translations.json | 211 ++++ src/datasets/ui/BrowsePage.tsx | 4 +- src/datasets/ui/DisplayPage.tsx | 2 +- src/datasets/ui/c/Card.tsx | 26 +- src/datasets/ui/c/DatasetDisplay.tsx | 126 +- src/datasets/ui/c/Filters.tsx | 51 +- src/datasets/ui/c/LocationMap.tsx | 28 +- src/datasets/ui/dataset-stepper/index.tsx | 20 +- src/datasets/ui/dataset-stepper/steps.ts | 18 +- .../steps/accessions-list/ListOfAccesion.tsx | 31 +- .../steps/basic-info/BasicInfoForm.tsx | 36 +- .../steps/creators/DatasetCreatorForm.tsx | 31 +- .../dataset-stepper/steps/files/FilesForm.tsx | 111 +- .../steps/location/CountryCodePicker.tsx | 12 +- .../steps/location/FormMap.tsx | 8 +- .../steps/location/LocationForm.tsx | 216 ++-- .../steps/pasting-traits/index.tsx | 15 +- src/datasets/ui/search/SuggestionsPage.tsx | 7 +- src/datasets/ui/search/c/SuggestionsForm.tsx | 9 +- src/datasets/ui/search/c/hits/_Generic.tsx | 4 +- src/descriptorlists/routes.ts | 4 +- src/descriptorlists/translations.json | 96 ++ src/descriptorlists/ui/BrowsePage.tsx | 10 +- src/descriptorlists/ui/DisplayPage.tsx | 10 +- src/descriptorlists/ui/c/Card.tsx | 32 +- .../ui/c/DescriptorListDisplay.tsx | 144 ++- .../ui/c/DescriptorListForm.tsx | 42 +- src/descriptorlists/ui/c/Extras.tsx | 11 +- src/descriptorlists/ui/c/Filters.tsx | 22 +- .../ui/descriptorlist-stepper/index.tsx | 33 +- .../ui/descriptorlist-stepper/steps.ts | 10 +- .../steps/import/index.tsx | 21 +- src/descriptors/routes.ts | 4 +- src/descriptors/translations.json | 213 ++++ src/descriptors/ui/BrowsePage.tsx | 12 +- src/descriptors/ui/DisplayPage.tsx | 134 +- src/descriptors/ui/EditPage.tsx | 21 +- src/descriptors/ui/c/DashboardFilters.tsx | 21 +- src/descriptors/ui/c/DescriptorCard.tsx | 72 +- src/descriptors/ui/c/DescriptorFilterForm.tsx | 16 +- src/descriptors/ui/c/DescriptorForm.tsx | 109 +- src/descriptors/ui/c/DescriptorListPicker.tsx | 12 +- src/descriptors/ui/c/DescriptorParser.tsx | 19 +- src/descriptors/ui/c/DescriptorPicker.tsx | 21 +- src/descriptors/ui/c/DescriptorSearchMenu.tsx | 7 +- src/descriptors/ui/c/Filters.tsx | 35 +- src/descriptors/ui/c/SelectVocabulary.tsx | 8 +- src/descriptors/ui/c/VocabularyForm.tsx | 60 +- src/model/catalog/Dataset.ts | 16 +- src/model/catalog/Descriptor.ts | 46 +- src/model/catalog/DescriptorList.ts | 4 +- src/model/common.model.ts | 6 +- src/model/genesys/Partner.ts | 4 +- src/model/vocabulary/Vocabulary.ts | 8 +- src/partners/routes.ts | 4 +- src/partners/translations.json | 62 + src/partners/ui/BrowsePage.tsx | 4 +- src/partners/ui/DisplayPage.tsx | 18 +- src/partners/ui/c/Filters.tsx | 16 +- src/partners/ui/c/PartnerCard.tsx | 8 +- src/partners/ui/c/PartnerForm.tsx | 38 +- src/partners/ui/c/SearchMenu.tsx | 20 +- src/partners/ui/c/SelectPartner.tsx | 6 +- src/partners/ui/c/Summary.tsx | 14 +- src/translations.json | 543 ++++++++ src/ui/catalog/Links.tsx | 2 +- src/ui/catalog/Properties.tsx | 8 +- .../catalog/accession/AccessionRefsTable.tsx | 16 +- src/ui/catalog/dashboard/MyDataPage.tsx | 8 +- .../dashboard/c/DashboardActionsArea.tsx | 10 +- .../dashboard/c/DashboardActionsButton.tsx | 20 +- .../dashboard/c/DashboardTableHeader.tsx | 18 +- .../catalog/dashboard/c/DashboardTableRow.tsx | 21 +- src/ui/catalog/dashboard/c/Filters.tsx | 17 +- src/ui/catalog/dashboard/c/MyDataTable.tsx | 25 +- src/ui/catalog/dashboard/c/StatusFilter.tsx | 9 +- src/ui/catalog/markdown/index.tsx | 9 +- src/ui/common/ItemsEditor.tsx | 15 +- src/ui/common/Tabs.tsx | 7 +- src/ui/common/buttons/BackButton.tsx | 11 +- src/ui/common/checkbox/index.tsx | 8 +- .../csv-configuration/CSVConfiguration.tsx | 34 +- src/ui/common/filter/BooleanFilter.tsx | 22 +- .../filter/CollapsibleComponentSearch.tsx | 8 +- .../common/filter/ExpandFiltersComponent.tsx | 8 +- src/ui/common/filter/FiltersBlock.tsx | 14 +- src/ui/common/filter/NumberFilter.tsx | 20 +- src/ui/common/filter/PrettyFilters.tsx | 28 +- src/ui/common/filter/StringArrFilter.tsx | 2 +- src/ui/common/filter/TextFilter.tsx | 14 +- src/ui/common/forms/FormControl.tsx | 8 +- src/ui/common/forms/Toggle.tsx | 7 +- src/ui/common/heading/ContentHeader.tsx | 23 +- src/ui/common/not-found/index.tsx | 4 +- src/ui/common/pagination/index.tsx | 14 +- src/ui/common/permission/Permissions.tsx | 20 +- .../permission/PermissionsTableForm.tsx | 34 +- src/ui/common/snackbar/Snackbar.tsx | 27 +- src/ui/common/stepper/BottomSection.tsx | 7 +- src/ui/common/stepper/StepNavigation.tsx | 16 +- src/ui/common/stepper/TopSection.tsx | 15 +- src/ui/common/stepper/progress-menu/index.tsx | 10 +- src/ui/common/time/McpdDate.tsx | 12 +- src/ui/common/time/PrettyDate.tsx | 10 +- src/ui/layout/Footer/index.tsx | 21 +- src/ui/layout/Header/LeftMenu.tsx | 14 +- src/ui/layout/Header/UserMenuComponent.tsx | 8 +- src/ui/layout/Header/index.tsx | 6 +- src/ui/layout/sidebar/SidebarWrapper.tsx | 10 +- src/ui/pages/login/LoginPage.tsx | 10 +- src/ui/pages/login/c/LoginForm.tsx | 11 +- src/ui/pages/profile/ProfilePage.tsx | 20 +- src/ui/pages/profile/c/PasswordForm.tsx | 15 +- src/ui/pages/welcome/index.tsx | 14 +- src/ui/routes.tsx | 4 +- src/vocabulary/routes.ts | 4 +- src/vocabulary/translations.json | 49 + src/vocabulary/ui/BrowsePage.tsx | 14 +- src/vocabulary/ui/DisplayPage.tsx | 36 +- src/vocabulary/ui/EditPage.tsx | 12 +- src/vocabulary/ui/c/VocabularyCard.tsx | 2 +- src/vocabulary/ui/c/VocabularyForm.tsx | 53 +- 134 files changed, 4014 insertions(+), 1374 deletions(-) create mode 100644 i18n/generateI18n.ts create mode 100644 src/crops/translations.json create mode 100644 src/datasets/translations.json create mode 100644 src/descriptorlists/translations.json create mode 100644 src/descriptors/translations.json create mode 100644 src/partners/translations.json create mode 100644 src/translations.json create mode 100644 src/vocabulary/translations.json diff --git a/i18n/generateI18n.ts b/i18n/generateI18n.ts new file mode 100644 index 0000000..c118834 --- /dev/null +++ b/i18n/generateI18n.ts @@ -0,0 +1,18 @@ +const fg = require('fast-glob'); +const _ = require('lodash'); +const fs = require('fs'); + +const getPrefix = (path) => path.substring(path.indexOf('./src/') + './src/'.length, path.indexOf('/translations.json')); + +fg([`./src/**/translations.json`, `./src/translations.json`]) + .then((entries) => entries.sort((a, b) => getPrefix(a).localeCompare(getPrefix(b)))) + .then((entries) => entries.map((path) => getTranslations(path))) + .then((content) => fs.writeFileSync(`locales/en/translations.json`, `{\n${content}}`)); + +const getTranslations = (path) => { + const prefix = getPrefix(path); + console.log('Loading translations of module', prefix); + const fileContent = fs.readFileSync(path, 'utf8'); + + return prefix !== '/' ? _(`"${prefix}": ${fileContent}\n`).value() : fileContent.substr(1, fileContent.length - 2); +}; diff --git a/locales/en/common.json b/locales/en/common.json index be479a2..8e07e29 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -1,25 +1,130 @@ { "action": { "add": "Add {{what, lowercase}}", + "approve": "Approve", "backTo": "Back to {{where, lowercase}}", + "backToDashboard": "Back to dashboard", "cancel": "Cancel", + "close": "Close", + "collapse": "Collapse", "delete": "Delete", + "download": "Download", "edit": "Edit", "login": "Login", "logout": "Logout", + "publish": "Publish", + "saveChanges": "Save changes", "search": "Search", - "viewDetails": "View details" + "unpublish": "Unpublish", + "un-publish": "Un-publish", + "viewDetails": "View details", + "applyFilters": "Apply filters", + "back": "Back", + "backToList": "Back to list", + "create": "Create", + "deleteData": "Delete data", + "manage": "Manage", + "nextStep": "Next step", + "openSidebar": "Open sidebar", + "read": "Read", + "reject": "Reject", + "remove": "Remove", + "reset": "Reset", + "return": "Return", + "save": "Save", + "show": "Show", + "sendToReview": "Send to review", + "view": "View", + "write": "Write", + "acceptAndPublish": "ACCEPT AND PUBLISH" + }, + "csv": { + "autoDetect": "Auto-detect ", + "autoDetectLabel": "Auto-detect CSV settings", + "comma": "Comma", + "configuration": "CSV configuration", + "escapeCharacter": "Escape character", + "other": "Other", + "quoteCharacter": "Quote character", + "semicolon": "Semicolon", + "separator": "Separator character", + "space": "Space", + "tab": "Tab", + "typeYourEscapeCharacter": "Type your escape character", + "typeYourQuoteCharacter": "Type your quote character", + "typeYourSeparator": "Type your separator" + }, + "f": { + "dateSearch": "Date search", + "keyword": "Keyword search", + "fromIncluding": "From including", + "lastModifiedDate": "Last modified date", + "textSearch": "Text search" }, "label": { "item": "Item", "item_plural": "Items", "list": "List", - "list_plural": "Lists" + "list_plural": "Lists", + "created": "Created", + "dateNotProvided": "Date not provided", + "description": "Description", + "either": "Either", + "filters": "Filters", + "from": "From", + "itemEditorWarn": "Don't use the ItemsEditor for more than 100 items!", + "lastModified": "Last modified", + "lastUpdated": "Last updated", + "modified": "Modified", + "no": "No", + "prettyNumber": "{{value, number}}", + "registered": "Registered", + "sortBy": "Sort By", + "status": "Status", + "stepsForDataPublication": "Steps for data publication completion", + "title": "Title", + "to": "To", + "untitled": "Untitled", + "UUID": "UUID", + "version": "Version {{version, numeric}}", + "yes": "Yes", + "basicMarkdown": "Basic markdown supported", + "fullMarkdown": "Full markdown supported", + "previewMarkdown": "Preview markdown" }, "message": { "confirmDelete": "Deleting the {{what, lowercase}} record is only possible when there is no associated data." }, "paginate": { - "numberOfItems": "{{count}} {{what, lowercase}}" + "numberOfItems": "{{count}} {{what, lowercase}}", + "estimatedNumberOfItems": "About {{count, number}} {{what, lowercase}}" + }, + "permissions": { + "label": "Permissions", + "managePermissions": "Manage permissions", + "class": "Class", + "objectID": "Object ID", + "owner": "Owner", + "inheritsPermissions": "inheritsPermissions", + "effectivePermissions": "Effective permissions", + "sid": "Sid" + }, + "snackbar": { + "resettingFilters": "Resetting filters...", + "applyingFilters": "Applying filters..." + }, + "sort": { + "title": "Title", + "version": "Version", + "owner": "Owner", + "createdDate": "Created date", + "latestEdit": "Latest edit", + "lastModifiedDateAsc": "Last modified date (oldest first)", + "lastModifiedDateDesc": "Last update (newest first)" + }, + "status": { + "published": "Published", + "inReview": "In review", + "inProgress": "In progress" } } diff --git a/locales/en/translations.json b/locales/en/translations.json index 5736eab..2efae31 100644 --- a/locales/en/translations.json +++ b/locales/en/translations.json @@ -1,19 +1,120 @@ { - "Genesys Catalog": "Genesys Catalog", - "Not found": "Not found", - "Nothing matches your request": "Nothing matches your request", - "footer": { - "About Genesys": "About Genesys", - "Contact Us": "Contact us", - "Copyright policy": "Copyright policy", - "Disclaimer": "Disclaimer", - "Privacy policy": "Privacy policy", - "Report an issue": "Report an issue", - "Source code": "Source code", - "Terms and Conditions of Use": "Terms and Conditions of Use", - "Translate Genesys": "Translate Genesys", - "copyright": "© {{from}} - {{to}} Data providers and the Crop Trust" + + "common": { + "menu": { + "dashboard": "My Dashboard", + "profile": "My profile", + "home": "Home" + }, + "Not found": "Not found", + "Nothing matches your request": "Nothing matches your request", + "footer": { + "About Genesys": "About Genesys", + "Contact Us": "Contact us", + "Copyright policy": "Copyright policy", + "Disclaimer": "Disclaimer", + "Privacy policy": "Privacy policy", + "Report an issue": "Report an issue", + "Source code": "Source code", + "Terms and Conditions of Use": "Terms and Conditions of Use", + "Translate Genesys": "Translate Genesys", + "copyright": "© {{from}} - {{to}} Data providers and the Crop Trust" + } + }, + "dashboard": { + "action": { + "submitSelected": "Submit selected", + "unpublishSelected": "Unpublish selected", + "approveSelected": "Approve selected", + "deleteSelected": "Delete selected" + }, + "c": { + "topSection": { + "dataPublication": "Data publication", + "newInData": "New in the data publication process?", + "seeTour": "SEE GUIDED TOUR", + "catalog": "CATALOG" + }, + "bottomSection": { + "allFields": "* All fields marked with * are required." + } + }, + "f": { + "selectOwner": "Select owner" + }, + "crop": "Crop", + "owner": "Owner", + "modified": "Modified", + "status": "Status" + }, + "public": { + "prettyF": { + "NOT": "Excluding {{what}}", + "accessions": { + "crop": "Crop", + "acceNumb": "Accession number", + "seqNo": "Sequential number", + "sampStat": "Biological status", + "storage": "Storage", + "geo": { + "latitude": "Latitude", + "longitude": "Longitude", + "elevation": "Elevation" + }, + "holder": { + "code": "Holder", + "country": { + "iso3": "Holder country", + "region": "Holder region" + } + }, + "lastModifiedDate": { + "": "Last updated", + "ge": "Updated after", + "le": "Updated before" + }, + "taxa": { + "genus": "Genus", + "species": "Species", + "subtaxa": "Subtaxa" + }, + "origin": { + "iso3": "Origin" + }, + "taxonomy": { + "genus": "Genus", + "species": "Species", + "subtaxa": "Subtaxon" + }, + "sgsv": "Svalbard", + "mlsStatus": "MLS", + "available": "Available", + "historic": "Historic" + }, + "subsets": { + "title": "Title", + "crop": "Crop", + "institutes": "Institute code", + "description": "Description" + }, + "wiews": { + "code": "Institute code", + "accessions": "Accessions in Genesys", + "country": { + "iso3": "Country code" + } + }, + "users": { + "role": "Role", + "enabled": "Active", + "expired": "Expired", + "locked": "Locked", + "email": "E-mail address", + "uuid": "UUID" + } + } }, + "Genesys Catalog": "Genesys Catalog", "label": { "crop": "Crop", "crop_plural": "Crops", @@ -26,81 +127,23 @@ "partner": "Data provider", "partner_plural": "Data providers", "vocabulary": "Controlled vocabulary", - "vocabulary_plural": "Controlled vocabularies", - "metadata": "Record metadata" - }, - "m": { - "crop": { - "helper": "Contact helpdesk@genesys-pgr.org to register additional crops." - } - }, - "menu": { - "Accessions": "Accessions", - "Controlled vocabularies": "Controlled vocabularies", - "Crops": "Crops", - "Datasets": "Datasets", - "Descriptor lists": "Descriptor lists", - "Descriptors": "Descriptors", - "Home": "Home", - "My Dashboard": "My dashboard", - "My profile": "My profile", - "Partners": "Data providers", - "Data providers": "Data providers" + "vocabulary_plural": "Controlled vocabularies" }, "p": { - "crop": { - "subtitle": "List of crops registered with Genesys", - "title": "Crops" - }, "dashboard": { "title": "My Dashboard", "subtitle": "Manage data published on Genesys" }, - "dataset": { - "title": "Dataset details" - }, - "datasets": { - "title": "Datasets", - "subtitle": "Datasets published by Genesys data providers" - }, - "descriptorlists": { - "title": "Descriptor lists", - "subtitle": "Compilations of crop descriptors" - }, - "descriptors": { - "title": "Descriptor definitions", - "subtitle": "Genesys Catalog of published descriptor definitions" - }, - "partner": { - "address": "Address", - "countries": "Countries", - "email": "Contact email", - "phone": "Phone number", - "urls": "Websites", - "wiewsCodes": "FAO WIEWS codes" - }, - "partners": { - "title": "Genesys Data providers", - "subtitle": "Data providers in the Catalog of Phenotypic Datasets" - }, "userprofile": { "title": "User profile", "subtitle": "You can see and update your profile information here" }, - "vocab": { - "subtitle": "Commonly used controlled vocabularies", - "title": "Vocabularies" + "welcome": { + "title": "Genesys Catalog" }, - "editPartner": { - "name": { - "new": "New data provider", - "label": "Data provider name", - "placeholder": "Data provider name" - }, - "description": { - "placeholder": "Data provider introduction text (markdown)" - }, - "websites": "Data provider websites" + "login": { + "title": "Welcome to Genesys", + "subtitle": "Log in to manage datasets" } }, "stats": { @@ -145,8 +188,7 @@ "DIGITIZER": "Digitizes data.", "CURATOR": "Organizes and validates data and metadata in correct format, ensures quality of both." } - }, - "contact": "Data provider contact information" + } }, "search": { "group": { @@ -191,15 +233,6 @@ "name": "Name", "shortName": "Short name" }, - "partners" : { - "partners": "Data providers", - "acronym": "Acronym", - "title": "Title", - "description": "Description", - "ICRISAT": "ICRISAT", - "international": "International", - "partnerDescription": "Data provider description" - }, "descriptorList": { "_text": "Keywords", "description": "Description", @@ -228,7 +261,7 @@ "accessions": { "crops": "Crop name", "institute": { - "code" : "Institute code" + "code": "Institute code" }, "orgCty": { "iso3": "ORIGCTY" @@ -292,12 +325,888 @@ "selectPartner": { "label": { "Data provider": "Data provider", - "Partner": "Data provider", "Select partner": "Select data provider", "Select Partner": "Select data provider", "Select owner": "Select owner" + } + } + }, + "accessions": { + "common": { + "modelName": "Accession", + "modelName_plural": "Accessions", + "menu": "Accessions", + "stats": "Accession record", + "stats_plural": "Accession records", + "acceNumb": "Accession number", + "countryOfOrigin": "Country of origin", + "instituteCode": "Institute code", + "taxonomy": "Taxonomy", + "genus": "Genus", + "species": "Species", + "doi": "DOI", + "sampStat": "Biological status of accession", + "storageType": "Type of germplasm storage", + "alias": { + "OTHERNUMB": "Other identifier", + "ACCENAME": "Accession name", + "DONORNUMB": "Donor accession number", + "COLLNUMB": "Collecting number" + }, + "available": { + "true": "Available for distribution", + "false": "Not available for distribution", + "null": "Availability not provided" + }, + "mlsStatus": { + "true": "Accession is part of the Multi-lateral system of ITPGRFA", + "false": "Not declared in the Multi-lateral system of ITPGRFA ", + "null": "Status not provided" + }, + "coll": { + "collCode": "Collecting institute code", + "collNumb": "Collecting number", + "collDate": "Collecting date of sample", + "collMissId": "Collecting mission identifier", + "collName": "Collecting institute name", + "collSite": "Location of collecting site", + "collSrc": "Collecting source" + }, + "geo": { + "latitude": "Latitude of collecting site", + "longitude": "Longitude of collecting site", + "uncertainty": "Coordinate uncertainty", + "datum": "Geodetic datum", + "method": "Georeferencing method", + "elevation": "Elevation of collecting site" + }, + "storage": { + "10": "Seed collection", + "11": "Short term seed collection", + "12": "Medium term seed collection", + "13": "Long term seed collection", + "20": "Field collection", + "30": "In vitro collection", + "40": "Cryopreserved collection", + "50": "DNA collection", + "99": "Other" }, - "select": "Select data provider" + "sampleStatus": { + "100": "Wild", + "110": "Natural", + "120": "Semi-natural/wild", + "130": "Semi-natural/sown", + "200": "Weedy", + "300": "Traditional cultivar/Landrace", + "400": "Breeding/Research Material", + "410": "Breeders Line", + "411": "Synthetic population", + "412": "Hybrid", + "413": "Founder stock/base population", + "414": "Inbred line", + "415": "Segregating population", + "416": "Clonal selection", + "420": "Genetic stock", + "421": "Mutant", + "422": "Cytogenetic stocks", + "423": "Other genetic stocks", + "500": "Advanced/improved cultivar", + "600": "GMO", + "999": "Other" + }, + "overview": { + "institute code": "Holding Institute", + "institute country code3": "Country of holding institute", + "crop shortName": "Crop", + "cropName": "Provided crop name", + "sampStat": "Biological status of accession", + "taxonomy genus": "Genus", + "taxonomy genusSpecies": "Species", + "countryOfOrigin code3": "Country of Origin", + "donorCode": "FAO WIEWS code of donor institute", + "mlsStatus": "ITGPRFA Multi-lateral system", + "available": "Available for distribution", + "duplSite": "Site of safety duplication", + "breederCode": "Breeder code", + "sgsv": "Safety duplicated in Svalbard", + "storage": "Type of Germplasm storage" + } } + }, + "crop": { + "admin": { + "c": { + "cropForm": { + "cropCode": "Crop code", + "cropTitle": "Crop title", + "back": "Back to crop list" + } + }, + "p": { + "edit": { + "onDelete": { + "message": "Delete crop '{{cropName, lowercase}}'?", + "description": "Note, deleting any crop causes mayhem." + } + } + } + }, + "common": { + "modelName": "Crop", + "modelName_plural": "Crops", + "subtitle": "List of crops registered with Genesys", + "title": "Crops" + }, + "public": { + "c": { + "cropSelector": { + "helper": "Contact helpdesk@genesys-pgr.org to register additional crops." + } + }, + "p": { + "browse": { + "title": "What do you want to do?" + } + } + } + }, + "institutes": { + "common": { + "modelName": "Institute", + "modelName_plural": "Institutes", + "menu": "Institutes", + "stats": "Institute", + "stats_plural": "Institutes", + "instCode": "INSTCODE", + "instituteCode": "Institute code", + "accessionsInGenesys": "Accessions in Genesys" + } + }, + "user": { + "dashboard": { + "c": { + "passwordForm": { + "newPassword": "New password", + "oldPassword": "Old password", + "confirmPassword": "Confirm password", + "changePassword": "Change password" + } + }, + "p": { + "profile": { + "accountExpires": "Account expires", + "passwordChange": "Password change" + } + } + }, + "common": { + "email": "Email", + "emailAddress": "E-mail address", + "name": "Name", + "fullName": "Full name", + "surname": "Surname", + "country": "Country", + "password": "Password", + "passChangeSuccess": "Password was changed successfully", + "passChangeError": "Password change error", + "phone": "Phone", + "statusLabel": "Status", + "status": { + "locked": "Locked", + "enabled": "Active", + "expired": "Expired" + }, + "roleLabel": "User roles", + "role": { + "USER": "User", + "ADMINISTRATOR": "Administrator", + "EVERYONE": "Everyone", + "VALIDATEDUSER": "Validated user", + "VETTEDUSER": "Vetted user", + "CONTENTMANAGER": "Content manager" + }, + "typeLabel": "Account type", + "type": { + "local": "LOCAL", + "google": "GOOGLE", + "system": "SYSTEM", + "deleted": "DELETED", + "ldap": "LDAP" + }, + "username": "Username" + } + }, + "geo": { + "common": { + "address": "Address" + } + } +,"crops": { + "admin": { + "c": { + "cropForm": { + "cropCode": "Crop code", + "cropTitle": "Crop title", + "back": "Back to crop list" + } + }, + "p": { + "edit": { + "onDelete": { + "message": "Delete crop '{{cropName, lowercase}}'?", + "description": "Note, deleting any crop causes mayhem." + } + } + } + }, + "common": { + "modelName": "Crop", + "modelName_plural": "Crops", + "subtitle": "List of crops registered with Genesys", + "title": "Crops" + }, + "public": { + "c": { + "cropSelector": { + "helper": "Contact helpdesk@genesys-pgr.org to register additional crops." + } + }, + "p": { + "browse": { + "title": "What do you want to do?" + } + } + } +} +,"datasets": { + "common": { + "delete": "Deleting the dataset will remove all related data.", + "modelName": "Dataset", + "modelName_plural": "Datasets", + "menu": "Datasets", + "creator": { + "role": { + "MANAGER": "Data manager", + "COLLECTOR": "Data collector", + "DIGITIZER": "Data digitizer", + "CURATOR": "Data curator", + "null": "Not specified" + } + } + }, + "dashboard": { + "p": { + "stepper": { + "basicInfo": { + "title": "Basic information", + "titleField": { + "label": "Dataset title", + "placeholder": "Name given to the dataset (e.g., Characterization of maize accessions in Kenya)" + }, + "partner": { + "label": "Select Data provider", + "placeholder": "Data provider" + }, + "version": { + "label": "Dataset version" + }, + "description": { + "label": "Dataset description", + "placeholder": "An abstract, short or long description of the dataset. Descriptive details improves discoverability of the resource." + }, + "created": { + "label": "Date of creation of the dataset", + "placeholder": "YYYYMMDD. If the month or day are missing, this should be indicated with ‘00’ [double zero] (e.g. 1975----, 19750000; 197506--, 19750600)" + }, + "language": { + "label": "Language", + "placeholder": "Select language of the dataset" + }, + "source": { + "label": "Source", + "placeholder": "A related resource from which the described dataset is derived (e.g., journal article, data article, conference proceedings). Use DOI, URL, Journal title, or Journal/conference title; vol., no. (year)" + }, + "crops": { + "label": "Crops" + } + }, + "attachments": { + "title": "Dataset attachments", + "name": "File name", + "label": "File label", + "description": "Description of file content", + "upload": "Upload file", + "datasetFile": "Dataset file" + }, + "creators": { + "role": "Role", + "title": "Dataset creators", + "name": "Full name", + "namePlaceholder": "Jane A. Doe", + "instAffiliation": "Institutional affiliation", + "email": "Email address", + "phone": "Phone number", + "fax": "Fax", + "phonePlaceholder": "+1 555 1231 Ext. 42", + "instAddress": "Institutional address", + "instAddressPlaceholder": "Address of institution of affiliation when the dataset was created.", + "add": "Add dataset creator" + }, + "location": { + "title": "Location and timing", + "search": "Search...", + "copyright": "{{openingTag}}OpenStreetMap{{closingTag}} contributors", + "add": "Add another location", + "iso": "ISO3 Country code", + "isoPlaceholder": "GBR", + "description": "Description of environment and conditions at the site of evaluation", + "endingDate": "Ending date of characterization/evaluation", + "datePlaceholder": "YYYYMMDD. If the month or day are missing, this should be indicated with ‘00’ [double zero] (e.g. 1975----, 19750000; 197506--, 19750600)", + "startingDate": "Starting date of characterization/evaluation", + "longitude": "Decimal longitude", + "longitudePlaceholder": "The longitude of the site where the characterization/evaluation was conducted (decimal degrees).", + "latitude": "Decimal latitude", + "latitudePlaceholder": "The latitude of the site where the characterization/evaluation was conducted (decimal degrees).", + "locality": "Locality", + "localityPlaceholder": "Description of the locality where the characterization/evaluation was performed.", + "state": "State", + "country": "Country", + "countryPlaceholder": "Select country where the characterization/evaluation was performed." + }, + "listOfAccessions": { + "title": "List of accessions", + "instructions": "INSTRUCTIONS FOR USE", + "useSheet": "Use the \"Acccessions\" sheet from the template", + "template": "Genesys Catalog template", + "rowsDescr": "The first row in the template contains the header names, whilst the second contains a description of the content expected on each column.", + "dontChangeNames": "Do not change the header names in the template", + "templateInstruction": "Fill the template with the informations of the accessions, remove the row that contains the description of each column (second row) and save the file.", + "excel": "Copy and paste the table from Excel into the text field \"List of accessions described in the dataset\".", + "listDescribed": "List of accessions described in the dataset", + "placeholder": "Paste accessions data here (comma separated)", + "rowCount": "Accessions {{row, numeric}} rows" + }, + "pastingTraits": { + "title": "Select descriptors in batch", + "matched": "Matched", + "noDescriptors": "No matching descriptors" + }, + "traits": "Browse and select descriptors", + "reorder": "Reorder descriptors", + "review": "Review and publish", + "subtitle": "Publish your datasets", + "publisher": "Phenotypic dataset publisher" + } + } + }, + "public": { + "common": { + "accessionsNumber": "Number of accessions", + "crop": "Crop", + "traitsNumber": "Number of traits" + }, + "c": { + "card": { + "evaluationPeriod": "Evaluation period" + }, + "datasetDisplay": { + "accessionsEvaluated": "Accessions evaluated", + "contact": "Data provider contact information", + "creators": "Dataset creators", + "dataAndResources": "Data and resources", + "datasetFile": "Dataset file", + "datasetMetadata": "Dataset metadata", + "otherMetadata": "Other Metadata", + "locations": "Locations", + "traitsObserved": "Traits observed", + "useAndLicensing": "Dataset use and licensing", + "confirmDescription": { + "approve": "After approving the dataset no changes are permitted, a new version must be created.", + "onPublish": "The dataset will be reviewing by administrator before publishing." + }, + "properties": { + "datasetDate": "Date of dataset", + "email": "Email", + "evaluationEnd": "End of evaluation", + "evaluationStart": "Start of evaluation", + "licensed": "Licensed under", + "metadataCreateDate": "Metadata create date", + "metadataUpdatedDate": "Metadata updated date", + "moreInformation": "More information", + "relatedResources": "Related resources", + "phone": "Phone number", + "address": "Address" + } + }, + "locationMap": { + "dateOfEvaluation": "Date of evaluation", + "description": "Description", + "iso": "ISO3 country code", + "location": "Location", + "link": "{{aOpen}}OpenStreetMap{{aClose}} contributors" + }, + "suggestionsForm": { + "matches": "Suggested matches", + "apply": "Apply filters" + }, + "generic": "Type {{boldHit}} does not have a SearchHit implementation." + }, + "f": { + "traitKeywords": "Trait keywords", + "rice": "mardi rice", + "partner": "Data provider", + "faoWiews": "FAO WIEWS Institute code", + "evaluationSite": "Evaluation site", + "countryEv": "Country evaluation", + "countryPlaceholder": "Germany", + "latitude": "Latitude", + "longitude": "Longitude", + "crop": "Crop", + "accession": "Accessions", + "genus": "Genus", + "genusPlaceholder": "Manihot", + "accNumber": "Accession number", + "accDoi": "Accession DOI", + "licence": "Licence", + "rights": "rights", + "keywordSearch": "Keyword search" + }, + "p": { + "browse": { + "subtitle": "Datasets published by Genesys data providers" + }, + "display": { + "title": "Dataset details" + } + } + }, + "sort": { + "accessionCountAsc": "Accession count (low to high)", + "accessionCountDesc": "Accession count (high to low)", + "descriptorCountAsc": "Descriptor count (low to high)", + "descriptorCountDesc": "Descriptor count (high to low)", + "startDate": "Experiment start date", + "endDate": "Experiment end date" + } +} +,"descriptorlists": { + "common": { + "modelName": "Descriptor list", + "modelName_plural": "Descriptor lists", + "menu": "Descriptor lists", + "subtitle": "Compilations of crop descriptors" + }, + "public": { + "common": { + "crop": "Crop", + "publisher": "Publisher", + "version": "Version", + "url": "URL", + "partner": "Data provider" + }, + "c": { + "card": { + "untitled": "Untitled" + }, + "descrListsDisplay": { + "publishDescription": "The descriptor list will be reviewed by administrator before publishing.", + "approveDescription": "After approving the descriptor list no changes are permitted, a new version must be created.", + "deleteDescription": "Deleting the descriptor is only possible when there is no associated data.", + "createDescription": "Creating the new descriptor version will replace current descriptor from the descriptor list.", + "create": "Create new version", + "createText": "Create a new version of the descriptor", + "createNew": "Create new version", + "notPublishedAlert": "This descriptor list is not yet published!", + "citation": "Bibliographic citation", + "maintained": "Maintained by", + "table": "Table of contents", + "notSpecified": "Not specified" + }, + "form": { + "selectPartner": "Select data provider", + "you": "You're", + "admin": "ADMINISTRATOR", + "get": "and you get to pick the", + "ifYou": "if you want to", + "text": "Leave field blank to have it automatically assigned. Standard codes used by the Catalog", + "descriptors": "Genesys PGR descriptors", + "listTitle": "Descriptor list title", + "maize": "maize", + "titlePlaceholder": "Descriptors for pearl millet", + "description": "Description", + "descriptionPlaceholder": "Full description of the descriptor list", + "urlToDef": "URL to definition", + "publisher": "Original publisher", + "citation": "Bibliographic citation", + "citationPlaceholder": "Complete bibliographic citation of the crop descriptor. Use APA formatting style (https://owl.english.purdue.edu/owl/resource/560/01/)" + }, + "extras": { + "json": "JSON mappings", + "save": "Save extras", + "defaults": "Defaults" + } + }, + "f": { + "title": "Crop descriptors", + "keywordSearch": "Keyword search", + "rice": "mardi rice", + "originalPublisher": "Original publisher" + + }, + "p": { + "display": { + "title": "Descriptor list details" + }, + "browse": { + "subtitle": "Compilations of crop descriptors" + } + } + }, + "dashboard": { + "p": { + "stepper": { + "basicInfo": "Basic information", + "import": { + "title": "Upload new descriptors", + "uploaded": "Uploaded {{count}} descriptor definitions", + "noDescriptors": "No descriptors uploaded", + "error": "error", + "error_plural": "errors", + "uploading": "due uploading" + }, + "select": "Select existing descriptors", + "reorder": "Reorder descriptors", + "review": "Review and publish", + "publishDescription": "The descriptor list will be reviewed by the administrator before publishing.", + "deleteDescription": "Deleting the descriptor list will remove all related data.", + "publisher": "Descriptor list publisher", + "subtitle": "Publish your descriptor list" + } + } + } +} +,"descriptors": { + "common": { + "modelName": "Descriptor", + "modelName_plural": "Descriptors", + "title": "Descriptor definitions", + "subtitle": "Genesys Catalog of published descriptor definitions", + "category": { + "passport": "Passport descriptor", + "management": "Management descriptor", + "environment": "Environment and Site descriptor", + "characterization": "Characterization descriptor", + "evaluation": "Evaluation descriptor", + "abioticstress": "Abiotic stress descriptor", + "bioticstress": "Biotic stress descriptor", + "molecular": "Molecular marker descriptor" + }, + "dataType": { + "numeric": "Numeric", + "text": "Free text", + "scale": "Scale", + "coded": "Coded", + "boolean": "Yes/no", + "date": "Date" + }, + "crop": { + "null": "undefined", + "maize": "Maize", + "yam": "Yam" + } + }, + "admin": { + "p": { + "edit": { + "title": "Edit descriptor", + "dataPublication": "Data publication", + "back": "BACK TO DASHBOARD" + } + }, + "c": { + "form": { + "dataType": "Data type", + "code": "Code", + "title": "Title", + "description": "Description", + "descriptorCategory": "Descriptor category", + "owner": "Owner of the descriptor", + "partner": "Data provider", + "crop": "Crop", + "descriptorTitle": "Descriptor title", + "color": "Color of magic", + "version": "Version", + "publisher": "Original publisher", + "fullDescription": "Full description of the descriptor", + "key": "Flag as key access and utilization descriptor as defined in Alercia (2011)", + "column": "Column name", + "columnDescription": "Common code name for descriptor in databases and spreadsheets.", + "unit": "Unit of measure", + "unitPlaceholder": "kg", + "citation": "Bibliographic citation", + "citationPlaceholder": "Complete bibliographic citation of the crop descriptor. Use APA formatting style (https://owl.english.purdue.edu/owl/resource/560/01/).", + "integers": "Allow only integers, no decimals", + "min": "Minimum value", + "minPlaceholder": "Minimum numeric value allowed by the descriptor.", + "max": "Maximum value", + "maxPlaceholder": "Maximum value allowed by the descriptor.", + "term": "Vocabulary term", + "term_plural": "Vocabulary terms", + "vocabulary": "Controlled vocabulary", + "approvePublish": "Approve and Publish" + }, + "selectVocabulary": { + "rename": "Rename this vocabulary" + }, + "vocabularyForm": { + "code": "Code", + "title": "Title", + "description": "Description", + "vocabularyDetails": "Vocabulary details", + "vocabularyTitle": "Vocabulary title", + "color": "Color of magic", + "descriptionPlaceholder": "Full description of the vocabulary", + "versionTag": "Version tag", + "urlToDef": "URL to definition", + "termUrlPrefix": "Term URL prefix", + "vocabularyTerm": "Vocabulary terms", + "vocabularyTerm_plural": "Vocabulary terms" + } + } + }, + "dashboard": { + "f": { + "title": "Filters", + "status": "Status", + "descriptorInUse": "Descriptor is in use", + "keywordSearch": "Keyword search", + "rice": "mardi rice", + "selectOwner": "Select owner", + "crop": "Crop" + }, + "c": { + "listPicker": { + "search": "Search..." + }, + "parser": { + "instructions": "INSTRUCTIONS FOR USE", + "firstItem": "Use \"Descriptors\" sheet from template", + "link": "Genesys Catalog template", + "secondItem": "The first row in the template contains the header names, the second row contains a description of the content expected on each column, and rows three and four contain examples to help you fill in the template.", + "thirdItem": "Do not change the header names in the template", + "fourthItem": "Fill the template with the descriptor information, remove the rows that contain the description of each column (second row) and examples (third and fourth row) and save the file.", + "fifthItem": "Copy and paste the table from Excel into the text field \"Descriptor definitions\".", + "paste": "Paste descriptor data here (tab separated)" + }, + "picker": { + "available": "descriptors available", + "addAll": "Add all", + "removeAll": "Remove all", + "selected": "selected", + "zero": "0 descriptors selected:", + "descriptor": "descriptor", + "descriptor_plural": "descriptors" + } + } + }, + "public": { + "c": { + "card": { + "untitled": "Untitled", + "keyDescriptor": "This is a key descriptor for access and utilization of PGR.", + "publisher": "Publisher", + "maintained": "Maintained by", + "version": "Version", + "cropName": "Crop", + "classification": "Classification", + "type": "Data type", + "notSpecified": "Not specified", + "select": "+ SELECT DESCRIPTOR", + "unselect": "UNSELECT" + }, + "descriptorFilterForm": { + "apply": "Apply", + "reset": "Reset" + }, + "searchMenu": { + "search": "Search" + } + }, + "f": { + "keyword": "Keyword search", + "rice": "mardi rice", + "partner": "Data provider", + "select": "Select descriptor list", + "crop": "Crop", + "category": "Descriptor category", + "tidbits": "Tidbits", + "isUsed": "Descriptor is used", + "key": "Key crop descriptor", + "columnName": "Column name", + "uom": "UOM", + "language": "Language" + }, + "p": { + "browse": { + "title": "Descriptor definitions", + "subtitle": "Genesys Catalog of published descriptor definitions" + }, + "display": { + "publishDescription": "The descriptor will be reviewed by the administrator before publishing.", + "approveDescription": "After approving the descriptor no changes are permitted, a new version must be created.", + "deleteDescription": "Deleting the descriptor is only possible when there is no associated data.", + "title": "Descriptor details", + "isNotPublishedAlert": "This descriptor is not yet published!", + "keyAlert": "This descriptor is a key descriptor for access and utilization of PGR.", + "crop": "Crop", + "version": "Version", + "category": "Category", + "maintainedBy": "Maintained by", + "definition": "Descriptor definition", + "type": "Descriptor type", + "databaseName": "Database name", + "min": "Minimum allowed value", + "max": "Maximum allowed value", + "scale": "Scale", + "allowedValues": "Allowed values:", + "integers": "Integers", + "decimals": "Decimals", + "codingTable": "Descriptor coding table", + "code": "Code", + "term": "Term", + "description": "Description", + "vocabulary": "Controlled vocabulary", + "note": "Note", + "thisDescriptor": "This descriptor uses a controlled vocabulary", + "publishedBy": "published by", + "maintainedBy_l": "maintained by", + "datasets": "Datasets", + "cropDescriptors": "Crop descriptors", + "recordMetadata": "Record metadata", + "uuid": "UUID", + "recordVersion": "Record version", + "lastUpdated": "Last updated" + } + } + }, + "sort": { + "crop": "Crop", + "dataType": "Data type", + "key": "Key", + "uom": "Unit of measure", + "columnName": "Column name", + "owner": "Maintainer" + } +} +,"partners": { + "admin": { + "c": { + "form": { + "descriptionPlaceholder": "Data provider introduction text (markdown)", + "name": "Data provider name", + "new": " New data provider", + "phonePlaceholder": "+1 555 1231 Ext. 13", + "shortName": "Short name or acronym", + "websites": "Data provider websites" + } + } + }, + "common": { + "countries": "Countries", + "modelName": "Data provider", + "modelName_plural": "Data providers", + "subtitle": "Data providers in the Catalog of Phenotypic Datasets", + "title": "Genesys Data providers", + "wiewsCodes": "FAO WIEWS codes", + "menu": "Data providers", + "partnerName": "Partner name", + "acronym": "Acronym" + }, + "public": { + "c": { + "searchMenu": { + "byDate": "BY DATE", + "byInstitute": "BY INSTITUTE", + "search": "SEARCH", + "searchCountry": "Search Country...", + "select": "Select institute..." + }, + "select": { + "select": "Select data provider" + }, + "summary": { + "countryNumber": "Number of data publishers", + "instituteNumber": "Number of data publishers", + "publisherNumber": "Number of data publishers", + "title": "SUMMARY" + } + }, + "f": { + "acronym": "Acronym", + "ICRISAT": "ICRISAT", + "international": "International", + "partnerDescription": "Data provider description" + }, + "p": { + "browse": { + "title": "Genesys Data providers", + "subtitle": "Data providers in the Catalog of Phenotypic Datasets" + }, + "display": { + "email": "Contact email", + "metadata": "Record metadata", + "urls": "Websites" + } + } + } +} +,"vocabulary": { + "common": { + "modelName": "Vocabulary", + "modelName_plural": "Vocabularies", + "subtitle": "Commonly used controlled vocabularies", + "vocabulary": "Controlled vocabulary", + "vocabulary_plural": "Controlled vocabularies", + "code": "Code" + }, + "admin": { + "c": { + "form": { + "descriptionPlaceholder": "Full description of the vocabulary", + "details": "Vocabulary details", + "error": "Don't use the ItemsEditor for more than 50 items!", + "list": "list", + "owner": "Owner of the vocabulary", + "ownerPlaceholder": "Data provider", + "publisher": "Original publisher", + "term": "Vocabulary term", + "term_plural": "Vocabulary terms", + "termUrlPrefix": "Term URL prefix", + "title": "Vocabulary title", + "titlePlaceholder": "Color of magic", + "url": "URL to definition", + "versionTag": "Version tag" + } + }, + "p": { + "browse": { + "create": "Create vocabulary", + "title": "What do you want to do?", + "update": "Update vocabularies" + }, + "display": { + "acceptedTerms": "Accepted vocabulary terms", + "deleteDescription": "Deleting the vocabulary is only possible when there is no associated data.", + "description": "Description", + "term": "Term", + "terms": "terms", + "title": "Vocabulary details" + } + } + }, + "sort": { + "maintainer": "Maintainer", + "URL": "URL" } } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 941e10a..5e1793f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,6 +88,20 @@ "resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-1.0.0-alpha.7.tgz", "integrity": "sha512-iwTKLFUIsIrmZzymfnxGmmJv52UvGTLjsLUWxHkEaW16vLtORQi3C8I8wVcE0CdEceSux+pdSD8C8fsIlrzysg==" }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.2.tgz", + "integrity": "sha512-yprFYuno9FtNsSHVlSWd+nRlmGoAbqbeCwOryP6sC/zoCjhpArcRMYp19EvpSUSizJAlsXEwJv+wcWS9XaXdMw==" + }, "@types/jss": { "version": "9.5.4", "resolved": "https://registry.npmjs.org/@types/jss/-/jss-9.5.4.tgz", @@ -402,20 +416,17 @@ "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" }, "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" }, "array-find-index": { "version": "1.0.2", @@ -462,8 +473,7 @@ "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, "arrify": { "version": "1.0.1", @@ -524,8 +534,7 @@ "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, "async": { "version": "2.6.0", @@ -556,8 +565,7 @@ "atob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", - "dev": true + "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=" }, "autoprefixer": { "version": "6.7.7", @@ -1493,7 +1501,6 @@ "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, "requires": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", @@ -1508,7 +1515,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -1517,7 +1523,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -1526,7 +1531,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -1535,7 +1539,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -1673,7 +1676,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -1691,7 +1693,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -1875,7 +1876,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, "requires": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", @@ -1942,6 +1942,11 @@ "bluebird": "3.x.x" } }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" + }, "camel-case": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", @@ -2150,7 +2155,6 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, "requires": { "arr-union": "^3.1.0", "define-property": "^0.2.5", @@ -2162,7 +2166,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -2310,7 +2313,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, "requires": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" @@ -2391,8 +2393,7 @@ "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "compressible": { "version": "2.0.14", @@ -2539,8 +2540,7 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "copy-webpack-plugin": { "version": "4.5.2", @@ -3006,7 +3006,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" @@ -3016,7 +3015,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -3025,7 +3023,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -3034,7 +3031,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -3684,7 +3680,6 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, "requires": { "debug": "^2.3.3", "define-property": "^0.2.5", @@ -3699,7 +3694,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -3708,7 +3702,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -3717,7 +3710,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -3868,7 +3860,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -3878,7 +3869,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "requires": { "is-plain-object": "^2.0.4" } @@ -3889,7 +3879,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, "requires": { "array-unique": "^0.3.2", "define-property": "^1.0.0", @@ -3905,7 +3894,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -3914,7 +3902,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -3923,7 +3910,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -3932,7 +3918,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -3941,7 +3926,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -3974,6 +3958,40 @@ "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", "dev": true }, + "fast-glob": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.3.tgz", + "integrity": "sha512-NiX+JXjnx43RzvVFwRWfPKo4U+1BrK5pJPsHQdKMlLoFHrrGktXglQhHliSihWAq+m1z6fHk3uwGHrtRbS9vLA==", + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.0.1", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.1", + "micromatch": "^3.1.10" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + } + } + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -4094,7 +4112,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -4106,7 +4123,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -4241,8 +4257,7 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" }, "for-own": { "version": "1.0.0", @@ -4284,7 +4299,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, "requires": { "map-cache": "^0.2.2" } @@ -4946,8 +4960,7 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, "getpass": { "version": "0.1.7", @@ -5072,6 +5085,11 @@ } } }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=" + }, "global": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", @@ -5196,7 +5214,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, "requires": { "get-value": "^2.0.6", "has-values": "^1.0.0", @@ -5207,7 +5224,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, "requires": { "is-number": "^3.0.0", "kind-of": "^4.0.0" @@ -5217,7 +5233,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -5988,7 +6003,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -5997,7 +6011,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -6063,7 +6076,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -6072,7 +6084,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -6094,7 +6105,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", "is-data-descriptor": "^0.1.4", @@ -6104,8 +6114,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -6133,14 +6142,12 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-finite": { "version": "1.0.2", @@ -6169,7 +6176,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -6194,7 +6200,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -6203,7 +6208,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -6354,8 +6358,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" }, "is-word-character": { "version": "1.0.1", @@ -6371,8 +6374,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -6648,8 +6650,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" }, "klaw": { "version": "1.3.1", @@ -7035,8 +7036,7 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" }, "map-obj": { "version": "1.0.1", @@ -7048,7 +7048,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, "requires": { "object-visit": "^1.0.0" } @@ -7149,6 +7148,11 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, + "merge2": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", + "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==" + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -7158,7 +7162,6 @@ "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -7275,7 +7278,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "dev": true, "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" @@ -7285,7 +7287,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "requires": { "is-plain-object": "^2.0.4" } @@ -7377,7 +7378,6 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -7702,7 +7702,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, "requires": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", @@ -7713,7 +7712,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -7722,7 +7720,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -7745,7 +7742,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, "requires": { "isobject": "^3.0.0" } @@ -7787,7 +7783,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, "requires": { "isobject": "^3.0.1" } @@ -8040,8 +8035,7 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" }, "path-browserify": { "version": "0.0.0", @@ -8052,8 +8046,7 @@ "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" }, "path-exists": { "version": "3.0.0", @@ -8329,8 +8322,7 @@ "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { "version": "5.2.18", @@ -13110,7 +13102,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" @@ -13269,8 +13260,7 @@ "repeat-element": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" }, "repeat-string": { "version": "1.6.1", @@ -13439,8 +13429,7 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "resolve-url-loader": { "version": "2.3.0", @@ -13470,8 +13459,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, "rework": { "version": "1.0.1", @@ -13560,7 +13548,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, "requires": { "ret": "~0.1.10" } @@ -13804,7 +13791,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -13816,7 +13802,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -13928,7 +13913,6 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, "requires": { "base": "^0.11.1", "debug": "^2.2.0", @@ -13944,7 +13928,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -13953,7 +13936,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -13962,7 +13944,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -13973,7 +13954,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, "requires": { "define-property": "^1.0.0", "isobject": "^3.0.0", @@ -13984,7 +13964,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -13993,7 +13972,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -14002,7 +13980,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -14011,7 +13988,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -14024,7 +14000,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, "requires": { "kind-of": "^3.2.0" }, @@ -14033,7 +14008,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -14111,8 +14085,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-loader": { "version": "0.2.3", @@ -14149,7 +14122,6 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, "requires": { "atob": "^2.1.1", "decode-uri-component": "^0.2.0", @@ -14170,8 +14142,7 @@ "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" }, "spdx-correct": { "version": "1.0.2", @@ -14255,7 +14226,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, "requires": { "extend-shallow": "^3.0.0" } @@ -14308,7 +14278,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, "requires": { "define-property": "^0.2.5", "object-copy": "^0.1.0" @@ -14318,7 +14287,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -15220,7 +15188,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -15229,7 +15196,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -15240,7 +15206,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, "requires": { "define-property": "^2.0.2", "extend-shallow": "^3.0.2", @@ -15252,7 +15217,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" @@ -15600,7 +15564,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", @@ -15612,7 +15575,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -15621,7 +15583,6 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -15734,7 +15695,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, "requires": { "has-value": "^0.3.1", "isobject": "^3.0.0" @@ -15744,7 +15704,6 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, "requires": { "get-value": "^2.0.3", "has-values": "^0.1.4", @@ -15755,7 +15714,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, "requires": { "isarray": "1.0.0" } @@ -15765,8 +15723,7 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" } } }, @@ -15794,8 +15751,7 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" }, "url": { "version": "0.11.0", @@ -15844,8 +15800,7 @@ "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, "util": { "version": "0.10.3", diff --git a/package.json b/package.json index 82f42ec..f5af33b 100644 --- a/package.json +++ b/package.json @@ -19,17 +19,18 @@ "main": "index.js", "scripts": { "clean": "rimraf target", - "build": "webpack --config config/webpack-development.config.js", + "build": "npm run generateI18n && webpack --config config/webpack-development.config.js", "build:production": "cross-env NODE_ENV=production SSR=true webpack --config config/webpack-production.config.js", "build:server": "cross-env NODE_ENV=production SSR=true webpack --config config/server.config.js", "build:serverdebug": "cross-env SSR=true webpack --config config/server.config.js", - "serve": "webpack-dev-server --config config/webpack-development.config.js", + "serve": "npm run generateI18n && webpack-dev-server --config config/webpack-development.config.js", "serve:production": "cross-env NODE_ENV=production webpack-dev-server --config config/webpack-production.config.js", - "build:ssr": "rimraf target && npm run build:production && npm run build:server", + "build:ssr": "npm run generateI18n && rimraf target && npm run build:production && npm run build:server", "serve:ssr": "npm run build:ssr && cd target/app/server && cross-env SSR=true node server.js", "build:ssrdebug": "rimraf target && npm run build && npm run build:serverdebug", "debug:ssr": "npm run build:ssrdebug && cd target/app/server && cross-env SSR=true node server.js", "ssr": "cd target/app/server && cross-env SSR=true node server.js", + "generateI18n": "node i18n/generateI18n.ts", "i18nscan": "i18next-scanner --config i18next-scanner.config.js 'src/**/*.tsx'" }, "dependencies": { @@ -47,6 +48,7 @@ "es-cookie": "^1.2.0", "express": "^4.16.3", "express-http-proxy": "^1.2.0", + "fast-glob": "^2.2.3", "flattenjs": "^1.0.4", "form-data": "^2.3.2", "history": "^4.7.2", diff --git a/src/actions/userProfile.ts b/src/actions/userProfile.ts index 054ee4d..083873a 100644 --- a/src/actions/userProfile.ts +++ b/src/actions/userProfile.ts @@ -25,11 +25,11 @@ export const loadUserProfile = () => (dispatch, getState) => { export const changePassword = (newPassword: string, oldPassword: string) => (dispatch, getState) => { return UserProfileService.changePassword(newPassword, oldPassword) .then(() => { - dispatch(showSnackbar('Password was changed successfully')); + dispatch(showSnackbar('user.common.passChangeSuccess')); }).catch((error) => { const data = _.get(error, 'response.data'); log('Save error', data.error); - dispatch(showSnackbar(data.error || 'Password change error')); + dispatch(showSnackbar(data.error || 'user.common.passChangeError')); throw new SubmissionError({ title: 'Password change error', _error: data.error }); }); }; diff --git a/src/crops/translations.json b/src/crops/translations.json new file mode 100644 index 0000000..71db9b9 --- /dev/null +++ b/src/crops/translations.json @@ -0,0 +1,37 @@ +{ + "admin": { + "c": { + "cropForm": { + "cropCode": "Crop code", + "cropTitle": "Crop title", + "back": "Back to crop list" + } + }, + "p": { + "edit": { + "onDelete": { + "message": "Delete crop '{{cropName, lowercase}}'?", + "description": "Note, deleting any crop causes mayhem." + } + } + } + }, + "common": { + "modelName": "Crop", + "modelName_plural": "Crops", + "subtitle": "List of crops registered with Genesys", + "title": "Crops" + }, + "public": { + "c": { + "cropSelector": { + "helper": "Contact helpdesk@genesys-pgr.org to register additional crops." + } + }, + "p": { + "browse": { + "title": "What do you want to do?" + } + } + } +} \ No newline at end of file diff --git a/src/crops/ui/BrowsePage.tsx b/src/crops/ui/BrowsePage.tsx index e136341..bbe31d8 100644 --- a/src/crops/ui/BrowsePage.tsx +++ b/src/crops/ui/BrowsePage.tsx @@ -36,7 +36,7 @@ class BrowsePage extends React.Component { return ( - { t('common:action.add', { what: t('label.crop') }) } } /> + { t('common:action.add', { what: t('crop.common.modelName') }) } } /> { crops && crops.sort((a, b) => a.name.localeCompare(b.name)).map((crop: Crop) => ( diff --git a/src/crops/ui/EditPage.tsx b/src/crops/ui/EditPage.tsx index 3205b1a..18c59ed 100644 --- a/src/crops/ui/EditPage.tsx +++ b/src/crops/ui/EditPage.tsx @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import Grid from '@material-ui/core/Grid'; import Paper from '@material-ui/core/Paper'; +import { translate } from 'react-i18next'; import {log} from 'utilities/debug'; @@ -20,6 +21,7 @@ interface IDescriptorEditPageProps extends React.ClassAttributes { saveCrop: (crop: Crop) => void; deleteCrop: (crop: Crop) => void; crop: Crop; + t: any; } class CropEditPage extends React.Component { @@ -42,12 +44,12 @@ class CropEditPage extends React.Component { } private onDelete = () => { - const { crop, deleteCrop } = this.props; + const { crop, deleteCrop, t } = this.props; - confirm(`Delete crop '${crop.name}'?`, { - description: `Note, deleting any crop causes mayhem.`, - confirmLabel: 'Delete', - abortLabel: 'Cancel', + confirm(t('crops.admin.p.edit.onDelete.message', {cropName: crop.name}), { + description: t('crops.admin.p.edit.onDelete.description'), + confirmLabel: t('common:action.delete'), + abortLabel: t('common:action.cancel'), }).then(() => { deleteCrop(crop); }).catch(() => { @@ -56,7 +58,7 @@ class CropEditPage extends React.Component { } public render() { - const {shortName} = this.props; + const {shortName, t} = this.props; let {crop} = this.props; if (!crop && !shortName) { @@ -72,7 +74,7 @@ class CropEditPage extends React.Component { - + @@ -92,4 +94,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ deleteCrop, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(CropEditPage); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(CropEditPage)); diff --git a/src/crops/ui/c/CropForm.tsx b/src/crops/ui/c/CropForm.tsx index d21b24e..e872f68 100644 --- a/src/crops/ui/c/CropForm.tsx +++ b/src/crops/ui/c/CropForm.tsx @@ -7,19 +7,19 @@ import {CROP_FORM} from 'constants/crop'; import {TextField} from 'ui/common/text-field'; import Validators from 'utilities/Validators'; -const CropForm = ({error, handleSubmit, initialValues, onDelete}) => { +const CropForm = ({error, handleSubmit, initialValues, onDelete, t}) => { return (
- { initialValues && initialValues.version &&
Version: { initialValues.version }
} + { initialValues && initialValues.version &&
{ t('common:label.version', {version: initialValues.version}) }
} - - + +
{ error && { error } }
- - { (initialValues._permissions && initialValues._permissions.delete) && } - + + { (initialValues._permissions && initialValues._permissions.delete) && } + ); }; diff --git a/src/crops/ui/c/CropSelector.tsx b/src/crops/ui/c/CropSelector.tsx index 61d488a..705fe08 100644 --- a/src/crops/ui/c/CropSelector.tsx +++ b/src/crops/ui/c/CropSelector.tsx @@ -54,7 +54,7 @@ const renderRadioButton = (input, code) => ( const CropSelector = ({t, crops, label, single, fields, input}: ICropSelectorProps) => crops && ( { label } - { t('m.crop.helper') } + { t('crop.public.c.cropSelector.helper') } { crops.sort((a, b) => a.name.localeCompare(b.name)).map((crop: Crop) => ( diff --git a/src/datasets/routes.ts b/src/datasets/routes.ts index 21fa1f9..233dbb9 100644 --- a/src/datasets/routes.ts +++ b/src/datasets/routes.ts @@ -15,8 +15,8 @@ const publicRoutes = [ path: '/datasets', component: Wrapper, extraProps: { - title: 'p.datasets.title', - subtitle: 'p.datasets.subtitle', + title: 'datasets.common.modelName_plural', + subtitle: 'datasets.common.subtitle', }, routes: [ { @@ -44,7 +44,7 @@ const dashboardRoutes = [ component: DatasetStepper, auth: [ROLE_USER, ROLE_ADMINISTRATOR], extraProps: { - title: 'Phenotypic dataset publisher', + title: 'datasets.dashboard.p.stepper.publisher', }, routes: [ { @@ -59,7 +59,7 @@ const dashboardRoutes = [ component: DatasetStepper, auth: [ROLE_USER, ROLE_ADMINISTRATOR], extraProps: { - title: 'Phenotypic dataset publisher', + title: 'datasets.dashboard.p.stepper.publisher', }, routes: [ ...datasetSteps, diff --git a/src/datasets/translations.json b/src/datasets/translations.json new file mode 100644 index 0000000..3909033 --- /dev/null +++ b/src/datasets/translations.json @@ -0,0 +1,211 @@ +{ + "common": { + "delete": "Deleting the dataset will remove all related data.", + "modelName": "Dataset", + "modelName_plural": "Datasets", + "menu": "Datasets", + "creator": { + "role": { + "MANAGER": "Data manager", + "COLLECTOR": "Data collector", + "DIGITIZER": "Data digitizer", + "CURATOR": "Data curator", + "null": "Not specified" + } + } + }, + "dashboard": { + "p": { + "stepper": { + "basicInfo": { + "title": "Basic information", + "titleField": { + "label": "Dataset title", + "placeholder": "Name given to the dataset (e.g., Characterization of maize accessions in Kenya)" + }, + "partner": { + "label": "Select Data provider", + "placeholder": "Data provider" + }, + "version": { + "label": "Dataset version" + }, + "description": { + "label": "Dataset description", + "placeholder": "An abstract, short or long description of the dataset. Descriptive details improves discoverability of the resource." + }, + "created": { + "label": "Date of creation of the dataset", + "placeholder": "YYYYMMDD. If the month or day are missing, this should be indicated with ‘00’ [double zero] (e.g. 1975----, 19750000; 197506--, 19750600)" + }, + "language": { + "label": "Language", + "placeholder": "Select language of the dataset" + }, + "source": { + "label": "Source", + "placeholder": "A related resource from which the described dataset is derived (e.g., journal article, data article, conference proceedings). Use DOI, URL, Journal title, or Journal/conference title; vol., no. (year)" + }, + "crops": { + "label": "Crops" + } + }, + "attachments": { + "title": "Dataset attachments", + "name": "File name", + "label": "File label", + "description": "Description of file content", + "upload": "Upload file", + "datasetFile": "Dataset file" + }, + "creators": { + "role": "Role", + "title": "Dataset creators", + "name": "Full name", + "namePlaceholder": "Jane A. Doe", + "instAffiliation": "Institutional affiliation", + "email": "Email address", + "phone": "Phone number", + "fax": "Fax", + "phonePlaceholder": "+1 555 1231 Ext. 42", + "instAddress": "Institutional address", + "instAddressPlaceholder": "Address of institution of affiliation when the dataset was created.", + "add": "Add dataset creator" + }, + "location": { + "title": "Location and timing", + "search": "Search...", + "copyright": "{{openingTag}}OpenStreetMap{{closingTag}} contributors", + "add": "Add another location", + "iso": "ISO3 Country code", + "isoPlaceholder": "GBR", + "description": "Description of environment and conditions at the site of evaluation", + "endingDate": "Ending date of characterization/evaluation", + "datePlaceholder": "YYYYMMDD. If the month or day are missing, this should be indicated with ‘00’ [double zero] (e.g. 1975----, 19750000; 197506--, 19750600)", + "startingDate": "Starting date of characterization/evaluation", + "longitude": "Decimal longitude", + "longitudePlaceholder": "The longitude of the site where the characterization/evaluation was conducted (decimal degrees).", + "latitude": "Decimal latitude", + "latitudePlaceholder": "The latitude of the site where the characterization/evaluation was conducted (decimal degrees).", + "locality": "Locality", + "localityPlaceholder": "Description of the locality where the characterization/evaluation was performed.", + "state": "State", + "country": "Country", + "countryPlaceholder": "Select country where the characterization/evaluation was performed." + }, + "listOfAccessions": { + "title": "List of accessions", + "instructions": "INSTRUCTIONS FOR USE", + "useSheet": "Use the \"Acccessions\" sheet from the template", + "template": "Genesys Catalog template", + "rowsDescr": "The first row in the template contains the header names, whilst the second contains a description of the content expected on each column.", + "dontChangeNames": "Do not change the header names in the template", + "templateInstruction": "Fill the template with the informations of the accessions, remove the row that contains the description of each column (second row) and save the file.", + "excel": "Copy and paste the table from Excel into the text field \"List of accessions described in the dataset\".", + "listDescribed": "List of accessions described in the dataset", + "placeholder": "Paste accessions data here (comma separated)", + "rowCount": "Accessions {{rows, numeric}} rows" + }, + "pastingTraits": { + "title": "Select descriptors in batch", + "matched": "Matched", + "noDescriptors": "No matching descriptors" + }, + "traits": "Browse and select descriptors", + "reorder": "Reorder descriptors", + "review": "Review and publish", + "subtitle": "Publish your datasets", + "publisher": "Phenotypic dataset publisher" + } + } + }, + "public": { + "common": { + "accessionsNumber": "Number of accessions", + "crop": "Crop", + "traitsNumber": "Number of traits" + }, + "c": { + "card": { + "evaluationPeriod": "Evaluation period" + }, + "datasetDisplay": { + "accessionsEvaluated": "Accessions evaluated", + "contact": "Data provider contact information", + "creators": "Dataset creators", + "dataAndResources": "Data and resources", + "datasetFile": "Dataset file", + "datasetMetadata": "Dataset metadata", + "otherMetadata": "Other Metadata", + "locations": "Locations", + "traitsObserved": "Traits observed", + "useAndLicensing": "Dataset use and licensing", + "confirmDescription": { + "approve": "After approving the dataset no changes are permitted, a new version must be created.", + "onPublish": "The dataset will be reviewing by administrator before publishing." + }, + "properties": { + "datasetDate": "Date of dataset", + "email": "Email", + "evaluationEnd": "End of evaluation", + "evaluationStart": "Start of evaluation", + "licensed": "Licensed under", + "metadataCreateDate": "Metadata create date", + "metadataUpdatedDate": "Metadata updated date", + "moreInformation": "More information", + "relatedResources": "Related resources", + "phone": "Phone number", + "address": "Address" + } + }, + "locationMap": { + "dateOfEvaluation": "Date of evaluation", + "description": "Description", + "iso": "ISO3 country code", + "location": "Location", + "link": "{{aOpen}}OpenStreetMap{{aClose}} contributors" + }, + "suggestionsForm": { + "matches": "Suggested matches", + "apply": "Apply filters" + }, + "generic": "Type {{boldHit}} does not have a SearchHit implementation." + }, + "f": { + "traitKeywords": "Trait keywords", + "rice": "mardi rice", + "partner": "Data provider", + "faoWiews": "FAO WIEWS Institute code", + "evaluationSite": "Evaluation site", + "countryEv": "Country evaluation", + "countryPlaceholder": "Germany", + "latitude": "Latitude", + "longitude": "Longitude", + "crop": "Crop", + "accession": "Accessions", + "genus": "Genus", + "genusPlaceholder": "Manihot", + "accNumber": "Accession number", + "accDoi": "Accession DOI", + "licence": "Licence", + "rights": "rights", + "keywordSearch": "Keyword search" + }, + "p": { + "browse": { + "subtitle": "Datasets published by Genesys data providers" + }, + "display": { + "title": "Dataset details" + } + } + }, + "sort": { + "accessionCountAsc": "Accession count (low to high)", + "accessionCountDesc": "Accession count (high to low)", + "descriptorCountAsc": "Descriptor count (low to high)", + "descriptorCountDesc": "Descriptor count (high to low)", + "startDate": "Experiment start date", + "endDate": "Experiment end date" + } +} \ No newline at end of file diff --git a/src/datasets/ui/BrowsePage.tsx b/src/datasets/ui/BrowsePage.tsx index 34d4688..5dccb90 100644 --- a/src/datasets/ui/BrowsePage.tsx +++ b/src/datasets/ui/BrowsePage.tsx @@ -120,12 +120,12 @@ class BrowsePage extends React.Component { }> - + diff --git a/src/datasets/ui/DisplayPage.tsx b/src/datasets/ui/DisplayPage.tsx index 1186873..4c2bb0b 100644 --- a/src/datasets/ui/DisplayPage.tsx +++ b/src/datasets/ui/DisplayPage.tsx @@ -59,7 +59,7 @@ class DatasetDetail extends React.Component { -

{ t('p.dataset.title') }

+

{ t('datasets.public.p.display.title') }

diff --git a/src/datasets/ui/c/Card.tsx b/src/datasets/ui/c/Card.tsx index 4afec57..19021d7 100644 --- a/src/datasets/ui/c/Card.tsx +++ b/src/datasets/ui/c/Card.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import Dataset from 'model/catalog/Dataset'; @@ -14,12 +15,13 @@ import McpdDate from 'ui/common/time/McpdDate'; interface IDatasetCardProps extends React.ClassAttributes { className?: string; dataset: Dataset; + t: any; } class DatasetCard extends React.Component { public render() { - const { dataset, className } = this.props; + const { dataset, className, t } = this.props; if (! dataset) { return null; @@ -39,13 +41,23 @@ class DatasetCard extends React.Component { - + + + { dataset.crops && dataset.crops.length > 0 && - + + + } - - { dataset.accessionCount } - { dataset.descriptorCount } + + + + + { dataset.accessionCount } + + + { dataset.descriptorCount } + @@ -53,4 +65,4 @@ class DatasetCard extends React.Component { } } -export default DatasetCard; +export default translate()(DatasetCard); diff --git a/src/datasets/ui/c/DatasetDisplay.tsx b/src/datasets/ui/c/DatasetDisplay.tsx index 63802d5..5f2da45 100644 --- a/src/datasets/ui/c/DatasetDisplay.tsx +++ b/src/datasets/ui/c/DatasetDisplay.tsx @@ -138,16 +138,16 @@ interface IDetailInfoProps extends React.ClassAttributes { class DetailInfo extends React.Component { private onPublish = (e) => { - const {dataset, publishDataset} = this.props; + const {dataset, publishDataset, t} = this.props; if (! publishDataset) { return; } - confirm(Publish { dataset.title }?, { - description: `The dataset will be reviewing by administrator before publishing.`, - confirmLabel: 'Publish', - abortLabel: 'Cancel', + confirm({ t('common:action.publish') } { dataset.title }?, { + description: t(`datasets.public.c.datasetDisplay.confirmDescription.onPublish`), + confirmLabel: t('common:action.publish'), + abortLabel: t('common:action.cancel'), }).then(() => { log('Publishing dataset', dataset); publishDataset(dataset); @@ -157,16 +157,16 @@ class DetailInfo extends React.Component { } private onUnpublish = (e) => { - const {dataset, rejectDataset} = this.props; + const {dataset, rejectDataset, t} = this.props; if (! rejectDataset) { return; } - confirm(Unpublish { dataset.title }?, { + confirm({ t('common:action.unpublish') } { dataset.title }?, { // description: `Deleting the descriptor is only possible when there is no associated data.`, - confirmLabel: 'Unpublish', - abortLabel: 'Cancel', + confirmLabel: t('common:action.unpublish'), + abortLabel: t('common:action.cancel'), }).then(() => { log('Unpublishing dataset', dataset); rejectDataset(dataset, false); @@ -176,16 +176,16 @@ class DetailInfo extends React.Component { } private onApprove = (e) => { - const {dataset, approveDataset} = this.props; + const {dataset, approveDataset, t} = this.props; if (! approveDataset) { return; } - confirm(Approve { dataset.title }?, { - description: `After approving the dataset no changes are permitted, a new version must be created.`, - confirmLabel: 'Approve', - abortLabel: 'Cancel', + confirm({ t('common:action.approve') } { dataset.title }?, { + description: t(`datasets.public.c.datasetDisplay.confirmDescription.approve`), + confirmLabel: t('common:action.approve'), + abortLabel: t('common:action.cancel'), }).then(() => { log('Approving dataset', dataset); approveDataset(dataset); @@ -206,16 +206,16 @@ class DetailInfo extends React.Component { } private onDelete = (e) => { - const {dataset, deleteDataset} = this.props; + const {dataset, deleteDataset, t} = this.props; if (! deleteDataset) { return; } - confirm(Delete { dataset.title }?, { - description: `Deleting the dataset will remove all related data.`, - confirmLabel: 'Delete', - abortLabel: 'Cancel', + confirm({ t('common:action.delete') } { dataset.title }?, { + description: t(`datasets.common.delete`), + confirmLabel: t('common:action.delete'), + abortLabel: t('common:action.cancel'), }).then(() => { deleteDataset(dataset); }).catch(() => { @@ -247,35 +247,57 @@ class DetailInfo extends React.Component { { dataset.description && } - + + + { dataset.crops && dataset.crops.length > 0 && - + + + } - { dataset.accessionCount } - { dataset.descriptorCount } - - + + { dataset.accessionCount } + + + { dataset.descriptorCount } + + + + + + + { publishDataset && (dataset._permissions.write || dataset._permissions.delete) && ( { dataset.state === PublishState.PUBLISHED ? (dataset._permissions.manage && !oneDayPassed) ? - + : - + : null } - { dataset.state === PublishState.DRAFT && dataset._permissions.write && } - { dataset.state !== PublishState.PUBLISHED && dataset._permissions.write && } + { dataset.state === PublishState.DRAFT && dataset._permissions.write && + + } + { dataset.state !== PublishState.PUBLISHED && dataset._permissions.write && + + } { dataset.state === PublishState.REVIEWING && - + } - { dataset.state !== PublishState.PUBLISHED && deleteDataset && dataset._permissions.delete && } + { dataset.state !== PublishState.PUBLISHED && deleteDataset && dataset._permissions.delete && + + } { dataset._permissions.manage && } ) } @@ -285,18 +307,18 @@ class DetailInfo extends React.Component { -
+
{ dataset.creators && dataset.creators.length > 0 &&
-

Dataset creators

+

{ t('datasets.public.c.datasetDisplay.creators') }

{ dataset.creators && dataset.creators.map((e: DatasetCreator, i) => ( - + { e.fullName } { e.institutionalAffiliation && { e.institutionalAffiliation } } @@ -312,15 +334,15 @@ class DetailInfo extends React.Component { }
-

Dataset use and licensing

+

{ t('datasets.public.c.datasetDisplay.useAndLicensing') }

{ license && - { license.code } { license.title } + { license.code } { license.title } { license.url && - + { license.url } } @@ -331,7 +353,7 @@ class DetailInfo extends React.Component {

- Data and resources + { t('datasets.public.c.datasetDisplay.dataAndResources') }

@@ -343,11 +365,11 @@ class DetailInfo extends React.Component { }> -

{ e.title || 'Dataset file' }

+

{ e.title || t('datasets.public.c.datasetDisplay.datasetFile') } }

{ e.originalFilename }

)) @@ -358,7 +380,7 @@ class DetailInfo extends React.Component {

- { t('dataset.contact') } + { t('datasets.public.c.datasetDisplay.contact') }

@@ -367,11 +389,11 @@ class DetailInfo extends React.Component { { dataset.owner.email && - + } - { dataset.owner.phone && { dataset.owner.phone } } - { dataset.owner.address && { dataset.owner.address } } + { dataset.owner.phone && { dataset.owner.phone } } + { dataset.owner.address && { dataset.owner.address } }
@@ -379,7 +401,7 @@ class DetailInfo extends React.Component {

- Other Metadata + { t('datasets.public.c.datasetDisplay.otherMetadata') }

@@ -387,14 +409,14 @@ class DetailInfo extends React.Component { - - + + - + { dataset.createdDate && } - + { dataset.lastModifiedDate && } @@ -408,7 +430,7 @@ class DetailInfo extends React.Component { { dataset.locations && dataset.locations.length > 0 && ( -
+
@@ -418,7 +440,7 @@ class DetailInfo extends React.Component { { dataset.descriptors && dataset.descriptors.length > 0 && ( -
+
{ dataset.descriptors.map((descriptor: Descriptor) => ( @@ -433,7 +455,7 @@ class DetailInfo extends React.Component { { dataset.accessionRefs && dataset.accessionRefs.length > 0 && ( -
+
diff --git a/src/datasets/ui/c/Filters.tsx b/src/datasets/ui/c/Filters.tsx index 3847d46..053bac7 100644 --- a/src/datasets/ui/c/Filters.tsx +++ b/src/datasets/ui/c/Filters.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { reduxForm } from 'redux-form'; +import { translate } from 'react-i18next'; import { DATASET_FILTERFORM } from 'datasets/constants'; @@ -15,35 +16,43 @@ import LicenceFilter from 'ui/common/filter/LicenceFilter'; // -const DatasetFilters = ({handleSubmit, initialize, ...other}) => ( - - - - - - +const DatasetFilters = ({handleSubmit, initialize, t, ...other}) => ( + + + + + + - - - - + + + + - + - - - - - + + + + + - - + + ); -export default reduxForm({ +export default translate()(reduxForm({ enableReinitialize: true, form: DATASET_FILTERFORM, -})(DatasetFilters); +})(DatasetFilters)); diff --git a/src/datasets/ui/c/LocationMap.tsx b/src/datasets/ui/c/LocationMap.tsx index cfbb161..eb4cfba 100644 --- a/src/datasets/ui/c/LocationMap.tsx +++ b/src/datasets/ui/c/LocationMap.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import {withStyles} from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; +import { translate } from 'react-i18next'; import {isNumeric} from 'utilities'; import { IGeoPoint } from 'model/common.model'; @@ -16,6 +17,7 @@ let TileLayer; interface ILocationMapProps extends React.ClassAttributes { classes: any; locations: DatasetLocation[]; + t: any; } const styles = (theme) => ({ @@ -54,7 +56,7 @@ class LocationMap extends React.Component { } public render() { - const {classes, locations} = this.props; + const {classes, locations, t} = this.props; if (!(locations && locations.length)) { return null; @@ -102,7 +104,7 @@ class LocationMap extends React.Component { doubleClickZoom={ false } > OpenStreetMap contributors' } + attribution={ `© ${ t('datasets.public.c.locationMap.link', {aOpen: '', aClose: ''}) }` } url={ 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' } /> { positions && positions.map((position, i) => ( @@ -116,16 +118,28 @@ class LocationMap extends React.Component { { locations.filter((location) => location.userCountry || location.stateProvince || location.verbatimLocality) .map((location, i) => ( - + { [location.verbatimLocality, location.stateProvince, location.userCountry] .filter((e) => e && e.trim()) .join(', ') } - { (location.startDate || location.endDate) && } - { location.description && } - { location.countryCode && { location.countryCode } } + { (location.startDate || location.endDate) && + + + + } + { location.description && + + + + } + { location.countryCode && + + { location.countryCode } + + } )) } @@ -134,4 +148,4 @@ class LocationMap extends React.Component { } -export default withStyles(styles)(LocationMap); +export default translate()(withStyles(styles)(LocationMap)); diff --git a/src/datasets/ui/dataset-stepper/index.tsx b/src/datasets/ui/dataset-stepper/index.tsx index 9c008bb..83a05e3 100644 --- a/src/datasets/ui/dataset-stepper/index.tsx +++ b/src/datasets/ui/dataset-stepper/index.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { translate } from 'react-i18next'; import { log } from 'utilities/debug'; import confirm from 'utilities/confirmAlert'; @@ -30,6 +31,7 @@ interface IDatasetProps extends React.ClassAttributes { setPageTitle: (title: string) => void; route: any; match: any; + t: any; } class DatasetStepper extends React.Component { @@ -64,12 +66,12 @@ class DatasetStepper extends React.Component { } protected onDelete = (e) => { - const {dataset, deleteDataset, navigateTo} = this.props; + const {dataset, deleteDataset, navigateTo, t} = this.props; log('Removing dataset', dataset); - confirm(Delete { dataset.title }?, { - description: `Deleting the dataset will remove all related data.`, - confirmLabel: 'Delete', - abortLabel: 'Cancel', + confirm({ t(`datasets.common.delete`) } { dataset.title }?, { + description: t(`datasets.common.delete`), + confirmLabel: t('common:action.delete'), + abortLabel: t('common:action.cancel'), }).then(() => { deleteDataset(dataset).then(() => navigateTo(`/dashboard`)); }).catch(() => { @@ -84,7 +86,7 @@ class DatasetStepper extends React.Component { } public render() { - const {uuid, pageTitle, route, match} = this.props; + const {uuid, pageTitle, route, match, t} = this.props; let {dataset} = this.props; const stillLoading: boolean = (! dataset || (uuid && (dataset.uuid !== uuid))); @@ -100,7 +102,7 @@ class DatasetStepper extends React.Component { return ( - + { renderRoutes(route.routes, match.path, { stillLoading, onGotoStep: this.gotoStep, onDelete: this.onDelete, onPublish: this.onPublish }) } @@ -126,6 +128,6 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ setPageTitle, }, dispatch); -export default connect( +export default translate()(connect( mapStateToProps, mapDispatchToProps, -)(DatasetStepper); +)(DatasetStepper)); diff --git a/src/datasets/ui/dataset-stepper/steps.ts b/src/datasets/ui/dataset-stepper/steps.ts index e56910d..2da5d5f 100644 --- a/src/datasets/ui/dataset-stepper/steps.ts +++ b/src/datasets/ui/dataset-stepper/steps.ts @@ -11,63 +11,63 @@ import Creators from 'datasets/ui/dataset-stepper/steps/creators'; const steps = [ { id: 1, - name: 'Basic information', + name: 'datasets.dashboard.p.stepper.basicInfo.title', path: 'edit', component: BasicInfoStep, exact: true, }, { id: 2, - name: 'Dataset attachments', + name: 'datasets.dashboard.p.stepper.attachments.title', path: 'edit/files', component: FilesStep, exact: true, }, { id: 3, - name: 'Dataset creators', + name: 'datasets.dashboard.p.stepper.creators.title', path: 'edit/dataset-creator', component: Creators, exact: true, }, { id: 4, - name: 'Location and timing', + name: 'datasets.dashboard.p.stepper.location.title', path: 'edit/location', component: LocationStep, exact: true, }, { id: 5, - name: 'List of accessions', + name: 'datasets.dashboard.p.stepper.listOfAccessions.title', path: 'edit/list-of-accessions', component: AccessionsListStep, exact: true, }, { id: 6, - name: 'Select descriptors in batch', + name: 'datasets.dashboard.p.stepper.pastingTraits.title', path: 'edit/pasting-traits', component: PastingTraitsStep, exact: true, }, { id: 7, - name: 'Browse and select descriptors', + name: 'datasets.dashboard.p.stepper.traits', path: 'edit/traits-observed', component: Traits, exact: true, }, { id: 8, - name: 'Reorder descriptors', + name: 'datasets.dashboard.p.stepper.reorder', path: 'edit/reorder-descriptors', component: ReorderTraitsStep, exact: true, }, { id: 9, - name: 'Review and publish', + name: 'datasets.dashboard.p.stepper.review', path: 'edit/review-and-publish', component: ReviewAndPublishStep, exact: true, diff --git a/src/datasets/ui/dataset-stepper/steps/accessions-list/ListOfAccesion.tsx b/src/datasets/ui/dataset-stepper/steps/accessions-list/ListOfAccesion.tsx index 2de8bf8..4cca09d 100644 --- a/src/datasets/ui/dataset-stepper/steps/accessions-list/ListOfAccesion.tsx +++ b/src/datasets/ui/dataset-stepper/steps/accessions-list/ListOfAccesion.tsx @@ -3,6 +3,7 @@ import { withStyles } from '@material-ui/core/styles'; import Input from '@material-ui/core/Input'; import InputLabel from '@material-ui/core/InputLabel'; import FormControl from '@material-ui/core/FormControl'; +import { translate } from 'react-i18next'; import { log } from 'utilities/debug'; @@ -17,6 +18,7 @@ interface IListOfAccession extends React.ClassAttributes { classes: any; onAccessionsUpdated: (accessionRefs: AccessionRef[]) => Promise; accessionRefs: AccessionRef[]; + t: any; } const styleSheet = { @@ -103,19 +105,24 @@ class ListOfAccession extends React.Component { public render() { - const {classes, accessionRefs} = this.props; + const {classes, accessionRefs, t} = this.props; const { uploading, uploadText } = this.state; return (
-

INSTRUCTIONS FOR USE

+

{ t('datasets.dashboard.p.stepper.listOfAccessions.instructions') }

    -
  1. Use the "Acccessions" sheet from the template: Genesys Catalog template.
  2. -
  3. The first row in the template contains the header names, whilst the second contains a description of the content expected on each column.
  4. -
  5. Do not change the header names in the template
  6. -
  7. Fill the template with the informations of the accessions, remove the row that contains the description of each column (second row) and save the file.
  8. -
  9. Copy and paste the table from Excel into the text field "List of accessions described in the dataset".
  10. +
  11. + { t('datasets.dashboard.p.stepper.listOfAccessions.useSheet') }' ' + + { t('datasets.dashboard.p.stepper.listOfAccessions.template') } + . +
  12. +
  13. { t('datasets.dashboard.p.stepper.listOfAccessions.rowsDescr') }
  14. +
  15. { t('datasets.dashboard.p.stepper.listOfAccessions.dontChangeNames') }
  16. +
  17. { t('datasets.dashboard.p.stepper.listOfAccessions.templateInstruction') }
  18. +
  19. { t('datasets.dashboard.p.stepper.listOfAccessions.excel') }
@@ -124,19 +131,21 @@ class ListOfAccession extends React.Component {
- List of accessions described in the dataset - + { t('datasets.dashboard.p.stepper.listOfAccessions.listDescribed') } +
{ uploading && } -

Accession list: { accessionRefs ? accessionRefs.length : 0 } rows

+

{ t('datasets.dashboard.p.stepper.listOfAccessions.rowCount', {rows: accessionRefs ? accessionRefs.length : 0}) }

); } } -export default withStyles(styleSheet)(ListOfAccession); +export default translate()(withStyles(styleSheet)(ListOfAccession)); diff --git a/src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoForm.tsx b/src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoForm.tsx index 6ba4fbf..039751e 100644 --- a/src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoForm.tsx +++ b/src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoForm.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import {Field, reduxForm, FieldArray} from 'redux-form'; +import { translate } from 'react-i18next'; import {DATASET_BASIC_INFO_FORM} from 'datasets/constants'; @@ -19,13 +20,14 @@ interface ILoginContainerProps extends React.ClassAttributes { initialValues: any; classes: any; uuid: string; + t: any; } class BasicInfoStep extends React.Component { public render() { - const {initialValues, handleSubmit} = this.props; + const {initialValues, handleSubmit, t} = this.props; return (
@@ -33,8 +35,8 @@ class BasicInfoStep extends React.Component { name="owner" component={ SelectPartner } onlyMine - label="Select Partner" - placeholder="Partner" + label="datasets.dashboard.p.stepper.basicInfo.partner.label" + placeholder={ t('datasets.dashboard.p.stepper.basicInfo.partner.placeholder') } editable={ ! (initialValues.uuid && initialValues.version) } validate={ [ Validators.required ] } /> @@ -42,28 +44,28 @@ class BasicInfoStep extends React.Component { name="title" basicMarkdown component={ MarkdownField } - label="Dataset title" - placeholder="Name given to the dataset (e.g., Characterization of maize accessions in Kenya)" + label={ t('datasets.dashboard.p.stepper.basicInfo.titleField.label') } + placeholder={ t('datasets.dashboard.p.stepper.basicInfo.titleField.placeholder') } validate={ [ Validators.required ] } /> { ); } } -export default reduxForm({ +export default translate()(reduxForm({ form: DATASET_BASIC_INFO_FORM, onSubmit: remoteSubmit, enableReinitialize: true, -})((BasicInfoStep)); +})((BasicInfoStep))); diff --git a/src/datasets/ui/dataset-stepper/steps/creators/DatasetCreatorForm.tsx b/src/datasets/ui/dataset-stepper/steps/creators/DatasetCreatorForm.tsx index 754ba4b..ddc487b 100644 --- a/src/datasets/ui/dataset-stepper/steps/creators/DatasetCreatorForm.tsx +++ b/src/datasets/ui/dataset-stepper/steps/creators/DatasetCreatorForm.tsx @@ -47,7 +47,7 @@ const renderRadioGroup = translate()(({input, meta, t, classes}) => { return ( - Role: + { t('datasets.dashboard.p.stepper.creators.role') } { className={ classes.RadioGrid } > { Object.keys(CreatorRole).map((role) => ( - } /> + } /> )) } @@ -74,6 +74,7 @@ class DatasetCreatorForm extends React.Component } public renderCreators = ({ classes, fields, meta: { touched, error, submitFailed } }) => { + const { t } = this.props; return(
@@ -87,8 +88,8 @@ class DatasetCreatorForm extends React.Component
), @@ -135,6 +136,8 @@ class DatasetCreatorForm extends React.Component } public render() { + const { t } = this.props; + return (
@@ -142,7 +145,7 @@ class DatasetCreatorForm extends React.Component
diff --git a/src/datasets/ui/dataset-stepper/steps/files/FilesForm.tsx b/src/datasets/ui/dataset-stepper/steps/files/FilesForm.tsx index 78cb739..a418c55 100644 --- a/src/datasets/ui/dataset-stepper/steps/files/FilesForm.tsx +++ b/src/datasets/ui/dataset-stepper/steps/files/FilesForm.tsx @@ -4,6 +4,7 @@ import {withStyles} from '@material-ui/core/styles'; import * as _ from 'lodash'; import Grid from '@material-ui/core/Grid'; import Button from '@material-ui/core/Button'; +import { translate } from 'react-i18next'; import {FILES_FORM} from 'datasets/constants'; @@ -20,6 +21,7 @@ interface IFilesFormProps extends React.ClassAttributes { uploadRepositoryFile: (datasetUUID: string, file: File) => any; updateRepositoryFile: (datasetUUID: string, repositoryFile: RepositoryFile) => any; deleteRepositoryFile: (datasetUUID: string, fileRepositoryUUID: string) => any; + t: any; } const styleSheet = (theme) => ({ @@ -64,63 +66,68 @@ class FilesForm extends React.Component { } } - private FilesEditor = (member, index, fields) => fields.get(`[${index}].uuid`) ? - ( - - - - - - - - - - - - ) : ( -
- - -
- ) + private FilesEditor = (member, index, fields) => { + const { t } = this.props; + + return ( + fields.get(`[${index}].uuid`) ? ( + + + + + + + + + + + + ) : ( +
+ + +
+ ) + ); + } public render() { - const { classes } = this.props; + const { classes, t } = this.props; return (
{ const styled = withStyles(styleSheet)(FilesForm); -export default reduxForm({ +export default translate()(reduxForm({ form: FILES_FORM, enableReinitialize: true, -})(styled); +})(styled)); diff --git a/src/datasets/ui/dataset-stepper/steps/location/CountryCodePicker.tsx b/src/datasets/ui/dataset-stepper/steps/location/CountryCodePicker.tsx index bb57443..2abed0d 100644 --- a/src/datasets/ui/dataset-stepper/steps/location/CountryCodePicker.tsx +++ b/src/datasets/ui/dataset-stepper/steps/location/CountryCodePicker.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import {Field} from 'redux-form'; +import { translate } from 'react-i18next'; // Model import VocabularyTerm from 'model/vocabulary/VocabularyTerm'; @@ -24,6 +25,7 @@ interface ISelectCountryCodeInternal extends React.ClassAttributes { autocomplete: (term: string) => Promise; label: string; meta?: any; + t: any; } class SelectCountryInternal extends React.Component { @@ -97,7 +99,7 @@ class SelectCountryInternal extends React.Component @@ -124,7 +126,7 @@ class SelectCountryInternal extends React.Component @@ -146,12 +148,13 @@ interface ICountryCodePicker extends React.ClassAttributes { label: string; className?: string; autocomplete: (term: string) => Promise; + t: any; } class CountryCodePicker extends React.Component { public render() { - const {input: {name}, label, className, autocomplete} = this.props; + const {input: {name}, label, className, autocomplete, t} = this.props; return (
@@ -160,6 +163,7 @@ class CountryCodePicker extends React.Component { component={ SelectCountryInternal } label={ label } autocomplete={ autocomplete } + t={ t } />
); @@ -170,4 +174,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ autocomplete: autocompleteGeoTerm, }, dispatch); -export default connect(null, mapDispatchToProps)(CountryCodePicker); +export default translate()(connect(null, mapDispatchToProps)(CountryCodePicker)); diff --git a/src/datasets/ui/dataset-stepper/steps/location/FormMap.tsx b/src/datasets/ui/dataset-stepper/steps/location/FormMap.tsx index 84569dc..9528192 100644 --- a/src/datasets/ui/dataset-stepper/steps/location/FormMap.tsx +++ b/src/datasets/ui/dataset-stepper/steps/location/FormMap.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import {withStyles} from '@material-ui/core/styles'; +import { translate } from 'react-i18next'; import {error} from 'utilities/debug'; import {isNumeric} from 'utilities'; @@ -10,6 +11,7 @@ let TileLayer; interface IFormMapProps extends React.ClassAttributes { classes: any; + t: any; locations: [{ decimalLatitude: any, decimalLongitude: any, @@ -96,7 +98,7 @@ class FormMap extends React.Component { return null; } - const {classes} = this.props; + const {classes, t} = this.props; const {decimalLatitude, decimalLongitude} = this.getCurrentLocation(); @@ -117,7 +119,7 @@ class FormMap extends React.Component { zoom={ 6 } > OpenStreetMap contributors' } + attribution={ `© ${ t('datasets.dashboard.p.stepper.location.copyright', {openingTag: '', closingTag: ''}) }` } url={ 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' } /> { position && } @@ -127,4 +129,4 @@ class FormMap extends React.Component { } } -export default withStyles(styleSheet)(FormMap); +export default translate()(withStyles(styleSheet)(FormMap)); diff --git a/src/datasets/ui/dataset-stepper/steps/location/LocationForm.tsx b/src/datasets/ui/dataset-stepper/steps/location/LocationForm.tsx index cca31a6..2532b13 100644 --- a/src/datasets/ui/dataset-stepper/steps/location/LocationForm.tsx +++ b/src/datasets/ui/dataset-stepper/steps/location/LocationForm.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { Field, Fields, FieldArray, reduxForm } from 'redux-form'; import Button from '@material-ui/core/Button'; +import { translate } from 'react-i18next'; import { LOCATION_FORM } from 'datasets/constants'; @@ -29,6 +30,7 @@ interface ILocationFormProps extends React.ClassAttributes { createLocation: (datasetUUID: string) => Promise; deleteLocation: (datasetUUID: string, location: Location) => Promise; changeFieldValue: (field: string, value: any) => void; + t: any; } class LocationForm extends React.Component { @@ -88,112 +90,116 @@ class LocationForm extends React.Component { }); } - protected renderLocations = ({ fields, meta: { touched, error, submitFailed } }) => ( -
- { fields.map((location, index) => ( -
-
- - - + protected renderLocations = ({ fields, meta: { touched, error, submitFailed } }) => { + const { t } = this.props; + + return ( +
+ { fields.map((location, index) => ( +
+
+ + + +
+ + + + + + + + + +
- - - - - - - - - - - + )) } +
+
- )) } -
-
-
- ) + ); + } public render() { return ( @@ -204,7 +210,7 @@ class LocationForm extends React.Component { } } -export default reduxForm({ +export default translate()(reduxForm({ form: LOCATION_FORM, enableReinitialize: true, -})(LocationForm); +})(LocationForm)); diff --git a/src/datasets/ui/dataset-stepper/steps/pasting-traits/index.tsx b/src/datasets/ui/dataset-stepper/steps/pasting-traits/index.tsx index bea529e..f1e2b29 100644 --- a/src/datasets/ui/dataset-stepper/steps/pasting-traits/index.tsx +++ b/src/datasets/ui/dataset-stepper/steps/pasting-traits/index.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; +import { translate } from 'react-i18next'; import Descriptor from 'model/catalog/Descriptor'; import Grid from '@material-ui/core/Grid'; @@ -24,6 +25,7 @@ interface IPastingDescriptorsStepProps extends React.ClassAttributes { onPublish: () => void; onGotoStep: (i: number) => () => void; location: any; + t: any; } class PastingDescriptorsStep extends React.Component { @@ -69,7 +71,7 @@ class PastingDescriptorsStep extends React.Component { this.state.matchingDescriptors ? (
-

{ `Matched ${this.state.matchingDescriptors.length} descriptor definitions` }

- +

{ t('datasets.dashboard.p.stepper.pastingTraits.matched', {length: this.state.matchingDescriptors.length}) }

{ this.state.matchingDescriptors.map((descriptor, index) => ( @@ -94,14 +95,14 @@ class PastingDescriptorsStep extends React.Component d.uuid).indexOf(descriptor.uuid) !== -1, - } } + } } /> )) }
) : ( -

No matching descriptors

+

{ t('datasets.dashboard.p.stepper.pastingTraits.noDescriptors') }

) } @@ -123,6 +124,6 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ addDescriptorsToDataset, }, dispatch); -export default connect( +export default translate()(connect( mapStateToProps, mapDispatchToProps, -)(PastingDescriptorsStep); +)(PastingDescriptorsStep)); diff --git a/src/datasets/ui/search/SuggestionsPage.tsx b/src/datasets/ui/search/SuggestionsPage.tsx index 60bdf5c..e63624c 100644 --- a/src/datasets/ui/search/SuggestionsPage.tsx +++ b/src/datasets/ui/search/SuggestionsPage.tsx @@ -17,6 +17,7 @@ import Grid from '@material-ui/core/Grid'; import TextField from '@material-ui/core/TextField'; import Loading from 'ui/common/Loading'; +import PageLayout from 'ui/layout/PageLayout'; import SuggestionsForm from './c/SuggestionsForm'; interface ISuggestionsPageProps extends React.ClassAttributes { @@ -103,8 +104,8 @@ class SuggestionsPage extends React.Component { const { search } = this.state; return ( - - + + { { loading && } { suggestions && } - + ); } } diff --git a/src/datasets/ui/search/c/SuggestionsForm.tsx b/src/datasets/ui/search/c/SuggestionsForm.tsx index e875844..31096d2 100644 --- a/src/datasets/ui/search/c/SuggestionsForm.tsx +++ b/src/datasets/ui/search/c/SuggestionsForm.tsx @@ -90,7 +90,9 @@ const SuggestionsForm = ({classes, t, suggestions, onReset, handleSubmit, initia { suggestions['search.matches'] && suggestions['search.matches'].hits.length > 0 && -

Suggested matches

+
+

{ t('datasets.public.c.suggestionsForm.matches') }

+
{ suggestions['search.matches'].hits.map((hit, i) => ( @@ -100,7 +102,10 @@ const SuggestionsForm = ({classes, t, suggestions, onReset, handleSubmit, initia }
- +
); diff --git a/src/datasets/ui/search/c/hits/_Generic.tsx b/src/datasets/ui/search/c/hits/_Generic.tsx index bab94b3..363f50e 100644 --- a/src/datasets/ui/search/c/hits/_Generic.tsx +++ b/src/datasets/ui/search/c/hits/_Generic.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import { translate } from 'react-i18next'; -const Generic = ({ hit }) => ( +const Generic = ({ hit, t }) => (
-

Type { hit._class } does not have a SearchHit implementation.

+

{ t('datasets.public.c.generic', { boldHit: { hit._class } }) }

{ JSON.stringify(hit, null, 2) }
); diff --git a/src/descriptorlists/routes.ts b/src/descriptorlists/routes.ts index 8af99b8..81cbe42 100644 --- a/src/descriptorlists/routes.ts +++ b/src/descriptorlists/routes.ts @@ -17,8 +17,8 @@ const publicRoutes = [ path: '/descriptorlists', component: Wrapper, extraProps: { - title: 'p.descriptorlists.title', - subtitle: 'p.descriptorlists.subtitle', + title: 'descriptorlists.common.modelName_plural', + subtitle: 'descriptorlists.common.subtitle', }, routes: [ { diff --git a/src/descriptorlists/translations.json b/src/descriptorlists/translations.json new file mode 100644 index 0000000..a9537ac --- /dev/null +++ b/src/descriptorlists/translations.json @@ -0,0 +1,96 @@ +{ + "common": { + "modelName": "Descriptor list", + "modelName_plural": "Descriptor lists", + "menu": "Descriptor lists", + "subtitle": "Compilations of crop descriptors" + }, + "public": { + "common": { + "crop": "Crop", + "publisher": "Publisher", + "version": "Version", + "url": "URL", + "partner": "Data provider" + }, + "c": { + "card": { + "untitled": "Untitled" + }, + "descrListsDisplay": { + "publishDescription": "The descriptor list will be reviewed by administrator before publishing.", + "approveDescription": "After approving the descriptor list no changes are permitted, a new version must be created.", + "deleteDescription": "Deleting the descriptor is only possible when there is no associated data.", + "createDescription": "Creating the new descriptor version will replace current descriptor from the descriptor list.", + "create": "Create new version", + "createText": "Create a new version of the descriptor", + "createNew": "Create new version", + "notPublishedAlert": "This descriptor list is not yet published!", + "citation": "Bibliographic citation", + "maintained": "Maintained by", + "table": "Table of contents", + "notSpecified": "Not specified" + }, + "form": { + "selectPartner": "Select data provider", + "you": "You're", + "admin": "ADMINISTRATOR", + "get": "and you get to pick the", + "ifYou": "if you want to", + "text": "Leave field blank to have it automatically assigned. Standard codes used by the Catalog", + "descriptors": "Genesys PGR descriptors", + "listTitle": "Descriptor list title", + "maize": "maize", + "titlePlaceholder": "Descriptors for pearl millet", + "description": "Description", + "descriptionPlaceholder": "Full description of the descriptor list", + "urlToDef": "URL to definition", + "publisher": "Original publisher", + "citation": "Bibliographic citation", + "citationPlaceholder": "Complete bibliographic citation of the crop descriptor. Use APA formatting style (https://owl.english.purdue.edu/owl/resource/560/01/)" + }, + "extras": { + "json": "JSON mappings", + "save": "Save extras", + "defaults": "Defaults" + } + }, + "f": { + "title": "Crop descriptors", + "keywordSearch": "Keyword search", + "rice": "mardi rice", + "originalPublisher": "Original publisher" + + }, + "p": { + "display": { + "title": "Descriptor list details" + }, + "browse": { + "subtitle": "Compilations of crop descriptors" + } + } + }, + "dashboard": { + "p": { + "stepper": { + "basicInfo": "Basic information", + "import": { + "title": "Upload new descriptors", + "uploaded": "Uploaded {{count}} descriptor definitions", + "noDescriptors": "No descriptors uploaded", + "error": "error", + "error_plural": "errors", + "uploading": "due uploading" + }, + "select": "Select existing descriptors", + "reorder": "Reorder descriptors", + "review": "Review and publish", + "publishDescription": "The descriptor list will be reviewed by the administrator before publishing.", + "deleteDescription": "Deleting the descriptor list will remove all related data.", + "publisher": "Descriptor list publisher", + "subtitle": "Publish your descriptor list" + } + } + } +} \ No newline at end of file diff --git a/src/descriptorlists/ui/BrowsePage.tsx b/src/descriptorlists/ui/BrowsePage.tsx index 7339841..51803b6 100644 --- a/src/descriptorlists/ui/BrowsePage.tsx +++ b/src/descriptorlists/ui/BrowsePage.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { translate } from 'react-i18next'; import { log } from 'utilities/debug'; import { parse } from 'query-string'; @@ -42,6 +43,7 @@ interface IBrowsePageProps extends React.ClassAttributes { crop: any; history: any; location: any; + t: any; } // Page to browse and filter descriptor lists @@ -118,7 +120,7 @@ class BrowsePage extends React.Component { ) public render() { - const {paged, pagination} = this.props; + const {paged, pagination, t} = this.props; const stillLoading: boolean = (! paged || ! paged.content); @@ -127,7 +129,9 @@ class BrowsePage extends React.Component { }> - + bindActionCreators({ filterCodeToUrl, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(BrowsePage)); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(BrowsePage))); diff --git a/src/descriptorlists/ui/DisplayPage.tsx b/src/descriptorlists/ui/DisplayPage.tsx index 28c26da..cf5aa3c 100644 --- a/src/descriptorlists/ui/DisplayPage.tsx +++ b/src/descriptorlists/ui/DisplayPage.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { withStyles } from '@material-ui/core/styles'; +import { translate } from 'react-i18next'; import { loadDescriptorList, publishDescriptorList, approveDescriptorList, rejectDescriptorList, @@ -29,6 +30,7 @@ interface IDescriptorListPageProps extends React.ClassAttributes { deleteDescriptorList: any; setDescriptorsToDescriptorList: any; copyDescriptor: any; + t: any; } const styles = (theme) => ({ @@ -53,7 +55,7 @@ class DescriptorListPage extends React.Component } public render() { - const {classes, uuid, deleteDescriptorList, setDescriptorsToDescriptorList, copyDescriptor, descriptorList} = this.props; + const {classes, uuid, deleteDescriptorList, setDescriptorsToDescriptorList, copyDescriptor, descriptorList, t} = this.props; const {approveDescriptorList, rejectDescriptorList, publishDescriptorList} = this.props; const stillLoading: boolean = (! descriptorList || (uuid && (descriptorList.uuid !== uuid))); @@ -61,7 +63,9 @@ class DescriptorListPage extends React.Component return ( - } /> + } + /> { stillLoading ? : bindActionCreators({ setDescriptorsToDescriptorList, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(DescriptorListPage)); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(DescriptorListPage))); diff --git a/src/descriptorlists/ui/c/Card.tsx b/src/descriptorlists/ui/c/Card.tsx index cee820b..eb8a0ba 100644 --- a/src/descriptorlists/ui/c/Card.tsx +++ b/src/descriptorlists/ui/c/Card.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { translate } from 'react-i18next'; import DescriptorList from 'model/catalog/DescriptorList'; import { DescriptorListLink, ExternalLink } from 'ui/catalog/Links'; @@ -8,24 +9,39 @@ import Card, { CardHeader, CardContent } from 'ui/common/Card'; import Markdown from 'ui/catalog/markdown'; import CropChips from 'crops/ui/c/CropChips'; -const DescriptorListCard = ({descriptorList, className}: {descriptorList: DescriptorList, className: string}) => { +const DescriptorListCard = ({descriptorList, className, t}: {descriptorList: DescriptorList, className: string, t: any}) => { if (descriptorList) { return ( { descriptorList.title ? : 'Untitled' } + + { descriptorList.title ? + + : + t('descriptorlists.public.c.card.untitled') + } + ) } /> - { descriptorList.crop && } + { descriptorList.crop && + + + } { descriptorList.publisher ? - { descriptorList.publisher } + { descriptorList.publisher } : - { descriptorList.owner.shortName } + { descriptorList.owner.shortName } + } + { descriptorList.url && + + { { descriptorList.url } } + } - { descriptorList.url && { { descriptorList.url } } } - { descriptorList.versionTag } + + { descriptorList.versionTag } + @@ -35,4 +51,4 @@ const DescriptorListCard = ({descriptorList, className}: {descriptorList: Descri } }; -export default DescriptorListCard; +export default translate()(DescriptorListCard); diff --git a/src/descriptorlists/ui/c/DescriptorListDisplay.tsx b/src/descriptorlists/ui/c/DescriptorListDisplay.tsx index 453403d..e3a5859 100644 --- a/src/descriptorlists/ui/c/DescriptorListDisplay.tsx +++ b/src/descriptorlists/ui/c/DescriptorListDisplay.tsx @@ -3,6 +3,7 @@ import { withStyles } from '@material-ui/core/styles'; import DescriptorList from 'model/catalog/DescriptorList'; import { PublishState } from 'model/common.model'; import confirm from 'utilities/confirmAlert'; +import { translate } from 'react-i18next'; import { log } from 'utilities/debug'; import { fixDate } from 'utilities'; @@ -66,6 +67,7 @@ interface IDetailInfoProps extends React.ClassAttributes { setDescriptorsToDescriptorList?: any; copyDescriptor?: any; review?: any; + t: any; } class DetailInfo extends React.Component { @@ -75,12 +77,12 @@ class DetailInfo extends React.Component { } protected onPublish = () => { - const {descriptorList, publishDescriptorList} = this.props; + const {descriptorList, publishDescriptorList, t} = this.props; - confirm(Publish { descriptorList.title }?, { - description: `The descriptor list will be reviewed by administrator before publishing.`, - confirmLabel: 'Publish', - abortLabel: 'Cancel', + confirm({ t('common:action.publish') } { descriptorList.title }?, { + description: t('descriptorlists.public.c.descrListsDisplay.publishDescription'), + confirmLabel: t('common:action.publish'), + abortLabel: t('common:action.cancel'), }).then(() => { log('Publishing descriptor list', descriptorList); publishDescriptorList(descriptorList); @@ -90,12 +92,12 @@ class DetailInfo extends React.Component { } private onUnpublish = (e) => { - const {descriptorList, rejectDescriptorList} = this.props; + const {descriptorList, rejectDescriptorList, t} = this.props; - confirm(Unpublish { descriptorList.title }?, { + confirm({ t('common:action.unpublish') } { descriptorList.title }?, { // description: `Deleting the descriptor is only possible when there is no associated data.`, - confirmLabel: 'Unpublish', - abortLabel: 'Cancel', + confirmLabel: t('common:action.unpublish'), + abortLabel: t('common:action.cancel'), }).then(() => { log('Publishing descriptor list', descriptorList); rejectDescriptorList(descriptorList, false); @@ -105,12 +107,12 @@ class DetailInfo extends React.Component { } private onApprove = (e) => { - const {descriptorList, approveDescriptorList} = this.props; + const {descriptorList, approveDescriptorList, t} = this.props; - confirm(Approve { descriptorList.title }?, { - description: `After approving the descriptor list no changes are permitted, a new version must be created.`, - confirmLabel: 'Approve', - abortLabel: 'Cancel', + confirm({ t('common:action.approve') } { descriptorList.title }?, { + description: t('descriptorlists.public.c.descrListsDisplay.approveDescription'), + confirmLabel: t('common:action.approve'), + abortLabel: t('common:action.cancel'), }).then(() => { log('Approving descriptorList', descriptorList); approveDescriptorList(descriptorList); @@ -126,12 +128,12 @@ class DetailInfo extends React.Component { } private onDelete = (e) => { - const {descriptorList, deleteDescriptorList} = this.props; + const {descriptorList, deleteDescriptorList, t} = this.props; - confirm(Delete { descriptorList.title }?, { - description: `Deleting the descriptor is only possible when there is no associated data.`, - confirmLabel: 'Delete', - abortLabel: 'Cancel', + confirm({ t('common:action.delete') } { descriptorList.title }?, { + description: t('descriptorlists.public.c.descrListsDisplay.deleteDescription'), + confirmLabel: t('common:action.delete'), + abortLabel: t('common:action.cancel'), }).then(() => { deleteDescriptorList(descriptorList); }).catch(() => { @@ -140,13 +142,13 @@ class DetailInfo extends React.Component { } private createNewDescriptorVersion = (descriptor: Descriptor) => async () => { - const {descriptorList, copyDescriptor, setDescriptorsToDescriptorList} = this.props; + const {descriptorList, copyDescriptor, setDescriptorsToDescriptorList, t} = this.props; try { - await confirm(Create a new version of the descriptor { descriptor.title }?, { - description: `Creating the new descriptor version will replace current descriptor from the descriptor list.`, - confirmLabel: 'Create new version', - abortLabel: 'Cancel', + await confirm({ t('descriptorlists.public.c.descrListsDisplay.createDescription') } { descriptor.title }?, { + description: t('descriptorlists.public.c.descrListsDisplay.createText'), + confirmLabel: t('descriptorlists.public.c.descrListsDisplay.create'), + abortLabel: t('common:action.cancel'), }); const copy: Descriptor = { @@ -165,16 +167,20 @@ class DetailInfo extends React.Component { } private getDescriptorActionButton = (descriptor: Descriptor) => { - const {descriptorList} = this.props; + const {descriptorList, t} = this.props; if (descriptorList.state !== PublishState.PUBLISHED && descriptorList._permissions.write) { if (descriptor.state === PublishState.PUBLISHED) { - return ; + return ( + + ); } return ( - + ); } @@ -183,7 +189,7 @@ class DetailInfo extends React.Component { } public render() { - const {classes, descriptorList, review} = this.props; + const {classes, descriptorList, review, t} = this.props; const oneDay = 24 * 60 * 60 * 1000; const oneDayPassed = descriptorList && (fixDate(descriptorList.lastModifiedDate).getTime() <= (new Date()).getTime() - oneDay); @@ -201,25 +207,50 @@ class DetailInfo extends React.Component { ) } /> - { descriptorList.state !== PublishState.PUBLISHED &&

This descriptor list is not yet published!

} - { descriptorList.description && } + { descriptorList.state !== PublishState.PUBLISHED && +

{ t('descriptorlists.public.c.descrListsDisplay.notPublishedAlert') }

+ } + { descriptorList.description && + + } { descriptorList.crop && - } + + + + } - { descriptorList.publisher || Not specified } + + { descriptorList.publisher || + { t('descriptorlists.public.c.descrListsDisplay.notSpecified') } + } + { descriptorList.url && - { { descriptorList.url } } } + + { + { descriptorList.url } + + } + + } { descriptorList.bibliographicCitation && - } + + + + } { (descriptorList.publisher === null && descriptorList.owner) && - } + + + + } - { descriptorList.versionTag } + + { descriptorList.versionTag } +
@@ -227,23 +258,44 @@ class DetailInfo extends React.Component { { descriptorList.state === PublishState.PUBLISHED ? descriptorList._permissions.manage && !oneDayPassed ? - + : - + : null } - { descriptorList.state === PublishState.DRAFT && descriptorList._permissions.write && } - { descriptorList.state !== PublishState.PUBLISHED && descriptorList._permissions.write && } + { descriptorList.state === PublishState.DRAFT && + descriptorList._permissions.write && + + } + { descriptorList.state !== PublishState.PUBLISHED && + descriptorList._permissions.write && + + } { descriptorList.state === PublishState.REVIEWING && - + } - { descriptorList.state !== PublishState.PUBLISHED && descriptorList._permissions.delete && } - { descriptorList._permissions.manage && } - + { descriptorList.state !== PublishState.PUBLISHED && + descriptorList._permissions.delete && + + } + { descriptorList._permissions.manage && + + } + + + ) } @@ -266,7 +318,7 @@ class DetailInfo extends React.Component { )) }
-
+
{ descriptorList.descriptors && descriptorList.descriptors.length > 0 && descriptorList.descriptors.map((descriptor: Descriptor) => ( )) } @@ -277,4 +329,4 @@ class DetailInfo extends React.Component { ); } } -export default ((withStyles as any)(styles)(DetailInfo)); +export default translate()(((withStyles as any)(styles)(DetailInfo))); diff --git a/src/descriptorlists/ui/c/DescriptorListForm.tsx b/src/descriptorlists/ui/c/DescriptorListForm.tsx index 1d519a5..b997802 100644 --- a/src/descriptorlists/ui/c/DescriptorListForm.tsx +++ b/src/descriptorlists/ui/c/DescriptorListForm.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import {Field, reduxForm} from 'redux-form'; +import { translate } from 'react-i18next'; import { DESCRIPTORLIST_FORM } from 'descriptors/constants'; import { TextField } from 'ui/common/text-field'; @@ -13,7 +14,7 @@ import CropSelector from 'crops/ui/c/CropSelector'; class DescriptorListForm extends React.Component { public render() { - const {error, handleSubmit, initialValues, partners} = this.props; + const {error, handleSubmit, initialValues, partners, t} = this.props; return (
{ initialValues && initialValues.version &&
Version: { initialValues.version }
} @@ -21,7 +22,7 @@ class DescriptorListForm extends React.Component { {

- You're ADMINISTRATOR and you get to pick the UUID if you want to. - Leave field blank to have it automatically assigned. - Standard codes used by the Catalog: + { t('descriptorlists.public.c.form.you') } { t('descriptorlists.public.c.form.admin') } + { t('descriptorlists.public.c.form.get') } UUID + { t('descriptorlists.public.c.form.ifYou') }. + { t('descriptorlists.public.c.form.text') }:

    -
  • dc1d4e81-a6dd-4f03-b682-53a3a1383988 Genesys PGR descriptors
  • +
  • dc1d4e81-a6dd-4f03-b682-53a3a1383988 + { t('descriptorlists.public.c.form.descriptors') } +
{ { { } } -export default reduxForm({ +export default translate()(reduxForm({ enableReinitialize: true, // https://redux-form.com/7.1.0/examples/initializefromstate/ form: DESCRIPTORLIST_FORM, onSubmit: remoteSubmit, -})(DescriptorListForm); +})(DescriptorListForm)); diff --git a/src/descriptorlists/ui/c/Extras.tsx b/src/descriptorlists/ui/c/Extras.tsx index 41eb86f..7281c82 100644 --- a/src/descriptorlists/ui/c/Extras.tsx +++ b/src/descriptorlists/ui/c/Extras.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; // import {connect} from 'react-redux'; // import {bindActionCreators} from 'redux'; +import { translate } from 'react-i18next'; import {log} from 'utilities/debug'; @@ -20,6 +21,7 @@ interface IExtras extends React.ClassAttributes { className?: any; descriptorList: DescriptorList; onSave: any; + t: any; } // Page to edit descriptor list extras @@ -74,20 +76,21 @@ class Extras extends React.Component { } public render() { + const { t } = this.props; return (
- JSON mappings + { t('descriptorlists.public.c.extras.json') } - - + +
); } } -export default Extras; +export default translate()(Extras); diff --git a/src/descriptorlists/ui/c/Filters.tsx b/src/descriptorlists/ui/c/Filters.tsx index ff8ac7c..44f712c 100644 --- a/src/descriptorlists/ui/c/Filters.tsx +++ b/src/descriptorlists/ui/c/Filters.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { reduxForm } from 'redux-form'; +import { translate } from 'react-i18next'; import { DESCRIPTORLIST_FILTERFORM } from 'descriptors/constants'; @@ -11,20 +12,23 @@ import TextFilter from 'ui/common/filter/TextFilter'; import PartnerFilter from 'partners/ui/c/PartnerFilter'; -const DescriptorListFilters = ({handleSubmit, initialize, ...other}) => ( - - - - +const DescriptorListFilters = ({handleSubmit, initialize, t, ...other}) => ( + + + + - - + + ); -export default reduxForm({ +export default translate()(reduxForm({ enableReinitialize: true, form: DESCRIPTORLIST_FILTERFORM, -})(DescriptorListFilters); +})(DescriptorListFilters)); diff --git a/src/descriptorlists/ui/descriptorlist-stepper/index.tsx b/src/descriptorlists/ui/descriptorlist-stepper/index.tsx index e9a2e31..082439e 100644 --- a/src/descriptorlists/ui/descriptorlist-stepper/index.tsx +++ b/src/descriptorlists/ui/descriptorlist-stepper/index.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import {bindActionCreators} from 'redux'; +import { translate } from 'react-i18next'; import { log } from 'utilities/debug'; import confirm from 'utilities/confirmAlert'; @@ -27,6 +28,7 @@ interface IDescriptorListProps extends React.ClassAttributes { pageTitle: string; navigateTo: (path: string) => void; setPageTitle: (title: string) => void; + t: any; publishDescriptorList: (descriptorList: DescriptorList, showDescriptorList: boolean) => void; deleteDescriptorList: (descriptorList: DescriptorList) => Promise; @@ -40,11 +42,11 @@ class DescriptorListStepper extends React.Component { public constructor(props: any) { super(props); - const {pageTitle, setPageTitle} = this.props; + const {pageTitle, setPageTitle, t} = this.props; this.state = { childrenRef: null, }; - setPageTitle(pageTitle); + setPageTitle(t(pageTitle)); } public componentWillMount() { @@ -66,12 +68,12 @@ class DescriptorListStepper extends React.Component { } protected onDelete = (e) => { - const {descriptorList, deleteDescriptorList, navigateTo} = this.props; + const {descriptorList, deleteDescriptorList, navigateTo, t} = this.props; log('Removing descriptorList', descriptorList); - confirm(Delete { descriptorList.title }?, { - description: `Deleting the descriptor list will remove all related data.`, - confirmLabel: 'Delete', - abortLabel: 'Cancel', + confirm({ t('common:action.delete') } { descriptorList.title }?, { + description: t('descriptorlists.dashboard.p.stepper.deleteDescription'), + confirmLabel: t('common:action.delete'), + abortLabel: t('common:action.cancel'), }).then(() => { deleteDescriptorList(descriptorList).then(() => navigateTo(`/dashboard/descriptorlists`)); }).catch(() => { @@ -80,12 +82,12 @@ class DescriptorListStepper extends React.Component { } protected onPublish = () => { - const {descriptorList, publishDescriptorList} = this.props; + const {descriptorList, publishDescriptorList, t} = this.props; - confirm(Publish { descriptorList.title }?, { - description: `The descriptor list will be reviewed by the administrator before publishing.`, - confirmLabel: 'Publish', - abortLabel: 'Cancel', + confirm({ t('common:action.publish') } { descriptorList.title }?, { + description: t('descriptorlists.dashboard.p.stepper.publishDescription'), + confirmLabel: t('common:action.publish'), + abortLabel: t('common:action.cancel'), }).then(() => { log('Publishing descriptor list', descriptorList); publishDescriptorList(descriptorList, true); @@ -95,12 +97,13 @@ class DescriptorListStepper extends React.Component { } public render() { - const {uuid, descriptorList, pageTitle, route, match} = this.props; + const {uuid, descriptorList, pageTitle, route, match, t} = this.props; const stillLoading: boolean = (! descriptorList || (uuid && (descriptorList.uuid !== uuid))); return ( - + { renderRoutes(route.routes, match.path, { stillLoading, onGotoStep: this.gotoStep, onDelete: this.onDelete, onPublish: this.onPublish }) } @@ -125,4 +128,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ navigateTo, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(DescriptorListStepper); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(DescriptorListStepper)); diff --git a/src/descriptorlists/ui/descriptorlist-stepper/steps.ts b/src/descriptorlists/ui/descriptorlist-stepper/steps.ts index db97762..6284e7e 100644 --- a/src/descriptorlists/ui/descriptorlist-stepper/steps.ts +++ b/src/descriptorlists/ui/descriptorlist-stepper/steps.ts @@ -8,35 +8,35 @@ import DescriptorListBasicStep from 'descriptorlists/ui/descriptorlist-stepper/s const steps = [ { id: 1, - name: 'Basic information', + name: 'descriptorlists.dashboard.p.stepper.basicInfo', path: 'edit', component: DescriptorListBasicStep, exact: true, }, { id: 2, - name: 'Upload new descriptors', + name: 'descriptorlists.dashboard.p.stepper.import.title', path: 'edit/import-descriptors', component: ImportDescriptorsStep, exact: true, }, { id: 3, - name: 'Select existing descriptors', + name: 'descriptorlists.dashboard.p.stepper.select', path: 'edit/select-descriptors', component: SelectDescriptorsStep, exact: true, }, { id: 4, - name: 'Reorder descriptors', + name: 'descriptorlists.dashboard.p.stepper.reorder', path: 'edit/reorder-descriptors', component: ReorderDescriptorsStep, exact: true, }, { id: 5, - name: 'Review and publish', + name: 'descriptorlists.dashboard.p.stepper.review', path: 'edit/review-and-publish', component: ReviewDescriptorListStep, exact: true, diff --git a/src/descriptorlists/ui/descriptorlist-stepper/steps/import/index.tsx b/src/descriptorlists/ui/descriptorlist-stepper/steps/import/index.tsx index b736650..4d974f7 100644 --- a/src/descriptorlists/ui/descriptorlist-stepper/steps/import/index.tsx +++ b/src/descriptorlists/ui/descriptorlist-stepper/steps/import/index.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; +import { translate } from 'react-i18next'; import { log } from 'utilities/debug'; import DescriptorList from 'model/catalog/DescriptorList'; @@ -24,6 +25,7 @@ interface IDescriptorListProps extends React.ClassAttributes { onPublish: () => void; onGotoStep: (i: number) => () => void; location: any; + t: any; } class ImportDescriptorsStep extends React.Component { @@ -74,7 +76,7 @@ class ImportDescriptorsStep extends React.Component { } public render() { - const { location, stillLoading, onDelete, onPublish } = this.props; + const { location, stillLoading, onDelete, onPublish, t } = this.props; const notSaved: number = this.state.nonSavedDescriptors.length; return ( @@ -85,7 +87,7 @@ class ImportDescriptorsStep extends React.Component { { this.state.uploadedDescriptors.length > 0 ? (

{ `Uploaded ${this.state.uploadedDescriptors.length} descriptor definitions` }

- +

{ t('descriptorlists.dashboard.p.stepper.import.uploaded', {count: this.state.uploadedDescriptors.length}) }

{ this.state.uploadedDescriptors.map((d, index) => ( @@ -95,12 +97,19 @@ class ImportDescriptorsStep extends React.Component {
) : ( -

No descriptors uploaded

+

{ t('descriptorlists.dashboard.p.stepper.import.noDescriptors') }

) } { notSaved > 0 && (
-

{ `${notSaved} error${notSaved !== 1 ? 's' : '' } due uploading` }

+

+ { notSaved } + { + notSaved !== 1 ? t('descriptorlists.dashboard.p.stepper.import.error_plural') : + t('descriptorlists.dashboard.p.stepper.import.error') + } + { ' ' }{ t('descriptorlists.dashboard.p.stepper.import.uploading') } +

{ this.state.nonSavedDescriptors.map((d, index) => ( @@ -134,6 +143,6 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ importDescriptor, }, dispatch); -export default connect( +export default translate()(connect( mapStateToProps, mapDispatchToProps, -)(ImportDescriptorsStep); +)(ImportDescriptorsStep)); diff --git a/src/descriptors/routes.ts b/src/descriptors/routes.ts index 459da07..544241f 100644 --- a/src/descriptors/routes.ts +++ b/src/descriptors/routes.ts @@ -12,8 +12,8 @@ const publicRoutes = [ path: '/descriptors', component: Wrapper, extraProps: { - title: 'p.descriptors.title', - subtitle: 'p.descriptors.subtitle', + title: 'descriptors.common.title', + subtitle: 'descriptors.common.subtitle', }, routes: [ { diff --git a/src/descriptors/translations.json b/src/descriptors/translations.json new file mode 100644 index 0000000..3af2427 --- /dev/null +++ b/src/descriptors/translations.json @@ -0,0 +1,213 @@ +{ + "common": { + "modelName": "Descriptor", + "modelName_plural": "Descriptors", + "title": "Descriptor definitions", + "subtitle": "Genesys Catalog of published descriptor definitions", + "category": { + "passport": "Passport descriptor", + "management": "Management descriptor", + "environment": "Environment and Site descriptor", + "characterization": "Characterization descriptor", + "evaluation": "Evaluation descriptor", + "abioticstress": "Abiotic stress descriptor", + "bioticstress": "Biotic stress descriptor", + "molecular": "Molecular marker descriptor" + }, + "dataType": { + "numeric": "Numeric", + "text": "Free text", + "scale": "Scale", + "coded": "Coded", + "boolean": "Yes/no", + "date": "Date" + }, + "crop": { + "null": "undefined", + "maize": "Maize", + "yam": "Yam" + } + }, + "admin": { + "p": { + "edit": { + "title": "Edit descriptor", + "dataPublication": "Data publication", + "back": "BACK TO DASHBOARD" + } + }, + "c": { + "form": { + "dataType": "Data type", + "code": "Code", + "title": "Title", + "description": "Description", + "descriptorCategory": "Descriptor category", + "owner": "Owner of the descriptor", + "partner": "Data provider", + "crop": "Crop", + "descriptorTitle": "Descriptor title", + "color": "Color of magic", + "version": "Version", + "publisher": "Original publisher", + "fullDescription": "Full description of the descriptor", + "key": "Flag as key access and utilization descriptor as defined in Alercia (2011)", + "column": "Column name", + "columnDescription": "Common code name for descriptor in databases and spreadsheets.", + "unit": "Unit of measure", + "unitPlaceholder": "kg", + "citation": "Bibliographic citation", + "citationPlaceholder": "Complete bibliographic citation of the crop descriptor. Use APA formatting style (https://owl.english.purdue.edu/owl/resource/560/01/).", + "integers": "Allow only integers, no decimals", + "min": "Minimum value", + "minPlaceholder": "Minimum numeric value allowed by the descriptor.", + "max": "Maximum value", + "maxPlaceholder": "Maximum value allowed by the descriptor.", + "term": "Vocabulary term", + "term_plural": "Vocabulary terms", + "vocabulary": "Controlled vocabulary", + "approvePublish": "Approve and Publish" + }, + "selectVocabulary": { + "rename": "Rename this vocabulary" + }, + "vocabularyForm": { + "code": "Code", + "title": "Title", + "description": "Description", + "vocabularyDetails": "Vocabulary details", + "vocabularyTitle": "Vocabulary title", + "color": "Color of magic", + "descriptionPlaceholder": "Full description of the vocabulary", + "versionTag": "Version tag", + "urlToDef": "URL to definition", + "termUrlPrefix": "Term URL prefix", + "vocabularyTerm": "Vocabulary terms", + "vocabularyTerm_plural": "Vocabulary terms" + } + } + }, + "dashboard": { + "f": { + "title": "Filters", + "status": "Status", + "descriptorInUse": "Descriptor is in use", + "keywordSearch": "Keyword search", + "rice": "mardi rice", + "selectOwner": "Select owner", + "crop": "Crop" + }, + "c": { + "listPicker": { + "search": "Search..." + }, + "parser": { + "instructions": "INSTRUCTIONS FOR USE", + "firstItem": "Use \"Descriptors\" sheet from template", + "link": "Genesys Catalog template", + "secondItem": "The first row in the template contains the header names, the second row contains a description of the content expected on each column, and rows three and four contain examples to help you fill in the template.", + "thirdItem": "Do not change the header names in the template", + "fourthItem": "Fill the template with the descriptor information, remove the rows that contain the description of each column (second row) and examples (third and fourth row) and save the file.", + "fifthItem": "Copy and paste the table from Excel into the text field \"Descriptor definitions\".", + "paste": "Paste descriptor data here (tab separated)" + }, + "picker": { + "available": "descriptors available", + "addAll": "Add all", + "removeAll": "Remove all", + "selected": "selected", + "zero": "0 descriptors selected:", + "descriptor": "descriptor", + "descriptor_plural": "descriptors" + } + } + }, + "public": { + "c": { + "card": { + "untitled": "Untitled", + "keyDescriptor": "This is a key descriptor for access and utilization of PGR.", + "publisher": "Publisher", + "maintained": "Maintained by", + "version": "Version", + "cropName": "Crop", + "classification": "Classification", + "type": "Data type", + "notSpecified": "Not specified", + "select": "+ SELECT DESCRIPTOR", + "unselect": "UNSELECT" + }, + "descriptorFilterForm": { + "apply": "Apply", + "reset": "Reset" + }, + "searchMenu": { + "search": "Search" + } + }, + "f": { + "keyword": "Keyword search", + "rice": "mardi rice", + "partner": "Data provider", + "select": "Select descriptor list", + "crop": "Crop", + "category": "Descriptor category", + "tidbits": "Tidbits", + "isUsed": "Descriptor is used", + "key": "Key crop descriptor", + "columnName": "Column name", + "uom": "UOM", + "language": "Language" + }, + "p": { + "browse": { + "title": "Descriptor definitions", + "subtitle": "Genesys Catalog of published descriptor definitions" + }, + "display": { + "publishDescription": "The descriptor will be reviewed by the administrator before publishing.", + "approveDescription": "After approving the descriptor no changes are permitted, a new version must be created.", + "deleteDescription": "Deleting the descriptor is only possible when there is no associated data.", + "title": "Descriptor details", + "isNotPublishedAlert": "This descriptor is not yet published!", + "keyAlert": "This descriptor is a key descriptor for access and utilization of PGR.", + "crop": "Crop", + "version": "Version", + "category": "Category", + "maintainedBy": "Maintained by", + "definition": "Descriptor definition", + "type": "Descriptor type", + "databaseName": "Database name", + "min": "Minimum allowed value", + "max": "Maximum allowed value", + "scale": "Scale", + "allowedValues": "Allowed values:", + "integers": "Integers", + "decimals": "Decimals", + "codingTable": "Descriptor coding table", + "code": "Code", + "term": "Term", + "description": "Description", + "vocabulary": "Controlled vocabulary", + "note": "Note", + "thisDescriptor": "This descriptor uses a controlled vocabulary", + "publishedBy": "published by", + "maintainedBy_l": "maintained by", + "datasets": "Datasets", + "cropDescriptors": "Crop descriptors", + "recordMetadata": "Record metadata", + "uuid": "UUID", + "recordVersion": "Record version", + "lastUpdated": "Last updated" + } + } + }, + "sort": { + "crop": "Crop", + "dataType": "Data type", + "key": "Key", + "uom": "Unit of measure", + "columnName": "Column name", + "owner": "Maintainer" + } +} \ No newline at end of file diff --git a/src/descriptors/ui/BrowsePage.tsx b/src/descriptors/ui/BrowsePage.tsx index 6e0992c..2365d89 100644 --- a/src/descriptors/ui/BrowsePage.tsx +++ b/src/descriptors/ui/BrowsePage.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { withStyles } from '@material-ui/core/styles'; +import { translate } from 'react-i18next'; import { log } from 'utilities/debug'; import { parse } from 'query-string'; @@ -33,6 +34,7 @@ interface IDescriptorListsPageProps extends React.ClassAttributes { listCrops: () => any; filterCodeToUrl: any; loading: any; + t: any; } const styles = (theme) => ({ @@ -105,7 +107,7 @@ class BrowsePage extends React.Component { } public render() { - const { paged, pagination, loadDescriptors } = this.props; + const { paged, pagination, loadDescriptors, t } = this.props; const stillLoading: boolean = (! paged || ! paged.content); @@ -119,7 +121,9 @@ class BrowsePage extends React.Component { }> - + bindActionCreators({ filterCodeToUrl, }, dispatch); -export default connect( +export default translate()(connect( mapStateToProps, mapDispatchToProps, -)((withStyles as any)(styles)(BrowsePage)); +)((withStyles as any)(styles)(BrowsePage))); diff --git a/src/descriptors/ui/DisplayPage.tsx b/src/descriptors/ui/DisplayPage.tsx index f56859a..dcff32d 100644 --- a/src/descriptors/ui/DisplayPage.tsx +++ b/src/descriptors/ui/DisplayPage.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { translate } from 'react-i18next'; import { log } from 'utilities/debug'; import { fixDate } from 'utilities'; @@ -46,6 +47,7 @@ interface IDisplayPageProps extends React.ClassAttributes { deleteDescriptor: (descriptor: Descriptor) => void; descriptor: Descriptor; descriptorExtra: any; + t: any; } class DisplayPage extends React.Component { @@ -63,12 +65,12 @@ class DisplayPage extends React.Component { } private onPublish = (e) => { - const {descriptor, publishDescriptor} = this.props; + const {descriptor, publishDescriptor, t} = this.props; - confirm(Publish { descriptor.title }?, { - description: `The descriptor will be reviewed by the administrator before publishing.`, - confirmLabel: 'Publish', - abortLabel: 'Cancel', + confirm({ t('common:action.publish') } { descriptor.title }?, { + description: t('descriptors.public.p.display.publishDescription'), + confirmLabel: t('common:action.publish'), + abortLabel: t('common:action.cancel'), }).then(() => { log('Publishing descriptor', descriptor); publishDescriptor(descriptor); @@ -78,12 +80,12 @@ class DisplayPage extends React.Component { } private onUnpublish = (e) => { - const {descriptor, rejectDescriptor} = this.props; + const {descriptor, rejectDescriptor, t} = this.props; - confirm(Unpublish { descriptor.title }?, { + confirm({ t('common:action.unpublish') } { descriptor.title }?, { // description: `Deleting the descriptor is only possible when there is no associated data.`, - confirmLabel: 'Unpublish', - abortLabel: 'Cancel', + confirmLabel: t('common:action.unpublish'), + abortLabel: t('common:action.cancel'), }).then(() => { log('Un-publishing descriptor', descriptor); rejectDescriptor(descriptor, false); @@ -93,12 +95,12 @@ class DisplayPage extends React.Component { } private onApprove = (e) => { - const {descriptor, approveDescriptor} = this.props; + const {descriptor, approveDescriptor, t} = this.props; - confirm(Approve { descriptor.title }?, { - description: `After approving the descriptor no changes are permitted, a new version must be created.`, - confirmLabel: 'Approve', - abortLabel: 'Cancel', + confirm({ t('common:action.approve') } { descriptor.title }?, { + description: t('descriptors.public.p.display.approveDescription'), + confirmLabel: t('common:action.approve'), + abortLabel: t('common:action.cancel'), }).then(() => { log('Approving descriptor', descriptor); approveDescriptor(descriptor); @@ -114,12 +116,12 @@ class DisplayPage extends React.Component { } private onDelete = (e) => { - const {descriptor, deleteDescriptor} = this.props; + const {descriptor, deleteDescriptor, t} = this.props; - confirm(Delete { descriptor.title }?, { - description: `Deleting the descriptor is only possible when there is no associated data.`, - confirmLabel: 'Delete', - abortLabel: 'Cancel', + confirm({ t('common:action.delete') } { descriptor.title }?, { + description: t('descriptors.public.p.display.deleteDescription'), + confirmLabel: t('common:action.delete'), + abortLabel: t('common:action.cancel'), }).then(() => { deleteDescriptor(descriptor); }).catch(() => { @@ -128,7 +130,7 @@ class DisplayPage extends React.Component { } public render() { - const {uuid, descriptor, descriptorExtra} = this.props; + const {uuid, descriptor, descriptorExtra, t} = this.props; const stillLoading: boolean = (! descriptor || (uuid && (descriptor.uuid !== uuid))); const oneDay = 24 * 60 * 60 * 1000; @@ -139,24 +141,35 @@ class DisplayPage extends React.Component { return ( - } /> + } /> { stillLoading ? : } /> - { descriptor.state !== PublishState.PUBLISHED &&

This descriptor is not yet published!

} - { descriptor.key &&

This descriptor is a key descriptor for access and utilization of PGR.

} + { descriptor.state !== PublishState.PUBLISHED &&

{ t('descriptors.public.p.display.isNotPublishedAlert') }

} + { descriptor.key &&

{ t('descriptors.public.p.display.keyAlert') }

} { descriptor.description && } - { descriptor.crop && } - { descriptor.versionTag } - { descriptor.category } + { descriptor.crop && + + + + } + + { descriptor.versionTag } + + + { descriptor.category } + { (descriptor.publisher === null && descriptor.owner) && - } + + + + }
@@ -164,21 +177,21 @@ class DisplayPage extends React.Component { { descriptor.state === PublishState.PUBLISHED ? descriptor._permissions.manage && !oneDayPassed ? - + : - + : null } - { descriptor.state === PublishState.DRAFT && descriptor._permissions.write && } - { descriptor.state !== PublishState.PUBLISHED && descriptor._permissions.write && } + { descriptor.state === PublishState.DRAFT && descriptor._permissions.write && } + { descriptor.state !== PublishState.PUBLISHED && descriptor._permissions.write && } { descriptor.state === PublishState.REVIEWING && - + } - { descriptor.state !== PublishState.PUBLISHED && descriptor._permissions.delete && } + { descriptor.state !== PublishState.PUBLISHED && descriptor._permissions.delete && } { descriptor._permissions.manage && } ) } @@ -186,21 +199,26 @@ class DisplayPage extends React.Component {
-
+
- { descriptor.dataType } - { descriptor.columnName && { descriptor.columnName } } - { descriptor.minValue !== null && { descriptor.minValue } } - { descriptor.maxValue !== null && { descriptor.maxValue } } + { descriptor.dataType } + { descriptor.columnName && { descriptor.columnName } } + { descriptor.minValue !== null && { descriptor.minValue } } + { descriptor.maxValue !== null && { descriptor.maxValue } }
{ descriptor.dataType === DescriptorDataType.SCALE && -
+
- { descriptor.integerOnly ? 'Integers' : 'Decimals' } + + { descriptor.integerOnly ? + t('descriptors.public.p.display.integers') : + t('descriptors.public.p.display.decimals') + } +
@@ -209,12 +227,12 @@ class DisplayPage extends React.Component { { descriptor.dataType === DescriptorDataType.CODED && descriptor.terms && descriptor.terms.length > 0 && ( -
+
- Code - Term - Description + { t('descriptors.public.p.display.code') } + { t('descriptors.public.p.display.term') } + { t('descriptors.public.p.display.description') } ) }> { descriptor.terms.map((term: VocabularyTerm) => ( @@ -231,11 +249,11 @@ class DisplayPage extends React.Component { { descriptor.vocabulary && ( -
+
-

Note: This descriptor uses a controlled vocabulary { descriptor.vocabulary.publisher && - published by { descriptor.vocabulary.publisher } - } maintained by .

+

{ t('descriptors.public.p.display.note') } { t('descriptors.public.p.display.thisDescriptor') } { descriptor.vocabulary.publisher && + { t('descriptors.public.p.display.publishedBy') } { descriptor.vocabulary.publisher } + } { t('descriptors.public.p.display.maintainedBy_l') } .

@@ -244,7 +262,7 @@ class DisplayPage extends React.Component { { (descriptorExtra && descriptorExtra.datasets && descriptorExtra.datasets.length > 0) && ( -
+
{ descriptorExtra.datasets.map((dataset) => ( @@ -260,7 +278,7 @@ class DisplayPage extends React.Component { { (descriptorExtra && descriptorExtra.descriptorLists && descriptorExtra.descriptorLists.length > 0) && ( -
+
{ descriptorExtra.descriptorLists.map((descriptorList) => ( @@ -275,11 +293,17 @@ class DisplayPage extends React.Component { ) } -
+
- { descriptor.uuid } - { descriptor.version } - + + { descriptor.uuid } + + + { descriptor.version } + + + +
@@ -304,4 +328,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ deleteDescriptor, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(DisplayPage); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(DisplayPage)); diff --git a/src/descriptors/ui/EditPage.tsx b/src/descriptors/ui/EditPage.tsx index 22fb079..2c152cc 100644 --- a/src/descriptors/ui/EditPage.tsx +++ b/src/descriptors/ui/EditPage.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; +import { translate } from 'react-i18next'; import {log} from 'utilities/debug'; @@ -25,6 +26,7 @@ interface IDescriptorEditPageProps extends React.ClassAttributes { saveDescriptor: (descriptor: Descriptor) => void; publishDescriptor: (descriptor: Descriptor, showDescriptor: boolean) => void; descriptor: Descriptor; + t: any; } class DescriptorEditPage extends React.Component { @@ -61,7 +63,7 @@ class DescriptorEditPage extends React.Component } public render() { - const {uuid} = this.props; + const {uuid, t} = this.props; let {descriptor} = this.props; if (! descriptor && ! uuid) { @@ -75,11 +77,22 @@ class DescriptorEditPage extends React.Component return ( - }/> + + } + /> - + { descriptor.vocabulary && ( @@ -102,4 +115,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ publishDescriptor, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(DescriptorEditPage); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(DescriptorEditPage)); diff --git a/src/descriptors/ui/c/DashboardFilters.tsx b/src/descriptors/ui/c/DashboardFilters.tsx index 5142d41..4a1bd64 100644 --- a/src/descriptors/ui/c/DashboardFilters.tsx +++ b/src/descriptors/ui/c/DashboardFilters.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { reduxForm } from 'redux-form'; +import { translate } from 'react-i18next'; import { DASHBOARD_FILTERFORM } from 'constants/dashboard'; @@ -12,24 +13,26 @@ import Authorize from 'ui/common/authorized/Authorize'; import PartnerFilter from 'partners/ui/c/PartnerFilter'; import StatusFilter from 'ui/catalog/dashboard/c/StatusFilter'; // move -const DashboardFilters = ({handleSubmit, initialize, ...other}) => ( - - +const DashboardFilters = ({handleSubmit, initialize, t, ...other}) => ( + + - + - + - + - + ); -export default reduxForm({ +export default translate()(reduxForm({ enableReinitialize: true, destroyOnUnmount: false, form: DASHBOARD_FILTERFORM, -})(DashboardFilters); +})(DashboardFilters)); diff --git a/src/descriptors/ui/c/DescriptorCard.tsx b/src/descriptors/ui/c/DescriptorCard.tsx index 2b42d8a..b32e932 100644 --- a/src/descriptors/ui/c/DescriptorCard.tsx +++ b/src/descriptors/ui/c/DescriptorCard.tsx @@ -4,6 +4,7 @@ import Card, {CardHeader, CardContent, CardActions} from 'ui/common/Card'; import Grid from '@material-ui/core/Grid'; import Markdown from 'ui/catalog/markdown'; import Button from '@material-ui/core/Button'; +import { translate } from 'react-i18next'; import { Properties, PropertiesItem } from 'ui/catalog/Properties'; @@ -11,34 +12,6 @@ import Descriptor from 'model/catalog/Descriptor'; import VocabularyTerm from 'model/vocabulary/VocabularyTerm'; import { DescriptorLink, PartnerLink, CropLink } from 'ui/catalog/Links'; - -// TODO Introduce T function to the project -const T = (code: string): string => { - switch (code) { - case 'descriptor.category.PASSPORT': return 'Passport descriptor'; - case 'descriptor.category.MANAGEMENT': return 'Management descriptor'; - case 'descriptor.category.ENVIRONMENT': return 'Environment and Site descriptor'; - case 'descriptor.category.CHARACTERIZATION': return 'Characterization descriptor'; - case 'descriptor.category.EVALUATION': return 'Evaluation descriptor'; - case 'descriptor.category.ABIOTICSTRESS': return 'Abiotic stress descriptor'; - case 'descriptor.category.BIOTICSTRESS': return 'Biotic stress descriptor'; - case 'descriptor.category.MOLECULAR': return 'Molecular marker descriptor'; - - case 'descriptor.dataType.NUMERIC': return 'Numeric'; - case 'descriptor.dataType.TEXT': return 'Text'; - case 'descriptor.dataType.SCALE': return 'Scale'; - case 'descriptor.dataType.CODED': return 'Coded'; - case 'descriptor.dataType.BOOLEAN': return 'Yes/no'; - case 'descriptor.dataType.DATE': return 'Date'; - - case 'crop.null': return null; - case 'crop.maize': return 'Maize'; - case 'crop.yam': return 'Yam'; - - default: return null; - } -}; - interface ICardProps { complete?: boolean; compact?: boolean; @@ -86,6 +59,7 @@ interface IDescriptorCardProps extends React.ClassAttributes { onSelect: (descriptor: Descriptor) => void, onUnselect: (descriptor: Descriptor) => void, }; + t: any; } const styles = (theme) => ({ @@ -147,7 +121,7 @@ const styles = (theme) => ({ btnDefault: theme.buttons.default, }); -const DescriptorCard = ({complete = true, compact, descriptor, classes, className, selectedProps, actions, errorMessage, ...other}: IDescriptorCardProps & ICardProps) => { +const DescriptorCard = ({complete = true, compact, descriptor, classes, className, selectedProps, actions, errorMessage, t, ...other}: IDescriptorCardProps & ICardProps) => { const onClick = (select) => () => { const {onSelect, onUnselect} = selectedProps; @@ -175,33 +149,49 @@ const DescriptorCard = ({complete = true, compact, descriptor, classes, classNam - : || 'Untitled' + + + + : || t('descriptors.public.c.card.untitled') } /> { descriptor.description && } - { descriptor.key &&
This is a key descriptor for access and utilization of PGR.
} + { descriptor.key &&
{ t('descriptors.public.c.card.keyDescriptor') }
} { (false /*displayPref.publisher*/) && ( - { descriptor.publisher || Not specified } + + { descriptor.publisher || { t('descriptors.public.c.card.notSpecified') } } + ) } { (displayPref.owner && descriptor.owner) && ( - { descriptor.owner.name || '' } + + { descriptor.owner.name || '' } + ) } { displayPref.version && ( - { descriptor.versionTag } + + { descriptor.versionTag } + ) } { displayPref.crop && ( - { descriptor.crop ? : Not specified } + + { descriptor.crop ? : + { t('descriptors.public.c.card.notSpecified') } + } + ) } { (displayPref.category && descriptor.category) && ( - { T(`descriptor.category.${descriptor.category}`) } + + { t(`descriptors.common.category.${descriptor.category.toLowerCase()}`) } + ) } { displayPref.dataType && ( - { T(`descriptor.dataType.${descriptor.dataType}`) } + + { t(`descriptors.common.dataType.${descriptor.dataType.toLowerCase()}`) } + ) } @@ -228,7 +218,7 @@ const DescriptorCard = ({complete = true, compact, descriptor, classes, classNam className={ classes.btnBlue } disabled={ selectedProps.disabled } > - + SELECT DESCRIPTOR + { t('descriptors.public.c.card.select') } ) } @@ -239,7 +229,7 @@ const DescriptorCard = ({complete = true, compact, descriptor, classes, classNam disabled={ selectedProps.disabled } className={ classes.btnDefault } > - UNSELECT + { t('descriptors.public.c.card.unselect') } ) } @@ -250,4 +240,4 @@ const DescriptorCard = ({complete = true, compact, descriptor, classes, classNam ); }; -export default (withStyles as any)(styles)(DescriptorCard); +export default translate()((withStyles as any)(styles)(DescriptorCard)); diff --git a/src/descriptors/ui/c/DescriptorFilterForm.tsx b/src/descriptors/ui/c/DescriptorFilterForm.tsx index c07a1eb..76a44ef 100644 --- a/src/descriptors/ui/c/DescriptorFilterForm.tsx +++ b/src/descriptors/ui/c/DescriptorFilterForm.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { reduxForm } from 'redux-form'; import {withStyles} from '@material-ui/core/styles'; import Button from '@material-ui/core/Button'; +import { translate } from 'react-i18next'; import DescriptorFilters from './Filters'; import { DESCRIPTOR_FILTER_FORM } from 'constants/filter'; @@ -10,6 +11,7 @@ interface IDescriptorFilterFormProps extends React.ClassAttributes { classes: any; handleSubmit: () => void; reset: any; + t: any; } const styleSheet = (theme) => ({ @@ -25,7 +27,7 @@ class DescriptorFilterForm extends React.Component @@ -35,14 +37,18 @@ class DescriptorFilterForm extends React.Component
- - + +
); } } -export default reduxForm({ +export default translate()(reduxForm({ form: DESCRIPTOR_FILTER_FORM, -})(withStyles(styleSheet)(DescriptorFilterForm)); +})(withStyles(styleSheet)(DescriptorFilterForm))); diff --git a/src/descriptors/ui/c/DescriptorForm.tsx b/src/descriptors/ui/c/DescriptorForm.tsx index 6b17aa7..004ae1b 100644 --- a/src/descriptors/ui/c/DescriptorForm.tsx +++ b/src/descriptors/ui/c/DescriptorForm.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import {Field, reduxForm, formValueSelector} from 'redux-form'; +import { translate } from 'react-i18next'; import {log} from 'utilities/debug'; @@ -42,9 +43,9 @@ const styles = (theme) => ({ /* tslint:enable */ // FIXME Use Descriptor.DATATYPES -const renderDataTypeRadioGroup = ({input, meta, classes, ...rest}) => ( +const renderDataTypeRadioGroup = ({input, meta, classes, t, ...rest}) => ( - Data type + { t(`descriptors.admin.c.form.dataType`) } ( } className={ classes.RadioGrid } > - }/> - }/> - }/> - }/> - }/> - }/> + }/> + }/> + }/> + }/> + }/> + }/> ); -const TermEditor = (member, index, fields) => ( - - - - - -); - // FIXME Use Descriptor.CATEGORIES -const renderCategoryRadioGroup = ({input, meta, classes, ...rest}) => ( +const renderCategoryRadioGroup = ({input, meta, classes, t, ...rest}) => ( - Descriptor category + { t(`descriptors.admin.c.form.descriptorCategory`) } ( } className={ classes.RadioGrid } > - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> + }/> + }/> + }/> + }/> + }/> + }/> + }/> + }/> ); @@ -131,7 +124,7 @@ class DescriptorForm extends React.Component { } public render() { - const {error, handleSubmit, initialValues, pageTitle, onPublish, invalid, submitting, anyTouched, currentOwner, currentVocabulary, classes} = this.props; + const {error, handleSubmit, initialValues, pageTitle, onPublish, invalid, submitting, anyTouched, currentOwner, currentVocabulary, classes, t} = this.props; if (! initialValues) { return null; @@ -146,6 +139,20 @@ class DescriptorForm extends React.Component { log('Removing term', item); }; + const TermEditor = (member, index, fields) => ( + + + + + + + + + + + + ); + return (
@@ -153,100 +160,108 @@ class DescriptorForm extends React.Component { { this.allowsIntegerOnly() && ( ) } { this.allowsNumericRange() && ( ) } { this.allowsNumericRange() && ( ) } - { this.withVocabulary() && } + { this.withVocabulary() && } { (this.withVocabulary() && ! currentVocabulary) && ( - + ) } { this.withVocabulary() && this.props.dataType === 'CODED' && ( ) }
{ error && { error } }
- - { (initialValues && initialValues.id > 0 && ! initialValues.published) && } + + { (initialValues && initialValues.id > 0 && ! initialValues.published) && + + }
); @@ -266,7 +281,7 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ setPageTitle, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)( +export default translate()(connect(mapStateToProps, mapDispatchToProps)( withStyles(styles)( reduxForm({ form: DESCRIPTOR_FORM, @@ -274,4 +289,4 @@ export default connect(mapStateToProps, mapDispatchToProps)( }) (DescriptorForm), ), -); +)); diff --git a/src/descriptors/ui/c/DescriptorListPicker.tsx b/src/descriptors/ui/c/DescriptorListPicker.tsx index 3da90f1..c38701f 100644 --- a/src/descriptors/ui/c/DescriptorListPicker.tsx +++ b/src/descriptors/ui/c/DescriptorListPicker.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { translate } from 'react-i18next'; import DescriptorList from 'model/catalog/DescriptorList'; import {autocomplete, promiseDescriptorList} from 'descriptorlists/actions/public'; @@ -21,6 +22,7 @@ interface ISelectDescriptorListInternal extends React.ClassAttributes { loadDescriptorList: (uuid: string) => Promise; label: string; meta?: any; + t: any; } class SelectDescriptorListInternal extends React.Component { @@ -97,7 +99,7 @@ class SelectDescriptorListInternal extends React.Component @@ -124,7 +126,7 @@ class SelectDescriptorListInternal extends React.Component @@ -147,12 +149,13 @@ interface IDescriptorListPicker extends React.ClassAttributes { className?: string; autocomplete: (term: string) => Promise; promiseDescriptorList: (uuid: string) => Promise; + t: any; } class DescriptorListPicker extends React.Component { public render() { - const { name , label, className, autocomplete, promiseDescriptorList } = this.props; + const { name , label, className, autocomplete, promiseDescriptorList, t } = this.props; return (
@@ -162,6 +165,7 @@ class DescriptorListPicker extends React.Component { label={ label } autocomplete={ autocomplete } loadDescriptorList={ promiseDescriptorList } + t={ t } />
); @@ -173,4 +177,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ promiseDescriptorList, }, dispatch); -export default connect(null, mapDispatchToProps)(DescriptorListPicker); +export default translate()(connect(null, mapDispatchToProps)(DescriptorListPicker)); diff --git a/src/descriptors/ui/c/DescriptorParser.tsx b/src/descriptors/ui/c/DescriptorParser.tsx index f0ee392..bc06a56 100644 --- a/src/descriptors/ui/c/DescriptorParser.tsx +++ b/src/descriptors/ui/c/DescriptorParser.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { translate } from 'react-i18next'; import { log } from 'utilities/debug'; @@ -16,6 +17,7 @@ interface IDescriptorParser extends React.ClassAttributes { className?: any; onHandleDescriptors: (descriptors: Descriptor[]) => void; children: any; + t: any; } // Page to edit a descriptor list @@ -123,16 +125,17 @@ class DescriptorParser extends React.Component { } public render() { + const { t } = this.props; return (
-

INSTRUCTIONS FOR USE

+

{ t('descriptors.dashboard.c.parser.instructions') }

    -
  1. Use "Descriptors" sheet from template: Genesys Catalog template.
  2. -
  3. The first row in the template contains the header names, the second row contains a description of the content expected on each column, and rows three and four contain examples to help you fill in the template.
  4. -
  5. Do not change the header names in the template
  6. -
  7. Fill the template with the descriptor information, remove the rows that contain the description of each column (second row) and examples (third and fourth row) and save the file.
  8. -
  9. Copy and paste the table from Excel into the text field "Descriptor definitions".
  10. +
  11. { t('descriptors.dashboard.c.parser.firstItem') }: { t('descriptors.dashboard.c.parser.link') }.
  12. +
  13. { t('descriptors.dashboard.c.parser.secondItem') }
  14. +
  15. { t('descriptors.dashboard.c.parser.thirdItem') }
  16. +
  17. { t('descriptors.dashboard.c.parser.fourthItem') }
  18. +
  19. { t('descriptors.dashboard.c.parser.fifthItem') }
@@ -140,7 +143,7 @@ class DescriptorParser extends React.Component { Descriptor definitions - + { this.props.children } @@ -149,4 +152,4 @@ class DescriptorParser extends React.Component { } } -export default DescriptorParser; +export default translate()(DescriptorParser); diff --git a/src/descriptors/ui/c/DescriptorPicker.tsx b/src/descriptors/ui/c/DescriptorPicker.tsx index 0dab31f..c18f416 100644 --- a/src/descriptors/ui/c/DescriptorPicker.tsx +++ b/src/descriptors/ui/c/DescriptorPicker.tsx @@ -3,6 +3,7 @@ import Grid from '@material-ui/core/Grid'; import { withStyles } from '@material-ui/core/styles'; import * as classnames from 'classnames'; import Card, { CardHeader } from 'ui/common/Card'; +import { translate } from 'react-i18next'; import { Page, Pagination } from 'model/common.model'; import DescriptorFilter from 'model/catalog/DescriptorFilter'; @@ -27,6 +28,7 @@ interface IDescriptorPickerProps extends React.ClassAttributes { currentDescriptors: Descriptor[]; // currently selected descriptors pagination?: Pagination; listCrops: () => any; + t: any; } const styles = (theme) => ({ @@ -122,7 +124,7 @@ class DescriptorPicker extends React.Component { public render() { - const {classes, pagination, matchingDescriptors, onAddAllDescriptors, onRemoveAllDescriptors} = this.props; + const {classes, pagination, matchingDescriptors, onAddAllDescriptors, onRemoveAllDescriptors, t} = this.props; // handle empty current descriptors let {currentDescriptors} = this.props; @@ -138,11 +140,11 @@ class DescriptorPicker extends React.Component { { matchingDescriptors && -

{ matchingDescriptors.totalElements || 0 } descriptors available

+

{ matchingDescriptors.totalElements || 0 } { t('descriptors.dashboard.c.picker.available') }

@@ -169,7 +171,7 @@ class DescriptorPicker extends React.Component { } @@ -177,12 +179,15 @@ class DescriptorPicker extends React.Component { 0 }) } even-row` }>

- { `${currentDescriptors.length} descriptor${currentDescriptors.length !== 1 ? 's' : '' } selected` } + { currentDescriptors.length } { currentDescriptors.length > 1 ? + t('descriptors.dashboard.c.picker.descriptor_plural') : + t('descriptors.dashboard.c.picker.descriptor') } { t('descriptors.dashboard.c.picker.selected') + }

@@ -196,7 +201,7 @@ class DescriptorPicker extends React.Component { } } title={ ( - 0 descriptors selected + { t('descriptors.dashboard.c.picker.zero') } ) } /> @@ -225,4 +230,4 @@ class DescriptorPicker extends React.Component { } } -export default withStyles(styles)(DescriptorPicker); +export default translate()(withStyles(styles)(DescriptorPicker)); diff --git a/src/descriptors/ui/c/DescriptorSearchMenu.tsx b/src/descriptors/ui/c/DescriptorSearchMenu.tsx index 8e37cd9..23ce378 100644 --- a/src/descriptors/ui/c/DescriptorSearchMenu.tsx +++ b/src/descriptors/ui/c/DescriptorSearchMenu.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import {withStyles} from '@material-ui/core/styles'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; +import { translate } from 'react-i18next'; import {updateDescriptorFilterModel} from 'actions/filter'; import DescriptorFilters from 'descriptors/ui/c/Filters'; @@ -16,6 +17,7 @@ interface IDescriptorSearchMenuProps extends React.ClassAttributes { paginationDescriptorPage: Page; sortBy: string; filter: IDescriptorFilter; + t: any; } const styles = (theme) => ({ @@ -41,8 +43,9 @@ class DescriptorSearchMenu extends React.Component + ); @@ -57,4 +60,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ updateDescriptorFilterModel, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(DescriptorSearchMenu)); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(DescriptorSearchMenu))); diff --git a/src/descriptors/ui/c/Filters.tsx b/src/descriptors/ui/c/Filters.tsx index 56e8f0d..70b158f 100644 --- a/src/descriptors/ui/c/Filters.tsx +++ b/src/descriptors/ui/c/Filters.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { reduxForm } from 'redux-form'; +import { translate } from 'react-i18next'; import FiltersBlock from 'ui/common/filter/FiltersBlock'; import CollapsibleComponentSearch from 'ui/common/filter/CollapsibleComponentSearch'; @@ -13,28 +14,34 @@ import Descriptor from 'model/catalog/Descriptor'; import DescriptorListPicker from './DescriptorListPicker'; import PartnerFilter from 'partners/ui/c/PartnerFilter'; -const DescriptorFilters = ({ handleSubmit, initialize, ...other }) => ( - - - - - +const DescriptorFilters = ({ handleSubmit, initialize, t, ...other }) => ( + + + + + - + - - - - - + + + + + ); -export default reduxForm({ +export default translate()(reduxForm({ enableReinitialize: true, form: DESCRIPTOR_FILTER_FORM, -})(DescriptorFilters); +})(DescriptorFilters)); diff --git a/src/descriptors/ui/c/SelectVocabulary.tsx b/src/descriptors/ui/c/SelectVocabulary.tsx index 06f3a8f..e143031 100644 --- a/src/descriptors/ui/c/SelectVocabulary.tsx +++ b/src/descriptors/ui/c/SelectVocabulary.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import {withStyles} from '@material-ui/core/styles'; +import { translate } from 'react-i18next'; import {log} from 'utilities/debug'; @@ -28,6 +29,7 @@ interface ISelectVocabularyProps extends React.ClassAttributes { vocabularies: any; listVocabularies: any; saveVocabulary: any; + t: any; } class SelectVocabulary extends React.Component { @@ -49,13 +51,13 @@ class SelectVocabulary extends React.Component { } private onNewVocabulary = () => { - const { input, owner, saveVocabulary, listVocabularies } = this.props; + const { input, owner, saveVocabulary, listVocabularies, t } = this.props; log(`Creating new vocabulary for ${owner.name}`); saveVocabulary({ owner, - title: 'Rename this vocabulary', + title: t('descriptors.admin.c.selectVocabulary.rename'), }).then((v: Vocabulary) => { listVocabularies(); input.onChange(v); @@ -93,4 +95,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ saveVocabulary, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(SelectVocabulary)); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(SelectVocabulary))); diff --git a/src/descriptors/ui/c/VocabularyForm.tsx b/src/descriptors/ui/c/VocabularyForm.tsx index d1accc2..8825384 100644 --- a/src/descriptors/ui/c/VocabularyForm.tsx +++ b/src/descriptors/ui/c/VocabularyForm.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import {Field, reduxForm} from 'redux-form'; +import { translate } from 'react-i18next'; import {log} from 'utilities/debug'; @@ -16,18 +17,10 @@ import ItemsEditor from 'ui/common/ItemsEditor'; import Button from '@material-ui/core/Button'; import Grid from '@material-ui/core/Grid'; -const TermEditor = (member, index, fields) => ( - - - - - -); - class VocabularyForm extends React.Component { public render() { - const {error, handleSubmit, initialValues} = this.props; + const {error, handleSubmit, initialValues, t} = this.props; const onAddMember = () => { log('Adding new term'); @@ -42,44 +35,71 @@ class VocabularyForm extends React.Component { return null; } + const TermEditor = (member, index, fields) => ( + + + + + + + + + + + + ); + return (
- + - - + +
{ error && { error } }
- +
); } } -export default connect((state) => ({}), null)(reduxForm({ +export default translate()(connect((state) => ({}), null)(reduxForm({ form: VOCABULARY_FORM, enableReinitialize: true, -})(VocabularyForm)); +})(VocabularyForm))); diff --git a/src/model/catalog/Dataset.ts b/src/model/catalog/Dataset.ts index 3b4ee46..aa136ec 100644 --- a/src/model/catalog/Dataset.ts +++ b/src/model/catalog/Dataset.ts @@ -47,14 +47,14 @@ class Dataset implements IUserPermissions { public static SORT_OPTIONS = { - title: { label: 'Title' }, - accessionCount: { property: 'accessionCount', label: 'Accession count (low to high)', direction: 'ASC' }, - accessionCountD: { property: 'accessionCount', label: 'Accession count (high to low)', direction: 'DESC' }, - descriptorCount: { property: 'descriptorCount', label: 'Descriptor count (low to high)', direction: 'ASC' }, - descriptorCountD: { property: 'descriptorCount', label: 'Descriptor count (high to low)', direction: 'DESC' }, - startDate: { label: 'Experiment start date' }, - endDate: { label: 'Experiment end date' }, - versionTag: { label: 'Version' }, + title: { label: 'common:sort.title' }, + accessionCount: { property: 'accessionCount', label: 'datasets.sort.accessionCountAsc', direction: 'ASC' }, + accessionCountD: { property: 'accessionCount', label: 'datasets.sort.accessionCountDesc', direction: 'DESC' }, + descriptorCount: { property: 'descriptorCount', label: 'datasets.sort.descriptorCountAsc', direction: 'ASC' }, + descriptorCountD: { property: 'descriptorCount', label: 'datasets.sort.descriptorCountDesc', direction: 'DESC' }, + startDate: { label: 'datasets.sort.startDate' }, + endDate: { label: 'datasets.sort.endDate' }, + versionTag: { label: 'common:sort.version' }, ...UuidModel.SORT_OPTIONS, }; } diff --git a/src/model/catalog/Descriptor.ts b/src/model/catalog/Descriptor.ts index 464b3a0..e9e642e 100644 --- a/src/model/catalog/Descriptor.ts +++ b/src/model/catalog/Descriptor.ts @@ -41,20 +41,20 @@ export default class Descriptor implements IUserPermissions { public static SORT_OPTIONS = { - crop: { label: 'Crop' }, - title: { label: 'Title' }, + crop: { label: 'descriptors.sort.crop' }, + title: { label: 'common:sort.title' }, // publisher: { label: 'Publisher' }, - versionTag: { label: 'Version' }, - dataType: { label: 'Data type' }, - key: { label: 'Key', direction: 'DESC' }, - uom: { label: 'Unit of measure' }, - columnName: { label: 'Column name' }, - owner: { label: 'Maintainer' }, + versionTag: { label: 'common:sort.version' }, + dataType: { label: 'descriptors.sort.dataType' }, + key: { label: 'descriptors.sort.key', direction: 'DESC' }, + uom: { label: 'descriptors.sort.uom' }, + columnName: { label: 'descriptors.sort.columnName' }, + owner: { label: 'descriptors.sort.owner' }, ...UuidModel.SORT_OPTIONS, }; public static DATATYPES: { [key: string]: any; } = { - TEXT: 'Free text', + TEXT: 'descriptors.common.category.TEXT', BOOLEAN: 'Yes/No', DATE: 'Date', CODED: 'Controlled vocabulary', @@ -63,24 +63,24 @@ export default class Descriptor implements IUserPermissions { }; public static CATEGORIES: { [key: string]: any; } = { - PASSPORT: 'Passport descriptor', - MANAGEMENT: 'Management descriptor', - CHARACTERIZATION: 'Characterization descriptor', - EVALUATION: 'Evaluation descriptor', - ENVIRONMENT: 'Environment or site descriptor', - ABIOTICSTRESS: 'Abiotic stress', - BIOTICSTRESS: 'Biotic stress', - MOLECULAR: 'Molecular marker', + PASSPORT: 'descriptors.common.category.passport', + MANAGEMENT: 'descriptors.common.category.management', + CHARACTERIZATION: 'descriptors.common.category.characterization', + EVALUATION: 'descriptors.common.category.evaluation', + ENVIRONMENT: 'descriptors.common.category.environment', + ABIOTICSTRESS: 'descriptors.common.category.abioticstress', + BIOTICSTRESS: 'descriptors.common.category.bioticstress', + MOLECULAR: 'descriptors.common.category.molecular', }; } export enum DataType { - TEXT = 'TEXT', - BOOLEAN = 'BOOLEAN', - DATE = 'DATE', - CODED = 'CODED', - NUMERIC = 'NUMERIC', - SCALE = 'SCALE', + TEXT = 'text', + BOOLEAN = 'boolean', + DATE = 'date', + CODED = 'coded', + NUMERIC = 'numeric', + SCALE = 'scale', } export enum DescriptorCategory { diff --git a/src/model/catalog/DescriptorList.ts b/src/model/catalog/DescriptorList.ts index 4312d4f..6114da9 100644 --- a/src/model/catalog/DescriptorList.ts +++ b/src/model/catalog/DescriptorList.ts @@ -37,8 +37,8 @@ class DescriptorList implements IUserPermissions { public static SORT_OPTIONS = { - title: { label: 'Title' }, - versionTag: { label: 'Version' }, + title: { label: 'common:sort.title' }, + versionTag: { label: 'common:sort.version' }, ...UuidModel.SORT_OPTIONS, }; diff --git a/src/model/common.model.ts b/src/model/common.model.ts index 66017eb..4436af5 100644 --- a/src/model/common.model.ts +++ b/src/model/common.model.ts @@ -24,9 +24,9 @@ export interface IAudited { export abstract class AuditedVersionedModel { public static SORT_OPTIONS = { - createdDate: { label: 'Created date', direction: 'DESC' }, - lastModifiedDate: {property: 'lastModifiedDate', label: 'Last update (newest first)', direction: 'DESC'}, - lastModifiedDateD: {property: 'lastModifiedDate', label: 'Last modified date (oldest first)', direction: 'ASC'}, + createdDate: { label: 'common:sort.createdDate', direction: 'DESC' }, + lastModifiedDate: {property: 'lastModifiedDate', label: 'common:sort.lastModifiedDateDesc', direction: 'DESC'}, + lastModifiedDateD: {property: 'lastModifiedDate', label: 'common:sort.lastModifiedDateAsc', direction: 'ASC'}, }; } diff --git a/src/model/genesys/Partner.ts b/src/model/genesys/Partner.ts index 1a63111..4c522a2 100644 --- a/src/model/genesys/Partner.ts +++ b/src/model/genesys/Partner.ts @@ -24,8 +24,8 @@ class Partner implements IUserPermissions { public wiewsCodes: string[]; public static SORT_OPTIONS = { - name: { label: 'Partner name' }, - shortName: { label: 'Acronym' }, + name: { label: 'partners.common.partnerName' }, + shortName: { label: 'partners.common.acronym' }, ...UuidModel.SORT_OPTIONS, }; } diff --git a/src/model/vocabulary/Vocabulary.ts b/src/model/vocabulary/Vocabulary.ts index 84fd292..c30917d 100644 --- a/src/model/vocabulary/Vocabulary.ts +++ b/src/model/vocabulary/Vocabulary.ts @@ -31,10 +31,10 @@ class Vocabulary implements IUserPermissions { public static SORT_OPTIONS = { - title: { label: 'Title' }, - versionTag: { label: 'Version' }, - url: { label: 'URL' }, - owner: { label: 'Maintainer' }, + title: { label: 'common:sort.title' }, + versionTag: { label: 'common:sort.version' }, + url: { label: 'vocabulary.sort.URL' }, + owner: { label: 'vocabulary.sort.maintainer' }, ...UuidModel.SORT_OPTIONS, }; } diff --git a/src/partners/routes.ts b/src/partners/routes.ts index 1c647e8..6503f6a 100644 --- a/src/partners/routes.ts +++ b/src/partners/routes.ts @@ -10,8 +10,8 @@ const publicRoutes = [ path: '/partners', component: Wrapper, extraProps: { - title: 'p.partners.title', - subtitle: 'p.partners.subtitle', + title: 'partners.common.title', + subtitle: 'partners.common.subtitle', }, routes: [ { diff --git a/src/partners/translations.json b/src/partners/translations.json new file mode 100644 index 0000000..5f72f5b --- /dev/null +++ b/src/partners/translations.json @@ -0,0 +1,62 @@ +{ + "admin": { + "c": { + "form": { + "descriptionPlaceholder": "Data provider introduction text (markdown)", + "name": "Data provider name", + "new": " New data provider", + "phonePlaceholder": "+1 555 1231 Ext. 13", + "shortName": "Short name or acronym", + "websites": "Data provider websites" + } + } + }, + "common": { + "countries": "Countries", + "modelName": "Data provider", + "modelName_plural": "Data providers", + "subtitle": "Data providers in the Catalog of Phenotypic Datasets", + "title": "Genesys Data providers", + "wiewsCodes": "FAO WIEWS codes", + "menu": "Data providers", + "partnerName": "Partner name", + "acronym": "Acronym" + }, + "public": { + "c": { + "searchMenu": { + "byDate": "BY DATE", + "byInstitute": "BY INSTITUTE", + "search": "SEARCH", + "searchCountry": "Search Country...", + "select": "Select institute..." + }, + "select": { + "select": "Select data provider" + }, + "summary": { + "countryNumber": "Number of data publishers", + "instituteNumber": "Number of data publishers", + "publisherNumber": "Number of data publishers", + "title": "SUMMARY" + } + }, + "f": { + "acronym": "Acronym", + "ICRISAT": "ICRISAT", + "international": "International", + "partnerDescription": "Data provider description" + }, + "p": { + "browse": { + "title": "Genesys Data providers", + "subtitle": "Data providers in the Catalog of Phenotypic Datasets" + }, + "display": { + "email": "Contact email", + "metadata": "Record metadata", + "urls": "Websites" + } + } + } +} \ No newline at end of file diff --git a/src/partners/ui/BrowsePage.tsx b/src/partners/ui/BrowsePage.tsx index 20dd4a1..4a5bad4 100644 --- a/src/partners/ui/BrowsePage.tsx +++ b/src/partners/ui/BrowsePage.tsx @@ -123,12 +123,12 @@ class PartnerListPage extends React.Component { }> - + diff --git a/src/partners/ui/DisplayPage.tsx b/src/partners/ui/DisplayPage.tsx index 350b077..8a4f6a4 100644 --- a/src/partners/ui/DisplayPage.tsx +++ b/src/partners/ui/DisplayPage.tsx @@ -69,7 +69,7 @@ class PartnerPage extends React.Component { const { t, partner, deletePartner } = this.props; confirm(Delete { partner.name }?, { - description: t('common:message.confirmDelete', { what: t('label.partner') }), + description: t('common:message.confirmDelete', { what: t('partners.common.modelName') }), confirmLabel: t('common:action.delete'), abortLabel: t('common:action.cancel'), }).then(() => { @@ -101,38 +101,38 @@ class PartnerPage extends React.Component { { partner.urls.length > 0 && ( - + { partner.urls.map((url) => (
{ url }
)) }
) } - { partner.wiewsCodes.length > 0 && ( - + { partner.wiewsCodes && partner.wiewsCodes.length > 0 && ( + { partner.wiewsCodes.map((wiewsCode) => (
{ wiewsCode }
)) }
) } { partner.countryCodes.length > 0 && ( - + { partner.countryCodes.map((countryCode) => (
{ countryCode }
)) }
) } { partner.email && ( - + ) } { partner.phone && ( - { partner.phone } + { partner.phone } ) } { partner.address && ( - { partner.address } + { partner.address } ) } - { partner.uuid }, v{ partner.version } + { partner.uuid }, v{ partner.version }
diff --git a/src/partners/ui/c/Filters.tsx b/src/partners/ui/c/Filters.tsx index f34588e..5518dc6 100644 --- a/src/partners/ui/c/Filters.tsx +++ b/src/partners/ui/c/Filters.tsx @@ -9,24 +9,24 @@ import StringFilter from 'ui/common/filter/StringFilter'; import StringArrFilter from 'ui/common/filter/StringArrFilter'; const PartnerFilters = ({handleSubmit, initialValues, initialize, t, ...other}) => ( - - + + diff --git a/src/partners/ui/c/PartnerCard.tsx b/src/partners/ui/c/PartnerCard.tsx index 521d8f0..fbc8163 100644 --- a/src/partners/ui/c/PartnerCard.tsx +++ b/src/partners/ui/c/PartnerCard.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import { withStyles } from '@material-ui/core/styles'; import Card, { CardHeader, CardContent } from 'ui/common/Card'; import { PartnerLink } from 'ui/catalog/Links'; @@ -11,6 +12,7 @@ import PrettyDate from 'ui/common/time/PrettyDate'; interface IPartnerCardProps extends React.ClassAttributes { partner: Partner; classes: any; + t: any; } const styles = (theme) => ({ @@ -55,7 +57,7 @@ const styles = (theme) => ({ class PartnerCard extends React.Component { public render() { - const {partner, classes} = this.props; + const {partner, classes, t} = this.props; if (! partner) { return null; @@ -69,7 +71,7 @@ class PartnerCard extends React.Component { subheader: classes.italic, } } title={ as any } - subheader={ partner.createdDate && Registered } + subheader={ partner.createdDate && { t('common:label.registered') } } /> { partner.description && } @@ -80,4 +82,4 @@ class PartnerCard extends React.Component { } -export default (withStyles as any)(styles)(PartnerCard); +export default translate()((withStyles as any)(styles)(PartnerCard)); diff --git a/src/partners/ui/c/PartnerForm.tsx b/src/partners/ui/c/PartnerForm.tsx index ec882a6..b66a669 100644 --- a/src/partners/ui/c/PartnerForm.tsx +++ b/src/partners/ui/c/PartnerForm.tsx @@ -24,8 +24,8 @@ class PartnerForm extends React.Component { public constructor(props: any) { super(props); - const {setPageTitle, initialValues} = this.props; - setPageTitle(initialValues.name || 'New data provider'); + const {setPageTitle, initialValues, t} = this.props; + setPageTitle(initialValues.name || t('partners.admin.c.form.new')); } private onAddString() { @@ -41,47 +41,47 @@ class PartnerForm extends React.Component { const {error, initialValues, handleSubmit, invalid, submitting, t} = this.props; return (
- - + - - + - - - - + - + - +
{ error && { error } }
- - + + ); } diff --git a/src/partners/ui/c/SearchMenu.tsx b/src/partners/ui/c/SearchMenu.tsx index 08d1978..0486c74 100644 --- a/src/partners/ui/c/SearchMenu.tsx +++ b/src/partners/ui/c/SearchMenu.tsx @@ -19,6 +19,7 @@ import TextField from '@material-ui/core/TextField'; import ArrowDropUp from '@material-ui/icons/ArrowDropUp'; import Close from '@material-ui/icons/Close'; import Button from '@material-ui/core/Button'; +import { translate } from 'react-i18next'; import {log} from 'utilities/debug'; @@ -29,6 +30,7 @@ import institutes from 'data/institutes'; interface ISearchMenuProps extends React.ClassAttributes { classes: any; width: any; + t: any; } const styles = (theme) => ({ @@ -192,7 +194,7 @@ class SearchMenu extends React.Component { public render() { - const {classes} = this.props; + const {classes, t} = this.props; return ( @@ -200,7 +202,7 @@ class SearchMenu extends React.Component { { { onClick={ this.handleToggle.bind(this, 'expandDate') } className={ this.state.expandDate ? classes.activeItem : null } > - + { this.state.expandDate && } @@ -273,7 +275,7 @@ class SearchMenu extends React.Component { { { onClick={ this.handleToggle.bind(this, 'expandInst') } className={ this.state.expandInst ? classes.activeItem : null } > - + { this.state.expandInst && } @@ -333,7 +335,7 @@ class SearchMenu extends React.Component {
{ } -export default compose(withStyles(styles), withWidth())(SearchMenu); +export default translate()(compose(withStyles(styles), withWidth())(SearchMenu)); diff --git a/src/partners/ui/c/SelectPartner.tsx b/src/partners/ui/c/SelectPartner.tsx index 4c4b1cb..03002bb 100644 --- a/src/partners/ui/c/SelectPartner.tsx +++ b/src/partners/ui/c/SelectPartner.tsx @@ -93,18 +93,18 @@ class SelectPartner extends React.Component { if (! allowsEdit) { return ( - + ); } return ( - + - Basic markdown supported: * ** + { t('common:label.basicMarkdown') }: * ** ); @@ -140,7 +141,7 @@ class MarkdownField extends OriginalMarkdownField { { ! this.state.previewMode ? -
Preview Markdown Full markdown supported
+
{ t('common:label.previewMarkdown') } { t('common:label.fullMarkdown') }
: @@ -159,6 +160,6 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ uploadMarkdownAttachment, }, dispatch); -const _markdownField = connect(null, mapDispatchToProps)(MarkdownField); +const _markdownField = translate()(connect(null, mapDispatchToProps)(MarkdownField)); export { Markdown as default, _markdownField as MarkdownField }; diff --git a/src/ui/common/ItemsEditor.tsx b/src/ui/common/ItemsEditor.tsx index 4d87924..6ca711c 100644 --- a/src/ui/common/ItemsEditor.tsx +++ b/src/ui/common/ItemsEditor.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import { FieldArray } from 'redux-form'; import Button from '@material-ui/core/Button'; import Grid from '@material-ui/core/Grid'; import {log} from 'utilities/debug'; -const renderMembers = ({ fields, itemLabel, itemEditor, addItem, removeItem }) => { +const renderMembers = ({ fields, itemLabel, itemEditor, addItem, removeItem, t }) => { const onAddMember = (e) => { const item = addItem(); @@ -23,7 +24,7 @@ const renderMembers = ({ fields, itemLabel, itemEditor, addItem, removeItem }) = } if (fields.length > 100) { - return
Don't use the ItemsEditor for more than 100 items!
; + return
{ t('common:label.itemEditorWarn') }
; } return ( @@ -35,24 +36,24 @@ const renderMembers = ({ fields, itemLabel, itemEditor, addItem, removeItem }) = { itemEditor(member, index, fields, itemLabel) } - +
)) } - +
); }; -const ItemsEditor = ({ name, itemLabel, addItem, removeItem, component }) => { +const ItemsEditor = ({ name, itemLabel, addItem, removeItem, component, t }) => { return ( - + ); }; -export default ItemsEditor; +export default translate()(ItemsEditor); diff --git a/src/ui/common/Tabs.tsx b/src/ui/common/Tabs.tsx index 6078dfa..d6a7d28 100644 --- a/src/ui/common/Tabs.tsx +++ b/src/ui/common/Tabs.tsx @@ -4,6 +4,7 @@ // Navigation is peformed using router // import * as React from 'react'; +import {translate} from 'react-i18next'; import {connect} from 'react-redux'; import { Link } from 'react-router-dom'; import {bindActionCreators} from 'redux'; @@ -75,7 +76,7 @@ class Tab extends React.Component { class Tabs extends React.Component { public render() { - const { tab, children, navigateTo, actions, classes } = this.props; + const { tab, children, navigateTo, actions, classes, t } = this.props; const tabs = (children as Tab[]).map((ch) => { return { @@ -97,7 +98,7 @@ class Tabs extends React.Component { - { tabs.map((tab) => ) } + { tabs.map((tab) => ) } @@ -117,6 +118,6 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ navigateTo, }, dispatch); -const tabs = connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Tabs)); +const tabs = translate()(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Tabs))); export { tabs as default, Tab }; diff --git a/src/ui/common/buttons/BackButton.tsx b/src/ui/common/buttons/BackButton.tsx index e19df25..13b3d1e 100644 --- a/src/ui/common/buttons/BackButton.tsx +++ b/src/ui/common/buttons/BackButton.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import Button from '@material-ui/core/Button'; @@ -12,6 +13,8 @@ interface IBackButtonProps extends React.ClassAttributes { lastLocation: string; preferDefaultTarget?: boolean; navigateTo: (path: string) => void; + style?: any; + t: any; } class BackButton extends React.Component { @@ -34,10 +37,10 @@ class BackButton extends React.Component { } public render() { - const { defaultBackText} = this.props; + const { defaultBackText, style, t } = this.props; return ( - ); } @@ -52,4 +55,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ navigateTo, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(BackButton); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(BackButton)); diff --git a/src/ui/common/checkbox/index.tsx b/src/ui/common/checkbox/index.tsx index a5653f3..2d7fe5d 100644 --- a/src/ui/common/checkbox/index.tsx +++ b/src/ui/common/checkbox/index.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; +import { translate } from 'react-i18next'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Checkbox from '@material-ui/core/Checkbox'; -const ReduxCheckbox = ({input: {value, onChange}, label = null, style = {height: '40px'}, ...rest}) => ( + +const ReduxCheckbox = ({input: {value, onChange}, label = null, style = {height: '40px'}, t, ...rest}) => ( } - label={ label } + label={ t(label) } { ...rest } /> ); -export default ReduxCheckbox; +export default translate()(ReduxCheckbox); diff --git a/src/ui/common/csv-configuration/CSVConfiguration.tsx b/src/ui/common/csv-configuration/CSVConfiguration.tsx index 7bc2713..94da4ea 100644 --- a/src/ui/common/csv-configuration/CSVConfiguration.tsx +++ b/src/ui/common/csv-configuration/CSVConfiguration.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import { ICsvConfiguration } from 'utilities/CSV'; @@ -23,6 +24,7 @@ class CSVConfig implements ICsvConfiguration { interface ICSVConfigurationProps extends React.ClassAttributes { config: CSVConfig; onChange: (value: CSVConfig) => void; + t: any; } class CSVConfiguration extends React.Component { @@ -88,15 +90,16 @@ class CSVConfiguration extends React.Component { } public render() { + const { t } = this.props; return (
- + - Auto-detect + { t('common:csv.autoDetect') } } @@ -105,53 +108,53 @@ class CSVConfiguration extends React.Component { - Separator character + { t('common:csv.separator') } } /> } /> } /> } /> } /> } + control={ } /> - Escape character - + { t('common:csv.escapeCharacter') } + - Quote character - + { t('common:csv.quoteCharacter') } + @@ -161,4 +164,5 @@ class CSVConfiguration extends React.Component { } } -export { CSVConfiguration as default, CSVConfig }; +const translatedCSV = translate()(CSVConfiguration); +export { translatedCSV as default, CSVConfig }; diff --git a/src/ui/common/filter/BooleanFilter.tsx b/src/ui/common/filter/BooleanFilter.tsx index 421c76d..0ea2db0 100644 --- a/src/ui/common/filter/BooleanFilter.tsx +++ b/src/ui/common/filter/BooleanFilter.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import { Field } from 'redux-form'; import Radio from '@material-ui/core/Radio'; @@ -10,6 +11,7 @@ import FormControl from '@material-ui/core/FormControl'; interface IBooleanFilterInternal extends React.ClassAttributes { input: any; label: string; + t: any; } class BooleanFilterInternal extends React.Component { @@ -25,7 +27,7 @@ class BooleanFilterInternal extends React.Component private handleChange = (event, value) => { const { input } = this.props; this.setState({ value }); - input.onChange(value === '' ? null : value); + input.onChange(value === '' ? null : value === 'true'); } public componentWillReceiveProps(nextProps) { @@ -33,7 +35,7 @@ class BooleanFilterInternal extends React.Component } public render() { - const { label } = this.props; + const { label, t } = this.props; return (
@@ -42,9 +44,9 @@ class BooleanFilterInternal extends React.Component value={ `${this.state.value}` } onChange={ this.handleChange } > - } label="Yes" /> - } label="No" /> - } label="Either" /> + } label={ t('common:label.yes') } /> + } label={ t('common:label.no') } /> + } label={ t('common:label.either') } />
@@ -54,23 +56,25 @@ class BooleanFilterInternal extends React.Component interface IBooleanFilter extends React.ClassAttributes { name: string; - label: string; + label?: string; + t: any; } class BooleanFilter extends React.Component { public render() { - const { name , label } = this.props; + const { name , label, t } = this.props; return (
); } } -export default BooleanFilter; +export default translate()(BooleanFilter); diff --git a/src/ui/common/filter/CollapsibleComponentSearch.tsx b/src/ui/common/filter/CollapsibleComponentSearch.tsx index 630c89c..142fc72 100644 --- a/src/ui/common/filter/CollapsibleComponentSearch.tsx +++ b/src/ui/common/filter/CollapsibleComponentSearch.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import PlayArrow from '@material-ui/icons/PlayArrow'; import {withStyles} from '@material-ui/core/styles'; import Collapse from '@material-ui/core/Collapse'; @@ -8,6 +9,7 @@ interface ICollapsibleComponentSearch extends React.ClassAttributes { classes: any; title: string; collapsed?: boolean; + t: any; } const styles = (theme) => ({ @@ -40,12 +42,12 @@ class CollapsibleComponentSearch extends React.Component
- { title } + { t(title) }
{ classes: any; width: any; title: string; + t: any; } const styles = (theme) => ({ @@ -17,12 +19,12 @@ const styles = (theme) => ({ class ExpandFiltersComponent extends React.Component { public render() { - const { classes, title, children} = this.props; + const { classes, title, children, t} = this.props; return (

- { title } + { t(title) }

@@ -32,4 +34,4 @@ class ExpandFiltersComponent extends React.Component ({ overflow: 'hidden' as 'hidden', [theme.breakpoints.down('sm')]: { bottom: '3rem', - position: 'fixed' as 'fixed', }, }, btnGreen: theme.buttons.green, @@ -31,7 +31,7 @@ const styles = (theme) => ({ // // Renders a standard filters block -const FiltersBlock = ({ title, children, handleSubmit, onSubmit, initialize, classes, showSnackbar }) => { +const FiltersBlock = ({ title, children, handleSubmit, onSubmit, initialize, classes, showSnackbar, t }) => { const scrollToTop = () => { if (window) { window.scrollTo(0, 0); @@ -39,7 +39,7 @@ const FiltersBlock = ({ title, children, handleSubmit, onSubmit, initialize, cla }; const onReset = (e) => { - showSnackbar('Resetting filters...'); + showSnackbar('common:snackbar.resettingFilters'); log('Clearing form'); initialize({}); setTimeout(handleSubmit, 100); @@ -47,7 +47,7 @@ const FiltersBlock = ({ title, children, handleSubmit, onSubmit, initialize, cla }; const processSubmit = handleSubmit((data) => { - showSnackbar('Applying filters...'); + showSnackbar('common:snackbar.applyingFilters'); const clean = cleanFilters(data); log('Submitting filters', clean); scrollToTop(); @@ -60,8 +60,8 @@ const FiltersBlock = ({ title, children, handleSubmit, onSubmit, initialize, cla
{ children }
- - + +
@@ -73,4 +73,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ showSnackbar, }, dispatch); -export default connect(null, mapDispatchToProps)(withStyles(styles)(FiltersBlock)); +export default translate()(connect(null, mapDispatchToProps)(withStyles(styles)(FiltersBlock))); diff --git a/src/ui/common/filter/NumberFilter.tsx b/src/ui/common/filter/NumberFilter.tsx index da8785a..f40843c 100644 --- a/src/ui/common/filter/NumberFilter.tsx +++ b/src/ui/common/filter/NumberFilter.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import { Field } from 'redux-form'; import TextField from '@material-ui/core/TextField'; @@ -10,6 +11,7 @@ interface INumberFilterInternal extends React.ClassAttributes { input: any; placeholder: string; label: string; + t: any; } class NumberFilterInternal extends React.Component { @@ -58,21 +60,21 @@ class NumberFilterInternal extends React.Component { } public render() { - const { label } = this.props; + const { label, t } = this.props; return (
@@ -83,6 +85,7 @@ class NumberFilterInternal extends React.Component { interface INumberFilter extends React.ClassAttributes { name: string; label: string; + t: any; } // NumberFilter supports eq, ge, gt, le, lt @@ -93,17 +96,18 @@ class NumberFilter extends React.Component { } public render() { - const { name, label } = this.props; + const { name, label, t } = this.props; return (
); } } -export default NumberFilter; +export default translate()(NumberFilter); diff --git a/src/ui/common/filter/PrettyFilters.tsx b/src/ui/common/filter/PrettyFilters.tsx index 2f794e0..6753c08 100644 --- a/src/ui/common/filter/PrettyFilters.tsx +++ b/src/ui/common/filter/PrettyFilters.tsx @@ -85,7 +85,7 @@ function handleFilterObj(filterObj, path) { * @param labels - decoded uuids values * @return IPrettyFiltersProps.filterObj. */ -function getLabelName(path, value, lookups, prefix, t, labels) { +function getLabelName(path, value, lookups, prefix, t) { const lastKey = path.replace(/\[(.+?)\]/g, '').split('.').pop(); let name = value; @@ -103,13 +103,21 @@ function getLabelName(path, value, lookups, prefix, t, labels) { // join .join('.'); - const translatedPrettyPath = t(`f.${prefix}.${prettyPath}`); - - if (labels && !_.isEmpty(labels) && prettyPath.includes('uuid')) { - name = labels.get(name); + if (typeof name === 'boolean') { + if (name) { + return t(`public.prettyF.${prefix}.${prettyPath}`); + } + return t('public.prettyF.NOT', { what: t(`public.prettyF.${prefix}.${prettyPath.replace(/^NOT\./, '')}`) }); } - return `${translatedPrettyPath}: ${name}`; + const translatedPrettyPath = prettyPath.startsWith('NOT') ? + t('public.prettyF.NOT', { what: t(`public.prettyF.${prefix}.${prettyPath.replace(/^NOT\./, '')}`) }) + : + t(`public.prettyF.${prefix}.${prettyPath}`); + + const translatedPrettyName = t(`${name}`); + + return `${translatedPrettyPath}: ${translatedPrettyName}`; } class PrettyFilters extends React.Component { @@ -135,17 +143,17 @@ class PrettyFilters extends React.Component { } public render() { - const {classes, lookups, prefix, t, labels } = this.props; - const {chipData} = this.state; + const { classes, lookups, prefix, t } = this.props; + const { chipData } = this.state; const dataArr = Object.getOwnPropertyNames(chipData); return dataArr.length > 0 && ( - + { dataArr.map((key) => { return ( diff --git a/src/ui/common/filter/StringArrFilter.tsx b/src/ui/common/filter/StringArrFilter.tsx index d509c7b..ff70185 100644 --- a/src/ui/common/filter/StringArrFilter.tsx +++ b/src/ui/common/filter/StringArrFilter.tsx @@ -201,7 +201,7 @@ class StringArrFilter extends React.Component { { input: any; label: string; placeholder?: string; + t: any; } class TextFilterInternal extends React.Component { @@ -35,14 +37,14 @@ class TextFilterInternal extends React.Component { } public render() { - const { label, placeholder } = this.props; + const { label, placeholder, t } = this.props; return (
@@ -55,18 +57,20 @@ interface ITextFilter extends React.ClassAttributes { label: string; placeholder?: string; className?: string; + t: any; } class TextFilter extends React.Component { public render() { - const { name , label, className, ...other } = this.props; + const { name , label, className, t, ...other } = this.props; return (
@@ -74,4 +78,4 @@ class TextFilter extends React.Component { } } -export default TextFilter; +export default translate()(TextFilter); diff --git a/src/ui/common/forms/FormControl.tsx b/src/ui/common/forms/FormControl.tsx index 2cc904e..32ae2fa 100644 --- a/src/ui/common/forms/FormControl.tsx +++ b/src/ui/common/forms/FormControl.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import { withStyles } from '@material-ui/core/styles'; import InputLabel from '@material-ui/core/InputLabel'; @@ -12,6 +13,7 @@ interface IProps { children: any; meta?: any; disableAnimation?: boolean; + t: any; } const styles = (theme) => { @@ -24,13 +26,13 @@ const styles = (theme) => { }); }; -const FormControl = ({ fullWidth = false, required = false, label = null, children, meta = {}, disableAnimation = false, classes }: IProps) => { +const FormControl = ({ fullWidth = false, required = false, label = null, children, meta = {}, disableAnimation = false, classes, t }: IProps) => { // console.log('Meta', meta); const { error, warning } = meta; return ( - { label && { label } } + { label && { t(label) } } { children } { ((error &&
{ error }
) @@ -41,4 +43,4 @@ const FormControl = ({ fullWidth = false, required = false, label = null, childr ); }; -export default withStyles(styles)(FormControl); +export default translate()(withStyles(styles)(FormControl)); diff --git a/src/ui/common/forms/Toggle.tsx b/src/ui/common/forms/Toggle.tsx index 7853ab2..fb0e9bf 100644 --- a/src/ui/common/forms/Toggle.tsx +++ b/src/ui/common/forms/Toggle.tsx @@ -1,11 +1,12 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Switch from '@material-ui/core/Switch'; // // Render a switch form control (a yes/no toggler) -const renderToggler = ({input, falseIsNull = false, label, meta, ...rest}: { falseIsNull?: boolean } & any) => { +const renderToggler = ({input, falseIsNull = false, label, meta, t, ...rest}: { falseIsNull?: boolean } & any) => { const onChange = (event, checked) => { input.onChange(checked ? checked : (falseIsNull && ! checked ? null : checked)); @@ -14,9 +15,9 @@ const renderToggler = ({input, falseIsNull = false, label, meta, ...rest}: { fal return ( } - label={ label } + label={ t(label) } /> ); }; -export default renderToggler; +export default translate()(renderToggler); diff --git a/src/ui/common/heading/ContentHeader.tsx b/src/ui/common/heading/ContentHeader.tsx index 03ec4ce..701b100 100644 --- a/src/ui/common/heading/ContentHeader.tsx +++ b/src/ui/common/heading/ContentHeader.tsx @@ -3,35 +3,44 @@ import Grid from '@material-ui/core/Grid'; import Hidden from '@material-ui/core/Hidden'; import { connect } from 'react-redux'; import { setPageTitle } from 'actions/pageTitle'; +import { translate } from 'react-i18next'; import { bindActionCreators } from 'redux'; interface IContentHeader extends React.ClassAttributes { title: string; - subtitle?: string; + subTitle?: string; setPageTitle: (title: string) => void; + t: any; } class ContentHeader extends React.Component { public constructor(props: any) { super(props); - const {title, setPageTitle} = this.props; - setPageTitle(title); + const {title, setPageTitle, t} = this.props; + setPageTitle(t(title)); + } + + public componentWillReceiveProps(nextProps) { + const {title, setPageTitle, t} = this.props; + if (t && title) { + setPageTitle(t(title)); + } } public render() { - const { title, subtitle} = this.props; + const { title, subTitle, t} = this.props; return (

- { title } + { t(title) }

- { subtitle } + { t(subTitle) }

@@ -44,5 +53,5 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ setPageTitle, }, dispatch); -export default connect(null, mapDispatchToProps)(ContentHeader); +export default connect(null, mapDispatchToProps)(translate()(ContentHeader)); diff --git a/src/ui/common/not-found/index.tsx b/src/ui/common/not-found/index.tsx index a383808..06ce4b1 100644 --- a/src/ui/common/not-found/index.tsx +++ b/src/ui/common/not-found/index.tsx @@ -13,8 +13,8 @@ class NotFound extends React.Component { } return (
-

{ t('Not found') }

-

{ t('Nothing matches your request') }

+

{ t('common.Not found') }

+

{ t('common.Nothing matches your request') }

); } diff --git a/src/ui/common/pagination/index.tsx b/src/ui/common/pagination/index.tsx index 1fb99a8..14948d8 100644 --- a/src/ui/common/pagination/index.tsx +++ b/src/ui/common/pagination/index.tsx @@ -15,7 +15,7 @@ import withWidth from '@material-ui/core/withWidth'; import Hidden from '@material-ui/core/Hidden'; import { Breakpoint } from '@material-ui/core/styles/createBreakpoints'; import Number from 'ui/common/Number'; -import { Page } from 'model/common.model'; +import Page from 'model/Page'; interface IPaginationComponentProps extends React.ClassAttributes { classes: any; @@ -187,7 +187,8 @@ class PaginationComponent extends React.Component
- { t('common:paginate.numberOfItems', { count: pageObj ? pageObj.totalElements : 0, what: t(displayName || 'common:label.item', { count: pageObj ? pageObj.totalElements : 0 }) }) } + { t(pageObj && pageObj.totalElements < 10000 ? 'common:paginate.numberOfItems' : 'common:paginate.estimatedNumberOfItems', + { count: pageObj ? pageObj.totalElements : '↺', what: t(displayName || 'common:label.item', { count: pageObj ? pageObj.totalElements : 0 }) }) }
{ !infinite && } MenuProps={ { PaperProps: { @@ -237,19 +237,19 @@ class PaginationComponent extends React.Component - Sort By + { t('common:label.sortBy') } { Object.keys(sortOptions).map((key, i) => { if (typeof sortOptions[key] === 'string') { return ( - { t(`f.sortOptions.${sortOptions[key]}`) } + { t(sortOptions[key]) } ); } else { return ( - { t(`f.sortOptions.${sortOptions[key].label}`) } + { t(sortOptions[key].label) } ); } diff --git a/src/ui/common/permission/Permissions.tsx b/src/ui/common/permission/Permissions.tsx index 2dcfd57..b741790 100644 --- a/src/ui/common/permission/Permissions.tsx +++ b/src/ui/common/permission/Permissions.tsx @@ -6,6 +6,7 @@ import Dialog from '@material-ui/core/Dialog'; import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; import DialogTitle from '@material-ui/core/DialogTitle'; +import { translate } from 'react-i18next'; import {permissions, addPermission, autocomplete} from 'actions/permission'; import Loading from 'ui/common/Loading'; @@ -22,6 +23,7 @@ interface IPermissionsProps extends React.ClassAttributes { permissions: (clazz: string, id: number) => Promise; autocomplete: (term: string) => Promise<{[key: string]: number}>; addPermission: (clazz: string, id: number, sidPermissions: SidPermissions) => Promise; + t: any; } class Permissions extends React.Component { @@ -73,12 +75,12 @@ class Permissions extends React.Component { } public render() { - const {autocomplete} = this.props; + const {autocomplete, t} = this.props; const {aclObjectIdentity, autocompleteObj} = this.state; return ( - + { this.state.open && { maxWidth="md" fullWidth > - Manage permissions + { t('common:permissions.managePermissions') } { !aclObjectIdentity ? :
- { aclObjectIdentity.aclClass.aclClass } - { aclObjectIdentity.objectIdIdentity } - { aclObjectIdentity.ownerSid.sid } - { `${aclObjectIdentity.entriesInheriting}` } + { aclObjectIdentity.aclClass.aclClass } + { aclObjectIdentity.objectIdIdentity } + { aclObjectIdentity.ownerSid.sid } + { aclObjectIdentity.entriesInheriting } {
@@ -133,4 +135,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ autocomplete, }, dispatch); -export default connect(null, mapDispatchToProps)(Permissions); +export default translate()(connect(null, mapDispatchToProps)(Permissions)); diff --git a/src/ui/common/permission/PermissionsTableForm.tsx b/src/ui/common/permission/PermissionsTableForm.tsx index f16bd38..13cd0a2 100644 --- a/src/ui/common/permission/PermissionsTableForm.tsx +++ b/src/ui/common/permission/PermissionsTableForm.tsx @@ -7,6 +7,7 @@ import TableCell from '@material-ui/core/TableCell'; import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; import Button from '@material-ui/core/Button'; +import {translate} from 'react-i18next'; import {PERMISSION_TABLE_FORM} from 'constants/permission'; import ReduxCheckbox from 'ui/common/checkbox'; @@ -23,6 +24,7 @@ interface IPermissionsTableFormProps extends React.ClassAttributes { autocompleteObj: {[key: string]: number}; onInputChange: (e) => void; inherits?: boolean; + t: any; } const styles = { @@ -157,18 +159,18 @@ class PermissionsTableForm extends React.Component
- Sid - Create - Read - Write - Delete - Manage + { t('common:permissions.sid') } + { t('common:action.create') } + { t('common:action.read') } + { t('common:action.write') } + { t('common:action.delete') } + { t('common:action.manage') } @@ -184,16 +186,16 @@ class PermissionsTableForm extends React.Component -
Inherited permissions
+
{ t('common:permissions.effectivePermissions') }
- Sid - Create - Read - Write - Delete - Manage + { t('common:permissions.sid') } + { t('common:action.create') } + { t('common:action.read') } + { t('common:action.write') } + { t('common:action.delete') } + { t('common:action.manage') } @@ -212,7 +214,7 @@ class PermissionsTableForm extends React.Component { this.processQueue(); } + private renderContent = () => { + const {messageInfo: {message} } = this.state; + const { button: renderButton, t } = this.props; + + if (!renderButton) { + return ({ t(message) }); + } + + return ( + + { t(message) } { renderButton() } + + ); + + } + public render() { - const { message, key } = this.state.messageInfo; + const { key } = this.state.messageInfo; + const snackContent = this.renderContent(); return ( { onClose={ this.handleClose } onExited={ this.handleExited } ContentProps={ { 'aria-describedby': 'message-id' } } - message={ { message } } + message={ snackContent } /> ); } @@ -90,6 +110,7 @@ class Snackbar extends React.Component { const mapStateToProps = (state) => ({ snack: state.snackbar.content, + button: state.snackbar.button, }); -export default connect(mapStateToProps, null)(Snackbar); +export default translate()(connect(mapStateToProps, null)(Snackbar)); diff --git a/src/ui/common/stepper/BottomSection.tsx b/src/ui/common/stepper/BottomSection.tsx index 005d42a..38b026d 100644 --- a/src/ui/common/stepper/BottomSection.tsx +++ b/src/ui/common/stepper/BottomSection.tsx @@ -1,13 +1,14 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import Grid from '@material-ui/core/Grid'; import {withStyles} from '@material-ui/core/styles'; const styleSheet = {}; -const BottomSection = ({classes}) => ( +const BottomSection = ({classes, t}) => ( - * All fields marked with * are required. + { t('dashboard.c.bottomSection.allFields') } ); -export default withStyles(styleSheet)<{}>(BottomSection); +export default translate()(withStyles(styleSheet)<{}>(BottomSection)); diff --git a/src/ui/common/stepper/StepNavigation.tsx b/src/ui/common/stepper/StepNavigation.tsx index 96a6a02..fef9e89 100644 --- a/src/ui/common/stepper/StepNavigation.tsx +++ b/src/ui/common/stepper/StepNavigation.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import Grid from '@material-ui/core/Grid'; import Divider from '@material-ui/core/Divider'; @@ -19,6 +20,7 @@ interface IStepNavigationProps extends React.ClassAttributes { showStepName: boolean; topDivider: boolean; bottomDivider: boolean; + t: any; } const styles = (theme) => ({ @@ -74,7 +76,7 @@ class StepNavigation extends React.Component { public render() { - const {classes, disabled, disabledNext, steps, showStepName, topDivider, bottomDivider, onGotoStep, onDelete, onPublish} = this.props; + const {classes, disabled, disabledNext, steps, showStepName, topDivider, bottomDivider, onGotoStep, onDelete, onPublish, t} = this.props; return ( @@ -82,30 +84,30 @@ class StepNavigation extends React.Component {

{ - showStepName && `${this.state.id}. ${steps.find((e) => e.id === this.state.id).name}` + showStepName && `${this.state.id}. ${t(steps.find((e) => e.id === this.state.id).name)}` }

{ this.state.id === 1 && ( ) } { this.state.id > 1 && ( ) } { this.state.id !== steps.length && ( ) } { this.state.id === steps.length && ( ) } @@ -116,4 +118,4 @@ class StepNavigation extends React.Component { } -export default withStyles(styles)(StepNavigation); +export default translate()(withStyles(styles)(StepNavigation)); diff --git a/src/ui/common/stepper/TopSection.tsx b/src/ui/common/stepper/TopSection.tsx index 6df4e2e..029c5e3 100644 --- a/src/ui/common/stepper/TopSection.tsx +++ b/src/ui/common/stepper/TopSection.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import Grid from '@material-ui/core/Grid'; import Divider from '@material-ui/core/Divider'; import Button from '@material-ui/core/Button'; @@ -39,7 +40,7 @@ const styles = (theme) => ({ }, }); -const TopSection = ({classes, pageTitle, subTitle, tab = ''}) => ( +const TopSection = ({classes, pageTitle, subTitle, t, tab = ''}) => (

@@ -53,26 +54,26 @@ const TopSection = ({classes, pageTitle, subTitle, tab = ''}) => (

- Data publication + { t('dashboard.c.topSection.dataPublication') }

- New in the data publication process? + { t('dashboard.c.topSection.newInData') }
- +
@@ -81,4 +82,4 @@ const TopSection = ({classes, pageTitle, subTitle, tab = ''}) => ( ); -export default (withStyles as any)(styles)(TopSection); +export default translate()((withStyles as any)(styles)(TopSection)); diff --git a/src/ui/common/stepper/progress-menu/index.tsx b/src/ui/common/stepper/progress-menu/index.tsx index 383a5fd..47eeecd 100644 --- a/src/ui/common/stepper/progress-menu/index.tsx +++ b/src/ui/common/stepper/progress-menu/index.tsx @@ -3,6 +3,7 @@ import {withStyles} from '@material-ui/core/styles'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; import ListItemText from '@material-ui/core/ListItemText'; +import { translate } from 'react-i18next'; import StepsListItem from './StepsListItem'; @@ -12,6 +13,7 @@ interface IProgressMenuProps extends React.ClassAttributes { location: any; steps: any; onGotoStep: (i: number) => void; + t: any; } const styleSheet = (theme) => ({ @@ -33,13 +35,13 @@ class ProgressMenu extends React.Component { public render() { - const {classes, disabled, steps, onGotoStep, location: {pathname}} = this.props; + const {classes, disabled, steps, onGotoStep, t, location: {pathname}} = this.props; const link = pathname.split('/').pop(); return ( - + { steps.map((step, i) => ( @@ -49,7 +51,7 @@ class ProgressMenu extends React.Component { onClick={ onGotoStep(i + 1) } active={ step.path.endsWith(link) } index={ i } - name = { step.name } + name = { t(step.name) } /> ), ) @@ -59,4 +61,4 @@ class ProgressMenu extends React.Component { } } -export default withStyles(styleSheet)(ProgressMenu); +export default translate()(withStyles(styleSheet)(ProgressMenu)); diff --git a/src/ui/common/time/McpdDate.tsx b/src/ui/common/time/McpdDate.tsx index c703690..676f743 100644 --- a/src/ui/common/time/McpdDate.tsx +++ b/src/ui/common/time/McpdDate.tsx @@ -1,15 +1,11 @@ import * as React from 'react'; -import Validators from 'utilities/Validators'; +import {translate} from 'react-i18next'; -export default function McpdDate({ value, locale = 'en-GB' }: { value: string, locale?: string }) { +export default translate()(function McpdDate({ value, locale = 'en-GB', t }: { value: string, locale?: string, t: any }) { if (!value) { - return (Date not provided); - } - - if (Validators.mcpdDate(value) === 'Invalid date') { - return { value }; + return ({ t('common:label.dateNotProvided') }); } const year = value.substr(0, 4); @@ -33,4 +29,4 @@ export default function McpdDate({ value, locale = 'en-GB' }: { value: string, l { mcpdDate } ); -} +}); diff --git a/src/ui/common/time/PrettyDate.tsx b/src/ui/common/time/PrettyDate.tsx index 0444ffd..f72eb22 100644 --- a/src/ui/common/time/PrettyDate.tsx +++ b/src/ui/common/time/PrettyDate.tsx @@ -1,11 +1,13 @@ import * as React from 'react'; import * as moment from 'moment'; +import {translate} from 'react-i18next'; import TimeAgo from 'react-time-ago'; -export default function PrettyDate({ +export default translate()(function PrettyDate({ value, -}: { value: Date }) { + t, +}: { value: Date, t: any }) { if (value) { const valueTime = value.getTime ? value.getTime() : new Date(value).getTime(); @@ -23,7 +25,7 @@ export default function PrettyDate({ ); } else { return ( - Date not provided + { t('common:label.dateNotProvided') } ); } -} +}); diff --git a/src/ui/layout/Footer/index.tsx b/src/ui/layout/Footer/index.tsx index df86c4f..e8a98f8 100644 --- a/src/ui/layout/Footer/index.tsx +++ b/src/ui/layout/Footer/index.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { ExternalLink } from 'ui/catalog/Links'; import { translate } from 'react-i18next'; +import { Link } from 'react-router-dom'; const THIS_YEAR = new Date().getFullYear(); @@ -17,36 +18,36 @@ const Footer = ({ t }) => (

- { t('footer.copyright', { from: 2017, to: THIS_YEAR }) } + { t('common.footer.copyright', { from: 2017, to: THIS_YEAR }) }

diff --git a/src/ui/layout/Header/LeftMenu.tsx b/src/ui/layout/Header/LeftMenu.tsx index 10e073e..a020807 100644 --- a/src/ui/layout/Header/LeftMenu.tsx +++ b/src/ui/layout/Header/LeftMenu.tsx @@ -94,26 +94,26 @@ class LeftMenu extends React.Component {
- { t('menu.Home') } + { t('common.menu.home') } - { t('menu.Datasets') } + { t('datasets.common.modelName_plural') } - { t('menu.Partners') } + { t('partners.common.modelName_plural') } - { t('menu.Descriptor lists') } + { t('descriptorlists.common.modelName_plural') } - { t('menu.Descriptors') } + { t('descriptors.common.modelName_plural') } - { t('menu.Crops') } + { t('crop.common.modelName_plural') } - { t('menu.Controlled vocabularies') } + { t('vocabulary.common.modelName_plural') }

diff --git a/src/ui/layout/Header/UserMenuComponent.tsx b/src/ui/layout/Header/UserMenuComponent.tsx index 36ffc22..ccd8eb7 100644 --- a/src/ui/layout/Header/UserMenuComponent.tsx +++ b/src/ui/layout/Header/UserMenuComponent.tsx @@ -110,10 +110,10 @@ class UserMenuComponent extends React.Component { onClose={ this.handleRequestClose } onClick={ this.handleRequestClose } > - { t('menu.My Dashboard') } - { t('menu.My profile') } - { t('menu.Crops') } - { t('menu.Controlled vocabularies') } + { t('common.menu.dashboard') } + { t('common.menu.profile') } + { t('crop.common.modelName_plural') } + { t('vocabulary.common.vocabulary_plural') } { t('common:action.logout') } diff --git a/src/ui/layout/Header/index.tsx b/src/ui/layout/Header/index.tsx index e28ac42..b47f733 100644 --- a/src/ui/layout/Header/index.tsx +++ b/src/ui/layout/Header/index.tsx @@ -174,9 +174,9 @@ class Header extends React.Component {
- { t('menu.Datasets') } - { t('menu.Data providers') } - { t('menu.Descriptor lists') } + { t('datasets.common.menu') } + { t('partners.common.menu') } + { t('descriptorlists.common.menu') }
{ this.renderLogin([ROLE_USER, ROLE_ADMINISTRATOR]) }
diff --git a/src/ui/layout/sidebar/SidebarWrapper.tsx b/src/ui/layout/sidebar/SidebarWrapper.tsx index 5d0f7d4..56d25f6 100644 --- a/src/ui/layout/sidebar/SidebarWrapper.tsx +++ b/src/ui/layout/sidebar/SidebarWrapper.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; import {withStyles} from '@material-ui/core/styles'; @@ -13,6 +14,7 @@ interface ISidebarProps extends React.ClassAttributes { sidebarContent: any; collapseSidebar: (isOpen: boolean) => void; isOpen: boolean; + t: any; } @@ -142,7 +144,7 @@ class SidebarWrapper extends React.Component { } public render() { - const { sidebarContent, classes, isOpen } = this.props; + const { sidebarContent, classes, isOpen, t } = this.props; return ( {
{ this.setIsCollapsed(true); }} className={ `${classes.collapseButton}` }> - Collapse + { t('common:action.collapse') }
} { !isOpen &&
{ this.setIsCollapsed(false); }}> -

Open sidebar

+

{ t('common:action.openSidebar') }

{ this.setIsCollapsed(false); }} @@ -188,4 +190,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ collapseSidebar, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(SidebarWrapper)); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(SidebarWrapper))); diff --git a/src/ui/pages/login/LoginPage.tsx b/src/ui/pages/login/LoginPage.tsx index 1632031..ba98c74 100644 --- a/src/ui/pages/login/LoginPage.tsx +++ b/src/ui/pages/login/LoginPage.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import * as _ from 'lodash'; + import { log } from 'utilities/debug'; import { loginRequest, checkTokenRequest } from 'actions/login'; @@ -19,6 +21,7 @@ interface ILoginContainerProps extends React.ClassAttributes { loginRequest: (u, p) => Promise; checkTokenRequest: (t) => Promise; history: any; + t: any; } class LoginContainer extends React.Component { @@ -43,13 +46,14 @@ class LoginContainer extends React.Component { } public render() { + const { t } = this.props; return ( - + - + @@ -70,4 +74,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ checkTokenRequest, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(LoginContainer)); diff --git a/src/ui/pages/login/c/LoginForm.tsx b/src/ui/pages/login/c/LoginForm.tsx index 2a68b46..096f83f 100644 --- a/src/ui/pages/login/c/LoginForm.tsx +++ b/src/ui/pages/login/c/LoginForm.tsx @@ -1,9 +1,11 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import TextField from '@material-ui/core/TextField'; import Button from '@material-ui/core/Button'; interface ILoginProps extends React.ClassAttributes { onTryLogin: (username, password) => Promise; + t: any; } class Login extends React.Component { @@ -36,16 +38,17 @@ class Login extends React.Component { private handlePassword = (e) => this.setState({...this.state, password: e.target.value}); public render() { + const {t} = this.props; return (
{ type="submit" style={ { margin: '1rem 0 1rem 0' } } > - Login + { t('common:action.login') } ); @@ -67,4 +70,4 @@ class Login extends React.Component { } -export default Login; +export default translate()(Login); diff --git a/src/ui/pages/profile/ProfilePage.tsx b/src/ui/pages/profile/ProfilePage.tsx index 9bb687d..81cb49c 100644 --- a/src/ui/pages/profile/ProfilePage.tsx +++ b/src/ui/pages/profile/ProfilePage.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { loadUserProfile, changePassword } from 'actions/userProfile'; @@ -17,6 +18,7 @@ interface IProfilePageProps extends React.ClassAttributes { loadUserProfile: () => void; userProfile: User; changePassword: (newPassword: string, oldPassword: string) => void; + t: any; } class ProfilePage extends React.Component { @@ -42,7 +44,7 @@ class ProfilePage extends React.Component { } public render() { - const { userProfile } = this.props; + const { userProfile, t } = this.props; const stillLoading: boolean = (! userProfile); @@ -62,13 +64,13 @@ class ProfilePage extends React.Component { - { userProfile.fullName || Not specified } - { userProfile.email } - { userProfile.accountType } - { - userProfile.roles.map((role) => (
{ role }
)) + { userProfile.fullName || Not specified } + { userProfile.email } + { t(`user.common.type.${userProfile.accountType.toLowerCase()}`) } + { + userProfile.roles.map((role) => (
{ t(`user.common.role.${role}`) }
)) }
- +
@@ -79,7 +81,7 @@ class ProfilePage extends React.Component { { userProfile.accountType === AccountType.LOCAL && - + @@ -101,4 +103,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ changePassword, }, dispatch); -export default connect(mapStateToProps, mapDispatchToProps)(ProfilePage); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(ProfilePage)); diff --git a/src/ui/pages/profile/c/PasswordForm.tsx b/src/ui/pages/profile/c/PasswordForm.tsx index f8b2cfe..ee2847e 100644 --- a/src/ui/pages/profile/c/PasswordForm.tsx +++ b/src/ui/pages/profile/c/PasswordForm.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {translate} from 'react-i18next'; import {CHANGE_PASSWORD_FORM} from 'constants/userProfile'; import {Field, reduxForm} from 'redux-form'; import Button from '@material-ui/core/Button'; @@ -12,34 +13,34 @@ class PasswordForm extends React.Component { } public render() { - const { error, handleSubmit, submitting } = this.props; + const { error, handleSubmit, submitting, t } = this.props; return (
{ error &&
{ error }
} ); @@ -55,7 +56,7 @@ const validate = (values) => { return {}; }; -export default reduxForm({ +export default translate()(reduxForm({ form: CHANGE_PASSWORD_FORM, enableReinitialize: true, validate, -})(PasswordForm); +})(PasswordForm)); diff --git a/src/ui/pages/welcome/index.tsx b/src/ui/pages/welcome/index.tsx index 844a8cb..9fd1467 100644 --- a/src/ui/pages/welcome/index.tsx +++ b/src/ui/pages/welcome/index.tsx @@ -219,8 +219,7 @@ class WelcomePage extends React.Component { constructor(props: any, context: any) { super(props, context); - const { t } = props; - this.state = { query: '', queryPlaceholder: t('welcome.search.placeholder') }; + this.state = { query: '', queryPlaceholder: 'welcome.search.placeholder' }; } public render() { @@ -247,21 +246,21 @@ class WelcomePage extends React.Component { const onSubmitSearch = (e) => { const { query } = this.state; - const { navigateTo, t } = this.props; + const { navigateTo } = this.props; e.preventDefault(); if (query && query.length > 0) { navigateTo(`/datasets/suggest`, { q: query }); } else { this.setState({ ...this.state, - queryPlaceholder: t(`welcome.search.suggestion${Math.floor(Math.random() * SEARCH_SUGGESTIONS_COUNT)}`), + queryPlaceholder: `welcome.search.suggestion${Math.floor(Math.random() * SEARCH_SUGGESTIONS_COUNT)}`, }); } }; return ( - +
-
+
+
diff --git a/src/ui/routes.tsx b/src/ui/routes.tsx index ce75d89..3465d78 100644 --- a/src/ui/routes.tsx +++ b/src/ui/routes.tsx @@ -30,8 +30,8 @@ const routes = [ path: '/crops', component: Wrapper, extraProps: { - title: 'p.crop.title', - subtitle: 'p.crop.subtitle', + title: 'crop.common.modelName_plural', + subtitle: 'crop.common.subtitle', }, routes: [ { diff --git a/src/vocabulary/routes.ts b/src/vocabulary/routes.ts index 3a1a7a5..16b476c 100644 --- a/src/vocabulary/routes.ts +++ b/src/vocabulary/routes.ts @@ -11,8 +11,8 @@ const publicRoutes = [ path: '/vocabulary', component: Wrapper, extraProps: { - title: 'p.vocab.title', - subtitle: 'p.vocab.subtitle', + title: 'vocabulary.common.modelName_plural', + subtitle: 'vocabulary.common.subtitle', }, routes: [ { diff --git a/src/vocabulary/translations.json b/src/vocabulary/translations.json new file mode 100644 index 0000000..4e6ff51 --- /dev/null +++ b/src/vocabulary/translations.json @@ -0,0 +1,49 @@ +{ + "common": { + "modelName": "Vocabulary", + "modelName_plural": "Vocabularies", + "subtitle": "Commonly used controlled vocabularies", + "vocabulary": "Controlled vocabulary", + "vocabulary_plural": "Controlled vocabularies", + "code": "Code" + }, + "admin": { + "c": { + "form": { + "descriptionPlaceholder": "Full description of the vocabulary", + "details": "Vocabulary details", + "error": "Don't use the ItemsEditor for more than 50 items!", + "list": "list", + "owner": "Owner of the vocabulary", + "ownerPlaceholder": "Data provider", + "publisher": "Original publisher", + "term": "Vocabulary term", + "term_plural": "Vocabulary terms", + "termUrlPrefix": "Term URL prefix", + "title": "Vocabulary title", + "titlePlaceholder": "Color of magic", + "url": "URL to definition", + "versionTag": "Version tag" + } + }, + "p": { + "browse": { + "create": "Create vocabulary", + "title": "What do you want to do?", + "update": "Update vocabularies" + }, + "display": { + "acceptedTerms": "Accepted vocabulary terms", + "deleteDescription": "Deleting the vocabulary is only possible when there is no associated data.", + "description": "Description", + "term": "Term", + "terms": "terms", + "title": "Vocabulary details" + } + } + }, + "sort": { + "maintainer": "Maintainer", + "URL": "URL" + } +} \ No newline at end of file diff --git a/src/vocabulary/ui/BrowsePage.tsx b/src/vocabulary/ui/BrowsePage.tsx index afc3476..64541dc 100644 --- a/src/vocabulary/ui/BrowsePage.tsx +++ b/src/vocabulary/ui/BrowsePage.tsx @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { withStyles } from '@material-ui/core/styles'; import { parse } from 'query-string'; +import { translate } from 'react-i18next'; import { listVocabularies, promiseListVocabularies, createVocabulary } from 'vocabulary/actions/public'; import { autoUpdateVocabularies } from 'vocabulary/actions/admin'; @@ -39,6 +40,7 @@ interface IBrowsePageProps extends React.ClassAttributes { listVocabularies: () => void; promiseListVocabularies: () => Promise>; createVocabulary: any; + t: any; } class BrowsePage extends React.Component { @@ -94,7 +96,7 @@ class BrowsePage extends React.Component { } public render() { - const {classes, paged, createVocabulary, pagination, promiseListVocabularies} = this.props; + const {classes, paged, createVocabulary, pagination, promiseListVocabularies, t} = this.props; const stillLoading: boolean = (!(paged && paged.content)); @@ -110,14 +112,14 @@ class BrowsePage extends React.Component {
} @@ -127,7 +129,7 @@ class BrowsePage extends React.Component { @@ -164,4 +166,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ const styled = withStyles(styles)(BrowsePage); -export default connect(mapStateToProps, mapDispatchToProps)(styled); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(styled)); diff --git a/src/vocabulary/ui/DisplayPage.tsx b/src/vocabulary/ui/DisplayPage.tsx index 463dce7..2874103 100644 --- a/src/vocabulary/ui/DisplayPage.tsx +++ b/src/vocabulary/ui/DisplayPage.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { withStyles } from '@material-ui/core/styles'; +import { translate } from 'react-i18next'; import { loadVocabulary, listVocabularyTerms } from 'vocabulary/actions/public'; import { deleteVocabulary } from 'vocabulary/actions/admin'; @@ -28,6 +29,7 @@ import Vocabulary from 'model/vocabulary/Vocabulary'; interface IDisplayPageProps extends React.ClassAttributes { classes: any; uuid?: string; + t: any; vocabularyTerms: Page; loadVocabulary: (uuid: string) => void; @@ -69,12 +71,12 @@ class DisplayPage extends React.Component { } private onDelete = (e) => { - const {vocabulary, deleteVocabulary} = this.props; + const {vocabulary, deleteVocabulary, t} = this.props; confirm(Delete { vocabulary.title }?, { - description: `Deleting the vocabulary is only possible when there is no associated data.`, - confirmLabel: 'Delete', - abortLabel: 'Cancel', + description: t('vocabulary.admin.p.display.deleteDescription'), + confirmLabel: t('common:action.delete'), + abortLabel: t('common:action.cancel'), }).then(() => { deleteVocabulary(vocabulary); }).catch(() => { @@ -83,7 +85,7 @@ class DisplayPage extends React.Component { } public render() { - const {classes, uuid, vocabulary, listVocabularyTerms, vocabularyTerms} = this.props; + const {classes, uuid, vocabulary, listVocabularyTerms, vocabularyTerms, t} = this.props; const stillLoading: boolean = (!vocabulary || (uuid && vocabulary && vocabulary.uuid !== uuid)); @@ -99,7 +101,8 @@ class DisplayPage extends React.Component { return ( - } /> + } /> { stillLoading ? : @@ -118,9 +121,14 @@ class DisplayPage extends React.Component { { (vocabulary._permissions.write || vocabulary._permissions.delete) && ( { vocabulary._permissions.write && - } + + + } { vocabulary._permissions.delete && - } + + } ) } @@ -128,19 +136,19 @@ class DisplayPage extends React.Component { { vocabularyTerms && vocabularyTerms.content && -
+
{ vocabularyTerms.totalElements > 50 && }
- Code - Term - Description + { t('vocabulary.common.code') } + { t('vocabulary.admin.p.display.term') } + { t('common:label.description') } ) }> bindActionCreators({ const styled = withStyles(styles)(DisplayPage); -export default connect(mapStateToProps, mapDispatchToProps)(styled); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(styled)); diff --git a/src/vocabulary/ui/EditPage.tsx b/src/vocabulary/ui/EditPage.tsx index 7b156da..6b4d38d 100644 --- a/src/vocabulary/ui/EditPage.tsx +++ b/src/vocabulary/ui/EditPage.tsx @@ -3,6 +3,7 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import {withStyles} from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; +import { translate } from 'react-i18next'; import {log} from 'utilities/debug'; @@ -28,6 +29,7 @@ interface IVocabularyEditPageProps extends React.ClassAttributes { loadVocabulary: (uuid: string) => void; saveVocabulary: (vocabulary: Vocabulary) => void; deleteVocabulary: (vocabulary: Vocabulary) => void; + t: any; } const styles = (theme) => ({ @@ -81,12 +83,12 @@ class VocabularyEditPage extends React.Component } private onDelete = () => { - const { vocabulary, deleteVocabulary } = this.props; + const { vocabulary, deleteVocabulary, t } = this.props; - confirm(Delete { vocabulary.title }?, { + confirm({ t('common:action.delete') } { vocabulary.title }?, { // description: `Note,.`, - confirmLabel: 'Delete', - abortLabel: 'Cancel', + confirmLabel: t('common:action.delete'), + abortLabel: t('common:action.cancel'), }).then(() => { deleteVocabulary(vocabulary); }); @@ -131,4 +133,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ const styled = withStyles(styles)(VocabularyEditPage); -export default connect(mapStateToProps, mapDispatchToProps)(styled); +export default translate()(connect(mapStateToProps, mapDispatchToProps)(styled)); diff --git a/src/vocabulary/ui/c/VocabularyCard.tsx b/src/vocabulary/ui/c/VocabularyCard.tsx index 2b5941f..07dd993 100644 --- a/src/vocabulary/ui/c/VocabularyCard.tsx +++ b/src/vocabulary/ui/c/VocabularyCard.tsx @@ -18,7 +18,7 @@ const VocabularyCard = ({vocabulary, className = '', textRows = 2, t}: { vocabul { vocabulary._permissions.write && - + } diff --git a/src/vocabulary/ui/c/VocabularyForm.tsx b/src/vocabulary/ui/c/VocabularyForm.tsx index 21b01c1..451e142 100644 --- a/src/vocabulary/ui/c/VocabularyForm.tsx +++ b/src/vocabulary/ui/c/VocabularyForm.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import {Field, reduxForm} from 'redux-form'; import { Link } from 'react-router-dom'; +import { translate } from 'react-i18next'; import {log} from 'utilities/debug'; @@ -20,18 +21,18 @@ import Card, {CardHeader, CardContent, CardActions} from 'ui/common/Card'; import Button from '@material-ui/core/Button'; import Grid from '@material-ui/core/Grid'; -const TermEditor = (member, index, fields) => ( +const TermEditor = (member, index, t, fields) => ( - - - + + + ); class VocabularyForm extends React.Component { public render() { - const {error, handleSubmit, initialValues, onDelete, partners, submitting, invalid} = this.props; + const {error, handleSubmit, initialValues, onDelete, partners, submitting, invalid, t} = this.props; const onAddMember = () => { log('Adding new term'); @@ -49,50 +50,52 @@ class VocabularyForm extends React.Component { return ( - + - + { initialValues.termCount > 50 ? -
Don't use the ItemsEditor for more than 50 items!
+
{ t('vocabulary.admin.c.form.error') }
: - + }
@@ -100,9 +103,17 @@ class VocabularyForm extends React.Component {
{ error }
) } - - { (initialValues._permissions && initialValues._permissions.delete) && } - + + { (initialValues._permissions && initialValues._permissions.delete) && + + } + + +
@@ -110,7 +121,7 @@ class VocabularyForm extends React.Component { } } -export default connect((state) => ({}), null)(reduxForm({ +export default translate()(connect((state) => ({}), null)(reduxForm({ form: VOCABULARY_FORM, enableReinitialize: true, -})(VocabularyForm)); +})(VocabularyForm))); -- GitLab