From 2efd01012ff838c8060f83da2f865cf09c472618 Mon Sep 17 00:00:00 2001 From: Saket Hatwar Date: Tue, 23 Sep 2025 12:26:32 -0400 Subject: [PATCH 1/5] Add Translations --- Makefile | 7 +- jsx/I18nSetup.d.ts | 4 + modules/dataquery/jsx/definefields.tsx | 7 +- .../jsx/definefilters.addfiltermodal.tsx | 68 +-- .../jsx/definefilters.importcsvmodal.tsx | 40 +- modules/dataquery/jsx/definefilters.tsx | 79 ++-- modules/dataquery/jsx/fielddisplay.tsx | 8 +- modules/dataquery/jsx/index.tsx | 9 +- modules/dataquery/jsx/nextsteps.tsx | 18 +- modules/dataquery/jsx/querytree.tsx | 17 +- modules/dataquery/jsx/viewdata.tsx | 39 +- .../dataquery/jsx/welcome.adminquerymodal.tsx | 18 +- .../dataquery/jsx/welcome.namequerymodal.tsx | 10 +- modules/dataquery/jsx/welcome.tsx | 141 +++---- modules/dataquery/locale/dataquery.pot | 372 ++++++++++++++++ .../locale/hi/LC_MESSAGES/dataquery.json | 128 ++++++ .../locale/hi/LC_MESSAGES/dataquery.po | 397 ++++++++++++++++++ 17 files changed, 1125 insertions(+), 237 deletions(-) create mode 100644 jsx/I18nSetup.d.ts create mode 100644 modules/dataquery/locale/hi/LC_MESSAGES/dataquery.json create mode 100644 modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po diff --git a/Makefile b/Makefile index 9e906cc7d45..6892349e0c2 100755 --- a/Makefile +++ b/Makefile @@ -97,6 +97,8 @@ locales: npx i18next-conv -l hi -s modules/datadict/locale/hi/LC_MESSAGES/datadict.po -t modules/datadict/locale/hi/LC_MESSAGES/datadict.json npx i18next-conv -l ja -s modules/datadict/locale/ja/LC_MESSAGES/datadict.po -t modules/datadict/locale/ja/LC_MESSAGES/datadict.json msgfmt -o modules/dataquery/locale/ja/LC_MESSAGES/dataquery.mo modules/dataquery/locale/ja/LC_MESSAGES/dataquery.po + msgfmt -o modules/dataquery/locale/hi/LC_MESSAGES/dataquery.mo modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po + npx i18next-conv -l hi -s modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po -t modules/dataquery/locale/hi/LC_MESSAGES/dataquery.json msgfmt -o modules/data_release/locale/ja/LC_MESSAGES/data_release.mo modules/data_release/locale/ja/LC_MESSAGES/data_release.po npx i18next-conv -l ja -s modules/data_release/locale/ja/LC_MESSAGES/data_release.po -t modules/data_release/locale/ja/LC_MESSAGES/data_release.json msgfmt -o modules/data_release/locale/hi/LC_MESSAGES/data_release.mo modules/data_release/locale/hi/LC_MESSAGES/data_release.po @@ -153,8 +155,9 @@ data_release: modules/data_release/locale/hi/LC_MESSAGES/data_release.mo modules instrument_manager: modules/instrument_manager/locale/ja/LC_MESSAGES/instrument_manager.mo target=instrument_manager npm run compile -dataquery: modules/dataquery/locale/ja/LC_MESSAGES/dataquery.mo - msgfmt -o modules/dataquery/locale/ja/LC_MESSAGES/dataquery.mo modules/dataquery/locale/ja/LC_MESSAGES/dataquery.po +dataquery: + msgfmt -o modules/dataquery/locale/hi/LC_MESSAGES/dataquery.mo modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po + npx i18next-conv -l hi -s modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po -t modules/dataquery/locale/hi/LC_MESSAGES/dataquery.json target=dataquery npm run compile login: modules/login/locale/ja/LC_MESSAGES/login.mo diff --git a/jsx/I18nSetup.d.ts b/jsx/I18nSetup.d.ts new file mode 100644 index 00000000000..f40a11c2c28 --- /dev/null +++ b/jsx/I18nSetup.d.ts @@ -0,0 +1,4 @@ +declare module "I18nSetup" { + import i18n from "i18next"; + export default i18n; +} diff --git a/modules/dataquery/jsx/definefields.tsx b/modules/dataquery/jsx/definefields.tsx index 0dbb11cb9d5..731c81f7bf6 100644 --- a/modules/dataquery/jsx/definefields.tsx +++ b/modules/dataquery/jsx/definefields.tsx @@ -6,7 +6,7 @@ import {CheckboxElement} from 'jsx/Form'; import {FullDictionary, FieldDictionary, DictionaryCategory} from './types'; import {CategoriesAPIReturn} from './hooks/usedatadictionary'; import {APIQueryField, VisitOption} from './types'; - +import {useTranslation} from 'react-i18next'; /** * Displays a single field to be selected for querying @@ -46,6 +46,7 @@ function QueryField(props: { ) => void, defaultVisits: string[], }) { + const {t} = useTranslation('dataquery'); const item=props.item; const className = props.selected ? 'list-group-item active' : @@ -93,14 +94,14 @@ function QueryField(props: { if (props.selected) { visits =
e.stopPropagation()}> -

Visits

+

{t('Visits', {ns: 'loris'})}

setCSVType('candidate')} - /> Candidates + /> {t('Candidates', {ns: 'dataquery'})} setCSVType('session')} - /> Sessions + /> {t('Sessions', {ns: 'dataquery'})} -
Candidate identifier type
+
{t('Candidate identifier type', {ns: 'dataquery'})}
setIdType('CandID')} - /> DCC ID + /> {t('DCC ID', {ns: 'dataquery'})} setIdType('PSCID')} - /> PSCID + /> {t('PSCID', {ns: 'dataquery'})}
- Does CSV contain a header line? + {t('Does CSV contain a header line?', {ns: 'dataquery'})}
setCSVHeader(true)} - /> Yes + /> {t('Yes', {ns: 'dataquery'})} setCSVHeader(false)} - /> No + /> {t('No', {ns: 'dataquery'})}
-
CSV File
+
{t('CSV File', {ns: 'dataquery'})}
void, removeQueryGroupItem: (group: QueryGroup, i: number) => QueryGroup, }) : React.ReactElement { + const {t} = useTranslation('dataquery'); let displayquery: React.ReactNode = null; const [addModal, setAddModal] = useState(false); const [csvModal, setCSVModal] = useState(false); @@ -118,7 +120,7 @@ function DefineFilters(props: { const mapModuleName = props.mapModuleName; const mapCategoryName = props.mapCategoryName; - const advancedLabel = showAdvanced ? 'Hide Advanced' : 'Show Advanced'; + const advancedLabel = showAdvanced ? t('Hide Advanced', {ns: 'dataquery'}) : t('Show Advanced', {ns: 'dataquery'}); let advancedButtons; const toggleAdvancedButton = (
@@ -138,16 +140,10 @@ function DefineFilters(props: { if (showAdvanced) { advancedButtons = (
-

The "nested groups" options are advanced options for queries - that do not have any specific condition at the - base of the query. - Use Add nested "or" condition groups if - you need to build a query of the form. - (a or b) and (c or d) [or (e and f)..]. -

+

{t('The "nested groups" options are advanced options for queries that do not have any specific condition at the base of the query. Use Add nested "or" condition groups if you need to build a query of the form. (a or b) and (c or d) [or (e and f)..]', {ns: 'dataquery'})}

{ e.preventDefault(); props.query.operator = 'and'; @@ -155,14 +151,10 @@ function DefineFilters(props: { }} />
-

- Use Add nested "and" condition groups if you - need to build a query of the form - (a and b) or (c and d) [or (e and f)..]. -

+

{t('Use Add nested "and" condition groups if you need to build a query of the form (a and b) or (c and d) [or (e and f)..]', {ns: 'dataquery'})}

{ e.preventDefault(); props.query.operator = 'or'; @@ -178,23 +170,18 @@ function DefineFilters(props: { displayquery =
-

Currently querying for ALL candidates.

-

You can add conditions by clicking one of the buttons below.

-

Click Add Condition to add one or more conditions - to your filters (ie. "Date Of Birth < 2015-02-15"). This is - most likely where you want to start your filters. -

-

You can also import a population from a CSV by clicking - the Import from CSV button.

-

The advanced options are for queries that do not have - a condition to add at the base of the query.

+

{t('Currently querying for ALL candidates.', {ns: 'dataquery'})}

+

{t('You can add conditions by clicking one of the buttons below.', {ns: 'dataquery'})}

+

{t('Click Add Condition to add one or more conditions to your filters (ie. "Date Of Birth < 2015-02-15"). This is most likely where you want to start your filters.', {ns: 'dataquery'})}

+

{t('You can also import a population from a CSV by clicking the Import from CSV button.', {ns: 'dataquery'})}

+

{t('The advanced options are for queries that do not have a condition to add at the base of the query.', {ns: 'dataquery'})}

{ e.preventDefault(); @@ -204,7 +191,7 @@ function DefineFilters(props: {
{ e.preventDefault(); @@ -231,29 +218,17 @@ function DefineFilters(props: { advancedButtons = (
-

Use New "and" subgroup if the rest of the - query you need to write is a subgroup consisting - of "and" conditions. ie your query is of the form: -

- (your condition above) or (c and d [and e and f..]) -
-

+

{t('Use New "and" subgroup if the rest of the query you need to write is a subgroup consisting of "and" conditions. ie your query is of the form: (your condition above) or (c and d [and e and f..])', {ns: 'dataquery'})}

{ e.preventDefault(); props.query.operator = 'or'; props.addNewQueryGroup(props.query); }} /> -

Use New "or" subgroup if the rest of the - query you need to write is a subgroup consisting - of "or" conditions. ie your query is of the form: -

- (your condition above) and (c or d [or e or f..]) -
-

+

{t('Use New "or" subgroup if the rest of the query you need to write is a subgroup consisting of "or" conditions. ie your query is of the form: (your condition above) and (c or d [or e or f..])', {ns: 'dataquery'})}

{ e.preventDefault(); props.query.operator = 'and'; @@ -265,7 +240,7 @@ function DefineFilters(props: { } // buttons for 1. Add "and" condition 2. Add "or" condition displayquery = (
-

Currently querying for any candidates with:

+

{t('Currently querying for any candidates with:', {ns: 'dataquery'})}

@@ -282,7 +257,7 @@ function DefineFilters(props: { />
{ const newquery = props.removeQueryGroupItem( props.query, @@ -299,7 +274,7 @@ function DefineFilters(props: {
{ e.preventDefault(); @@ -307,7 +282,7 @@ function DefineFilters(props: { setAddModal(true); }} /> { e.preventDefault(); @@ -325,7 +300,7 @@ function DefineFilters(props: { // Add buttons are delegated to the QueryTree rendering so they // can be placed at the right level displayquery =
-

Currently querying for any candidates with:

+

{t('Currently querying for any candidates with:', {ns: 'dataquery'})}

 
// So the header doesn't jump around - :
Query matches {queryMatches} candidates
; + :
{t('Query matches {{count}} candidates', {ns: 'dataquery', count: queryMatches})}
; return (
{modal} {csvModalHTML} @@ -386,13 +361,11 @@ function DefineFilters(props: { justifyContent: 'space-between', alignItems: 'baseline', }}> -

Current Query

+

{t('Current Query', {ns: 'dataquery'})}

{matchCount}
- Note that only candidates which you have permission to - access in LORIS are included in results. Number of - results may vary from other users running the same query. + {t('Note that only candidates which you have permission to access in LORIS are included in results. Number of results may vary from other users running the same query.', {ns: 'dataquery'})} {displayquery}
diff --git a/modules/dataquery/jsx/fielddisplay.tsx b/modules/dataquery/jsx/fielddisplay.tsx index e58cad1f60a..ece1189b05f 100644 --- a/modules/dataquery/jsx/fielddisplay.tsx +++ b/modules/dataquery/jsx/fielddisplay.tsx @@ -1,6 +1,6 @@ import {FullDictionary} from './types'; - import getDictionaryDescription from './getdictionarydescription'; +import {useTranslation} from 'react-i18next'; /** * A single field to display @@ -18,11 +18,11 @@ function FieldDisplay(props: { module: string, category: string, fieldname: string, - fulldictionary: FullDictionary, mapModuleName: (module: string) => string, mapCategoryName: (module: string, category: string) => string, }) { + const {t} = useTranslation('dataquery'); const description = getDictionaryDescription( props.module, props.category, @@ -35,8 +35,8 @@ function FieldDisplay(props: { {description}
- {props.mapCategoryName(props.module, props.category)} -  ({props.mapModuleName(props.module)}) + {t('Category', {ns: 'dataquery'})}: {props.mapCategoryName(props.module, props.category)} +  ({t('Module', {ns: 'dataquery'})}: {props.mapModuleName(props.module)})
); diff --git a/modules/dataquery/jsx/index.tsx b/modules/dataquery/jsx/index.tsx index 4b0d3a88b54..52e76cb7fb3 100644 --- a/modules/dataquery/jsx/index.tsx +++ b/modules/dataquery/jsx/index.tsx @@ -16,6 +16,10 @@ import {useSharedQueries, useLoadQueryFromURL} from './hooks/usesharedqueries'; import {useDataDictionary, useCategories} from './hooks/usedatadictionary'; import {ModuleDictionary, DictionaryCategory} from './types'; +// @ts-ignore +import i18n from 'I18nSetup'; +import {useTranslation, withTranslation} from 'react-i18next'; +const hiStrings = require('../locale/hi/LC_MESSAGES/dataquery.json'); type ActiveCategoryType = { module: string, @@ -244,6 +248,9 @@ function DataQueryApp(props: { declare const loris: any; window.addEventListener('load', () => { + i18n.addResourceBundle('hi', 'dataquery', hiStrings); + const TranslatedDataQueryApp = withTranslation(['dataquery','loris'])(DataQueryApp); + const element = document.getElementById('lorisworkspace'); if (!element) { throw new Error('Missing lorisworkspace'); @@ -251,7 +258,7 @@ window.addEventListener('load', () => { const root = createRoot(element); root.render( - , diff --git a/modules/dataquery/jsx/nextsteps.tsx b/modules/dataquery/jsx/nextsteps.tsx index 59a88cdfac2..6503a2f441b 100644 --- a/modules/dataquery/jsx/nextsteps.tsx +++ b/modules/dataquery/jsx/nextsteps.tsx @@ -1,4 +1,5 @@ import React, {useState} from 'react'; +import {useTranslation} from 'react-i18next'; import {APIQueryField} from './types'; import {ButtonElement} from 'jsx/Form'; import {QueryGroup} from './querydef'; @@ -19,17 +20,18 @@ function NextSteps(props: { page: string, changePage: (newpage: string) => void, }) { + const {t} = useTranslation('dataquery'); const [expanded, setExpanded] = useState(true); const steps: React.ReactElement[] = []; const canRun = (props.fields && props.fields.length > 0); const fieldLabel = (props.fields && props.fields.length > 0) - ? 'Modify Fields' - : 'Choose Fields'; + ? t('Modify Fields', {ns: 'dataquery'}) + : t('Choose Fields', {ns: 'dataquery'}); const filterLabel = (props.filters && props.filters.group.length > 0) - ? 'Modify Filters' - : 'Add Filters'; + ? t('Modify Filters', {ns: 'dataquery'}) + : t('Add Filters', {ns: 'dataquery'}); switch (props.page) { case 'Info': if (canRun) { @@ -48,7 +50,7 @@ function NextSteps(props: { onUserInput={() => props.changePage('DefineFilters')} />); steps.push( props.changePage('ViewData')} @@ -72,7 +74,7 @@ function NextSteps(props: { />); if (canRun) { steps.push( props.changePage('ViewData')} @@ -82,7 +84,7 @@ function NextSteps(props: { case 'DefineFilters': if (canRun) { steps.push( props.changePage('ViewData')} @@ -148,7 +150,7 @@ function NextSteps(props: { paddingRight: '14px', }}>
-

Next Steps

+

{t('Next Steps', {ns: 'dataquery'})}

{steps}
diff --git a/modules/dataquery/jsx/querytree.tsx b/modules/dataquery/jsx/querytree.tsx index 6b045f65275..6f6f0ac985b 100644 --- a/modules/dataquery/jsx/querytree.tsx +++ b/modules/dataquery/jsx/querytree.tsx @@ -4,6 +4,7 @@ import {CriteriaTerm} from './criteriaterm'; import {ButtonElement} from 'jsx/Form'; import {FullDictionary} from './types'; import {useEffect} from 'react'; // already present +import {useTranslation} from 'react-i18next'; // <-- ADD THIS /** * Alternate background colour for a QueryTree @@ -61,6 +62,7 @@ function QueryTree(props: { mapCategoryName: (module: string, category: string) => string, }) { const [deleteItemIndex, setDeleteItemIndex] = useState(null); + const {t} = useTranslation('dataquery'); // <-- ADD THIS useEffect(() => { // Reset strikethrough when group is empty or changed @@ -114,7 +116,7 @@ function QueryTree(props: { if (item instanceof QueryTerm) { const deleteIcon = props.removeQueryGroupItem ? (
- setDeleteItemIndex(i)} @@ -190,7 +192,7 @@ function QueryTree(props: { marginLeft: 10, }}>
- Group does not have any items. + {t('Group does not have any items.', {ns: 'dataquery'})}
; break; @@ -204,8 +206,7 @@ function QueryTree(props: { marginLeft: 10, }}>
- Group only has 1 item. A group with only 1 item is equivalent - to not having the group. + {t('Group only has 1 item. A group with only 1 item is equivalent to not having the group.', {ns: 'dataquery'})}
; break; @@ -248,8 +249,7 @@ function QueryTree(props: { deleteGroupHTML = (
Query not yet run; + return

{t('Query not yet run', {ns: 'dataquery'})}

; } return (
- + - {props.value} of {props.max} candidates + {t('{{value}} of {{max}} candidates', {ns: 'dataquery', value: props.value, max: props.max})}
); case 'headers': return (
- + - {props.value} of {props.max} columns + {t('{{value}} of {{max}} columns', {ns: 'dataquery', value: props.value, max: props.max})}
); case 'dataorganization': return (
- + - {props.value} of {props.max} columns + {t('{{value}} of {{max}} columns', {ns: 'dataquery', value: props.value, max: props.max})}
); } - return

Invalid progress type: {props.type}

; + return

{t('Invalid progress type: {{type}}', {ns: 'dataquery', type: props.type})}

; } type RunQueryType = { @@ -798,6 +800,7 @@ function organizedFormatter( displayEmptyVisits: boolean, enumDisplay: EnumDisplayTypes, ) { + const {t} = useTranslation('dataquery'); let callback; switch (visitOrganization) { case 'raw': @@ -841,20 +844,18 @@ function organizedFormatter( } if (fielddict.scope == 'candidate') { if (cell === '') { - return (No data); + return {t('(No data)', {ns: 'dataquery'})}; } switch (fielddict.cardinality) { case 'many': - return (Not implemented); + return {t('(Not implemented)', {ns: 'dataquery'})}; case 'single': case 'unique': case 'optional': return ; default: return ( - (Internal Error. Unhandled cardinality: - {fielddict.cardinality}) - + {t('(Internal Error. Unhandled cardinality: {{cardinality}})', {ns: 'dataquery', cardinality: fielddict.cardinality})} ); } } @@ -932,7 +933,7 @@ function organizedFormatter( return null; } catch (e) { console.error(e); - return (Internal error); + return {t('(Internal error)', {ns: 'dataquery'})}; } }; let theval = visitval(visit, cell); @@ -940,7 +941,7 @@ function organizedFormatter( return
; } if (theval === null) { - theval = (No data); + theval = {t('(No data)', {ns: 'dataquery'})}; } return (
(Internal error); + return {t('(Internal error)', {ns: 'dataquery'})}; } return null; }; @@ -1006,7 +1007,7 @@ function organizedFormatter( return
; } if (theval === null) { - theval = (No data); + theval = {t('(No data)', {ns: 'dataquery'})}; } return (
{cells.map((cell: LongitudinalExpansion) => { if (cell.value === null) { - return (No data); + return {t('(No data)', {ns: 'dataquery'})}; } return ( @@ -1102,7 +1103,7 @@ function organizedFormatter( fieldNo: number ): ReactNode => { if (cell === null) { - return No data for visit; + return {t('No data for visit', {ns: 'dataquery'})}; } if (fieldNo == 0) { // automatically added Visit column diff --git a/modules/dataquery/jsx/welcome.adminquerymodal.tsx b/modules/dataquery/jsx/welcome.adminquerymodal.tsx index ea7a839c5b9..0c67b9583e2 100644 --- a/modules/dataquery/jsx/welcome.adminquerymodal.tsx +++ b/modules/dataquery/jsx/welcome.adminquerymodal.tsx @@ -2,6 +2,7 @@ import Modal from 'jsx/Modal'; import swal from 'sweetalert2'; import {useState} from 'react'; import {CheckboxElement, TextboxElement, FieldsetElement} from 'jsx/Form'; +import {useTranslation} from 'react-i18next'; /** * Render a modal window for naming a query @@ -25,6 +26,7 @@ function AdminQueryModal(props: { ) => void, }) { + const {t} = useTranslation('dataquery'); const [queryName, setQueryName] = useState(props.defaultName || ''); const [topQuery, setTopQuery] = useState(true); const [dashboardQuery, setDashboardQuery] = useState(true); @@ -40,7 +42,7 @@ function AdminQueryModal(props: { if (queryName.trim() == '') { swal.fire({ type: 'error', - text: 'Must provide a query name to pin query as.', + text: t('Must provide a query name to pin query as.', {ns: 'dataquery'}), }); reject(); return; @@ -48,7 +50,7 @@ function AdminQueryModal(props: { if (!topQuery && !dashboardQuery && !loginQuery) { swal.fire({ type: 'error', - text: 'Must pin as study query, to dashboard, or to the login page.', + text: t('Must pin as study query, to dashboard, or to the login page.', {ns: 'dataquery'}), }); reject(); return; @@ -63,16 +65,16 @@ function AdminQueryModal(props: { } return sbmt; }; - return + legend={t('Study Query', {ns: 'dataquery'})}> setQueryName(value) } @@ -82,11 +84,11 @@ function AdminQueryModal(props: { onUserInput={ (name: string, value: boolean) => setTopQuery(value) } - label='Pin Study Query' + label={t('Pin Study Query', {ns: 'dataquery'})} /> setDashboardQuery(value) @@ -94,7 +96,7 @@ function AdminQueryModal(props: { /> setLoginQuery(value) diff --git a/modules/dataquery/jsx/welcome.namequerymodal.tsx b/modules/dataquery/jsx/welcome.namequerymodal.tsx index 189e5ac880e..a726888585c 100644 --- a/modules/dataquery/jsx/welcome.namequerymodal.tsx +++ b/modules/dataquery/jsx/welcome.namequerymodal.tsx @@ -2,6 +2,7 @@ import Modal from 'jsx/Modal'; import swal from 'sweetalert2'; import {useState} from 'react'; import {TextboxElement, FieldsetElement} from 'jsx/Form'; +import {useTranslation} from 'react-i18next'; /** * Render a modal window for naming a query @@ -19,6 +20,7 @@ function NameQueryModal(props: { closeModal: () => void, onSubmit: (name: string) => void, }) { + const {t} = useTranslation('dataquery'); const [queryName, setQueryName] = useState(props.defaultName || ''); /** * Convert the onSubmit callback function to a promise expected by Modal @@ -30,7 +32,7 @@ function NameQueryModal(props: { if (queryName == '') { swal.fire({ type: 'error', - text: 'Must provide a query name.', + text: t('Must provide a query name.', {ns: 'dataquery'}), }); reject(); return; @@ -42,16 +44,16 @@ function NameQueryModal(props: { } return sbmt; }; - return + legend={t('Query name', {ns: 'dataquery'})}> setQueryName(value) } diff --git a/modules/dataquery/jsx/welcome.tsx b/modules/dataquery/jsx/welcome.tsx index 86103fcee4c..1b737751cbc 100644 --- a/modules/dataquery/jsx/welcome.tsx +++ b/modules/dataquery/jsx/welcome.tsx @@ -12,6 +12,14 @@ import {ButtonElement, CheckboxElement, TextboxElement} from 'jsx/Form'; import {APIQueryField} from './types'; import {FullDictionary} from './types'; import {FlattenedField, FlattenedQuery, VisitOption} from './types'; +import {useTranslation} from 'react-i18next'; +// @ts-ignore +import i18n from 'I18nSetup'; +import {withTranslation} from 'react-i18next'; +import {createRoot} from 'react-dom/client'; + +const hiStrings = require('../locale/hi/LC_MESSAGES/dataquery.json'); + /** * Return the welcome tab for the DQT * @@ -52,6 +60,7 @@ function Welcome(props: { mapCategoryName: (module: string, category: string) => string, fulldictionary:FullDictionary, }) { + const {t} = useTranslation('dataquery'); const panels: { title: string, content: React.ReactElement, @@ -61,21 +70,18 @@ function Welcome(props: { }[] = []; if (props.topQueries.length > 0) { panels.push({ - title: 'Study Queries', + title: t('Study Queries', {ns: 'dataquery'}), content: (
@@ -87,7 +93,7 @@ function Welcome(props: { }); } panels.push({ - title: 'Instructions', + title: t('Instructions', {ns: 'dataquery'}), content: 0} onContinue={props.onContinue} @@ -97,22 +103,18 @@ function Welcome(props: { id: 'p2', }); panels.push({ - title: 'Recent Queries', + title: t('Recent Queries', {ns: 'dataquery'}), content: (
0) { panels.push({ - title: 'Shared Queries', + title: t('Shared Queries', {ns: 'dataquery'}), content: (
@@ -160,7 +159,7 @@ function Welcome(props: { textAlign: 'center', padding: '30px 0 0 0', }}> - Welcome to the Data Query Tool + {t('Welcome to the Data Query Tool', {ns: 'dataquery'})}
@@ -207,6 +206,7 @@ function QueryList(props: { mapCategoryName: (module: string, category: string) => string, fulldictionary:FullDictionary, }) { + const {t} = useTranslation('dataquery'); const [nameModalID, setNameModalID] = useState(null); const [adminModalID, setAdminModalID] = useState(null); const [queryName, setQueryName] = useState(null); @@ -491,14 +491,14 @@ function QueryList(props: { }); } const starFilter = props.starQuery ? - setOnlyStarred(value) }/> : ; const shareFilter = props.shareQuery ? - setQueryFilter(value) @@ -538,14 +538,14 @@ function QueryList(props: { }}> {starFilter} {shareFilter} - setOnlyNamed(value) }/> {duplicateFilter} - (No filters for query); + return {t('(No filters for query)', {ns: 'dataquery'})}; } return ((1); const rowsPerPage = 5; @@ -658,6 +660,7 @@ function Pager(props: { onChangePage={setPageNum} RowsPerPage={rowsPerPage} Active={pageNum} + //label={t('Page', {ns: 'dataquery'})} /> {displayedRange}
; } @@ -715,6 +719,7 @@ function SingleQueryDisplay(props: { mapCategoryName: (module: string, category: string) => string, fulldictionary:FullDictionary, }) { + const {t} = useTranslation('dataquery'); const [showFullQuery, setShowFullQuery] = useState(props.showFullQueryDefault); // Reset the collapsed state if the checkbox gets toggled @@ -732,7 +737,7 @@ function SingleQueryDisplay(props: { onClick={ () => props.unstarQuery(query.QueryID) } - title="Unstar" + title={t('Unstar', {ns: 'dataquery'})} className="fa-stack"> props.starQuery(query.QueryID) } @@ -758,7 +763,7 @@ function SingleQueryDisplay(props: { if (query.Public) { sharedIcon = props.unshareQuery(query.QueryID) @@ -767,7 +772,7 @@ function SingleQueryDisplay(props: { } else { sharedIcon = props.shareQuery(query.QueryID) @@ -800,15 +805,15 @@ function SingleQueryDisplay(props: { ); swal.fire({ type: 'success', - title: 'Query Loaded', - text: 'Successfully loaded query.', + title: t('Query Loaded', {ns: 'dataquery'}), + text: t('Successfully loaded query.', {ns: 'dataquery'}), }); }; const loadIcon = ; const pinIcon = props.queryAdmin - ? { @@ -825,15 +830,15 @@ function SingleQueryDisplay(props: { let desc = query.Name ? {query.Name} -  (Run at {query.RunTime}) +  {t('(Run at {{runTime}})', {ns: 'dataquery', runTime: query.RunTime})} - : You ran this query at {query.RunTime}; + : {t('You ran this query at {{runTime}}', {ns: 'dataquery', runTime: query.RunTime})}; if (!props.includeRuns) { desc = query.Name ? {query.Name} - : You ran this query; + : {t('You ran this query', {ns: 'dataquery'})}; } const nameIcon = {query.Name} -  (Shared by {query.SharedBy.join(', ')}) +  {t('(Shared by {{sharedBy}})', {ns: 'dataquery', sharedBy: query.SharedBy.join(', ')})} - : Query shared by {query.SharedBy.join(', ')}; + : {t('Query shared by {{sharedBy}}', {ns: 'dataquery', sharedBy: query.SharedBy.join(', ')})}; msg =
{desc}  {loadIcon}{pinIcon}
; } else if (query.Name || query.AdminName) { const name = props.useAdminName ? query.AdminName : query.Name; const unpinIcon = props.queryAdmin - ? { @@ -876,7 +881,7 @@ function SingleQueryDisplay(props: { const queryDisplay = !showFullQuery ?
:
-

Fields

+

{t('Fields', {ns: 'dataquery'})}

{query.fields.map( (fieldobj, fidx) => {query.criteria ?
-

Filters

+

{t('Filters', {ns: 'dataquery'})}

void, hasStudyQueries: boolean, }): React.ReactElement { + const {t} = useTranslation('dataquery'); const studyQueriesParagraph = props.hasStudyQueries ? ( -

Above, there is also a Study Queries panel. This - are a special type of shared queries that have been pinned - by a study administer to always display at the top of this - page.

+

+ {t('Above, there is also a Study Queries panel. This are a special type of shared queries that have been pinned by a study administer to always display at the top of this page.', {ns: 'dataquery'})} +

) : ''; return (
-

The data query tool allows you to query data - within LORIS. There are three steps to defining - a query: -

+

{t('The data query tool allows you to query data within LORIS. There are three steps to defining a query:', {ns: 'dataquery'})}

    -
  1. First, you must select the fields that you're - interested in on the Define Fields - page.
  2. -
  3. Next, you can optionally define filters on the - Define Filters page to restrict - the population that is returned.
  4. -
  5. Finally, you view your query results on - the View Data page
  6. +
  7. {t("First, you must select the fields that you're interested in on the Define Fields page.", {ns: 'dataquery'})}
  8. +
  9. {t("Next, you can optionally define filters on the Define Filters page to restrict the population that is returned.", {ns: 'dataquery'})}
  10. +
  11. {t("Finally, you view your query results on the View Data page", {ns: 'dataquery'})}
-

The Next Steps on the bottom right of your - screen always the context-sensitive next steps that you - can do to build your query.

-

Your recently run queries will be displayed in the - Recent Queries panel below. Instead of building - a new query, you can reload a query that you've recently run - by clicking on the icon next to the query.

-

Queries can be shared with others by clicking the - icon. This will cause the query to be shared with all users who - have access to the fields used by the query. It will display - in a Shared Queries panel below the - Recent Queries.

-

You may also give a query a name at any time by clicking the - icon. This makes it easier to find queries you care - about by giving them an easier to remember name that can be used - for filtering. When you share a query, the name will be shared - along with it.

+

{t('The Next Steps on the bottom right of your screen always the context-sensitive next steps that you can do to build your query.', {ns: 'dataquery'})}

+

{t("Your recently run queries will be displayed in the Recent Queries panel below. Instead of building a new query, you can reload a query that you've recently run by clicking on the icon next to the query.", {ns: 'dataquery'})}

+

{t("Queries can be shared with others by clicking the icon. This will cause the query to be shared with all users who have access to the fields used by the query. It will display in a Shared Queries panel below the Recent Queries.", {ns: 'dataquery'})}

+

{t("You may also give a query a name at any time by clicking the icon. This makes it easier to find queries you care about by giving them an easier to remember name that can be used for filtering. When you share a query, the name will be shared along with it.", {ns: 'dataquery'})}

{studyQueriesParagraph}
+ label={t('Continue to Define Fields', {ns: 'dataquery'})} />
); } + +window.addEventListener('load', () => { + i18n.addResourceBundle('hi', 'dataquery', hiStrings); + const TranslatedWelcome = withTranslation(['dataquery','loris'])(Welcome); + + const container = document.getElementById('lorisworkspace'); + if (container) { + const root = createRoot(container); + root.render(); + } else { + console.error("Element with id 'lorisworkspace' not found in DOM"); + } +}); + + export default Welcome; diff --git a/modules/dataquery/locale/dataquery.pot b/modules/dataquery/locale/dataquery.pot index 84b3f199537..3e2bd227def 100644 --- a/modules/dataquery/locale/dataquery.pot +++ b/modules/dataquery/locale/dataquery.pot @@ -24,6 +24,303 @@ msgstr "" msgid "Starred Queries" msgstr "" +msgid "Query not yet run" +msgstr "" + +msgid "Loading data:" +msgstr "" + +msgid "{{value}} of {{max}} candidates" +msgstr "" + +msgid "Organizing headers:" +msgstr "" + +msgid "{{value}} of {{max}} columns" +msgstr "" + +msgid "Organizing data:" +msgstr "" + +msgid "Invalid progress type: {{type}}" +msgstr "" + +msgid "No data for visit" +msgstr "" + +msgid "Field Name" +msgstr "" + +msgid "Field Description" +msgstr "" + +msgid "Field Name: Field Description" +msgstr "" + +msgid "Rows (Cross-sectional)" +msgstr "" + +msgid "Columns (Longitudinal)" +msgstr "" + +msgid "Inline values (no download)" +msgstr "" + +msgid "Raw JSON (debugging only)" +msgstr "" + +msgid "Header Display Format" +msgstr "" + +msgid "Display visits as" +msgstr "" + +msgid "Labels" +msgstr "" + +msgid "Values" +msgstr "" + +msgid "Display options as" +msgstr "" + +msgid "Display empty visits?" +msgstr "" + +msgid "Row Number" +msgstr "" + +msgid "Modify Fields" +msgstr "" + +msgid "Choose Fields" +msgstr "" + +msgid "Modify Filters" +msgstr "" + +msgid "Add Filters" +msgstr "" + +msgid "Run Query" +msgstr "" + +msgid "Next Steps" +msgstr "" + +msgid "Delete item" +msgstr "" + +msgid "Delete Group" +msgstr "" + +msgid "Group does not have any items." +msgstr "" + +msgid "Group only has 1 item. A group with only 1 item is equivalent to not having the group." +msgstr "" + +msgid "Add \"{{operator}}\" condition to group" +msgstr "" + +msgid "New \"{{antiOperator}}\" subgroup" +msgstr "" + +msgid "Hide Advanced" +msgstr "" + +msgid "Show Advanced" +msgstr "" + +msgid "The \"nested groups\" options are advanced options for queries that do not have any specific condition at the base of the query. Use Add nested \"or\" condition groups if you need to build a query of the form. (a or b) and (c or d) [or (e and f)..]" +msgstr "" + +msgid "Add nested \"or\" condition groups" +msgstr "" + +msgid "Use Add nested \"and\" condition groups if you need to build a query of the form (a and b) or (c and d) [or (e and f)..]" +msgstr "" + +msgid "Add nested \"and\" condition groups" +msgstr "" + +msgid "Currently querying for ALL candidates." +msgstr "" + +msgid "You can add conditions by clicking one of the buttons below." +msgstr "" + +msgid "Click Add Condition to add one or more conditions to your filters (ie. \"Date Of Birth < 2015-02-15\"). This is most likely where you want to start your filters." +msgstr "" + +msgid "You can also import a population from a CSV by clicking the Import from CSV button." +msgstr "" + +msgid "The advanced options are for queries that do not have a condition to add at the base of the query." +msgstr "" + +msgid "Add Condition" +msgstr "" + +msgid "Import from CSV" +msgstr "" + +msgid "Use New \"and\" subgroup if the rest of the query you need to write is a subgroup consisting of \"and\" conditions. ie your query is of the form: (your condition above) or (c and d [and e and f..])" +msgstr "" + +msgid "New \"and\" subgroup" +msgstr "" + +msgid "Use New \"or\" subgroup if the rest of the query you need to write is a subgroup consisting of \"or\" conditions. ie your query is of the form: (your condition above) and (c or d [or e or f..])" +msgstr "" + +msgid "New \"or\" subgroup" +msgstr "" + +msgid "Currently querying for any candidates with:" +msgstr "" + +msgid "Add \"and\" condition" +msgstr "" + +msgid "Add \"or\" condition" +msgstr "" + +msgid "Delete Item" +msgstr "" + +msgid "Query matches {{count}} candidates" +msgstr "" + +msgid "Current Query" +msgstr "" + +msgid "Note that only candidates which you have permission to access in LORIS are included in results. Number of results may vary from other users running the same query." +msgstr "" + +msgid "Candidate matches" +msgstr "" + +msgid "Note: matches count only includes candidates that you have access to. Results may vary from other users due to permissions." +msgstr "" + +msgid "Select Visits" +msgstr "" + +msgid "Criteria" +msgstr "" + +msgid "Select an operator" +msgstr "" + +msgid "for at least one of the following visits" +msgstr "" + +msgid "This field may exist multiple times for a single {{scope}}. Adding a criteria based on it means that it must match for at least one of the data points." +msgstr "" + +msgid "Invalid field" +msgstr "" + +msgid "You must select a field for the criteria." +msgstr "" + +msgid "Invalid operator" +msgstr "" + +msgid "You must select an operator for the criteria." +msgstr "" + +msgid "Invalid value" +msgstr "" + +msgid "You must enter a value to compare the field against." +msgstr "" + +msgid "Invalid visits" +msgstr "" + +msgid "No visits selected for criteria." +msgstr "" + +msgid "Add criteria" +msgstr "" + +msgid "Field" +msgstr "" + +msgid "Select a field" +msgstr "" + +msgid "in" +msgstr "" + +msgid "starts with" +msgstr "" + +msgid "contains" +msgstr "" + +msgid "ends with" +msgstr "" + +msgid "has data" +msgstr "" + +msgid "has no data" +msgstr "" + +msgid "exists" +msgstr "" + +msgid "does not exist" +msgstr "" + +msgid "number of" +msgstr "" + +msgid "true" +msgstr "" + +msgid "false" +msgstr "" + +msgid "Select a value" +msgstr "" + +msgid "Import Population From CSV" +msgstr "" + +msgid "CSV containing list of" +msgstr "" + +msgid "Candidate identifier type" +msgstr "" + +msgid "Does CSV contain a header line?" +msgstr "" + +msgid "CSV File" +msgstr "" + +msgid "Invalid CSV" +msgstr "" + +msgid "Could not parse CSV file" +msgstr "" + +msgid "Expected {{expectedLength}} columns in CSV. Got {{gotLength}} on line {{line}}." +msgstr "" + +msgid "Invalid DCC ID" +msgstr "" + +msgid "Invalid DCC ID ({{id}}) on line {{line}}." +msgstr "" + +msgid "Welcome to the Data Query Tool" +msgstr "" + msgid "Study Queries" msgstr "" @@ -32,3 +329,78 @@ msgstr "" msgid "Note: matches count only includes candidates that you have access to. Results may vary from other users due to permissions." msgstr "" + +msgid "Instructions" +msgstr "" + +msgid "Recent Queries" +msgstr "" + +msgid "Shared Queries" +msgstr "" + +msgid "Starred Only" +msgstr "" + +msgid "Shared Only" +msgstr "" + +msgid "No run times (eliminate duplicates)" +msgstr "" + +msgid "Filter" +msgstr "" + +msgid "Named Only" +msgstr "" + +msgid "Collapse queries" +msgstr "" + +msgid "(No filters for query)" +msgstr "" + +msgid "Page" +msgstr "" + +msgid "Unstar" +msgstr "" + +msgid "Star" +msgstr "" + +msgid "Unshare" +msgstr "" + +msgid "Share" +msgstr "" + +msgid "Query Loaded" +msgstr "" + +msgid "Successfully loaded query." +msgstr "" + +msgid "(Run at {{runTime}})" +msgstr "" + +msgid "You ran this query at {{runTime}}" +msgstr "" + +msgid "You ran this query" +msgstr "" + +msgid "(Shared by {{sharedBy}})" +msgstr "" + +msgid "Query shared by {{sharedBy}}" +msgstr "" + +msgid "Name query" +msgstr "" + +msgid "Fields" +msgstr "" + +msgid "Filters" +msgstr "" diff --git a/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.json b/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.json new file mode 100644 index 00000000000..118eda2c676 --- /dev/null +++ b/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.json @@ -0,0 +1,128 @@ +{ + "Data Query Tool (Beta)": "डेटा क्वेरी टूल (बीटा)", + "Query not yet run": "क्वेरी अभी तक नहीं चलाई गई", + "Loading data:": "डेटा लोड हो रहा है:", + "{{value}} of {{max}} candidates": "{{max}} में से {{value}} उम्मीदवार", + "Organizing headers:": "हेडर व्यवस्थित किए जा रहे हैं:", + "{{value}} of {{max}} columns": "{{max}} में से {{value}} कॉलम", + "Organizing data:": "डेटा व्यवस्थित किया जा रहा है:", + "Invalid progress type: {{type}}": "अमान्य प्रगति प्रकार: {{type}}", + "No data for visit": "इस विज़िट के लिए कोई डेटा नहीं", + "Field Name": "फ़ील्ड नाम", + "Field Description": "फ़ील्ड विवरण", + "Field Name: Field Description": "फ़ील्ड नाम: फ़ील्ड विवरण", + "Rows (Cross-sectional)": "पंक्तियाँ (क्रॉस-सेक्शनल)", + "Columns (Longitudinal)": "कॉलम (लॉन्गिट्यूडिनल)", + "Inline values (no download)": "इनलाइन मान (कोई डाउनलोड नहीं)", + "Raw JSON (debugging only)": "कच्चा JSON (सिर्फ डीबगिंग के लिए)", + "Header Display Format": "हेडर प्रदर्शन प्रारूप", + "Display visits as": "विज़िट प्रदर्शित करें इस प्रकार", + "Labels": "लेबल्स", + "Values": "मान", + "Display options as": "विकल्प प्रदर्शित करें इस प्रकार", + "Display empty visits?": "खाली विज़िट दिखाएँ?", + "Row Number": "पंक्ति संख्या", + "Modify Fields": "फ़ील्ड संशोधित करें", + "Choose Fields": "फ़ील्ड चुनें", + "Modify Filters": "फ़िल्टर संशोधित करें", + "Add Filters": "फ़िल्टर जोड़ें", + "Run Query": "क्वेरी चलाएँ", + "Next Steps": "अगले चरण", + "Delete item": "आइटम हटाएँ", + "Delete Group": "समूह हटाएँ", + "Group does not have any items.": "समूह में कोई आइटम नहीं है।", + "Group only has 1 item. A group with only 1 item is equivalent to not having the group.": "समूह में केवल 1 आइटम है। केवल 1 आइटम वाला समूह होना, समूह न होने के बराबर है।", + "Add \"{{operator}}\" condition to group": "समूह में \"{{operator}}\" शर्त जोड़ें", + "New \"{{antiOperator}}\" subgroup": "नया \"{{antiOperator}}\" उपसमूह", + "Hide Advanced": "उन्नत विकल्प छिपाएँ", + "Show Advanced": "उन्नत विकल्प दिखाएँ", + "The \"nested groups\" options are advanced options for queries that do not have any specific condition at the base of the query. Use Add nested \"or\" condition groups if you need to build a query of the form. (a or b) and (c or d) [or (e and f)..]": "\"नेस्टेड समूह\" विकल्प उन क्वेरीज़ के लिए उन्नत विकल्प हैं जिनके आधार में कोई विशिष्ट शर्त नहीं होती। Add nested \"or\" condition groups का उपयोग करें यदि आपको (a या b) और (c या d) [या (e और f)..] जैसी क्वेरी बनानी हो।", + "Add nested \"or\" condition groups": "नेस्टेड \"or\" शर्त समूह जोड़ें", + "Use Add nested \"and\" condition groups if you need to build a query of the form (a and b) or (c and d) [or (e and f)..]": "यदि आपको (a और b) या (c और d) [या (e और f)..] जैसी क्वेरी बनानी है तो Add nested \"and\" condition groups का उपयोग करें।", + "Add nested \"and\" condition groups": "नेस्टेड \"and\" शर्त समूह जोड़ें", + "Currently querying for ALL candidates.": "वर्तमान में सभी उम्मीदवारों के लिए क्वेरी की जा रही है।", + "You can add conditions by clicking one of the buttons below.": "आप नीचे दिए गए बटनों में से किसी पर क्लिक करके शर्तें जोड़ सकते हैं।", + "Click Add Condition to add one or more conditions to your filters (ie. \"Date Of Birth < 2015-02-15\"). This is most likely where you want to start your filters.": "फ़िल्टर में एक या अधिक शर्तें जोड़ने के लिए Add Condition पर क्लिक करें (जैसे \"जन्म तिथि < 2015-02-15\")। संभवतः यही वह जगह है जहाँ से आप अपने फ़िल्टर शुरू करना चाहेंगे।", + "You can also import a population from a CSV by clicking the Import from CSV button.": "आप Import from CSV बटन पर क्लिक करके CSV से जनसंख्या भी आयात कर सकते हैं।", + "The advanced options are for queries that do not have a condition to add at the base of the query.": "उन्नत विकल्प उन क्वेरीज़ के लिए हैं जिनके आधार में कोई शर्त जोड़ने के लिए नहीं है।", + "Add Condition": "शर्त जोड़ें", + "Import from CSV": "CSV से आयात करें", + "Use New \"and\" subgroup if the rest of the query you need to write is a subgroup consisting of \"and\" conditions. ie your query is of the form: (your condition above) or (c and d [and e and f..])": "नया \"and\" उपसमूह उपयोग करें यदि बाकी क्वेरी \"and\" शर्तों वाले उपसमूह से बनी हो। जैसे: (ऊपर की शर्त) या (c और d [और e और f..])।", + "New \"and\" subgroup": "नया \"and\" उपसमूह", + "Use New \"or\" subgroup if the rest of the query you need to write is a subgroup consisting of \"or\" conditions. ie your query is of the form: (your condition above) and (c or d [or e or f..])": "नया \"or\" उपसमूह उपयोग करें यदि बाकी क्वेरी \"or\" शर्तों वाले उपसमूह से बनी हो। जैसे: (ऊपर की शर्त) और (c या d [या e या f..])।", + "New \"or\" subgroup": "नया \"or\" उपसमूह", + "Currently querying for any candidates with:": "वर्तमान में ऐसे किसी भी उम्मीदवार के लिए क्वेरी की जा रही है जिनमें हो:", + "Add \"and\" condition": "\"and\" शर्त जोड़ें", + "Add \"or\" condition": "\"or\" शर्त जोड़ें", + "Delete Item": "आइटम हटाएँ", + "Query matches {{count}} candidates": "क्वेरी {{count}} उम्मीदवारों से मेल खाती है", + "Current Query": "वर्तमान क्वेरी", + "Note that only candidates which you have permission to access in LORIS are included in results. Number of results may vary from other users running the same query.": "ध्यान दें कि केवल वे उम्मीदवार शामिल हैं जिन्हें LORIS में एक्सेस करने की आपको अनुमति है। परिणामों की संख्या अन्य उपयोगकर्ताओं से भिन्न हो सकती है।", + "Candidate matches": "उम्मीदवार मेल खाते हैं", + "Note: matches count only includes candidates that you have access to. Results may vary from other users due to permissions.": "नोट: मेल गिनती केवल उन्हीं उम्मीदवारों को शामिल करती है जिन तक आपकी पहुँच है। अनुमतियों के कारण परिणाम अन्य उपयोगकर्ताओं से भिन्न हो सकते हैं।", + "Select Visits": "विज़िट चुनें", + "Criteria": "मानदंड", + "Select an operator": "ऑपरेटर चुनें", + "for at least one of the following visits": "निम्नलिखित में से कम से कम एक विज़िट के लिए", + "This field may exist multiple times for a single {{scope}}. Adding a criteria based on it means that it must match for at least one of the data points.": "यह फ़ील्ड एक {{scope}} के लिए कई बार हो सकती है। इस पर आधारित मानदंड जोड़ने का मतलब है कि यह कम से कम एक डेटा बिंदु के लिए मेल खाना चाहिए।", + "Invalid field": "अमान्य फ़ील्ड", + "You must select a field for the criteria.": "आपको मानदंड के लिए एक फ़ील्ड चुननी होगी।", + "Invalid operator": "अमान्य ऑपरेटर", + "You must select an operator for the criteria.": "आपको मानदंड के लिए एक ऑपरेटर चुनना होगा।", + "Invalid value": "अमान्य मान", + "You must enter a value to compare the field against.": "फ़ील्ड की तुलना करने के लिए आपको एक मान दर्ज करना होगा।", + "Invalid visits": "अमान्य विज़िट", + "No visits selected for criteria.": "मानदंड के लिए कोई विज़िट चयनित नहीं है।", + "Add criteria": "मानदंड जोड़ें", + "Field": "फ़ील्ड", + "Select a field": "फ़ील्ड चुनें", + "in": "में", + "starts with": "से शुरू होता है", + "contains": "शामिल है", + "ends with": "से समाप्त होता है", + "has data": "डेटा है", + "has no data": "कोई डेटा नहीं है", + "exists": "मौजूद है", + "does not exist": "मौजूद नहीं है", + "number of": "संख्या", + "true": "सही", + "false": "ग़लत", + "Select a value": "मान चुनें", + "Import Population From CSV": "CSV से जनसंख्या आयात करें", + "CSV containing list of": "CSV जिसमें सूची है", + "Candidate identifier type": "उम्मीदवार पहचानकर्ता प्रकार", + "Does CSV contain a header line?": "क्या CSV में हेडर पंक्ति है?", + "CSV File": "CSV फ़ाइल", + "Invalid CSV": "अमान्य CSV", + "Could not parse CSV file": "CSV फ़ाइल पार्स नहीं की जा सकी", + "Expected {{expectedLength}} columns in CSV. Got {{gotLength}} on line {{line}}.": "CSV में {{expectedLength}} कॉलम अपेक्षित थे। पंक्ति {{line}} पर {{gotLength}} मिले।", + "Invalid DCC ID": "अमान्य DCC ID", + "Invalid DCC ID ({{id}}) on line {{line}}.": "अमान्य DCC ID ({{id}}) पंक्ति {{line}} पर।", + "Welcome to the Data Query Tool": "डेटा क्वेरी टूल में आपका स्वागत है", + "Study Queries": "अध्ययन क्वेरीज़", + "Instructions": "निर्देश", + "Recent Queries": "हाल की क्वेरीज़", + "Shared Queries": "साझा की गई क्वेरीज़", + "Starred Only": "केवल स्टार की गई", + "Shared Only": "केवल साझा की गई", + "No run times (eliminate duplicates)": "कोई रन समय नहीं (डुप्लीकेट हटाएँ)", + "Filter": "फ़िल्टर", + "Named Only": "केवल नाम वाली", + "Collapse queries": "क्वेरीज़ समेटें", + "(No filters for query)": "(क्वेरी के लिए कोई फ़िल्टर नहीं)", + "Page": "पृष्ठ", + "Unstar": "स्टार हटाएँ", + "Star": "स्टार करें", + "Unshare": "साझा हटाएँ", + "Share": "साझा करें", + "Query Loaded": "क्वेरी लोड हो गई", + "Successfully loaded query.": "क्वेरी सफलतापूर्वक लोड हो गई।", + "(Run at {{runTime}})": "(चलाया गया {{runTime}} पर)", + "You ran this query at {{runTime}}": "आपने यह क्वेरी {{runTime}} पर चलाई", + "You ran this query": "आपने यह क्वेरी चलाई", + "(Shared by {{sharedBy}})": "(साझा किया गया {{sharedBy}} द्वारा)", + "Query shared by {{sharedBy}}": "क्वेरी {{sharedBy}} द्वारा साझा की गई", + "Name query": "क्वेरी का नाम दें", + "Fields": "फ़ील्ड्स", + "Filters": "फ़िल्टर्स" +} \ No newline at end of file diff --git a/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po b/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po new file mode 100644 index 00000000000..337d85e7c63 --- /dev/null +++ b/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po @@ -0,0 +1,397 @@ +# Default LORIS strings to be translated (English). +# Copy this to a language specific file and add translations to the +# new file. +# Copyright (C) 2025 +# This file is distributed under the same license as the LORIS package. +# Dave MacFarlane , 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: LORIS 27\n" +"Report-Msgid-Bugs-To: https://github.com/aces/Loris/issues\n" +"POT-Creation-Date: 2025-04-08 14:37-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: hi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Data Query Tool (Beta)" +msgstr "डेटा क्वेरी टूल (बीटा)" + +msgid "Query not yet run" +msgstr "क्वेरी अभी तक नहीं चलाई गई" + +msgid "Loading data:" +msgstr "डेटा लोड हो रहा है:" + +msgid "{{value}} of {{max}} candidates" +msgstr "{{max}} में से {{value}} उम्मीदवार" + +msgid "Organizing headers:" +msgstr "हेडर व्यवस्थित किए जा रहे हैं:" + +msgid "{{value}} of {{max}} columns" +msgstr "{{max}} में से {{value}} कॉलम" + +msgid "Organizing data:" +msgstr "डेटा व्यवस्थित किया जा रहा है:" + +msgid "Invalid progress type: {{type}}" +msgstr "अमान्य प्रगति प्रकार: {{type}}" + +msgid "No data for visit" +msgstr "इस विज़िट के लिए कोई डेटा नहीं" + +msgid "Field Name" +msgstr "फ़ील्ड नाम" + +msgid "Field Description" +msgstr "फ़ील्ड विवरण" + +msgid "Field Name: Field Description" +msgstr "फ़ील्ड नाम: फ़ील्ड विवरण" + +msgid "Rows (Cross-sectional)" +msgstr "पंक्तियाँ (क्रॉस-सेक्शनल)" + +msgid "Columns (Longitudinal)" +msgstr "कॉलम (लॉन्गिट्यूडिनल)" + +msgid "Inline values (no download)" +msgstr "इनलाइन मान (कोई डाउनलोड नहीं)" + +msgid "Raw JSON (debugging only)" +msgstr "कच्चा JSON (सिर्फ डीबगिंग के लिए)" + +msgid "Header Display Format" +msgstr "हेडर प्रदर्शन प्रारूप" + +msgid "Display visits as" +msgstr "विज़िट प्रदर्शित करें इस प्रकार" + +msgid "Labels" +msgstr "लेबल्स" + +msgid "Values" +msgstr "मान" + +msgid "Display options as" +msgstr "विकल्प प्रदर्शित करें इस प्रकार" + +msgid "Display empty visits?" +msgstr "खाली विज़िट दिखाएँ?" + +msgid "Row Number" +msgstr "पंक्ति संख्या" + +msgid "Modify Fields" +msgstr "फ़ील्ड संशोधित करें" + +msgid "Choose Fields" +msgstr "फ़ील्ड चुनें" + +msgid "Modify Filters" +msgstr "फ़िल्टर संशोधित करें" + +msgid "Add Filters" +msgstr "फ़िल्टर जोड़ें" + +msgid "Run Query" +msgstr "क्वेरी चलाएँ" + +msgid "Next Steps" +msgstr "अगले चरण" + +msgid "Delete item" +msgstr "आइटम हटाएँ" + +msgid "Delete Group" +msgstr "समूह हटाएँ" + +msgid "Group does not have any items." +msgstr "समूह में कोई आइटम नहीं है।" + +msgid "Group only has 1 item. A group with only 1 item is equivalent to not having the group." +msgstr "समूह में केवल 1 आइटम है। केवल 1 आइटम वाला समूह होना, समूह न होने के बराबर है।" + +msgid "Add \"{{operator}}\" condition to group" +msgstr "समूह में \"{{operator}}\" शर्त जोड़ें" + +msgid "New \"{{antiOperator}}\" subgroup" +msgstr "नया \"{{antiOperator}}\" उपसमूह" + +msgid "Hide Advanced" +msgstr "उन्नत विकल्प छिपाएँ" + +msgid "Show Advanced" +msgstr "उन्नत विकल्प दिखाएँ" + +msgid "The \"nested groups\" options are advanced options for queries that do not have any specific condition at the base of the query. Use Add nested \"or\" condition groups if you need to build a query of the form. (a or b) and (c or d) [or (e and f)..]" +msgstr "\"नेस्टेड समूह\" विकल्प उन क्वेरीज़ के लिए उन्नत विकल्प हैं जिनके आधार में कोई विशिष्ट शर्त नहीं होती। Add nested \"or\" condition groups का उपयोग करें यदि आपको (a या b) और (c या d) [या (e और f)..] जैसी क्वेरी बनानी हो।" + +msgid "Add nested \"or\" condition groups" +msgstr "नेस्टेड \"or\" शर्त समूह जोड़ें" + +msgid "Use Add nested \"and\" condition groups if you need to build a query of the form (a and b) or (c and d) [or (e and f)..]" +msgstr "यदि आपको (a और b) या (c और d) [या (e और f)..] जैसी क्वेरी बनानी है तो Add nested \"and\" condition groups का उपयोग करें।" + +msgid "Add nested \"and\" condition groups" +msgstr "नेस्टेड \"and\" शर्त समूह जोड़ें" + +msgid "Currently querying for ALL candidates." +msgstr "वर्तमान में सभी उम्मीदवारों के लिए क्वेरी की जा रही है।" + +msgid "You can add conditions by clicking one of the buttons below." +msgstr "आप नीचे दिए गए बटनों में से किसी पर क्लिक करके शर्तें जोड़ सकते हैं।" + +msgid "Click Add Condition to add one or more conditions to your filters (ie. \"Date Of Birth < 2015-02-15\"). This is most likely where you want to start your filters." +msgstr "फ़िल्टर में एक या अधिक शर्तें जोड़ने के लिए Add Condition पर क्लिक करें (जैसे \"जन्म तिथि < 2015-02-15\")। संभवतः यही वह जगह है जहाँ से आप अपने फ़िल्टर शुरू करना चाहेंगे।" + +msgid "You can also import a population from a CSV by clicking the Import from CSV button." +msgstr "आप Import from CSV बटन पर क्लिक करके CSV से जनसंख्या भी आयात कर सकते हैं।" + +msgid "The advanced options are for queries that do not have a condition to add at the base of the query." +msgstr "उन्नत विकल्प उन क्वेरीज़ के लिए हैं जिनके आधार में कोई शर्त जोड़ने के लिए नहीं है।" + +msgid "Add Condition" +msgstr "शर्त जोड़ें" + +msgid "Import from CSV" +msgstr "CSV से आयात करें" + +msgid "Use New \"and\" subgroup if the rest of the query you need to write is a subgroup consisting of \"and\" conditions. ie your query is of the form: (your condition above) or (c and d [and e and f..])" +msgstr "नया \"and\" उपसमूह उपयोग करें यदि बाकी क्वेरी \"and\" शर्तों वाले उपसमूह से बनी हो। जैसे: (ऊपर की शर्त) या (c और d [और e और f..])।" + +msgid "New \"and\" subgroup" +msgstr "नया \"and\" उपसमूह" + +msgid "Use New \"or\" subgroup if the rest of the query you need to write is a subgroup consisting of \"or\" conditions. ie your query is of the form: (your condition above) and (c or d [or e or f..])" +msgstr "नया \"or\" उपसमूह उपयोग करें यदि बाकी क्वेरी \"or\" शर्तों वाले उपसमूह से बनी हो। जैसे: (ऊपर की शर्त) और (c या d [या e या f..])।" + +msgid "New \"or\" subgroup" +msgstr "नया \"or\" उपसमूह" + +msgid "Currently querying for any candidates with:" +msgstr "वर्तमान में ऐसे किसी भी उम्मीदवार के लिए क्वेरी की जा रही है जिनमें हो:" + +msgid "Add \"and\" condition" +msgstr "\"and\" शर्त जोड़ें" + +msgid "Add \"or\" condition" +msgstr "\"or\" शर्त जोड़ें" + +msgid "Delete Item" +msgstr "आइटम हटाएँ" + +msgid "Query matches {{count}} candidates" +msgstr "क्वेरी {{count}} उम्मीदवारों से मेल खाती है" + +msgid "Current Query" +msgstr "वर्तमान क्वेरी" + +msgid "Note that only candidates which you have permission to access in LORIS are included in results. Number of results may vary from other users running the same query." +msgstr "ध्यान दें कि केवल वे उम्मीदवार शामिल हैं जिन्हें LORIS में एक्सेस करने की आपको अनुमति है। परिणामों की संख्या अन्य उपयोगकर्ताओं से भिन्न हो सकती है।" + +msgid "Candidate matches" +msgstr "उम्मीदवार मेल खाते हैं" + +msgid "Note: matches count only includes candidates that you have access to. Results may vary from other users due to permissions." +msgstr "नोट: मेल गिनती केवल उन्हीं उम्मीदवारों को शामिल करती है जिन तक आपकी पहुँच है। अनुमतियों के कारण परिणाम अन्य उपयोगकर्ताओं से भिन्न हो सकते हैं।" + +msgid "Select Visits" +msgstr "विज़िट चुनें" + +msgid "Criteria" +msgstr "मानदंड" + +msgid "Select an operator" +msgstr "ऑपरेटर चुनें" + +msgid "for at least one of the following visits" +msgstr "निम्नलिखित में से कम से कम एक विज़िट के लिए" + +msgid "This field may exist multiple times for a single {{scope}}. Adding a criteria based on it means that it must match for at least one of the data points." +msgstr "यह फ़ील्ड एक {{scope}} के लिए कई बार हो सकती है। इस पर आधारित मानदंड जोड़ने का मतलब है कि यह कम से कम एक डेटा बिंदु के लिए मेल खाना चाहिए।" + +msgid "Invalid field" +msgstr "अमान्य फ़ील्ड" + +msgid "You must select a field for the criteria." +msgstr "आपको मानदंड के लिए एक फ़ील्ड चुननी होगी।" + +msgid "Invalid operator" +msgstr "अमान्य ऑपरेटर" + +msgid "You must select an operator for the criteria." +msgstr "आपको मानदंड के लिए एक ऑपरेटर चुनना होगा।" + +msgid "Invalid value" +msgstr "अमान्य मान" + +msgid "You must enter a value to compare the field against." +msgstr "फ़ील्ड की तुलना करने के लिए आपको एक मान दर्ज करना होगा।" + +msgid "Invalid visits" +msgstr "अमान्य विज़िट" + +msgid "No visits selected for criteria." +msgstr "मानदंड के लिए कोई विज़िट चयनित नहीं है।" + +msgid "Add criteria" +msgstr "मानदंड जोड़ें" + +msgid "Field" +msgstr "फ़ील्ड" + +msgid "Select a field" +msgstr "फ़ील्ड चुनें" + +msgid "in" +msgstr "में" + +msgid "starts with" +msgstr "से शुरू होता है" + +msgid "contains" +msgstr "शामिल है" + +msgid "ends with" +msgstr "से समाप्त होता है" + +msgid "has data" +msgstr "डेटा है" + +msgid "has no data" +msgstr "कोई डेटा नहीं है" + +msgid "exists" +msgstr "मौजूद है" + +msgid "does not exist" +msgstr "मौजूद नहीं है" + +msgid "number of" +msgstr "संख्या" + +msgid "true" +msgstr "सही" + +msgid "false" +msgstr "ग़लत" + +msgid "Select a value" +msgstr "मान चुनें" + +msgid "Import Population From CSV" +msgstr "CSV से जनसंख्या आयात करें" + +msgid "CSV containing list of" +msgstr "CSV जिसमें सूची है" + +msgid "Candidate identifier type" +msgstr "उम्मीदवार पहचानकर्ता प्रकार" + +msgid "Does CSV contain a header line?" +msgstr "क्या CSV में हेडर पंक्ति है?" + +msgid "CSV File" +msgstr "CSV फ़ाइल" + +msgid "Invalid CSV" +msgstr "अमान्य CSV" + +msgid "Could not parse CSV file" +msgstr "CSV फ़ाइल पार्स नहीं की जा सकी" + +msgid "Expected {{expectedLength}} columns in CSV. Got {{gotLength}} on line {{line}}." +msgstr "CSV में {{expectedLength}} कॉलम अपेक्षित थे। पंक्ति {{line}} पर {{gotLength}} मिले।" + +msgid "Invalid DCC ID" +msgstr "अमान्य DCC ID" + +msgid "Invalid DCC ID ({{id}}) on line {{line}}." +msgstr "अमान्य DCC ID ({{id}}) पंक्ति {{line}} पर।" + +msgid "Welcome to the Data Query Tool" +msgstr "डेटा क्वेरी टूल में आपका स्वागत है" + +msgid "Study Queries" +msgstr "अध्ययन क्वेरीज़" + +msgid "Instructions" +msgstr "निर्देश" + +msgid "Recent Queries" +msgstr "हाल की क्वेरीज़" + +msgid "Shared Queries" +msgstr "साझा की गई क्वेरीज़" + +msgid "Starred Only" +msgstr "केवल स्टार की गई" + +msgid "Shared Only" +msgstr "केवल साझा की गई" + +msgid "No run times (eliminate duplicates)" +msgstr "कोई रन समय नहीं (डुप्लीकेट हटाएँ)" + +msgid "Filter" +msgstr "फ़िल्टर" + +msgid "Named Only" +msgstr "केवल नाम वाली" + +msgid "Collapse queries" +msgstr "क्वेरीज़ समेटें" + +msgid "(No filters for query)" +msgstr "(क्वेरी के लिए कोई फ़िल्टर नहीं)" + +msgid "Page" +msgstr "पृष्ठ" + +msgid "Unstar" +msgstr "स्टार हटाएँ" + +msgid "Star" +msgstr "स्टार करें" + +msgid "Unshare" +msgstr "साझा हटाएँ" + +msgid "Share" +msgstr "साझा करें" + +msgid "Query Loaded" +msgstr "क्वेरी लोड हो गई" + +msgid "Successfully loaded query." +msgstr "क्वेरी सफलतापूर्वक लोड हो गई।" + +msgid "(Run at {{runTime}})" +msgstr "(चलाया गया {{runTime}} पर)" + +msgid "You ran this query at {{runTime}}" +msgstr "आपने यह क्वेरी {{runTime}} पर चलाई" + +msgid "You ran this query" +msgstr "आपने यह क्वेरी चलाई" + +msgid "(Shared by {{sharedBy}})" +msgstr "(साझा किया गया {{sharedBy}} द्वारा)" + +msgid "Query shared by {{sharedBy}}" +msgstr "क्वेरी {{sharedBy}} द्वारा साझा की गई" + +msgid "Name query" +msgstr "क्वेरी का नाम दें" + +msgid "Fields" +msgstr "फ़ील्ड्स" + +msgid "Filters" +msgstr "फ़िल्टर्स" From 4013d76153c6a72e4a27160362957c50fba4f48b Mon Sep 17 00:00:00 2001 From: Saket Hatwar Date: Tue, 14 Oct 2025 09:40:33 -0400 Subject: [PATCH 2/5] lint --- modules/dataquery/jsx/querytree.tsx | 10 +- modules/dataquery/jsx/viewdata.tsx | 24 ++-- .../dataquery/jsx/welcome.adminquerymodal.tsx | 6 +- modules/dataquery/jsx/welcome.tsx | 114 +++++++++++------- tsconfig.json | 3 +- 5 files changed, 100 insertions(+), 57 deletions(-) diff --git a/modules/dataquery/jsx/querytree.tsx b/modules/dataquery/jsx/querytree.tsx index 6f6f0ac985b..e38255ec3c0 100644 --- a/modules/dataquery/jsx/querytree.tsx +++ b/modules/dataquery/jsx/querytree.tsx @@ -40,6 +40,7 @@ function alternateColour(c: string): string { * @param {object} props.mapModuleName - Function to map the backend module name to a user friendly name * @param {object} props.mapCategoryName - Function to map the backend category name to a user friendly name * @param {object} props.fulldictionary - The dictionary of all modules that have been loaded + * @param props.setDeleteItemIndex * @returns {React.ReactElement} - the react element */ function QueryTree(props: { @@ -206,7 +207,8 @@ function QueryTree(props: { marginLeft: 10, }}>
- {t('Group only has 1 item. A group with only 1 item is equivalent to not having the group.', {ns: 'dataquery'})} + {t('Group only has 1 item. A group with only 1 item is equivalent' + +' to not having the group.', {ns: 'dataquery'})}
; break; @@ -269,7 +271,8 @@ function QueryTree(props: {
{t('Query not yet run', {ns: 'dataquery'})}; } return (
- + - {t('{{value}} of {{max}} candidates', {ns: 'dataquery', value: props.value, max: props.max})} + {t('{{value}} of {{max}} candidates', + {ns: 'dataquery', value: props.value, max: props.max})}
); case 'headers': return (
- + - {t('{{value}} of {{max}} columns', {ns: 'dataquery', value: props.value, max: props.max})} + {t('{{value}} of {{max}} columns', {ns: 'dataquery', + value: props.value, max: props.max})}
); case 'dataorganization': return (
- + - {t('{{value}} of {{max}} columns', {ns: 'dataquery', value: props.value, max: props.max})} + {t('{{value}} of {{max}} columns', {ns: 'dataquery', + value: props.value, max: props.max})}
); } - return

{t('Invalid progress type: {{type}}', {ns: 'dataquery', type: props.type})}

; + return

{t('Invalid progress type: {{type}}', {ns: 'dataquery', + type: props.type})}

; } type RunQueryType = { @@ -855,7 +862,8 @@ function organizedFormatter( return ; default: return ( - {t('(Internal Error. Unhandled cardinality: {{cardinality}})', {ns: 'dataquery', cardinality: fielddict.cardinality})} + {t('(Internal Error. Unhandled cardinality: {{cardinality}})', + {ns: 'dataquery', cardinality: fielddict.cardinality})} ); } } diff --git a/modules/dataquery/jsx/welcome.adminquerymodal.tsx b/modules/dataquery/jsx/welcome.adminquerymodal.tsx index 0c67b9583e2..7f3296f0f19 100644 --- a/modules/dataquery/jsx/welcome.adminquerymodal.tsx +++ b/modules/dataquery/jsx/welcome.adminquerymodal.tsx @@ -42,7 +42,8 @@ function AdminQueryModal(props: { if (queryName.trim() == '') { swal.fire({ type: 'error', - text: t('Must provide a query name to pin query as.', {ns: 'dataquery'}), + text: t('Must provide a query name to pin query as.', + {ns: 'dataquery'}), }); reject(); return; @@ -50,7 +51,8 @@ function AdminQueryModal(props: { if (!topQuery && !dashboardQuery && !loginQuery) { swal.fire({ type: 'error', - text: t('Must pin as study query, to dashboard, or to the login page.', {ns: 'dataquery'}), + text: t('Must pin as study query, to dashboard, or to the ' + +'login page.', {ns: 'dataquery'}), }); reject(); return; diff --git a/modules/dataquery/jsx/welcome.tsx b/modules/dataquery/jsx/welcome.tsx index 1b737751cbc..59d1103dab9 100644 --- a/modules/dataquery/jsx/welcome.tsx +++ b/modules/dataquery/jsx/welcome.tsx @@ -18,7 +18,7 @@ import i18n from 'I18nSetup'; import {withTranslation} from 'react-i18next'; import {createRoot} from 'react-dom/client'; -const hiStrings = require('../locale/hi/LC_MESSAGES/dataquery.json'); +import hiStrings from '../locale/hi/LC_MESSAGES/dataquery.json'; /** * Return the welcome tab for the DQT @@ -491,19 +491,21 @@ function QueryList(props: { }); } const starFilter = props.starQuery ? - setOnlyStarred(value) - }/> : ; + setOnlyStarred(value) + }/> : ; const shareFilter = props.shareQuery ? - setOnlyShared(value) - }/> + setOnlyShared(value) + }/> : ; // Use whether shareQuery prop is defined as proxy // to determine if this is a shared query or a recent @@ -538,20 +540,22 @@ function QueryList(props: { }}> {starFilter} {shareFilter} - setOnlyNamed(value) - }/> + setOnlyNamed(value) + }/> {duplicateFilter} - - setFullQuery(!value) - }/> + + setFullQuery(!value) + }/>
@@ -660,7 +664,7 @@ function Pager(props: { onChangePage={setPageNum} RowsPerPage={rowsPerPage} Active={pageNum} - //label={t('Page', {ns: 'dataquery'})} + // label={t('Page', {ns: 'dataquery'})} /> {displayedRange}
; } @@ -830,9 +834,11 @@ function SingleQueryDisplay(props: { let desc = query.Name ? {query.Name} -  {t('(Run at {{runTime}})', {ns: 'dataquery', runTime: query.RunTime})} +  {t('(Run at {{runTime}})', {ns: 'dataquery', + runTime: query.RunTime})} - : {t('You ran this query at {{runTime}}', {ns: 'dataquery', runTime: query.RunTime})}; + : {t('You ran this query at {{runTime}}', {ns: 'dataquery', + runTime: query.RunTime})}; if (!props.includeRuns) { desc = query.Name ? @@ -853,9 +859,11 @@ function SingleQueryDisplay(props: { const desc = query.Name ? {query.Name} -  {t('(Shared by {{sharedBy}})', {ns: 'dataquery', sharedBy: query.SharedBy.join(', ')})} +  {t('(Shared by {{sharedBy}})', {ns: 'dataquery', + sharedBy: query.SharedBy.join(', ')})} - : {t('Query shared by {{sharedBy}}', {ns: 'dataquery', sharedBy: query.SharedBy.join(', ')})}; + : {t('Query shared by {{sharedBy}}', {ns: 'dataquery', + sharedBy: query.SharedBy.join(', ')})}; msg =
{desc}  {loadIcon}{pinIcon}
; @@ -1075,21 +1083,41 @@ function IntroductionMessage(props: { const {t} = useTranslation('dataquery'); const studyQueriesParagraph = props.hasStudyQueries ? (

- {t('Above, there is also a Study Queries panel. This are a special type of shared queries that have been pinned by a study administer to always display at the top of this page.', {ns: 'dataquery'})} + {t('Above, there is also a Study Queries panel. This are a' + +' special type of shared queries that have been pinned by a study' + +' administer to always display at the top of this page.', + {ns: 'dataquery'})}

) : ''; return (
-

{t('The data query tool allows you to query data within LORIS. There are three steps to defining a query:', {ns: 'dataquery'})}

+

{t('The data query tool allows you to query data within LORIS. ' + +'There are three steps to defining a query:', {ns: 'dataquery'})}

    -
  1. {t("First, you must select the fields that you're interested in on the Define Fields page.", {ns: 'dataquery'})}
  2. -
  3. {t("Next, you can optionally define filters on the Define Filters page to restrict the population that is returned.", {ns: 'dataquery'})}
  4. -
  5. {t("Finally, you view your query results on the View Data page", {ns: 'dataquery'})}
  6. +
  7. {t('First, you must select the fields that you\'re interested in' + +' on the Define Fields page.', {ns: 'dataquery'})}
  8. +
  9. {t('Next, you can optionally define filters on the Define ' + +'Filters page to restrict the population that is returned.', + {ns: 'dataquery'})}
  10. +
  11. {t('Finally, you view your query results on the View Data page', + {ns: 'dataquery'})}
-

{t('The Next Steps on the bottom right of your screen always the context-sensitive next steps that you can do to build your query.', {ns: 'dataquery'})}

-

{t("Your recently run queries will be displayed in the Recent Queries panel below. Instead of building a new query, you can reload a query that you've recently run by clicking on the icon next to the query.", {ns: 'dataquery'})}

-

{t("Queries can be shared with others by clicking the icon. This will cause the query to be shared with all users who have access to the fields used by the query. It will display in a Shared Queries panel below the Recent Queries.", {ns: 'dataquery'})}

-

{t("You may also give a query a name at any time by clicking the icon. This makes it easier to find queries you care about by giving them an easier to remember name that can be used for filtering. When you share a query, the name will be shared along with it.", {ns: 'dataquery'})}

+

{t('The Next Steps on the bottom right of your screen always the ' + +'context-sensitive next steps that you can do to build your query.', + {ns: 'dataquery'})}

+

{t('Your recently run queries will be displayed in the Recent ' + +'Queries panel below. Instead of building a new query, you can ' + +'reload a query that you\'ve recently run by clicking on the icon' + +' next to the query.', {ns: 'dataquery'})}

+

{t('Queries can be shared with others by clicking the icon. This will' + +' cause the query to be shared with all users who have access to the ' + +'fields used by the query. It will display in a Shared Queries panel ' + +'below the Recent Queries.', {ns: 'dataquery'})}

+

{t('You may also give a query a name at any time by clicking the icon.' + +'This makes it easier to find queries you care about by giving them an' + +' easier to remember name that can be used for filtering. When you ' + +'share a query, the name will be shared along with it.', + {ns: 'dataquery'})}

{studyQueriesParagraph}
{ i18n.addResourceBundle('hi', 'dataquery', hiStrings); - const TranslatedWelcome = withTranslation(['dataquery','loris'])(Welcome); + const TranslatedWelcome = withTranslation(['dataquery', 'loris'])(Welcome); const container = document.getElementById('lorisworkspace'); if (container) { const root = createRoot(container); root.render(); } else { - console.error("Element with id 'lorisworkspace' not found in DOM"); + console.error('Element with id \'lorisworkspace\' not found in DOM'); } }); diff --git a/tsconfig.json b/tsconfig.json index 86af508d2d1..660eecf95e3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,7 @@ "lib": [ "es6", "dom", "es2017" ], - "strict" : true + "strict" : true, + "resolveJsonModule": true } } From 3fc2147239defdd4ffaae0d95082b916970b8fab Mon Sep 17 00:00:00 2001 From: Saket Hatwar Date: Tue, 14 Oct 2025 13:15:00 -0400 Subject: [PATCH 3/5] lint --- jsx/I18nSetup.d.ts | 4 +- .../jsx/definefilters.addfiltermodal.tsx | 20 ++++--- .../jsx/definefilters.importcsvmodal.tsx | 13 +++-- modules/dataquery/jsx/definefilters.tsx | 52 ++++++++++++++----- modules/dataquery/jsx/fielddisplay.tsx | 6 ++- modules/dataquery/jsx/index.tsx | 8 +-- modules/dataquery/jsx/querytree.tsx | 13 ++--- 7 files changed, 80 insertions(+), 36 deletions(-) diff --git a/jsx/I18nSetup.d.ts b/jsx/I18nSetup.d.ts index f40a11c2c28..ae231792a8f 100644 --- a/jsx/I18nSetup.d.ts +++ b/jsx/I18nSetup.d.ts @@ -1,4 +1,4 @@ -declare module "I18nSetup" { - import i18n from "i18next"; +declare module 'I18nSetup' { + import i18n from 'i18next'; export default i18n; } diff --git a/modules/dataquery/jsx/definefilters.addfiltermodal.tsx b/modules/dataquery/jsx/definefilters.addfiltermodal.tsx index ac82071df2f..993f0eaa691 100644 --- a/modules/dataquery/jsx/definefilters.addfiltermodal.tsx +++ b/modules/dataquery/jsx/definefilters.addfiltermodal.tsx @@ -132,7 +132,7 @@ function AddFilterModal(props: { if (fieldDictionary) { let valueSelect; if (op) { - valueSelect = valueInput(fieldDictionary, op, value, setValue,t); + valueSelect = valueInput(fieldDictionary, op, value, setValue, t); } criteriaSelect =
@@ -154,7 +154,8 @@ function AddFilterModal(props: { if (fieldDictionary.scope == 'session' && fieldDictionary.visits) { visitSelect =
e.stopPropagation()}> -

{t('for at least one of the following visits', {ns: 'dataquery'})}

+

{t('for at least one of the following visits', + {ns: 'dataquery'})}

{t('This field may exist multiple times for a single {{scope}}. Adding a criteria based on it means that it must match for at least one of the data points.', {ns: 'dataquery', scope: fieldDictionary.scope})}
+ }}>{t('This field may exist multiple times for a single {{scope}}.' + +' Adding a criteria based on it means that it must match for ' + +'at least one of the data points.', {ns: 'dataquery', + scope: fieldDictionary.scope})}
; } } @@ -194,7 +198,8 @@ function AddFilterModal(props: { swal.fire({ type: 'error', title: t('Invalid field', {ns: 'dataquery'}), - text: t('You must select a field for the criteria.', {ns: 'dataquery'}), + text: t('You must select a field for the criteria.', + {ns: 'dataquery'}), }); reject(); return; @@ -203,7 +208,8 @@ function AddFilterModal(props: { swal.fire({ type: 'error', title: t('Invalid operator', {ns: 'dataquery'}), - text: t('You must select an operator for the criteria.', {ns: 'dataquery'}), + text: t('You must select an operator for the criteria.', + {ns: 'dataquery'}), }); reject(); return; @@ -215,7 +221,8 @@ function AddFilterModal(props: { swal.fire({ type: 'error', title: t('Invalid value', {ns: 'dataquery'}), - text: t('You must enter a value to compare the field against.', {ns: 'dataquery'}), + text: t('You must enter a value to compare the field against.', + {ns: 'dataquery'}), }); reject(); return; @@ -362,6 +369,7 @@ function getOperatorOptions(dict: FieldDictionary) { * @param {string} op - The operator selected * @param {string|string[]} value - The current value * @param {string} setValue - a callback when a new value is selected + * @param {function} t - Translation function from i18next * @returns {React.ReactElement} - the react element */ function valueInput(fielddict: FieldDictionary, diff --git a/modules/dataquery/jsx/definefilters.importcsvmodal.tsx b/modules/dataquery/jsx/definefilters.importcsvmodal.tsx index dad83680c04..be4e764d3ac 100644 --- a/modules/dataquery/jsx/definefilters.importcsvmodal.tsx +++ b/modules/dataquery/jsx/definefilters.importcsvmodal.tsx @@ -61,7 +61,9 @@ function ImportCSVModal(props: { swal.fire({ type: 'error', title: t('Invalid CSV', {ns: 'dataquery'}), - text: t('Expected {{expectedLength}} columns in CSV. Got {{gotLength}} on line {{line}}.', {ns: 'dataquery', expectedLength, gotLength: value.data[i].length, line: i+1}), + text: t('Expected {{expectedLength}} columns in CSV. ' + +'Got {{gotLength}} on line {{line}}.', {ns: 'dataquery', + expectedLength, gotLength: value.data[i].length, line: i+1}), }); return; } @@ -70,7 +72,8 @@ function ImportCSVModal(props: { swal.fire({ type: 'error', title: t('Invalid DCC ID', {ns: 'dataquery'}), - text: t('Invalid DCC ID ({{id}}) on line {{line}}.', {ns: 'dataquery', id: value.data[i][0], line: i+1}), + text: t('Invalid DCC ID ({{id}}) on line {{line}}.', + {ns: 'dataquery', id: value.data[i][0], line: i+1}), }); return; } @@ -131,7 +134,8 @@ function ImportCSVModal(props: {
-
{t('CSV containing list of', {ns: 'dataquery'})}
+
{t('CSV containing list of', + {ns: 'dataquery'})}
setCSVType('session')} /> {t('Sessions', {ns: 'dataquery'})}
-
{t('Candidate identifier type', {ns: 'dataquery'})}
+
{t('Candidate identifier type', + {ns: 'dataquery'})}
setIdType('CandID')} diff --git a/modules/dataquery/jsx/definefilters.tsx b/modules/dataquery/jsx/definefilters.tsx index 65e1bf626e3..adec47339cb 100644 --- a/modules/dataquery/jsx/definefilters.tsx +++ b/modules/dataquery/jsx/definefilters.tsx @@ -120,7 +120,8 @@ function DefineFilters(props: { const mapModuleName = props.mapModuleName; const mapCategoryName = props.mapCategoryName; - const advancedLabel = showAdvanced ? t('Hide Advanced', {ns: 'dataquery'}) : t('Show Advanced', {ns: 'dataquery'}); + const advancedLabel = showAdvanced ? t('Hide Advanced', + {ns: 'dataquery'}) : t('Show Advanced', {ns: 'dataquery'}); let advancedButtons; const toggleAdvancedButton = (
@@ -140,7 +141,11 @@ function DefineFilters(props: { if (showAdvanced) { advancedButtons = (
-

{t('The "nested groups" options are advanced options for queries that do not have any specific condition at the base of the query. Use Add nested "or" condition groups if you need to build a query of the form. (a or b) and (c or d) [or (e and f)..]', {ns: 'dataquery'})}

+

{t('The "nested groups" options are advanced options for ' + +'queries that do not have any specific condition at the base' + +' of the query. Use Add nested "or" condition groups if you' + +' need to build a query of the form. (a or b) and (c or d)' + +' [or (e and f)..]', {ns: 'dataquery'})}

-

{t('Use Add nested "and" condition groups if you need to build a query of the form (a and b) or (c and d) [or (e and f)..]', {ns: 'dataquery'})}

+

{t('Use Add nested "and" condition groups if you need to ' + +'build a query of the form (a and b) or (c and d) ' + +'[or (e and f)..]', {ns: 'dataquery'})}

{t('Currently querying for ALL candidates.', {ns: 'dataquery'})}

-

{t('You can add conditions by clicking one of the buttons below.', {ns: 'dataquery'})}

-

{t('Click Add Condition to add one or more conditions to your filters (ie. "Date Of Birth < 2015-02-15"). This is most likely where you want to start your filters.', {ns: 'dataquery'})}

-

{t('You can also import a population from a CSV by clicking the Import from CSV button.', {ns: 'dataquery'})}

-

{t('The advanced options are for queries that do not have a condition to add at the base of the query.', {ns: 'dataquery'})}

+

{t('You can add conditions by clicking one of the buttons below.', + {ns: 'dataquery'})}

+

{t('Click Add Condition to add one or more conditions to your' + +' filters (ie. "Date Of Birth < 2015-02-15"). This is most likely' + +' where you want to start your filters.', {ns: 'dataquery'})}

+

{t('You can also import a population from a CSV by clicking the' + +' Import from CSV button.', {ns: 'dataquery'})}

+

{t('The advanced options are for queries that do not have a ' + +'condition to add at the base of the query.', {ns: 'dataquery'})}

@@ -218,7 +230,11 @@ function DefineFilters(props: { advancedButtons = (
-

{t('Use New "and" subgroup if the rest of the query you need to write is a subgroup consisting of "and" conditions. ie your query is of the form: (your condition above) or (c and d [and e and f..])', {ns: 'dataquery'})}

+

{t('Use New "and" subgroup if the rest of the query you' + +' need to write is a subgroup consisting of "and" ' + +'conditions. ie your query is of the form: (your condition' + +' above) or (c and d [and e and f..])', + {ns: 'dataquery'})}

{ @@ -226,7 +242,11 @@ function DefineFilters(props: { props.query.operator = 'or'; props.addNewQueryGroup(props.query); }} /> -

{t('Use New "or" subgroup if the rest of the query you need to write is a subgroup consisting of "or" conditions. ie your query is of the form: (your condition above) and (c or d [or e or f..])', {ns: 'dataquery'})}

+

{t('Use New "or" subgroup if the rest of the query you ' + +'need to write is a subgroup consisting of "or" ' + +'conditions. ie your query is of the form: (your ' + +'condition above) and (c or d [or e or f..])', + {ns: 'dataquery'})}

{ @@ -240,7 +260,8 @@ function DefineFilters(props: { } // buttons for 1. Add "and" condition 2. Add "or" condition displayquery = (
-

{t('Currently querying for any candidates with:', {ns: 'dataquery'})}

+

{t('Currently querying for any candidates with:', + {ns: 'dataquery'})}

@@ -300,7 +321,8 @@ function DefineFilters(props: { // Add buttons are delegated to the QueryTree rendering so they // can be placed at the right level displayquery =
-

{t('Currently querying for any candidates with:', {ns: 'dataquery'})}

+

{t('Currently querying for any candidates with:', + {ns: 'dataquery'})}

 
// So the header doesn't jump around - :
{t('Query matches {{count}} candidates', {ns: 'dataquery', count: queryMatches})}
; + :
{t('Query matches {{count}} candidates', + {ns: 'dataquery', count: queryMatches})}
; return (
{modal} {csvModalHTML} @@ -365,7 +388,10 @@ function DefineFilters(props: { {matchCount}
- {t('Note that only candidates which you have permission to access in LORIS are included in results. Number of results may vary from other users running the same query.', {ns: 'dataquery'})} + {t('Note that only candidates which you have permission to ' + +'access in LORIS are included in results. Number of results' + +' may vary from other users running the same query.', + {ns: 'dataquery'})} {displayquery}
diff --git a/modules/dataquery/jsx/fielddisplay.tsx b/modules/dataquery/jsx/fielddisplay.tsx index ece1189b05f..7bf4ee1544d 100644 --- a/modules/dataquery/jsx/fielddisplay.tsx +++ b/modules/dataquery/jsx/fielddisplay.tsx @@ -35,8 +35,10 @@ function FieldDisplay(props: { {description}
- {t('Category', {ns: 'dataquery'})}: {props.mapCategoryName(props.module, props.category)} -  ({t('Module', {ns: 'dataquery'})}: {props.mapModuleName(props.module)}) + {t('Category', {ns: 'dataquery'})}: + {props.mapCategoryName(props.module, props.category)} +  ({t('Module', {ns: 'dataquery'})}: + {props.mapModuleName(props.module)})
); diff --git a/modules/dataquery/jsx/index.tsx b/modules/dataquery/jsx/index.tsx index 52e76cb7fb3..1d9d7686cf2 100644 --- a/modules/dataquery/jsx/index.tsx +++ b/modules/dataquery/jsx/index.tsx @@ -18,8 +18,9 @@ import {useDataDictionary, useCategories} from './hooks/usedatadictionary'; import {ModuleDictionary, DictionaryCategory} from './types'; // @ts-ignore import i18n from 'I18nSetup'; -import {useTranslation, withTranslation} from 'react-i18next'; -const hiStrings = require('../locale/hi/LC_MESSAGES/dataquery.json'); +import {withTranslation} from 'react-i18next'; + +import hiStrings from '../locale/hi/LC_MESSAGES/dataquery.json'; type ActiveCategoryType = { module: string, @@ -249,7 +250,8 @@ function DataQueryApp(props: { declare const loris: any; window.addEventListener('load', () => { i18n.addResourceBundle('hi', 'dataquery', hiStrings); - const TranslatedDataQueryApp = withTranslation(['dataquery','loris'])(DataQueryApp); + const TranslatedDataQueryApp = withTranslation( + ['dataquery', 'loris'])(DataQueryApp); const element = document.getElementById('lorisworkspace'); if (!element) { diff --git a/modules/dataquery/jsx/querytree.tsx b/modules/dataquery/jsx/querytree.tsx index e38255ec3c0..f77142f323f 100644 --- a/modules/dataquery/jsx/querytree.tsx +++ b/modules/dataquery/jsx/querytree.tsx @@ -3,8 +3,8 @@ import {QueryGroup, QueryTerm} from './querydef'; import {CriteriaTerm} from './criteriaterm'; import {ButtonElement} from 'jsx/Form'; import {FullDictionary} from './types'; -import {useEffect} from 'react'; // already present -import {useTranslation} from 'react-i18next'; // <-- ADD THIS +import {useEffect} from 'react'; +import {useTranslation} from 'react-i18next'; /** * Alternate background colour for a QueryTree @@ -40,7 +40,7 @@ function alternateColour(c: string): string { * @param {object} props.mapModuleName - Function to map the backend module name to a user friendly name * @param {object} props.mapCategoryName - Function to map the backend category name to a user friendly name * @param {object} props.fulldictionary - The dictionary of all modules that have been loaded - * @param props.setDeleteItemIndex + * @param {function} props.setDeleteItemIndex - Callback to set or clear the index of the item marked for deletion * @returns {React.ReactElement} - the react element */ function QueryTree(props: { @@ -63,7 +63,7 @@ function QueryTree(props: { mapCategoryName: (module: string, category: string) => string, }) { const [deleteItemIndex, setDeleteItemIndex] = useState(null); - const {t} = useTranslation('dataquery'); // <-- ADD THIS + const {t} = useTranslation('dataquery'); useEffect(() => { // Reset strikethrough when group is empty or changed @@ -271,8 +271,9 @@ function QueryTree(props: {
Date: Tue, 14 Oct 2025 15:47:58 -0400 Subject: [PATCH 4/5] Additional Translations --- .../jsx/components/filterableselectgroup.tsx | 4 +- modules/dataquery/jsx/definefields.tsx | 37 ++++++------ .../jsx/definefilters.addfiltermodal.tsx | 2 +- .../dataquery/jsx/hooks/usebreadcrumbs.tsx | 12 ++-- .../dataquery/jsx/hooks/usedatadictionary.tsx | 8 ++- modules/dataquery/jsx/viewdata.tsx | 29 ++++----- modules/dataquery/jsx/welcome.tsx | 2 +- modules/dataquery/locale/dataquery.pot | 60 +++++++++++++++++++ .../locale/hi/LC_MESSAGES/dataquery.json | 21 ++++++- .../locale/hi/LC_MESSAGES/dataquery.po | 57 ++++++++++++++++++ 10 files changed, 189 insertions(+), 43 deletions(-) diff --git a/modules/dataquery/jsx/components/filterableselectgroup.tsx b/modules/dataquery/jsx/components/filterableselectgroup.tsx index 49d937e76c6..a06656fbe83 100644 --- a/modules/dataquery/jsx/components/filterableselectgroup.tsx +++ b/modules/dataquery/jsx/components/filterableselectgroup.tsx @@ -1,4 +1,5 @@ import Select, {SingleValue} from 'react-select'; +import {useTranslation} from 'react-i18next'; type SelectOption = { label: string, @@ -27,8 +28,9 @@ function FilterableSelectGroup(props: { groups: object, mapGroupName?: (module: string) => string, }) { + const {t} = useTranslation('dataquery'); const groups: SelectGroup[] = []; - const placeholder = props.placeholder || 'Select a category'; + const placeholder = props.placeholder || t('Select a category'); for (const [module, subcategories] of Object.entries(props.groups)) { const options: SelectOption[] = []; diff --git a/modules/dataquery/jsx/definefields.tsx b/modules/dataquery/jsx/definefields.tsx index 731c81f7bf6..79c803e9e39 100644 --- a/modules/dataquery/jsx/definefields.tsx +++ b/modules/dataquery/jsx/definefields.tsx @@ -94,14 +94,14 @@ function QueryField(props: { if (props.selected) { visits =
e.stopPropagation()}> -

{t('Visits', {ns: 'loris'})}

+

{t('Visits')}

- setSyncVisits(value) - } /> + setSyncVisits(value) + } />
; } @@ -373,7 +376,7 @@ function DefineFields(props: { fieldList = (
-

{cname} fields

+

{cname} {t('fields')}

@@ -399,11 +402,11 @@ function DefineFields(props: { }}>
@@ -416,7 +419,7 @@ function DefineFields(props: {
-

Available Fields

+

{t('Available Fields', {ns: 'dataquery'})}

props.allCategories.modules[key]} onChange={props.onCategoryChange} @@ -438,11 +441,11 @@ function DefineFields(props: { alignItems: 'flex-end', marginBottom: '1em', }}> -

Selected Fields

+

{t('Selected Fields', {ns: 'dataquery'})}

+ onClick={props.onClearAll}>{t('Clear')}
; + placeholder={t('Select a field', {ns: 'dataquery'})} />; } if (fieldDictionary) { diff --git a/modules/dataquery/jsx/hooks/usebreadcrumbs.tsx b/modules/dataquery/jsx/hooks/usebreadcrumbs.tsx index ba65fb9c5b7..c3c405f64cc 100644 --- a/modules/dataquery/jsx/hooks/usebreadcrumbs.tsx +++ b/modules/dataquery/jsx/hooks/usebreadcrumbs.tsx @@ -1,4 +1,5 @@ import React, {useEffect} from 'react'; +import {useTranslation} from 'react-i18next'; import Breadcrumbs from 'jsx/Breadcrumbs'; // Declared in smarty main.tpl @@ -15,11 +16,12 @@ function useBreadcrumbs( activeTab: string, setActiveTab: (newtab: string) => void ) { + const {t} = useTranslation('dataquery'); // update breadcrumbs breadcrumbs useEffect(() => { const breadcrumbs = [ { - text: 'Data Query Tool (Beta)', + text: t('Data Query Tool (Beta)', {ns: 'dataquery'}), /** * OnClick handler for the main breadcrumb * @@ -36,7 +38,7 @@ function useBreadcrumbs( || activeTab == 'DefineFilters' || activeTab == 'ViewData') { breadcrumbs.push({ - text: 'Define Fields', + text: t('Define Fields', {ns: 'dataquery'}), /** * OnClick handler for the define fields breadcrumb * @@ -52,7 +54,7 @@ function useBreadcrumbs( if (activeTab == 'DefineFilters' || activeTab == 'ViewData') { breadcrumbs.push({ - text: 'Define Filters', + text: t('Define Filters', {ns: 'dataquery'}), /** * OnClick handler for the define filters breadcrumb * @@ -68,7 +70,7 @@ function useBreadcrumbs( if (activeTab == 'ViewData') { breadcrumbs.push({ - text: 'View Data', + text: t('View Data', {ns: 'dataquery'}), /** * OnClick handler for the View Data breadcrumb * @@ -90,7 +92,7 @@ function useBreadcrumbs( />, ); } - }, [activeTab]); + }, [activeTab, t]); } export default useBreadcrumbs; diff --git a/modules/dataquery/jsx/hooks/usedatadictionary.tsx b/modules/dataquery/jsx/hooks/usedatadictionary.tsx index 5f7b93dd8c4..679a7500142 100644 --- a/modules/dataquery/jsx/hooks/usedatadictionary.tsx +++ b/modules/dataquery/jsx/hooks/usedatadictionary.tsx @@ -1,4 +1,5 @@ import {useState, useEffect} from 'react'; +import {useTranslation} from 'react-i18next'; import {ModuleDictionary, FullDictionary} from '../types'; @@ -23,6 +24,7 @@ export type CategoriesAPIReturn = { */ function useCategories(): CategoriesAPIReturn|null { const [categories, setCategories] = useState(null); + const {t} = useTranslation('dataquery'); useEffect(() => { if (categories !== null) { return; @@ -30,7 +32,7 @@ function useCategories(): CategoriesAPIReturn|null { fetch('/dictionary/categories', {credentials: 'same-origin'}) .then((resp) => { if (!resp.ok) { - throw new Error('Invalid response'); + throw new Error(t('Invalid response', {ns: 'dataquery'})); } return resp.json(); }).then((result) => { @@ -59,7 +61,7 @@ function useDataDictionary(): DataDictionaryReturnType { // typescript says the key is always defined when we try and check if // it's set, need to figure out the correct way to do that, for now just use any const [pendingModules, setPendingModules] = useState({}); - + const {t} = useTranslation('dataquery'); /** * Fetch a module's dictionary and cache it into fulldictionary. * @@ -80,7 +82,7 @@ function useDataDictionary(): DataDictionaryReturnType { {credentials: 'same-origin'} ).then((resp) => { if (!resp.ok) { - throw new Error('Invalid response'); + throw new Error(t('Invalid response', {ns: 'dataquery'})); } return resp.json(); }).then((result) => { diff --git a/modules/dataquery/jsx/viewdata.tsx b/modules/dataquery/jsx/viewdata.tsx index 7b3d7526827..1c9b6747ca0 100644 --- a/modules/dataquery/jsx/viewdata.tsx +++ b/modules/dataquery/jsx/viewdata.tsx @@ -319,6 +319,7 @@ function ViewData(props: { onRun: () => void fulldictionary: FullDictionary, }) { + const {t} = useTranslation('dataquery'); const [visitOrganization, setVisitOrganization] = useState('inline'); const [headerDisplay, setHeaderDisplay] @@ -344,7 +345,7 @@ function ViewData(props: { } else { switch (organizedData['status']) { case null: - return queryTable =

Query not yet run

; + return queryTable =

{t('Query not yet run')}

; case 'headers': queryTable = { return {show: true, label: val}; @@ -407,7 +408,7 @@ function ViewData(props: { setEmptyVisits(value) @@ -418,9 +419,9 @@ function ViewData(props: {

{t('You may also give a query a name at any time by clicking the icon.' - +'This makes it easier to find queries you care about by giving them an' + +' This makes it easier to find queries you care about by giving them an' +' easier to remember name that can be used for filtering. When you ' +'share a query, the name will be shared along with it.', {ns: 'dataquery'})}

diff --git a/modules/dataquery/locale/dataquery.pot b/modules/dataquery/locale/dataquery.pot index 3e2bd227def..8fb49d8dcb0 100644 --- a/modules/dataquery/locale/dataquery.pot +++ b/modules/dataquery/locale/dataquery.pot @@ -404,3 +404,63 @@ msgstr "" msgid "Filters" msgstr "" + +msgid "Continue to Define Fields" +msgstr "" + +msgid "Above, there is also a Study Queries panel. This are a special type of shared queries that have been pinned by a study administer to always display at the top of this page." +msgstr "" + +msgid "The data query tool allows you to query data within LORIS. There are three steps to defining a query:" +msgstr "" + +msgid "First, you must select the fields that you're interested in on the Define Fields page." +msgstr "" + +msgid "Next, you can optionally define filters on the Define Filters page to restrict the population that is returned." +msgstr "" + +msgid "Finally, you view your query results on the View Data page" +msgstr "" + +msgid "The Next Steps on the bottom right of your screen always the context-sensitive next steps that you can do to build your query." +msgstr "" + +msgid "Your recently run queries will be displayed in the Recent Queries panel below. Instead of building a new query, you can reload a query that you've recently run by clicking on the icon next to the query." +msgstr "" + +msgid "Queries can be shared with others by clicking the icon. This will cause the query to be shared with all users who have access to the fields used by the query. It will display in a Shared Queries panel below the Recent Queries." +msgstr "" + +msgid "You may also give a query a name at any time by clicking the icon. This makes it easier to find queries you care about by giving them an easier to remember name that can be used for filtering. When you share a query, the name will be shared along with it." +msgstr "" + +msgid "Selected Fields" +msgstr "" + +msgid "Available Fields" +msgstr "" + +msgid "Add all" +msgstr "" + +msgid "Remove all" +msgstr "" + +msgid "Filter within category" +msgstr "" + +msgid "Default Visits" +msgstr "" + +msgid "Sync with selected fields" +msgstr "" + +msgid "Select a field" +msgstr "" + +msgid "Select a category" +msgstr "" + +msgid "Invalid response" +msgstr "" \ No newline at end of file diff --git a/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.json b/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.json index 118eda2c676..fbe37a99e60 100644 --- a/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.json +++ b/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.json @@ -124,5 +124,24 @@ "Query shared by {{sharedBy}}": "क्वेरी {{sharedBy}} द्वारा साझा की गई", "Name query": "क्वेरी का नाम दें", "Fields": "फ़ील्ड्स", - "Filters": "फ़िल्टर्स" + "Filters": "फ़िल्टर्स", + "Continue to Define Fields": "फ़ील्ड परिभाषित करना जारी रखें", + "Above, there is also a Study Queries panel. This are a special type of shared queries that have been pinned by a study administer to always display at the top of this page.": "ऊपर एक अध्ययन क्वेरी पैनल भी है। यह साझा क्वेरी का एक विशेष प्रकार है जिसे अध्ययन प्रशासक द्वारा इस पृष्ठ के शीर्ष पर हमेशा प्रदर्शित होने के लिए पिन किया गया है।", + "The data query tool allows you to query data within LORIS. There are three steps to defining a query:": "डेटा क्वेरी टूल आपको LORIS के भीतर डेटा क्वेरी करने की अनुमति देता है। क्वेरी को परिभाषित करने के तीन चरण हैं:", + "First, you must select the fields that you're interested in on the Define Fields page.": "पहले, आपको 'फ़ील्ड परिभाषित करें' पृष्ठ पर वे फ़ील्ड चुननी होंगी जिनमें आपकी रुचि है।", + "Next, you can optionally define filters on the Define Filters page to restrict the population that is returned.": "इसके बाद, आप वैकल्पिक रूप से 'फ़िल्टर परिभाषित करें' पृष्ठ पर फ़िल्टर परिभाषित कर सकते हैं ताकि लौटाई गई जनसंख्या को सीमित किया जा सके।", + "Finally, you view your query results on the View Data page": "अंत में, आप 'डेटा देखें' पृष्ठ पर अपनी क्वेरी के परिणाम देख सकते हैं।", + "The Next Steps on the bottom right of your screen always the context-sensitive next steps that you can do to build your query.": "आपकी स्क्रीन के नीचे दाईं ओर स्थित 'अगले चरण' हमेशा संदर्भ-संवेदनशील चरण दिखाते हैं जो आप अपनी क्वेरी बनाने के लिए कर सकते हैं।", + "Your recently run queries will be displayed in the Recent Queries panel below. Instead of building a new query, you can reload a query that you've recently run by clicking on the icon next to the query.": "आपकी हाल ही में चलाई गई क्वेरी नीचे 'हाल की क्वेरी' पैनल में दिखाई देंगी। नई क्वेरी बनाने के बजाय, आप क्वेरी के बगल में स्थित आइकन पर क्लिक करके हाल ही में चलाई गई क्वेरी को फिर से लोड कर सकते हैं।", + "Queries can be shared with others by clicking the icon. This will cause the query to be shared with all users who have access to the fields used by the query. It will display in a Shared Queries panel below the Recent Queries.": "क्वेरी को आइकन पर क्लिक करके दूसरों के साथ साझा किया जा सकता है। इससे क्वेरी उन सभी उपयोगकर्ताओं के साथ साझा हो जाएगी जिन्हें उस क्वेरी में उपयोग किए गए फ़ील्ड तक पहुँच है। यह 'हाल की क्वेरी' के नीचे 'साझा क्वेरी' पैनल में दिखाई देगी।", + "You may also give a query a name at any time by clicking the icon. This makes it easier to find queries you care about by giving them an easier to remember name that can be used for filtering. When you share a query, the name will be shared along with it.": "आप किसी भी समय आइकन पर क्लिक करके क्वेरी को एक नाम भी दे सकते हैं। इससे उन क्वेरियों को ढूंढना आसान हो जाता है जिनकी आपको परवाह है, क्योंकि आप उन्हें याद रखने में आसान नाम दे सकते हैं जिसका उपयोग फ़िल्टर करने के लिए किया जा सकता है। जब आप क्वेरी साझा करते हैं, तो उसका नाम भी उसके साथ साझा किया जाएगा।", + "Selected Fields": "चयनित फ़ील्ड", + "Available Fields": "उपलब्ध फ़ील्ड", + "Add all": "सभी जोड़ें", + "Remove all": "सभी हटाएँ", + "Filter within category": "श्रेणी के भीतर फ़िल्टर करें", + "Default Visits": "डिफ़ॉल्ट विज़िट", + "Sync with selected fields": "चयनित फ़ील्ड के साथ समन्वय करें", + "Select a category": "एक फ़ील्ड चुनें", + "Invalid response": "अमान्य उत्तर" } \ No newline at end of file diff --git a/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po b/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po index 337d85e7c63..b29d21f34d0 100644 --- a/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po +++ b/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po @@ -395,3 +395,60 @@ msgstr "फ़ील्ड्स" msgid "Filters" msgstr "फ़िल्टर्स" + +msgid "Continue to Define Fields" +msgstr "फ़ील्ड परिभाषित करना जारी रखें" + +msgid "Above, there is also a Study Queries panel. This are a special type of shared queries that have been pinned by a study administer to always display at the top of this page." +msgstr "ऊपर एक अध्ययन क्वेरी पैनल भी है। यह साझा क्वेरी का एक विशेष प्रकार है जिसे अध्ययन प्रशासक द्वारा इस पृष्ठ के शीर्ष पर हमेशा प्रदर्शित होने के लिए पिन किया गया है।" + +msgid "The data query tool allows you to query data within LORIS. There are three steps to defining a query:" +msgstr "डेटा क्वेरी टूल आपको LORIS के भीतर डेटा क्वेरी करने की अनुमति देता है। क्वेरी को परिभाषित करने के तीन चरण हैं:" + +msgid "First, you must select the fields that you're interested in on the Define Fields page." +msgstr "पहले, आपको 'फ़ील्ड परिभाषित करें' पृष्ठ पर वे फ़ील्ड चुननी होंगी जिनमें आपकी रुचि है।" + +msgid "Next, you can optionally define filters on the Define Filters page to restrict the population that is returned." +msgstr "इसके बाद, आप वैकल्पिक रूप से 'फ़िल्टर परिभाषित करें' पृष्ठ पर फ़िल्टर परिभाषित कर सकते हैं ताकि लौटाई गई जनसंख्या को सीमित किया जा सके।" + +msgid "Finally, you view your query results on the View Data page" +msgstr "अंत में, आप 'डेटा देखें' पृष्ठ पर अपनी क्वेरी के परिणाम देख सकते हैं।" + +msgid "The Next Steps on the bottom right of your screen always the context-sensitive next steps that you can do to build your query." +msgstr "आपकी स्क्रीन के नीचे दाईं ओर स्थित 'अगले चरण' हमेशा संदर्भ-संवेदनशील चरण दिखाते हैं जो आप अपनी क्वेरी बनाने के लिए कर सकते हैं।" + +msgid "Your recently run queries will be displayed in the Recent Queries panel below. Instead of building a new query, you can reload a query that you've recently run by clicking on the icon next to the query." +msgstr "आपकी हाल ही में चलाई गई क्वेरी नीचे 'हाल की क्वेरी' पैनल में दिखाई देंगी। नई क्वेरी बनाने के बजाय, आप क्वेरी के बगल में स्थित आइकन पर क्लिक करके हाल ही में चलाई गई क्वेरी को फिर से लोड कर सकते हैं।" + +msgid "Queries can be shared with others by clicking the icon. This will cause the query to be shared with all users who have access to the fields used by the query. It will display in a Shared Queries panel below the Recent Queries." +msgstr "क्वेरी को आइकन पर क्लिक करके दूसरों के साथ साझा किया जा सकता है। इससे क्वेरी उन सभी उपयोगकर्ताओं के साथ साझा हो जाएगी जिन्हें उस क्वेरी में उपयोग किए गए फ़ील्ड तक पहुँच है। यह 'हाल की क्वेरी' के नीचे 'साझा क्वेरी' पैनल में दिखाई देगी।" + +msgid "You may also give a query a name at any time by clicking the icon. This makes it easier to find queries you care about by giving them an easier to remember name that can be used for filtering. When you share a query, the name will be shared along with it." +msgstr "आप किसी भी समय आइकन पर क्लिक करके क्वेरी को एक नाम भी दे सकते हैं। इससे उन क्वेरियों को ढूंढना आसान हो जाता है जिनकी आपको परवाह है, क्योंकि आप उन्हें याद रखने में आसान नाम दे सकते हैं जिसका उपयोग फ़िल्टर करने के लिए किया जा सकता है। जब आप क्वेरी साझा करते हैं, तो उसका नाम भी उसके साथ साझा किया जाएगा।" + +msgid "Selected Fields" +msgstr "चयनित फ़ील्ड" + +msgid "Available Fields" +msgstr "उपलब्ध फ़ील्ड" + +msgid "Add all" +msgstr "सभी जोड़ें" + +msgid "Remove all" +msgstr "सभी हटाएँ" + +msgid "Filter within category" +msgstr "श्रेणी के भीतर फ़िल्टर करें" + +msgid "Default Visits" +msgstr "डिफ़ॉल्ट विज़िट" + +msgid "Sync with selected fields" +msgstr "चयनित फ़ील्ड के साथ समन्वय करें" + +msgid "Select a category" +msgstr "एक फ़ील्ड चुनें" + +msgid "Invalid response" +msgstr "अमान्य उत्तर" From 8b0cbdcf70b316e04fc2eefed1e4a40fc91683c4 Mon Sep 17 00:00:00 2001 From: Saket Hatwar Date: Fri, 17 Oct 2025 13:58:16 -0400 Subject: [PATCH 5/5] fixes --- jsx/Modal.tsx | 17 ++++++++------- locale/hi/LC_MESSAGES/loris.po | 9 ++++++++ locale/loris.pot | 9 ++++++++ modules/dataquery/jsx/welcome.tsx | 3 ++- modules/dataquery/locale/dataquery.pot | 9 ++++++++ .../locale/hi/LC_MESSAGES/dataquery.po | 21 +++++++++++++------ 6 files changed, 54 insertions(+), 14 deletions(-) diff --git a/jsx/Modal.tsx b/jsx/Modal.tsx index 9a2d0f46f19..78c7a6f3bd7 100644 --- a/jsx/Modal.tsx +++ b/jsx/Modal.tsx @@ -1,4 +1,5 @@ import {useState, PropsWithChildren, CSSProperties, ReactNode} from 'react'; +import {useTranslation} from 'react-i18next'; import Swal from 'sweetalert2'; import Loader from './Loader'; import { @@ -38,6 +39,7 @@ const Modal = ({ }: ModalProps) => { const [loading, setLoading] = useState(false); // Tracks loading during submit const [success, setSuccess] = useState(false); // Tracks success after submit + const {t} = useTranslation('loris'); /** * Handles modal close event. Shows a confirmation if `throwWarning` is true. @@ -45,13 +47,14 @@ const Modal = ({ const handleClose = () => { if (throwWarning) { // Display warning if enabled Swal.fire({ - title: 'Are You Sure?', - text: 'Leaving the form will result in the loss of any information ' + - 'entered.', + title: t('Are You Sure?', {ns: 'loris'}), + text: t('Leaving the form will result in the loss ' + +'of any information entered.', + {ns: 'loris'}), type: 'warning', showCancelButton: true, - confirmButtonText: 'Proceed', - cancelButtonText: 'Cancel', + confirmButtonText: t('Proceed', {ns: 'loris'}), + cancelButtonText: t('Cancel', {ns: 'loris'}), }).then((result) => result.value && onClose()); } else { onClose(); // Close immediately if no warning @@ -175,7 +178,7 @@ const Modal = ({ const loader = loading && (
-
Saving
+
{t('Saving', {ns: 'loris'})}
); @@ -188,7 +191,7 @@ const Modal = ({ style={{color: 'green', marginBottom: '2px'}} className='glyphicon glyphicon-ok-circle' /> -
Success!
+
{t('Success!', {ns: 'loris'})}
); diff --git a/locale/hi/LC_MESSAGES/loris.po b/locale/hi/LC_MESSAGES/loris.po index 337fc38afde..8e074d6d169 100644 --- a/locale/hi/LC_MESSAGES/loris.po +++ b/locale/hi/LC_MESSAGES/loris.po @@ -130,6 +130,15 @@ msgstr "रद्द करें" msgid "Success!" msgstr "सफलता!" +msgid "Saving" +msgstr "सहेजा जा रहा है" + +msgid "Are You Sure?" +msgstr "क्या आप सुनिश्चित हैं?" + +msgid "Leaving the form will result in the loss of any information entered." +msgstr "फॉर्म छोड़ने पर दर्ज की गई सभी जानकारी खो जाएगी।" + # Common candidate terms msgid "PSCID" msgstr "पीएससीआईडी" diff --git a/locale/loris.pot b/locale/loris.pot index e39c09b8359..bd132421886 100644 --- a/locale/loris.pot +++ b/locale/loris.pot @@ -129,6 +129,15 @@ msgstr "" msgid "Success!" msgstr "" +msgid "Saving" +msgstr "" + +msgid "Are You Sure?" +msgstr "" + +msgid "Leaving the form will result in the loss of any information entered." +msgstr "" + # Common candidate terms msgid "PSCID" msgstr "" diff --git a/modules/dataquery/jsx/welcome.tsx b/modules/dataquery/jsx/welcome.tsx index 738f87a7e69..17dc14e8b26 100644 --- a/modules/dataquery/jsx/welcome.tsx +++ b/modules/dataquery/jsx/welcome.tsx @@ -1018,8 +1018,9 @@ function QueryRunList(props:{ function LoadIcon(props: { onClick?: () => void, }) { + const {t} = useTranslation('dataquery'); return diff --git a/modules/dataquery/locale/dataquery.pot b/modules/dataquery/locale/dataquery.pot index 8fb49d8dcb0..873ff1bfc2c 100644 --- a/modules/dataquery/locale/dataquery.pot +++ b/modules/dataquery/locale/dataquery.pot @@ -405,6 +405,15 @@ msgstr "" msgid "Filters" msgstr "" +msgid "Define Filters" +msgstr "" + +msgid "Define Fields" +msgstr "" + +msgid "Reload query" +msgstr "" + msgid "Continue to Define Fields" msgstr "" diff --git a/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po b/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po index b29d21f34d0..d31a58fdf98 100644 --- a/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po +++ b/modules/dataquery/locale/hi/LC_MESSAGES/dataquery.po @@ -130,16 +130,16 @@ msgid "Show Advanced" msgstr "उन्नत विकल्प दिखाएँ" msgid "The \"nested groups\" options are advanced options for queries that do not have any specific condition at the base of the query. Use Add nested \"or\" condition groups if you need to build a query of the form. (a or b) and (c or d) [or (e and f)..]" -msgstr "\"नेस्टेड समूह\" विकल्प उन क्वेरीज़ के लिए उन्नत विकल्प हैं जिनके आधार में कोई विशिष्ट शर्त नहीं होती। Add nested \"or\" condition groups का उपयोग करें यदि आपको (a या b) और (c या d) [या (e और f)..] जैसी क्वेरी बनानी हो।" +msgstr "\"नेस्टेड समूह\" विकल्प उन क्वेरीज़ के लिए उन्नत विकल्प हैं जिनके आधार में कोई विशिष्ट शर्त नहीं होती। नेस्टेड \"या\" शर्त समूह जोड़ें का उपयोग करें यदि आपको (a या b) और (c या d) [या (e और f)..] जैसी क्वेरी बनानी हो।" msgid "Add nested \"or\" condition groups" -msgstr "नेस्टेड \"or\" शर्त समूह जोड़ें" +msgstr "नेस्टेड \"या\" शर्त समूह जोड़ें" msgid "Use Add nested \"and\" condition groups if you need to build a query of the form (a and b) or (c and d) [or (e and f)..]" -msgstr "यदि आपको (a और b) या (c और d) [या (e और f)..] जैसी क्वेरी बनानी है तो Add nested \"and\" condition groups का उपयोग करें।" +msgstr "यदि आपको (a और b) या (c और d) [या (e और f)..] जैसी क्वेरी बनानी है तो नेस्टेड \"और\" शर्त समूह जोड़ें का उपयोग करें।" msgid "Add nested \"and\" condition groups" -msgstr "नेस्टेड \"and\" शर्त समूह जोड़ें" +msgstr "नेस्टेड \"और\" शर्त समूह जोड़ें" msgid "Currently querying for ALL candidates." msgstr "वर्तमान में सभी उम्मीदवारों के लिए क्वेरी की जा रही है।" @@ -148,10 +148,10 @@ msgid "You can add conditions by clicking one of the buttons below." msgstr "आप नीचे दिए गए बटनों में से किसी पर क्लिक करके शर्तें जोड़ सकते हैं।" msgid "Click Add Condition to add one or more conditions to your filters (ie. \"Date Of Birth < 2015-02-15\"). This is most likely where you want to start your filters." -msgstr "फ़िल्टर में एक या अधिक शर्तें जोड़ने के लिए Add Condition पर क्लिक करें (जैसे \"जन्म तिथि < 2015-02-15\")। संभवतः यही वह जगह है जहाँ से आप अपने फ़िल्टर शुरू करना चाहेंगे।" +msgstr "फ़िल्टर में एक या अधिक शर्तें जोड़ने के लिए 'शर्त जोड़ें' पर क्लिक करें (जैसे \"जन्म तिथि < 2015-02-15\")। संभवतः यही वह जगह है जहाँ से आप अपने फ़िल्टर शुरू करना चाहेंगे।" msgid "You can also import a population from a CSV by clicking the Import from CSV button." -msgstr "आप Import from CSV बटन पर क्लिक करके CSV से जनसंख्या भी आयात कर सकते हैं।" +msgstr "आप 'CSV से आयात करें' बटन पर क्लिक करके CSV से जनसंख्या भी आयात कर सकते हैं।" msgid "The advanced options are for queries that do not have a condition to add at the base of the query." msgstr "उन्नत विकल्प उन क्वेरीज़ के लिए हैं जिनके आधार में कोई शर्त जोड़ने के लिए नहीं है।" @@ -396,6 +396,15 @@ msgstr "फ़ील्ड्स" msgid "Filters" msgstr "फ़िल्टर्स" +msgid "Define Filters" +msgstr "फ़िल्टर निर्धारित करें" + +msgid "Define Fields" +msgstr "फ़ील्ड निर्धारित करें" + +msgid "Reload query" +msgstr "क्वेरी पुनः लोड करें" + msgid "Continue to Define Fields" msgstr "फ़ील्ड परिभाषित करना जारी रखें"