Skip to content

Commit cf25194

Browse files
committed
pkp#9425 Make submission language selection and metadata forms independent from website language settings
1 parent 369166c commit cf25194

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1041
-143
lines changed

api/v1/submissions/PKPSubmissionFileController.php

+8-8
Original file line numberDiff line numberDiff line change
@@ -320,12 +320,12 @@ public function add(Request $illuminateRequest): JsonResponse
320320
$params['submissionId'] = $submission->getId();
321321
$params['uploaderUserId'] = (int) $request->getUser()->getId();
322322

323-
$primaryLocale = $request->getContext()->getPrimaryLocale();
324-
$allowedLocales = $request->getContext()->getData('supportedSubmissionLocales');
323+
$submissionLocale = $submission->getData('locale');
324+
$allowedLocales = $request->getContext()->getSupportedSubmissionMetadataLocales();
325325

326326
// Set the name if not passed with the request
327327
if (empty($params['name'])) {
328-
$params['name'][$primaryLocale] = $_FILES['file']['name'];
328+
$params['name'][$submissionLocale] = $_FILES['file']['name'];
329329
}
330330

331331
// If no genre has been set and there is only one genre possible, set it automatically
@@ -344,7 +344,7 @@ public function add(Request $illuminateRequest): JsonResponse
344344
null,
345345
$params,
346346
$allowedLocales,
347-
$primaryLocale
347+
$submissionLocale
348348
);
349349

350350
if (!empty($errors)) {
@@ -430,15 +430,15 @@ public function edit(Request $illuminateRequest): JsonResponse
430430
], Response::HTTP_BAD_REQUEST);
431431
}
432432

433-
$primaryLocale = $request->getContext()->getPrimaryLocale();
434-
$allowedLocales = $request->getContext()->getData('supportedSubmissionLocales');
433+
$submissionLocale = $submission->getData('locale');
434+
$allowedLocales = $request->getContext()->getSupportedSubmissionMetadataLocales();
435435

436436
$errors = Repo::submissionFile()
437437
->validate(
438438
$submissionFile,
439439
$params,
440440
$allowedLocales,
441-
$primaryLocale
441+
$submissionLocale
442442
);
443443

444444
if (!empty($errors)) {
@@ -466,7 +466,7 @@ public function edit(Request $illuminateRequest): JsonResponse
466466
$params['fileId'] = $fileId;
467467
$params['uploaderUserId'] = $request->getUser()->getId();
468468
if (empty($params['name'])) {
469-
$params['name'][$primaryLocale] = $_FILES['file']['name'];
469+
$params['name'][$submissionLocale] = $_FILES['file']['name'];
470470
}
471471
}
472472

api/v1/vocabs/PKPVocabController.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
namespace PKP\API\v1\vocabs;
1919

2020
use APP\core\Application;
21+
use APP\facades\Repo;
2122
use Illuminate\Http\JsonResponse;
2223
use Illuminate\Http\Request;
2324
use Illuminate\Http\Response;
@@ -106,8 +107,9 @@ public function getMany(Request $illuminateRequest): JsonResponse
106107
$vocab = $requestParams['vocab'] ?? '';
107108
$locale = $requestParams['locale'] ?? Locale::getLocale();
108109
$term = $requestParams['term'] ?? null;
110+
$locales = array_merge($context->getSupportedSubmissionMetadataLocales(), isset($requestParams['submissionId']) ? Repo::submission()->get((int) $requestParams['submissionId'])?->getPublicationLanguages() ?? [] : []);
109111

