diff --git a/classes/migration/upgrade/v3_5_0/I9425_SeparateUIAndSubmissionLocales.php b/classes/migration/upgrade/v3_5_0/I9425_SeparateUIAndSubmissionLocales.php new file mode 100644 index 00000000000..4a95bd2c795 --- /dev/null +++ b/classes/migration/upgrade/v3_5_0/I9425_SeparateUIAndSubmissionLocales.php @@ -0,0 +1,31 @@ +getSupportedSubmissionLocales(); - $primaryLocale = $submission->getLocale(); + $primaryLocale = $submission->getData('locale'); // Ensure that the specified section exists $section = null; @@ -77,6 +76,7 @@ public function validate($publication, array $props, Submission $submission, Con } // Check the word count on abstracts + $allowedLocales = $submission->getPublicationLanguages($context->getSupportedSubmissionMetadataLocales()); foreach ($allowedLocales as $localeKey) { if (empty($props['abstract'][$localeKey])) { continue; diff --git a/classes/publication/maps/Schema.php b/classes/publication/maps/Schema.php index e01c6337286..d8d813f48ce 100644 --- a/classes/publication/maps/Schema.php +++ b/classes/publication/maps/Schema.php @@ -49,7 +49,9 @@ protected function mapByProperties(array $props, Publication $publication, bool ); } - $output = $this->schemaService->addMissingMultilingualValues(PKPSchemaService::SCHEMA_PUBLICATION, $output, $this->context->getSupportedSubmissionLocales()); + $locales = $this->submission->getPublicationLanguages($this->context->getSupportedSubmissionMetadataLocales()); + + $output = $this->schemaService->addMissingMultilingualValues(PKPSchemaService::SCHEMA_PUBLICATION, $output, $locales); ksort($output); diff --git a/classes/submission/Repository.php b/classes/submission/Repository.php index 579c0c9a536..10d6ee5ac46 100644 --- a/classes/submission/Repository.php +++ b/classes/submission/Repository.php @@ -80,7 +80,8 @@ public function validateSubmit(Submission $submission, Context $context): array $abstracts = $publication->getData('abstract'); if ($abstracts) { $abstractErrors = []; - foreach ($context->getSupportedSubmissionLocales() as $localeKey) { + $allowedLocales = $submission->getPublicationLanguages($context->getSupportedSubmissionMetadataLocales()); + foreach ($allowedLocales as $localeKey) { $abstract = $publication->getData('abstract', $localeKey); $wordCount = $abstract ? PKPString::getWordCount($abstract) : 0; if ($wordCount > $section->getAbstractWordCount()) { diff --git a/classes/submission/maps/Schema.php b/classes/submission/maps/Schema.php index 272fda89697..12f2097c384 100644 --- a/classes/submission/maps/Schema.php +++ b/classes/submission/maps/Schema.php @@ -36,7 +36,13 @@ protected function mapByProperties(array $props, Submission $submission): array ); } - $output = $this->schemaService->addMissingMultilingualValues($this->schemaService::SCHEMA_SUBMISSION, $output, $this->context->getSupportedSubmissionLocales()); + $locales = $this->context->getSupportedSubmissionMetadataLocales(); + + if (!in_array($primaryLocale = $submission->getData('locale'), $locales)) { + $locales[] = $primaryLocale; + } + + $output = $this->schemaService->addMissingMultilingualValues($this->schemaService::SCHEMA_SUBMISSION, $output, $locales); ksort($output); diff --git a/controllers/grid/articleGalleys/form/ArticleGalleyForm.php b/controllers/grid/articleGalleys/form/ArticleGalleyForm.php index 0ad5450ca89..8896f7d741e 100644 --- a/controllers/grid/articleGalleys/form/ArticleGalleyForm.php +++ b/controllers/grid/articleGalleys/form/ArticleGalleyForm.php @@ -62,7 +62,7 @@ public function __construct($request, $submission, $publication, $articleGalley $this->addCheck(new \PKP\form\validation\FormValidatorCSRF($this)); // Ensure a locale is provided and valid - $journal = $request->getJournal(); + $locales = $request->getJournal()->getSupportedSubmissionMetadataLocaleNames() + $submission->getPublicationLanguageNames() + ($articleGalley?->getLanguageNames() ?? []); $this->addCheck( new \PKP\form\validation\FormValidator( $this, @@ -70,9 +70,7 @@ public function __construct($request, $submission, $publication, $articleGalley 'required', 'editor.issues.galleyLocaleRequired' ), - function ($locale) use ($journal) { - return in_array($locale, $journal->getSupportedSubmissionLocaleNames()); - } + fn ($locale) => in_array($locale, $locales) ); } @@ -93,9 +91,12 @@ public function fetch($request, $template = null, $display = false) 'supportsDependentFiles' => $articleGalleyFile ? Repo::submissionFile()->supportsDependentFiles($articleGalleyFile) : null, ]); } - $context = $request->getContext(); + + $supportedLocales = $request->getContext()->getSupportedSubmissionMetadataLocaleNames() + $this->_submission->getPublicationLanguageNames() + ($this->_articleGalley?->getLanguageNames() ?? []); + ksort($supportedLocales); + $templateMgr->assign([ - 'supportedLocales' => $context->getSupportedSubmissionLocaleNames(), + 'supportedLocales' => $supportedLocales, 'submissionId' => $this->_submission->getId(), 'publicationId' => $this->_publication->getId(), 'formDisabled' => !$this->_isEditable diff --git a/cypress/tests/data/10-ApplicationSetup/20-CreateContext.cy.js b/cypress/tests/data/10-ApplicationSetup/20-CreateContext.cy.js index feac3e8ca6d..4d45190b0fb 100644 --- a/cypress/tests/data/10-ApplicationSetup/20-CreateContext.cy.js +++ b/cypress/tests/data/10-ApplicationSetup/20-CreateContext.cy.js @@ -70,8 +70,13 @@ describe('Data suite tests', function() { cy.get('#appearance [role="status"]').contains('Saved'); cy.get('button[id="languages-button"]').click(); - cy.get('input[id^=select-cell-fr_CA-submissionLocale]').click(); - cy.contains('Locale settings saved.'); + cy.get('input[id^="select-cell-fr_CA-formLocale"]').click(); + cy.get('a[id^=component-grid-settings-languages-submissionlanguagegrid-addLanguageModal-button]').click(); + cy.get('#locale-fr_CA').should('exist').click(); + cy.get('#addLanguageForm button[name="submitFormButton"]').click(); + cy.contains('Submission locales updated.').should('exist'); + cy.get('input[id^="select-cell-fr_CA-submissionLocale"]').click(); + cy.get('input[id^="select-cell-fr_CA-submissionMetadataLocale"]').should('be.checked'); cy.get('button[id="indexing-button"]').click(); cy.get('input[name="searchDescription-en"]').type(Cypress.env('contextDescriptions')['en'], {delay: 0}); diff --git a/cypress/tests/data/60-content/AmwandengaSubmission.cy.js b/cypress/tests/data/60-content/AmwandengaSubmission.cy.js index a9c5fc14a45..a6de75ba6ba 100644 --- a/cypress/tests/data/60-content/AmwandengaSubmission.cy.js +++ b/cypress/tests/data/60-content/AmwandengaSubmission.cy.js @@ -224,7 +224,7 @@ describe('Data suite: Amwandenga', function() { .find('h4').contains('Keywords').siblings('.submissionWizard__reviewPanel__item__value').contains('None provided') .parents('.submissionWizard__reviewPanel') .find('h4').contains('Abstract').siblings('.submissionWizard__reviewPanel__item__value').contains(submission.abstract); - cy.get('h3').contains('Details (French)') + cy.get('h3').contains('Details (French (Canada))') .parents('.submissionWizard__reviewPanel') .find('h4').contains('Title').siblings('.submissionWizard__reviewPanel__item__value').contains('None provided') .parents('.submissionWizard__reviewPanel') @@ -234,7 +234,7 @@ describe('Data suite: Amwandenga', function() { cy.get('h3').contains('For the Editors (English)') .parents('.submissionWizard__reviewPanel') .find('h4').contains('Comments for the Editor').siblings('.submissionWizard__reviewPanel__item__value').contains('None'); - cy.get('h3').contains('For the Editors (French)') // FIXME: Should be French + cy.get('h3').contains('For the Editors (French (Canada))') // Save for later cy.get('button').contains('Save for Later').click(); diff --git a/cypress/tests/integration/SubmissionWizard.cy.js b/cypress/tests/integration/SubmissionWizard.cy.js index 1011d67375e..6b9e000ebd0 100644 --- a/cypress/tests/integration/SubmissionWizard.cy.js +++ b/cypress/tests/integration/SubmissionWizard.cy.js @@ -479,20 +479,20 @@ describe('Submission Wizard', function() { cy.get('label:contains("Abstract *")'); cy.contains('Word Count: 0/500'); - // Change submission language to French and section to Reviews + // Change submission language to French (Canada) and section to Reviews cy.contains('Submitting to the Articles section in English'); cy.get('button:contains("Change")').click(); cy.get('h2:contains("Change Submission Settings")') .parents('.modal') .within(() => { - cy.get('label:contains("French")').click(); + cy.get('label:contains("French (Canada)")').click(); cy.get('label:contains("Reviews")').click(); cy.get('button:contains("Save")').click(); }); - // Forms load with French fields displayed instead of English - cy.contains('Submitting to the Reviews section in French'); - cy.get('span.pkpFormLocales__locale:contains("French")'); + // Forms load with French (Canada) fields displayed instead of English + cy.contains('Submitting to the Reviews section in French (Canada)'); + cy.get('span.pkpFormLocales__locale:contains("French (Canada)")'); cy.get('#titleAbstract-keywords-control-fr_CA').type('Transformation Sociale', {delay: 0}); cy.get('li:contains("Transformation Sociale")'); cy.get('#titleAbstract-keywords-control-fr_CA').type('{downarrow}{enter}', {delay: 0}); @@ -502,7 +502,7 @@ describe('Submission Wizard', function() { cy.get('label:contains("Abstract *")').should('not.exist'); cy.get('*:contains("Word Count: 0/500")').should('not.exist'); - // Show English fields alongside French fields + // Show English fields alongside French (Canada) fields cy.get('.pkpStep:contains("Submission Details") button.pkpFormLocales__locale:contains("English")').click(); cy.get('label:contains("Title in English")'); cy.get('label:contains("Keywords in English")'); @@ -523,7 +523,7 @@ describe('Submission Wizard', function() { cy.get('button:contains("Continue")').click(); cy.get('button:contains("Continue")').click(); - // Check metadata form shows in French only at first + // Check metadata form shows in French (Canada) only at first const metadata = { subjects: "Subjects", disciplines: "Disciplines", @@ -539,7 +539,7 @@ describe('Submission Wizard', function() { cy.get('label:contains("' + metadata[prop] + ' in English")').should('not.be.visible'); }); - // Show English fields alongside French fields + // Show English fields alongside French (Canada) fields cy.get('.pkpStep:contains("For the Editors") button.pkpFormLocales__locale:contains("English")').click(); Object.keys(metadata).forEach((prop) => { cy.get('label:contains("' + metadata[prop] + ' in English")'); @@ -551,13 +551,13 @@ describe('Submission Wizard', function() { // Errors in review cy.get('button:contains("Continue")').click(); cy.contains('There are one or more problems'); - cy.get('h3:contains("Details (French)")') + cy.get('h3:contains("Details (French (Canada))")') .parents('.submissionWizard__reviewPanel') .find('h4:contains("Title")') .parent() .contains('This field is required.'); - cy.contains('The given name is missing in French for one or more of the contributors.'); - cy.get('h3:contains("For the Editors (French)")') + cy.contains('The given name is missing in French (Canada) for one or more of the contributors.'); + cy.get('h3:contains("For the Editors (French (Canada))")') .parents('.submissionWizard__reviewPanel') .find('h4:contains("Subjects")') .parent() diff --git a/dbscripts/xml/upgrade.xml b/dbscripts/xml/upgrade.xml index a1ea787c3b2..5a437cc85e6 100644 --- a/dbscripts/xml/upgrade.xml +++ b/dbscripts/xml/upgrade.xml @@ -229,6 +229,7 @@ + diff --git a/pages/submission/SubmissionHandler.php b/pages/submission/SubmissionHandler.php index 6485286889c..a2718152293 100644 --- a/pages/submission/SubmissionHandler.php +++ b/pages/submission/SubmissionHandler.php @@ -94,7 +94,7 @@ protected function getSubmittingTo(Context $context, Submission $submission, arr 'submission.wizard.submittingToSectionInLanguage', [ 'section' => $section->getLocalizedTitle(), - 'language' => Locale::getMetadata($submission->getData('locale'))->getDisplayName(), + 'language' => Locale::getSubmissionLocaleDisplayNames([$submission->getData('locale')])[$submission->getData('locale')], ] ); } elseif ($sectionCount) { @@ -108,7 +108,7 @@ protected function getSubmittingTo(Context $context, Submission $submission, arr return __( 'submission.wizard.submittingInLanguage', [ - 'language' => Locale::getMetadata($submission->getData('locale'))->getDisplayName(), + 'language' => Locale::getMetadata($submission->getData('locale'))->getDisplayName(withCountry: true), ] ); } diff --git a/pages/workflow/WorkflowHandler.php b/pages/workflow/WorkflowHandler.php index 98c9ef4b5c2..d0547e867fb 100644 --- a/pages/workflow/WorkflowHandler.php +++ b/pages/workflow/WorkflowHandler.php @@ -85,11 +85,15 @@ public function setupIndex($request) $submissionContext = Services::get('context')->get($submission->getContextId()); } - $locales = $submissionContext->getSupportedSubmissionLocaleNames(); - $locales = array_map(fn (string $locale, string $name) => ['key' => $locale, 'label' => $name], array_keys($locales), $locales); - $latestPublication = $submission->getLatestPublication(); + $submissionLocale = $submission->getData('locale'); + $locales = collect($submissionContext->getSupportedSubmissionMetadataLocaleNames() + $submission->getPublicationLanguageNames()) + ->map(fn (string $name, string $locale) => ['key' => $locale, 'label' => $name]) + ->sortBy('key') + ->values() + ->toArray(); + $latestPublicationApiUrl = $request->getDispatcher()->url($request, Application::ROUTE_API, $submissionContext->getPath(), 'submissions/' . $submission->getId() . '/publications/' . $latestPublication->getId()); $temporaryFileApiUrl = $request->getDispatcher()->url($request, Application::ROUTE_API, $submissionContext->getPath(), 'temporaryFiles'); $issueApiUrl = $request->getDispatcher()->url($request, Application::ROUTE_API, $submissionContext->getData('urlPath'), 'issues/__issueId__'); @@ -113,7 +117,7 @@ class_exists(\APP\components\forms\publication\AssignToIssueForm::class); // For ]); $components = $templateMgr->getState('components'); - $components[FORM_ISSUE_ENTRY] = $issueEntryForm->getConfig(); + $components[FORM_ISSUE_ENTRY] = $this->getLocalizedForm($issueEntryForm, $submissionLocale, $locales); $canEditPublication = Repo::submission()->canEditPublication($submission->getId(), $request->getUser()->getId()); diff --git a/schemas/galley.json b/schemas/galley.json index b0aac2e72c5..6959d0ba61e 100644 --- a/schemas/galley.json +++ b/schemas/galley.json @@ -48,7 +48,7 @@ "description": "The primary locale of this galley.", "apiSummary": true, "validation": [ - "regex:/^([a-z]{2})((_[A-Z]{2})?)(@[a-z]{0,})?$/" + "regex:/^([A-Za-z]{2,4})([_-]([A-Za-z]{4,5}|[0-9]{3,4}))?([_-]([A-Za-z]{2}|[0-9]{3}))?(@[A-Za-z_]{2,30})?$/" ] }, "label": {