110-
if (!in_array($locale, $context->getData('supportedSubmissionLocales'))) {
112+
if (!in_array($locale, $locales)) {
111113
return response()->json([
112114
'error' => __('api.vocabs.400.localeNotSupported', ['locale' => $locale]),
113115
], Response::HTTP_BAD_REQUEST);

classes/author/Repository.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ public function getSchemaMap(): maps\Schema
9999
public function validate($author, $props, Submission $submission, Context $context)
100100
{
101101
$schemaService = Services::get('schema');
102-
$allowedLocales = $context->getSupportedSubmissionLocales();
103102
$primaryLocale = $submission->getData('locale');
103+
$allowedLocales = $submission->getPublicationLanguages($context->getSupportedSubmissionMetadataLocales());
104104

105105
$validator = ValidatorFactory::make(
106106
$props,

classes/author/maps/Schema.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ protected function mapByProperties(array $props, Author $item): array
106106
}
107107
}
108108

109-
$output = $this->schemaService->addMissingMultilingualValues($this->schema, $output, $this->context->getSupportedSubmissionLocales());
109+
$locales = Repo::submission()->get(Repo::publication()->get($item->getData('publicationId'))->getData('submissionId'))->getPublicationLanguages($this->context->getSupportedSubmissionMetadataLocales());
110+
111+
$output = $this->schemaService->addMissingMultilingualValues($this->schema, $output, $locales);
110112

111113
ksort($output);
112114

classes/components/listPanels/ContributorsListPanel.php

+8-6
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,22 @@ protected function getPublicationUrlFormat(): string
107107
*/
108108
protected function getLocalizedForm(): array
109109
{
110-
uksort($this->locales, fn ($a, $b) => $a === $this->submission->getData('locale') ? -1 : 1);
111-
112110
$apiUrl = Application::get()->getRequest()->getDispatcher()->url(
113111
Application::get()->getRequest(),
114112
Application::ROUTE_API,
115113
$this->context->getPath(),
116114
'submissions/' . $this->submission->getId() . '/publications/__publicationId__/contributors'
117115
);
118116

119-
$form = $this->getForm($apiUrl);
117+
$submissionLocale = $this->submission->getData('locale');
118+
$data = $this->getForm($apiUrl)->getConfig();
120119

121-
$data = $form->getConfig();
122-
$data['primaryLocale'] = $this->submission->getData('locale');
123-
$data['visibleLocales'] = [$this->submission->getData('locale')];
120+
$data['primaryLocale'] = $submissionLocale;
121+
$data['visibleLocales'] = [$submissionLocale];
122+
$data['supportedFormLocales'] = collect($this->locales)
123+
->sortBy([fn (array $a, array $b) => $b['key'] === $submissionLocale ? 1 : -1])
124+
->values()
125+
->toArray();
124126

125127
return $data;
126128
}

classes/context/Context.php

+51-6
Original file line numberDiff line numberDiff line change
@@ -360,14 +360,10 @@ public function getSupportedSubmissionLocales()
360360
/**
361361
* Return associative array of all locales supported by submissions on the
362362
* context.
363-
*
364-
* @param int $langLocaleStatus The const value of one of LocaleMetadata:LANGUAGE_LOCALE_*
365-
*
366-
* @return array
367363
*/
368-
public function getSupportedSubmissionLocaleNames(int $langLocaleStatus = LocaleMetadata::LANGUAGE_LOCALE_WITHOUT)
364+
public function getSupportedSubmissionLocaleNames(): array
369365
{
370-
return $this->getData('supportedSubmissionLocaleNames') ?? Locale::getFormattedDisplayNames($this->getSupportedSubmissionLocales(), null, $langLocaleStatus);
366+
return $this->getData('supportedSubmissionLocaleNames') ?? Locale::getSubmissionLocaleDisplayNames($this->getSupportedSubmissionLocales());
371367
}
372368

373369
/**
@@ -393,6 +389,55 @@ public function getSupportedLocaleNames(int $langLocaleStatus = LocaleMetadata::
393389
return $this->getData('supportedLocaleNames') ?? Locale::getFormattedDisplayNames($this->getSupportedLocales(), null, $langLocaleStatus);
394390
}
395391

392+
/**
393+
* Get the supported added submission locales.
394+
*/
395+
public function getSupportedAddedSubmissionLocales(): array
396+
{
397+
return $this->getData('supportedAddedSubmissionLocales');
398+
}
399+
400+
/**
401+
* Return associative array of added locales supported by submissions on the
402+
* context.
403+
*/
404+
public function getSupportedAddedSubmissionLocaleNames(): array
405+
{
406+
return Locale::getSubmissionLocaleDisplayNames($this->getSupportedAddedSubmissionLocales());
407+
}
408+
409+
/**
410+
* Get the supported default submission locale.
411+
*/
412+
public function getSupportedDefaultSubmissionLocale(): string
413+
{
414+
return $this->getData('supportedDefaultSubmissionLocale');
415+
}
416+
417+
/**
418+
* Return string default submission locale supported by the site.
419+
*/
420+
public function getSupportedDefaultSubmissionLocaleName(): string
421+
{
422+
return Locale::getSubmissionLocaleDisplayNames([$l = $this->getSupportedDefaultSubmissionLocale()])[$l];
423+
}
424+
425+
/**
426+
* Get the supported metadata locales.
427+
*/
428+
public function getSupportedSubmissionMetadataLocales(): array
429+
{
430+
return $this->getData('supportedSubmissionMetadataLocales');
431+
}
432+
433+
/**
434+
* Return associative array of all locales supported by submission metadata forms on the site.
435+
*/
436+
public function getSupportedSubmissionMetadataLocaleNames(): array
437+
{
438+
return Locale::getSubmissionLocaleDisplayNames($this->getSupportedSubmissionMetadataLocales());
439+
}
440+
396441
/**
397442
* Return date or/and time formats available for forms, fallback to the default if not set
398443
*

classes/dev/ComposerScript.php

+41
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,45 @@ public static function isoFileCheck(): void
3232
throw new Exception("The ISO639-2b file {$iso6392bFile} does not exist.");
3333
}
3434
}
35+
36+
/**
37+
* A post-install-cmd custom composer script that
38+
* creates languages.json from downloaded Weblate languages.csv.
39+
*/
40+
public static function weblateFilesDownload(): void
41+
{
42+
try {
43+
$dirPath = dirname(__FILE__, 3) . "/lib/weblateLanguages";
44+
$urlCsvFilePath = "$dirPath/urlCsv.txt";
45+
$langFilePath = "$dirPath/languages.json";
46+
47+
$urlCsv = !file_exists($urlCsvFilePath) ?: file_get_contents($urlCsvFilePath);
48+
if (!is_string($urlCsv)) {
49+
throw new Exception(__METHOD__ . " : File $urlCsvFilePath does not exist !");
50+
}
51+
52+
$streamContext = stream_context_create(['http' => ['method' => 'HEAD']]);
53+
$languagesCsv = !str_contains(get_headers($urlCsv, context: $streamContext)[0] ?? "", '200 OK') ?: file($urlCsv, FILE_SKIP_EMPTY_LINES);
54+
if (!is_array($languagesCsv) || empty(count($languagesCsv))) {
55+
throw new Exception(__METHOD__ . " : The Weblate file 'languages.csv' cannot be downloaded !");
56+
}
57+
58+
array_shift($languagesCsv);
59+
$languages = [];
60+
foreach($languagesCsv as $languageCsv) {
61+
$localeAndName = str_getcsv($languageCsv, ",");
62+
if (isset($localeAndName[0], $localeAndName[1]) && preg_match('/^[\w@-]{2,50}$/', $localeAndName[0]) === 1) {
63+
$displayName = locale_get_display_name($localeAndName[0], 'en');
64+
$languages[$localeAndName[0]] = (($displayName && $displayName !== $localeAndName[0]) ? $displayName : $localeAndName[1]);
65+
}
66+
}
67+
68+
$languagesJson = json_encode($languages, JSON_THROW_ON_ERROR);
69+
if (empty($languagesJson) || !file_put_contents($langFilePath, $languagesJson)) {
70+
throw new Exception(__METHOD__ . " : Json file empty, or save unsuccessful: $langFilePath !");
71+
}
72+
} catch (Exception $e) {
73+
error_log($e->getMessage());
74+
}
75+
}
3576
}

classes/galley/Galley.php

+27
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616

1717
namespace PKP\galley;
1818

19+
use APP\core\Services;
1920
use APP\facades\Repo;
2021
use PKP\facades\Locale;
22+
use PKP\i18n\LocaleMetadata;
23+
use PKP\services\PKPSchemaService;
2124
use PKP\submission\Representation;
2225
use PKP\submissionFile\SubmissionFile;
2326

@@ -183,6 +186,30 @@ public function setStoredPubId($pubIdType, $pubId)
183186
parent::setStoredPubId($pubIdType, $pubId);
184187
}
185188
}
189+
190+
/**
191+
* Get metadata language names
192+
*/
193+
public function getLanguageNames(): array
194+
{
195+
return Locale::getSubmissionLocaleDisplayNames($this->getLanguages());
196+
}
197+
198+
/**
199+
* Get metadata languages
200+
*/
201+
public function getLanguages(): array
202+
{
203+
$props = Services::get('schema')->getMultilingualProps(PKPSchemaService::SCHEMA_GALLEY);
204+
$locales = array_map(fn (string $prop): array => array_keys($this->getData($prop) ?? []), $props);
205+
return collect([$this->getData('locale')])
206+
->concat($locales)
207+
->flatten()
208+
->filter()
209+
->unique()
210+
->values()
211+
->toArray();
212+
}
186213
}
187214

188215
if (!PKP_STRICT_MODE) {

classes/galley/maps/Schema.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,9 @@ protected function mapByProperties(array $props, Galley $galley): array
140140
}
141141
}
142142

143-
$output = $this->schemaService->addMissingMultilingualValues($this->schema, $output, $this->context->getSupportedFormLocales());
143+
$locales = $this->publication->getLanguages($this->context->getSupportedSubmissionMetadataLocales(), $galley->getLanguages());
144+
145+
$output = $this->schemaService->addMissingMultilingualValues($this->schema, $output, $locales);
144146

145147
ksort($output);
146148

classes/i18n/Locale.php

+69
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Illuminate\Support\Facades\Cache;
3030
use InvalidArgumentException;
3131
use PKP\config\Config;
32+
use PKP\core\Core;
3233
use PKP\core\PKPRequest;
3334
use PKP\facades\Repo;
3435
use PKP\i18n\interfaces\LocaleInterface;
@@ -95,6 +96,9 @@ class Locale implements LocaleInterface
9596
/** Keeps cached data related only to the current locale */
9697
protected array $cache = [];
9798

99+
/** @var string[]|null Available submission locales cache, where key = locale and value = name */
100+
protected ?array $submissionLocaleNames = null;
101+
98102
/**
99103
* @copy \Illuminate\Contracts\Translation\Translator::get()
100104
*
@@ -204,6 +208,14 @@ public function isLocaleValid(?string $locale): bool
204208
return !empty($locale) && preg_match(LocaleInterface::LOCALE_EXPRESSION, $locale);
205209
}
206210

211+
/**
212+
* @copy LocaleInterface::isSubmissionLocaleValid()
213+
*/
214+
public function isSubmissionLocaleValid(?string $locale): bool
215+
{
216+
return !empty($locale) && preg_match(LocaleInterface::LOCALE_EXPRESSION_SUBMISSION, $locale);
217+
}
218+
207219
/**
208220
* @copy LocaleInterface::getMetadata()
209221
*/
@@ -405,6 +417,38 @@ public function getUiTranslator(): UITranslator
405417
return new UITranslator($locale, $this->paths, $localeBundleCacheKey);
406418
}
407419

420+
/**
421+
* Get appropriately localized display names for submission locales to array
422+
* If $filterByLocales empty, return all languages.
423+
* Add '*' (= in English) to display name if no translation available
424+
*
425+
* @param array $filterByLocales Optional list of locale codes/code-name-pairs to filter
426+
* @param ?string $displayLocale Optional display locale
427+
*
428+
* @return array The list of locales with formatted display name
429+
*/
430+
public function getSubmissionLocaleDisplayNames(array $filterByLocales = [], ?string $displayLocale = null): array
431+
{
432+
$convDispLocale = $this->convertSubmissionLocaleCode($displayLocale ?: $this->getLocale());
433+
return collect($this->_getSubmissionLocaleNames())
434+
->when(!empty(count($filterByLocales)),
435+
fn ($sln) => $sln->intersectByKeys(array_is_list($filterByLocales) ? array_flip(array_filter($filterByLocales)) : $filterByLocales))
436+
->when($convDispLocale !== 'en', fn ($sln) => $sln->map(function ($nameEn, $l) use ($convDispLocale) {
437+
$cl = $this->convertSubmissionLocaleCode($l);
438+
$dn = locale_get_display_name($cl, $convDispLocale);
439+
return ($dn && $dn !== $cl) ? $dn : "*$nameEn";
440+
}))
441+
->toArray();
442+
}
443+
444+
/**
445+
* Convert submission locale code
446+
*/
447+
public function convertSubmissionLocaleCode(string $locale): string
448+
{
449+
return str_replace(['@cyrillic', '@latin'], ['_Cyrl', '_Latn'], $locale);
450+
}
451+
408452
/**
409453
* Get the filtered locales by locale codes
410454
*
@@ -513,4 +557,29 @@ private function _getSupportedLocales(): array
513557
?? array_map(fn (LocaleMetadata $locale) => $locale->locale, $this->getLocales());
514558
return $this->supportedLocales = array_combine($locales, $locales);
515559
}
560+
561+
/**
562+
* Get Weblate submission languages to array
563+
* Combine app's language names with weblate's in English.
564+
* Weblate's names override app's if same locale key
565+
*
566+
* @return string[]
567+
*/
568+
private function _getSubmissionLocaleNames(): array
569+
{
570+
return $this->submissionLocaleNames ??= (function (): array {
571+
$file = Core::getBaseDir() . '/' . PKP_LIB_PATH . '/lib/weblateLanguages/languages.json';
572+
$key = __METHOD__ . self::MAX_CACHE_LIFETIME . sha1($file);
573+
$expiration = DateInterval::createFromDateString(static::MAX_CACHE_LIFETIME);
574+
return Cache::remember($key, $expiration, fn (): array => collect($this->getLocales())
575+
->map(function (LocaleMetadata $lm, string $l): string {
576+
$cl = $this->convertSubmissionLocaleCode($l);
577+
$n = locale_get_display_name($cl, 'en');
578+
return ($n && $n !== $cl) ? $n : $lm->getDisplayName('en', true);
579+
})
580+
->merge(json_decode(file_get_contents($file), true) ?: [])
581+
->sortKeys()
582+
->toArray());
583+
})();
584+
}
516585
}

0 commit comments

Comments
 (0)