diff --git a/Support/Jobs/BaseJob.php b/Support/Jobs/BaseJob.php
index 3f2973aa069..1574a8af070 100644
--- a/Support/Jobs/BaseJob.php
+++ b/Support/Jobs/BaseJob.php
@@ -17,6 +17,7 @@
namespace PKP\Support\Jobs;
+use APP\core\Application;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -56,7 +57,7 @@ public function __construct()
protected function defaultConnection(): string
{
- if (defined('RUNNING_UPGRADE')) {
+ if (Application::isUnderMaintenance()) {
return 'sync';
}
diff --git a/api/v1/submissions/PKPSubmissionHandler.inc.php b/api/v1/submissions/PKPSubmissionHandler.inc.php
index a23786bdeab..4fdde34d762 100644
--- a/api/v1/submissions/PKPSubmissionHandler.inc.php
+++ b/api/v1/submissions/PKPSubmissionHandler.inc.php
@@ -17,7 +17,6 @@
use APP\core\Application;
use APP\core\Services;
use APP\facades\Repo;
-use APP\i18n\AppLocale;
use APP\notification\Notification;
use APP\notification\NotificationManager;
use APP\submission\Collector;
@@ -379,11 +378,6 @@ protected function getSubmissionCollector(array $queryParams): Collector
*/
public function get($slimRequest, $response, $args)
{
- AppLocale::requireComponents(
- LOCALE_COMPONENT_PKP_READER,
- LOCALE_COMPONENT_PKP_SUBMISSION
- );
-
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$userGroupDao = DAORegistry::getDAO('UserGroupDAO'); /** @var UserGroupDAO $userGroupDao */
@@ -403,8 +397,6 @@ public function get($slimRequest, $response, $args)
*/
public function add($slimRequest, $response, $args)
{
- AppLocale::requireComponents(LOCALE_COMPONENT_APP_AUTHOR);
-
$request = $this->getRequest();
// Don't allow submissions to be added via the site-wide API
@@ -693,7 +685,6 @@ public function addPublication($slimRequest, $response, $args)
public function versionPublication($slimRequest, $response, $args)
{
$request = $this->getRequest();
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_SUBMISSION, LOCALE_COMPONENT_APP_SUBMISSION); // notification.type.submissionNewVersion
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$publication = Repo::publication()->get((int) $args['publicationId']);
@@ -837,8 +828,6 @@ public function publishPublication($slimRequest, $response, $args)
return $response->withStatus(403)->withJsonError('api.publication.403.alreadyPublished');
}
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_SUBMISSION, LOCALE_COMPONENT_APP_SUBMISSION);
-
$submissionContext = $request->getContext();
if (!$submissionContext || $submissionContext->getId() !== $submission->getData('contextId')) {
$submissionContext = Services::get('context')->get($submission->getData('contextId'));
diff --git a/api/v1/users/PKPUserHandler.inc.php b/api/v1/users/PKPUserHandler.inc.php
index 19897f95f0c..c40ee9dc3cd 100644
--- a/api/v1/users/PKPUserHandler.inc.php
+++ b/api/v1/users/PKPUserHandler.inc.php
@@ -15,7 +15,7 @@
*/
use APP\facades\Repo;
-use APP\i18n\AppLocale;
+use PKP\facades\Locale;
use PKP\handler\APIHandler;
use PKP\plugins\HookRegistry;
use PKP\security\authorization\ContextAccessPolicy;
@@ -123,7 +123,7 @@ public function getMany($slimRequest, $response, $args)
->assignedToCategoryIds(isset($params['assignedToCategory']) ? [$params['assignedToCategory']] : null)
->filterByRoleIds($params['roleIds'] ?? null)
->searchPhrase($params['searchPhrase'] ?? null)
- ->orderBy($orderBy, $orderDirection, [AppLocale::getLocale(), $request->getSite()->getPrimaryLocale()])
+ ->orderBy($orderBy, $orderDirection, [Locale::getLocale(), $request->getSite()->getPrimaryLocale()])
->limit($params['count'] ?? null)
->offset($params['offset'] ?? null)
->filterByStatus($params['status'] ?? $collector::STATUS_ALL);
diff --git a/api/v1/vocabs/PKPVocabHandler.inc.php b/api/v1/vocabs/PKPVocabHandler.inc.php
index cbc8167ffb7..28811deca0b 100644
--- a/api/v1/vocabs/PKPVocabHandler.inc.php
+++ b/api/v1/vocabs/PKPVocabHandler.inc.php
@@ -15,7 +15,6 @@
*/
use APP\core\Application;
-use APP\i18n\AppLocale;
use PKP\core\APIResponse;
use PKP\core\PKPString;
use PKP\db\DAORegistry;
@@ -29,8 +28,8 @@
use PKP\submission\SubmissionLanguageDAO;
use PKP\submission\SubmissionSubjectDAO;
use Slim\Http\Request;
-use Sokil\IsoCodes\IsoCodesFactory;
use Stringy\Stringy;
+use PKP\facades\Locale;
class PKPVocabHandler extends APIHandler
{
@@ -77,7 +76,7 @@ public function getMany(Request $slimRequest, APIResponse $response, array $args
$requestParams = $slimRequest->getQueryParams();
$vocab = $requestParams['vocab'] ?? '';
- $locale = $requestParams['locale'] ?? AppLocale::getLocale();
+ $locale = $requestParams['locale'] ?? Locale::getLocale();
$term = $requestParams['term'] ?? null;
if (!in_array($locale, $context->getData('supportedSubmissionLocales'))) {
@@ -98,11 +97,9 @@ public function getMany(Request $slimRequest, APIResponse $response, array $args
$entries = $submissionDisciplineEntryDao->getByContextId($vocab, $context->getId(), $locale, $term)->toArray();
break;
case SubmissionLanguageDAO::CONTROLLED_VOCAB_SUBMISSION_LANGUAGE:
- /** @var IsoCodesFactory */
- $isoCodes = app(IsoCodesFactory::class);
$words = array_filter(PKPString::regexp_split('/\s+/', $term), 'strlen');
$languageNames = [];
- foreach ($isoCodes->getLanguages(IsoCodesFactory::OPTIMISATION_IO) as $language) {
+ foreach (Locale::getLanguages() as $language) {
if ($language->getAlpha2() && $language->getType() === 'L' && $language->getScope() === 'I' && Stringy::create($language->getLocalName())->containsAny($words, false)) {
$languageNames[] = $language->getLocalName();
}
diff --git a/classes/announcement/Repository.inc.php b/classes/announcement/Repository.inc.php
index 4959965c76c..238854f6e44 100644
--- a/classes/announcement/Repository.inc.php
+++ b/classes/announcement/Repository.inc.php
@@ -14,7 +14,6 @@
namespace PKP\announcement;
use APP\core\Request;
-use APP\i18n\AppLocale;
use Illuminate\Support\Collection;
use Illuminate\Support\LazyCollection;
use PKP\core\Core;
@@ -106,11 +105,6 @@ public function getSchemaMap(): maps\Schema
*/
public function validate(?Announcement $object, array $props, array $allowedLocales, string $primaryLocale): array
{
- AppLocale::requireComponents(
- LOCALE_COMPONENT_PKP_MANAGER,
- LOCALE_COMPONENT_APP_MANAGER
- );
-
$validator = ValidatorFactory::make(
$props,
$this->schemaService->getValidationRules($this->dao->schema, $allowedLocales),
diff --git a/classes/author/Author.inc.php b/classes/author/Author.inc.php
index 705c9b83284..eb62a102456 100644
--- a/classes/author/Author.inc.php
+++ b/classes/author/Author.inc.php
@@ -17,7 +17,7 @@
namespace PKP\author;
-use APP\i18n\AppLocale;
+use PKP\facades\Locale;
use PKP\db\DAORegistry;
use PKP\identity\Identity;
@@ -34,7 +34,7 @@ class Author extends Identity
public function &getLocalizedData($key, $preferredLocale = null)
{
if (is_null($preferredLocale)) {
- $preferredLocale = AppLocale::getLocale();
+ $preferredLocale = Locale::getLocale();
}
$localePrecedence = [$preferredLocale];
// the submission locale is the default locale
@@ -43,8 +43,8 @@ public function &getLocalizedData($key, $preferredLocale = null)
}
// for settings other than givenName, familyName and affiliation (that are required)
// consider also the application primary locale
- if (!in_array(AppLocale::getPrimaryLocale(), $localePrecedence)) {
- $localePrecedence[] = AppLocale::getPrimaryLocale();
+ if (!in_array(Locale::getPrimaryLocale(), $localePrecedence)) {
+ $localePrecedence[] = Locale::getPrimaryLocale();
}
foreach ($localePrecedence as $locale) {
if (empty($locale)) {
diff --git a/classes/category/Repository.inc.php b/classes/category/Repository.inc.php
index 13ed8591473..dbb6aa8dffb 100644
--- a/classes/category/Repository.inc.php
+++ b/classes/category/Repository.inc.php
@@ -14,7 +14,6 @@
namespace PKP\category;
use APP\core\Request;
-use APP\i18n\AppLocale;
use Illuminate\Support\Collection;
use Illuminate\Support\LazyCollection;
use PKP\plugins\HookRegistry;
@@ -105,11 +104,6 @@ public function getSchemaMap(): maps\Schema
*/
public function validate(?Announcement $object, array $props, array $allowedLocales, string $primaryLocale): array
{
- AppLocale::requireComponents(
- LOCALE_COMPONENT_PKP_MANAGER,
- LOCALE_COMPONENT_APP_MANAGER
- );
-
$validator = ValidatorFactory::make(
$props,
$this->schemaService->getValidationRules($this->dao->schema, $allowedLocales),
diff --git a/classes/cliTool/CommandLineTool.inc.php b/classes/cliTool/CommandLineTool.inc.php
index 64bc0f97ad2..696c83563a2 100644
--- a/classes/cliTool/CommandLineTool.inc.php
+++ b/classes/cliTool/CommandLineTool.inc.php
@@ -25,6 +25,12 @@
use APP\core\Application;
use APP\core\PageRouter;
+use APP\facades\Repo;
+use PKP\core\Registry;
+use PKP\db\DAORegistry;
+use PKP\plugins\PluginRegistry;
+use PKP\security\Role;
+use PKP\session\SessionManager;
/** Initialization code */
define('PWD', getcwd());
@@ -32,16 +38,8 @@
if (!defined('STDIN')) {
define('STDIN', fopen('php://stdin', 'r'));
}
-define('SESSION_DISABLE_INIT', 1);
-require('./lib/pkp/includes/bootstrap.inc.php');
-
-use APP\facades\Repo;
-use APP\i18n\AppLocale;
-use PKP\core\Registry;
-
-use PKP\db\DAORegistry;
-use PKP\plugins\PluginRegistry;
-use PKP\security\Role;
+require_once './lib/pkp/includes/bootstrap.inc.php';
+SessionManager::disable();
class CommandLineTool
{
@@ -69,7 +67,6 @@ public function __construct($argv = [])
$request->setRouter($router);
// Initialize the locale and load generic plugins.
- AppLocale::initialize($request);
PluginRegistry::loadCategory('generic');
$this->argv = isset($argv) && is_array($argv) ? $argv : [];
diff --git a/classes/cliTool/InstallTool.inc.php b/classes/cliTool/InstallTool.inc.php
index 139c2a0a4e2..a0efad7e7a4 100644
--- a/classes/cliTool/InstallTool.inc.php
+++ b/classes/cliTool/InstallTool.inc.php
@@ -86,8 +86,6 @@ public function readParams()
$this->printTitle('installer.localeSettings');
$this->readParamOptions('locale', 'locale.primary', $installForm->supportedLocales, 'en_US');
$this->readParamOptions('additionalLocales', 'installer.additionalLocales', $installForm->supportedLocales, '', true);
- $this->readParamOptions('clientCharset', 'installer.clientCharset', $installForm->supportedClientCharsets, 'utf-8');
- $this->readParamOptions('connectionCharset', 'installer.connectionCharset', $installForm->supportedConnectionCharsets, '');
// File Settings
$this->printTitle('installer.fileSettings');
@@ -108,7 +106,7 @@ public function readParams()
// Database Settings
$this->printTitle('installer.databaseSettings');
- $this->readParamOptions('databaseDriver', 'installer.databaseDriver', $installForm->checkDBDrivers());
+ $this->readParamOptions('databaseDriver', 'installer.databaseDriver', $installForm->getDatabaseDriversOptions());
$this->readParam('databaseHost', 'installer.databaseHost', '');
$this->readParam('databaseUsername', 'installer.databaseUsername', '');
$this->readParam('databasePassword', 'installer.databasePassword', '');
diff --git a/classes/cliTool/UpgradeTool.inc.php b/classes/cliTool/UpgradeTool.inc.php
index eb652d76cfa..ac9c9a9b06e 100644
--- a/classes/cliTool/UpgradeTool.inc.php
+++ b/classes/cliTool/UpgradeTool.inc.php
@@ -17,14 +17,12 @@
namespace PKP\cliTool;
-define('RUNNING_UPGRADE', 1);
-
use APP\core\Application;
-
-use APP\i18n\AppLocale;
use APP\install\Upgrade;
use PKP\site\VersionCheck;
+Application::upgrade();
+
class UpgradeTool extends \PKP\cliTool\CommandLineTool
{
/** @var string command to execute (check|upgrade|download) */
@@ -37,7 +35,6 @@ class UpgradeTool extends \PKP\cliTool\CommandLineTool
*/
public function __construct($argv = [])
{
- Application::get()->initializeLaravelContainer();
parent::__construct($argv);
if (!isset($this->argv[0]) || !in_array($this->argv[0], ['check', 'latest', 'upgrade', 'download'])) {
@@ -46,7 +43,6 @@ public function __construct($argv = [])
}
$this->command = $this->argv[0];
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_INSTALLER);
}
/**
diff --git a/classes/components/PKPStatsComponent.inc.php b/classes/components/PKPStatsComponent.inc.php
index 9ddd79263da..2e0f8f23967 100644
--- a/classes/components/PKPStatsComponent.inc.php
+++ b/classes/components/PKPStatsComponent.inc.php
@@ -42,9 +42,6 @@ class PKPStatsComponent
*/
public function __construct($apiUrl, $args = [])
{
- \AppLocale::requireComponents(LOCALE_COMPONENT_PKP_MANAGER);
- \AppLocale::requireComponents(LOCALE_COMPONENT_APP_MANAGER);
-
$this->apiUrl = $apiUrl;
$this->init($args);
}
diff --git a/classes/components/forms/FormComponent.inc.php b/classes/components/forms/FormComponent.inc.php
index 4148c6d66cb..06f47fc7750 100644
--- a/classes/components/forms/FormComponent.inc.php
+++ b/classes/components/forms/FormComponent.inc.php
@@ -16,6 +16,7 @@
namespace PKP\components\forms;
use Exception;
+use PKP\facades\Locale;
define('FIELD_POSITION_BEFORE', 'before');
define('FIELD_POSITION_AFTER', 'after');
@@ -278,9 +279,9 @@ public function getConfig()
$fieldsConfig = array_map([$this, 'getFieldConfig'], $this->fields);
- $visibleLocales = [\AppLocale::getLocale()];
- if (\AppLocale::getLocale() !== \AppLocale::getPrimaryLocale()) {
- array_unshift($visibleLocales, \AppLocale::getPrimaryLocale());
+ $visibleLocales = [Locale::getLocale()];
+ if (Locale::getLocale() !== Locale::getPrimaryLocale()) {
+ array_unshift($visibleLocales, Locale::getPrimaryLocale());
}
$config = [
@@ -290,7 +291,7 @@ public function getConfig()
'fields' => $fieldsConfig,
'groups' => $this->groups,
'pages' => $this->pages,
- 'primaryLocale' => \AppLocale::getPrimaryLocale(),
+ 'primaryLocale' => Locale::getPrimaryLocale(),
'visibleLocales' => $visibleLocales,
'supportedFormLocales' => array_values($this->locales), // See #5690
'errors' => (object) [],
diff --git a/classes/components/forms/context/PKPContextForm.inc.php b/classes/components/forms/context/PKPContextForm.inc.php
index a1ca422efdd..3fff9358c34 100644
--- a/classes/components/forms/context/PKPContextForm.inc.php
+++ b/classes/components/forms/context/PKPContextForm.inc.php
@@ -19,7 +19,7 @@
use PKP\components\forms\FieldSelect;
use PKP\components\forms\FieldText;
use PKP\components\forms\FormComponent;
-use Sokil\IsoCodes\IsoCodesFactory;
+use PKP\facades\Locale;
define('FORM_CONTEXT', 'context');
@@ -45,10 +45,8 @@ public function __construct($action, $locales, $baseUrl, $context)
$this->locales = $locales;
$this->method = $context ? 'PUT' : 'POST';
- $isoCodes = app(IsoCodesFactory::class);
-
$countries = [];
- foreach ($isoCodes->getCountries() as $country) {
+ foreach (Locale::getCountries() as $country) {
$countries[] = [
'value' => $country->getAlpha2(),
'label' => $country->getLocalName()
diff --git a/classes/components/forms/context/PKPMastheadForm.inc.php b/classes/components/forms/context/PKPMastheadForm.inc.php
index 52c48e976ff..c6c9819b4d0 100644
--- a/classes/components/forms/context/PKPMastheadForm.inc.php
+++ b/classes/components/forms/context/PKPMastheadForm.inc.php
@@ -18,7 +18,7 @@
use PKP\components\forms\FieldSelect;
use PKP\components\forms\FieldText;
use PKP\components\forms\FormComponent;
-use Sokil\IsoCodes\IsoCodesFactory;
+use PKP\facades\Locale;
define('FORM_MASTHEAD', 'masthead');
@@ -43,9 +43,8 @@ public function __construct($action, $locales, $context, $imageUploadUrl)
$this->action = $action;
$this->locales = $locales;
- $isoCodes = app(IsoCodesFactory::class);
$countries = [];
- foreach ($isoCodes->getCountries() as $country) {
+ foreach (Locale::getCountries() as $country) {
$countries[] = [
'value' => $country->getAlpha2(),
'label' => $country->getLocalName()
diff --git a/classes/components/forms/context/PKPPaymentSettingsForm.inc.php b/classes/components/forms/context/PKPPaymentSettingsForm.inc.php
index 1b169bf9f8d..2ef6f01d387 100644
--- a/classes/components/forms/context/PKPPaymentSettingsForm.inc.php
+++ b/classes/components/forms/context/PKPPaymentSettingsForm.inc.php
@@ -17,8 +17,8 @@
use PKP\components\forms\FieldOptions;
use PKP\components\forms\FieldSelect;
use PKP\components\forms\FormComponent;
-
-use Sokil\IsoCodes\IsoCodesFactory;
+use PKP\facades\Locale;
+use PKP\plugins\PluginRegistry;
define('FORM_PAYMENT_SETTINGS', 'paymentSettings');
@@ -43,8 +43,7 @@ public function __construct($action, $locales, $context)
$this->locales = $locales;
$currencies = [];
- $isoCodes = app(IsoCodesFactory::class);
- foreach ($isoCodes->getCurrencies() as $currency) {
+ foreach (Locale::getCurrencies() as $currency) {
$currencies[] = [
'value' => $currency->getLetterCode(),
'label' => $currency->getLocalName(),
@@ -52,7 +51,7 @@ public function __construct($action, $locales, $context)
}
// Ensure payment method plugins can hook in
- $paymentPlugins = \PluginRegistry::loadCategory('paymethod', true);
+ $paymentPlugins = PluginRegistry::loadCategory('paymethod', true);
$pluginList = [];
foreach ($paymentPlugins as $plugin) {
$pluginList[] = [
diff --git a/classes/components/listPanels/PKPAnnouncementsListPanel.inc.php b/classes/components/listPanels/PKPAnnouncementsListPanel.inc.php
index b2b81088176..a7b32464f30 100644
--- a/classes/components/listPanels/PKPAnnouncementsListPanel.inc.php
+++ b/classes/components/listPanels/PKPAnnouncementsListPanel.inc.php
@@ -36,34 +36,24 @@ class PKPAnnouncementsListPanel extends ListPanel
*/
public function getConfig()
{
- \AppLocale::requireComponents(LOCALE_COMPONENT_PKP_MANAGER);
- \AppLocale::requireComponents(LOCALE_COMPONENT_APP_MANAGER);
$request = \Application::get()->getRequest();
-
- $config = parent::getConfig();
-
- $config = array_merge(
- $config,
- [
- 'addAnnouncementLabel' => __('grid.action.addAnnouncement'),
- 'apiUrl' => $this->apiUrl,
- 'confirmDeleteMessage' => __('manager.announcements.confirmDelete'),
- 'count' => $this->count,
- 'deleteAnnouncementLabel' => __('manager.announcements.deleteAnnouncement'),
- 'editAnnouncementLabel' => __('manager.announcements.edit'),
- 'form' => $this->form->getConfig(),
- 'itemsMax' => $this->itemsMax,
- 'urlBase' => $request->getDispatcher()->url(
- $request,
- \PKPApplication::ROUTE_PAGE,
- $request->getContext()->getPath(),
- 'announcement',
- 'view',
- '__id__'
- )
- ]
- );
-
- return $config;
+ return parent::getConfig() + [
+ 'addAnnouncementLabel' => __('grid.action.addAnnouncement'),
+ 'apiUrl' => $this->apiUrl,
+ 'confirmDeleteMessage' => __('manager.announcements.confirmDelete'),
+ 'count' => $this->count,
+ 'deleteAnnouncementLabel' => __('manager.announcements.deleteAnnouncement'),
+ 'editAnnouncementLabel' => __('manager.announcements.edit'),
+ 'form' => $this->form->getConfig(),
+ 'itemsMax' => $this->itemsMax,
+ 'urlBase' => $request->getDispatcher()->url(
+ $request,
+ \PKPApplication::ROUTE_PAGE,
+ $request->getContext()->getPath(),
+ 'announcement',
+ 'view',
+ '__id__'
+ )
+ ];
}
}
diff --git a/classes/components/listPanels/PKPContributorsListPanel.inc.php b/classes/components/listPanels/PKPContributorsListPanel.inc.php
index e78fc8c36d2..b889b6c01eb 100644
--- a/classes/components/listPanels/PKPContributorsListPanel.inc.php
+++ b/classes/components/listPanels/PKPContributorsListPanel.inc.php
@@ -14,8 +14,6 @@
namespace PKP\components\listPanels;
-use APP\i18n\AppLocale;
-
class PKPContributorsListPanel extends ListPanel
{
/** @param \PKP\components\forms\publication\PKPContributorForm Form for adding or editing a contributor */
@@ -32,9 +30,6 @@ class PKPContributorsListPanel extends ListPanel
*/
public function getConfig()
{
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_MANAGER);
- AppLocale::requireComponents(LOCALE_COMPONENT_APP_MANAGER);
-
$config = parent::getConfig();
// Remove some props not used in this list panel
diff --git a/classes/components/listPanels/PKPDoiListPanel.inc.php b/classes/components/listPanels/PKPDoiListPanel.inc.php
index c681e24e522..a4d83c8999b 100644
--- a/classes/components/listPanels/PKPDoiListPanel.inc.php
+++ b/classes/components/listPanels/PKPDoiListPanel.inc.php
@@ -16,7 +16,6 @@
namespace PKP\components\listPanels;
use APP\core\Application;
-use APP\i18n\AppLocale;
use APP\template\TemplateManager;
use PKP\doi\Doi;
use PKP\plugins\HookRegistry;
@@ -61,14 +60,6 @@ abstract class PKPDoiListPanel extends ListPanel
*/
public function getConfig()
{
- AppLocale::requireComponents(
- [
- LOCALE_COMPONENT_PKP_SUBMISSION,
- LOCALE_COMPONENT_PKP_MANAGER,
- LOCALE_COMPONENT_APP_MANAGER
- ]
- );
-
$config = parent::getConfig();
$config['apiUrl'] = $this->apiUrl;
$config['doiApiUrl'] = $this->doiApiUrl;
diff --git a/classes/components/listPanels/PKPEmailTemplatesListPanel.inc.php b/classes/components/listPanels/PKPEmailTemplatesListPanel.inc.php
index 7023343ebc5..c47ae56a745 100644
--- a/classes/components/listPanels/PKPEmailTemplatesListPanel.inc.php
+++ b/classes/components/listPanels/PKPEmailTemplatesListPanel.inc.php
@@ -54,10 +54,6 @@ public function __construct($id, $title, $supportedLocales, $args = [])
*/
public function getConfig()
{
- \AppLocale::requireComponents(LOCALE_COMPONENT_PKP_MANAGER);
- \AppLocale::requireComponents(LOCALE_COMPONENT_APP_MANAGER);
- \AppLocale::requireComponents(LOCALE_COMPONENT_APP_DEFAULT);
-
$config = parent::getConfig();
$config['apiUrl'] = $this->apiUrl;
diff --git a/classes/components/listPanels/PKPSubmissionsListPanel.inc.php b/classes/components/listPanels/PKPSubmissionsListPanel.inc.php
index b291fb0aa28..b6229df2fdf 100644
--- a/classes/components/listPanels/PKPSubmissionsListPanel.inc.php
+++ b/classes/components/listPanels/PKPSubmissionsListPanel.inc.php
@@ -16,7 +16,6 @@
use APP\core\Application;
use APP\facades\Repo;
-use APP\i18n\AppLocale;
use APP\template\TemplateManager;
use PKP\components\forms\FieldAutosuggestPreset;
use PKP\components\forms\FieldSelectUsers;
@@ -57,7 +56,6 @@ abstract class PKPSubmissionsListPanel extends ListPanel
*/
public function getConfig()
{
- AppLocale::requireComponents([LOCALE_COMPONENT_PKP_SUBMISSION, LOCALE_COMPONENT_APP_SUBMISSION, LOCALE_COMPONENT_PKP_EDITOR, LOCALE_COMPONENT_APP_EDITOR]);
$request = Application::get()->getRequest();
$context = $request->getContext();
diff --git a/classes/context/Context.inc.php b/classes/context/Context.inc.php
index 8f1157d20f3..f92dac453ce 100644
--- a/classes/context/Context.inc.php
+++ b/classes/context/Context.inc.php
@@ -16,10 +16,10 @@
namespace PKP\context;
use APP\core\Application;
-use APP\i18n\AppLocale;
-
-use APP\plugins\IDoiRegistrationAgency;
+use Illuminate\Support\Arr;
use PKP\config\Config;
+use PKP\facades\Locale;
+use APP\plugins\IDoiRegistrationAgency;
use PKP\plugins\Plugin;
use PKP\plugins\PluginRegistry;
use PKP\statistics\PKPStatisticsHelper;
@@ -304,13 +304,9 @@ public function getAcronym($locale)
*/
public function getLocalizedFavicon()
{
- $faviconArray = $this->getData('favicon');
- foreach ([AppLocale::getLocale(), AppLocale::getPrimaryLocale()] as $locale) {
- if (isset($faviconArray[$locale])) {
- return $faviconArray[$locale];
- }
- }
- return null;
+ $favicons = $this->getData('favicon');
+ $locale = Arr::first([Locale::getLocale(), Locale::getPrimaryLocale()], fn (string $locale) => isset($favicons[$locale]));
+ return $favicons[$locale] ?? null;
}
/**
@@ -331,23 +327,10 @@ public function getSupportedFormLocales()
*/
public function getSupportedFormLocaleNames()
{
- $supportedLocales = & $this->getData('supportedFormLocaleNames');
-
- if (!isset($supportedLocales)) {
- $supportedLocales = [];
- $localeNames = & AppLocale::getAllLocales();
-
- $locales = $this->getSupportedFormLocales();
- if (!isset($locales) || !is_array($locales)) {
- $locales = [];
- }
-
- foreach ($locales as $localeKey) {
- $supportedLocales[$localeKey] = $localeNames[$localeKey];
- }
- }
-
- return $supportedLocales;
+ return $this->getData('supportedFormLocaleNames') ?? array_map(
+ fn (string $locale) => Locale::getMetadata($locale)->getDisplayName(),
+ array_combine($locales = $this->getSupportedFormLocales(), $locales)
+ );
}
/**
@@ -369,23 +352,10 @@ public function getSupportedSubmissionLocales()
*/
public function getSupportedSubmissionLocaleNames()
{
- $supportedLocales = & $this->getData('supportedSubmissionLocaleNames');
-
- if (!isset($supportedLocales)) {
- $supportedLocales = [];
- $localeNames = & AppLocale::getAllLocales();
-
- $locales = $this->getSupportedSubmissionLocales();
- if (!isset($locales) || !is_array($locales)) {
- $locales = [];
- }
-
- foreach ($locales as $localeKey) {
- $supportedLocales[$localeKey] = $localeNames[$localeKey];
- }
- }
-
- return $supportedLocales;
+ return $this->getData('supportedSubmissionLocaleNames') ?? array_map(
+ fn (string $locale) => Locale::getMetadata($locale)->getDisplayName(),
+ array_combine($locales = $this->getSupportedSubmissionLocales(), $locales)
+ );
}
/**
@@ -406,23 +376,10 @@ public function getSupportedLocales()
*/
public function getSupportedLocaleNames()
{
- $supportedLocales = & $this->getData('supportedLocaleNames');
-
- if (!isset($supportedLocales)) {
- $supportedLocales = [];
- $localeNames = & AppLocale::getAllLocales();
-
- $locales = $this->getSupportedLocales();
- if (!isset($locales) || !is_array($locales)) {
- $locales = [];
- }
-
- foreach ($locales as $localeKey) {
- $supportedLocales[$localeKey] = $localeNames[$localeKey];
- }
- }
-
- return $supportedLocales;
+ return $this->getData('supportedLocaleNames') ?? array_map(
+ fn (string $locale) => Locale::getMetadata($locale)->getDisplayName(),
+ array_combine($locales = $this->getSupportedLocales(), $locales)
+ );
}
/**
@@ -454,15 +411,7 @@ public function getDateTimeFormats($format)
*/
public function getLocalizedDateFormatShort($locale = null)
{
- if (is_null($locale)) {
- $locale = AppLocale::getLocale();
- }
- $localizedData = $this->getData('dateFormatShort', $locale);
- if (empty($localizedData)) {
- $localizedData = Config::getVar('general', 'date_format_short');
- }
-
- return $localizedData;
+ return $this->getData('dateFormatShort', $locale ?? Locale::getLocale()) ?: Config::getVar('general', 'date_format_short');
}
/**
@@ -474,15 +423,7 @@ public function getLocalizedDateFormatShort($locale = null)
*/
public function getLocalizedDateFormatLong($locale = null)
{
- if (is_null($locale)) {
- $locale = AppLocale::getLocale();
- }
- $localizedData = $this->getData('dateFormatLong', $locale);
- if (empty($localizedData)) {
- $localizedData = Config::getVar('general', 'date_format_long');
- }
-
- return $localizedData;
+ return $this->getData('dateFormatLong', $locale ?? Locale::getLocale()) ?: Config::getVar('general', 'date_format_long');
}
/**
@@ -494,15 +435,7 @@ public function getLocalizedDateFormatLong($locale = null)
*/
public function getLocalizedTimeFormat($locale = null)
{
- if (is_null($locale)) {
- $locale = AppLocale::getLocale();
- }
- $localizedData = $this->getData('timeFormat', $locale);
- if (empty($localizedData)) {
- $localizedData = Config::getVar('general', 'time_format');
- }
-
- return $localizedData;
+ return $this->getData('timeFormat', $locale ?? Locale::getLocale()) ?: Config::getVar('general', 'time_format');
}
/**
@@ -514,15 +447,7 @@ public function getLocalizedTimeFormat($locale = null)
*/
public function getLocalizedDateTimeFormatShort($locale = null)
{
- if (is_null($locale)) {
- $locale = AppLocale::getLocale();
- }
- $localizedData = $this->getData('datetimeFormatShort', $locale);
- if (empty($localizedData)) {
- $localizedData = Config::getVar('general', 'datetime_format_short');
- }
-
- return $localizedData;
+ return $this->getData('datetimeFormatShort', $locale ?? Locale::getLocale()) ?: Config::getVar('general', 'datetime_format_short');
}
/**
@@ -534,15 +459,7 @@ public function getLocalizedDateTimeFormatShort($locale = null)
*/
public function getLocalizedDateTimeFormatLong($locale = null)
{
- if (is_null($locale)) {
- $locale = AppLocale::getLocale();
- }
- $localizedData = $this->getData('datetimeFormatLong', $locale);
- if (empty($localizedData)) {
- $localizedData = Config::getVar('general', 'datetime_format_long');
- }
-
- return $localizedData;
+ return $this->getData('datetimeFormatLong', $locale ?? Locale::getLocale()) ?: Config::getVar('general', 'datetime_format_long');
}
/**
diff --git a/classes/controlledVocab/ControlledVocabDAO.inc.php b/classes/controlledVocab/ControlledVocabDAO.inc.php
index fefc94bf1be..c60e91cc672 100644
--- a/classes/controlledVocab/ControlledVocabDAO.inc.php
+++ b/classes/controlledVocab/ControlledVocabDAO.inc.php
@@ -17,7 +17,7 @@
namespace PKP\controlledVocab;
-use APP\i18n\AppLocale;
+use PKP\facades\Locale;
use PKP\db\DAORegistry;
class ControlledVocabDAO extends \PKP\db\DAO
@@ -250,8 +250,8 @@ public function enumerate($controlledVocabId, $settingName = 'name')
WHERE e.controlled_vocab_id = ?
ORDER BY e.seq',
[
- $settingName, AppLocale::getLocale(), // Current locale
- $settingName, AppLocale::getPrimaryLocale(), // Primary locale
+ $settingName, Locale::getLocale(), // Current locale
+ $settingName, Locale::getPrimaryLocale(), // Primary locale
$settingName, '', // No locale
(int) $controlledVocabId
]
diff --git a/classes/controllers/grid/DataObjectGridCellProvider.inc.php b/classes/controllers/grid/DataObjectGridCellProvider.inc.php
index f8443bf40cd..3b38accaf06 100644
--- a/classes/controllers/grid/DataObjectGridCellProvider.inc.php
+++ b/classes/controllers/grid/DataObjectGridCellProvider.inc.php
@@ -19,7 +19,7 @@
namespace PKP\controllers\grid;
-use APP\i18n\AppLocale;
+use PKP\facades\Locale;
class DataObjectGridCellProvider extends GridCellProvider
{
@@ -48,7 +48,7 @@ public function setLocale($locale)
public function getLocale()
{
if (empty($this->_locale)) {
- return AppLocale::getLocale();
+ return Locale::getLocale();
}
return $this->_locale;
}
diff --git a/classes/controllers/grid/GridCellProvider.inc.php b/classes/controllers/grid/GridCellProvider.inc.php
index ab1b8157acf..9c3fea3758b 100644
--- a/classes/controllers/grid/GridCellProvider.inc.php
+++ b/classes/controllers/grid/GridCellProvider.inc.php
@@ -18,7 +18,7 @@
namespace PKP\controllers\grid;
-use APP\i18n\AppLocale;
+use PKP\facades\Locale;
use APP\template\TemplateManager;
class GridCellProvider
@@ -63,7 +63,7 @@ public function render($request, $row, $column)
'column' => $column,
'actions' => $this->getCellActions($request, $row, $column),
'flags' => $column->getFlags(),
- 'formLocales' => AppLocale::getSupportedFormLocales(),
+ 'formLocales' => Locale::getSupportedFormLocales(),
]);
$template = $column->getTemplate();
assert(!empty($template));
diff --git a/classes/controllers/grid/GridHandler.inc.php b/classes/controllers/grid/GridHandler.inc.php
index eb3876ced75..f9dfb89c5cb 100644
--- a/classes/controllers/grid/GridHandler.inc.php
+++ b/classes/controllers/grid/GridHandler.inc.php
@@ -38,7 +38,6 @@
namespace PKP\controllers\grid;
-use APP\i18n\AppLocale;
use APP\template\TemplateManager;
use Illuminate\Support\Enumerable;
use Illuminate\Support\LazyCollection;
@@ -671,9 +670,6 @@ public function initialize($request, $args = null)
{
parent::initialize($request);
- // Load grid-specific translations
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_GRID, LOCALE_COMPONENT_APP_COMMON);
-
if ($this->getFilterForm() && $this->isFilterFormCollapsible()) {
$this->addAction(
new LinkAction(
diff --git a/classes/controllers/grid/plugins/PluginGridHandler.inc.php b/classes/controllers/grid/plugins/PluginGridHandler.inc.php
index 83eebf2cb06..c572f3e7542 100644
--- a/classes/controllers/grid/plugins/PluginGridHandler.inc.php
+++ b/classes/controllers/grid/plugins/PluginGridHandler.inc.php
@@ -15,7 +15,6 @@
namespace PKP\controllers\grid\plugins;
-use APP\i18n\AppLocale;
use APP\notification\NotificationManager;
use PKP\controllers\grid\CategoryGridHandler;
use PKP\controllers\grid\GridColumn;
@@ -79,9 +78,6 @@ public function initialize($request, $args = null)
{
parent::initialize($request, $args);
- // Load language components
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_MANAGER, LOCALE_COMPONENT_PKP_COMMON, LOCALE_COMPONENT_APP_MANAGER);
-
// Basic grid configuration
$this->setTitle('common.plugins');
diff --git a/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.inc.php b/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.inc.php
index e0f8018e9fb..0ebb3590fa3 100644
--- a/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.inc.php
+++ b/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.inc.php
@@ -17,7 +17,6 @@
use APP\core\Application;
use APP\facades\Repo;
-use APP\i18n\AppLocale;
use APP\log\SubmissionEventLogEntry;
use APP\notification\NotificationManager;
use APP\template\TemplateManager;
@@ -213,17 +212,6 @@ public function getReviewRound()
public function initialize($request, $args = null)
{
parent::initialize($request, $args);
-
- // Load submission-specific translations
- AppLocale::requireComponents(
- LOCALE_COMPONENT_PKP_SUBMISSION,
- LOCALE_COMPONENT_PKP_MANAGER,
- LOCALE_COMPONENT_PKP_USER,
- LOCALE_COMPONENT_PKP_EDITOR,
- LOCALE_COMPONENT_PKP_REVIEWER,
- LOCALE_COMPONENT_APP_EDITOR
- );
-
$this->setTitle('user.role.reviewers');
// Grid actions
diff --git a/classes/controllers/listbuilder/MultilingualListbuilderGridColumn.inc.php b/classes/controllers/listbuilder/MultilingualListbuilderGridColumn.inc.php
index 46cc9f809eb..ff97f955d6f 100644
--- a/classes/controllers/listbuilder/MultilingualListbuilderGridColumn.inc.php
+++ b/classes/controllers/listbuilder/MultilingualListbuilderGridColumn.inc.php
@@ -15,7 +15,7 @@
namespace PKP\controllers\listbuilder;
-use APP\i18n\AppLocale;
+use PKP\facades\Locale;
class MultilingualListbuilderGridColumn extends ListbuilderGridColumn
{
@@ -44,7 +44,7 @@ public function __construct(
// Provide a default set of available locales if not specified
if (!$availableLocales) {
- $availableLocales = AppLocale::getSupportedFormLocales();
+ $availableLocales = Locale::getSupportedFormLocales();
}
// Set some flags for multilingual support
diff --git a/classes/controllers/modals/editorDecision/PKPEditorDecisionHandler.inc.php b/classes/controllers/modals/editorDecision/PKPEditorDecisionHandler.inc.php
index 3d8ea7442b4..7687a5dfbd5 100644
--- a/classes/controllers/modals/editorDecision/PKPEditorDecisionHandler.inc.php
+++ b/classes/controllers/modals/editorDecision/PKPEditorDecisionHandler.inc.php
@@ -16,7 +16,6 @@
namespace PKP\controllers\modals\editorDecision;
use APP\handler\Handler;
-use APP\i18n\AppLocale;
use APP\notification\NotificationManager;
use APP\workflow\EditorDecisionActionsManager;
use PKP\core\JSONMessage;
@@ -68,20 +67,6 @@ public function authorize($request, &$args, $roleAssignments)
return true;
}
- /**
- * @copydoc PKPHandler::initialize()
- */
- public function initialize($request)
- {
- AppLocale::requireComponents(
- LOCALE_COMPONENT_APP_COMMON,
- LOCALE_COMPONENT_APP_EDITOR,
- LOCALE_COMPONENT_APP_SUBMISSION,
- LOCALE_COMPONENT_PKP_EDITOR,
- LOCALE_COMPONENT_PKP_SUBMISSION
- );
- }
-
//
// Public handler actions
@@ -293,7 +278,6 @@ public function importPeerReviews($args, $request)
$reviewAssignments = $reviewAssignmentDao->getBySubmissionId($submission->getId(), $reviewRound->getId());
$reviewIndexes = $reviewAssignmentDao->getReviewIndexesForRound($submission->getId(), $reviewRound->getId());
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_SUBMISSION);
$body = '';
$textSeparator = '------------------------------------------------------';
diff --git a/classes/controllers/tab/workflow/PKPReviewRoundTabHandler.inc.php b/classes/controllers/tab/workflow/PKPReviewRoundTabHandler.inc.php
index d4fdcb7f6b9..00c0e69c87e 100644
--- a/classes/controllers/tab/workflow/PKPReviewRoundTabHandler.inc.php
+++ b/classes/controllers/tab/workflow/PKPReviewRoundTabHandler.inc.php
@@ -16,7 +16,6 @@
namespace PKP\controllers\tab\workflow;
use APP\handler\Handler;
-use APP\i18n\AppLocale;
use APP\notification\Notification;
use APP\template\TemplateManager;
use PKP\core\JSONMessage;
@@ -61,7 +60,6 @@ public function externalReviewRound($args, $request)
*/
public function setupTemplate($request)
{
- AppLocale::requireComponents(LOCALE_COMPONENT_APP_EDITOR);
parent::setupTemplate($request);
}
diff --git a/classes/core/APIResponse.inc.php b/classes/core/APIResponse.inc.php
index 7ea3731cf8c..45e39b6f727 100644
--- a/classes/core/APIResponse.inc.php
+++ b/classes/core/APIResponse.inc.php
@@ -31,7 +31,7 @@ public function withJsonError($msg, $params = null)
return $this->withJson(
[
'error' => $msg,
- 'errorMessage' => __($msg, $params),
+ 'errorMessage' => __($msg, $params ?? []),
]
);
}
diff --git a/classes/core/APIRouter.inc.php b/classes/core/APIRouter.inc.php
index 40f425ecf6d..115b75ef2fb 100644
--- a/classes/core/APIRouter.inc.php
+++ b/classes/core/APIRouter.inc.php
@@ -19,7 +19,6 @@
namespace PKP\core;
-use APP\i18n\AppLocale;
use Exception;
use PKP\config\Config;
@@ -109,7 +108,6 @@ public function route($request)
$sourceFile = sprintf('api/%s/%s/index.php', $this->getVersion(), $this->getEntity());
if (!file_exists($sourceFile)) {
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_API, LOCALE_COMPONENT_APP_API);
http_response_code('404');
header('Content-Type: application/json');
echo json_encode([
@@ -119,7 +117,7 @@ public function route($request)
exit;
}
- if (!defined('SESSION_DISABLE_INIT')) {
+ if (!SessionManager::isDisabled()) {
// Initialize session
SessionManager::getManager();
}
@@ -161,7 +159,6 @@ public function handleAuthorizationFailure(
$authorizationMessage,
array $messageParams = []
) {
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_API, LOCALE_COMPONENT_APP_API);
http_response_code('403');
header('Content-Type: application/json');
echo json_encode([
diff --git a/classes/core/Core.inc.php b/classes/core/Core.inc.php
index 7ca97f33b8a..d2c5b383ecc 100644
--- a/classes/core/Core.inc.php
+++ b/classes/core/Core.inc.php
@@ -43,13 +43,7 @@ class Core
public static function getBaseDir()
{
static $baseDir;
-
- if (!isset($baseDir)) {
- // Need to change if the index file moves
- $baseDir = dirname(INDEX_FILE_LOCATION);
- }
-
- return $baseDir;
+ return $baseDir ??= dirname(INDEX_FILE_LOCATION);
}
/**
diff --git a/classes/core/DataObject.inc.php b/classes/core/DataObject.inc.php
index aa081a3d987..b76f2fcc683 100644
--- a/classes/core/DataObject.inc.php
+++ b/classes/core/DataObject.inc.php
@@ -17,7 +17,8 @@
namespace PKP\core;
-use APP\i18n\AppLocale;
+use APP\core\Application;
+use PKP\facades\Locale;
class DataObject
{
@@ -59,7 +60,7 @@ public function __construct()
*/
public function getLocalizedData($key, $preferredLocale = null)
{
- $localePrecedence = AppLocale::getLocalePrecedence();
+ $localePrecedence = $this->_getLocalePrecedence();
foreach ($localePrecedence as $locale) {
$value = & $this->getData($key, $locale);
if (!empty($value)) {
@@ -69,17 +70,39 @@ public function getLocalizedData($key, $preferredLocale = null)
}
// Fallback: Get the first available piece of data.
- $data = & $this->getData($key, null);
+ $data = $this->getData($key, null);
if (!empty($data)) {
$locales = array_keys($data);
$firstLocale = array_shift($locales);
return $data[$firstLocale];
}
- // No data available; return null.
- unset($data);
- $data = null;
- return $data;
+ return null;
+ }
+
+ /**
+ * Get the stack of "important" locales, most important first.
+
+ * @return string[]
+ */
+ private function _getLocalePrecedence(): array
+ {
+ static $localePrecedence;
+ if (!isset($localePrecedence)) {
+ $request = Application::get()->getRequest();
+ $localePrecedence = [Locale::getLocale()];
+
+ $context = $request->getContext();
+ if ($context && !in_array($context->getPrimaryLocale(), $localePrecedence)) {
+ $localePrecedence[] = $context->getPrimaryLocale();
+ }
+
+ $site = $request->getSite();
+ if ($site && !in_array($site->getPrimaryLocale(), $localePrecedence)) {
+ $localePrecedence[] = $site->getPrimaryLocale();
+ }
+ }
+ return $localePrecedence;
}
/**
diff --git a/classes/core/Dispatcher.inc.php b/classes/core/Dispatcher.inc.php
index 433887ac1ef..0e139c92c45 100644
--- a/classes/core/Dispatcher.inc.php
+++ b/classes/core/Dispatcher.inc.php
@@ -16,7 +16,6 @@
namespace PKP\core;
use APP\core\Services;
-use APP\i18n\AppLocale;
use PKP\config\Config;
use PKP\plugins\HookRegistry;
@@ -134,8 +133,6 @@ public function dispatch($request)
fatalError('None of the configured routers supports this request.');
}
- AppLocale::initialize($request);
-
// Can we serve a cached response?
if ($router->isCacheable($request)) {
$this->_requestCallbackHack = & $request;
@@ -259,7 +256,7 @@ public function _displayCached($router, $request)
return false;
}
- header('Content-Type: text/html; charset=' . Config::getVar('i18n', 'client_charset'));
+ header('Content-Type: text/html; charset=utf-8');
echo $contents;
return true;
diff --git a/classes/core/ExportableTrait.inc.php b/classes/core/ExportableTrait.inc.php
new file mode 100644
index 00000000000..363cbeb05ee
--- /dev/null
+++ b/classes/core/ExportableTrait.inc.php
@@ -0,0 +1,31 @@
+ $value) {
+ $object->$key = $value;
+ }
+ return $object;
+ }
+}
diff --git a/classes/core/PKPApplication.inc.php b/classes/core/PKPApplication.inc.php
index e24a86e0aff..428deccd0ee 100644
--- a/classes/core/PKPApplication.inc.php
+++ b/classes/core/PKPApplication.inc.php
@@ -19,15 +19,20 @@
use APP\core\Application;
use APP\core\Request;
-use APP\i18n\AppLocale;
+use PKP\facades\Locale;
use APP\statistics\StatisticsHelper;
+use DateTime;
+use DateTimeZone;
use Exception;
+use GuzzleHttp\Client;
+use Illuminate\Database\Events\QueryExecuted;
+use Illuminate\Database\MySqlConnection;
+use Illuminate\Support\Facades\DB;
use PKP\config\Config;
-
use PKP\db\DAORegistry;
-use PKP\i18n\PKPLocale;
use PKP\plugins\PluginRegistry;
use PKP\security\Role;
+use PKP\session\SessionManager;
use PKP\statistics\PKPStatisticsHelper;
interface iPKPApplicationInfoProvider
@@ -193,29 +198,26 @@ class_alias('\PKP\core\PKPApplication', '\PKPApplication');
}
ini_set('display_errors', Config::getVar('debug', 'display_errors', ini_get('display_errors')));
- if (!defined('SESSION_DISABLE_INIT') && !Config::getVar('general', 'installed')) {
- define('SESSION_DISABLE_INIT', true);
+ if (!static::isInstalled()) {
+ SessionManager::disable();
}
Registry::set('application', $this);
- PKPString::init();
-
$microTime = Core::microtime();
Registry::set('system.debug.startTime', $microTime);
- $notes = [];
- Registry::set('system.debug.notes', $notes);
+ $this->initializeLaravelContainer();
+ PKPString::initialize();
- if (Config::getVar('general', 'installed')) {
- $this->initializeLaravelContainer();
- }
+ // Load default locale files
+ Locale::registerPath(BASE_SYS_DIR . '/lib/pkp/locale');
}
/**
* Initialize Laravel container and register service providers
*/
- public function initializeLaravelContainer()
+ public function initializeLaravelContainer(): void
{
// Ensure multiple calls to this function don't cause trouble
static $containerInitialized = false;
@@ -229,10 +231,45 @@ public function initializeLaravelContainer()
$laravelContainer = new PKPContainer();
$laravelContainer->registerConfiguredProviders();
+ $this->initializeTimeZone();
+
if (Config::getVar('database', 'debug')) {
- \Illuminate\Support\Facades\DB::listen(function ($query) {
- error_log("Database query\n{$query->sql}\n" . json_encode($query->bindings));
- });
+ DB::listen(fn(QueryExecuted $query) => error_log("Database query\n{$query->sql}\n" . json_encode($query->bindings)));
+ }
+ }
+
+ /**
+ * Setup the internal time zone for the database and PHP.
+ */
+ protected function initializeTimeZone(): void
+ {
+ $timeZone = null;
+ // Loads the time zone from the configuration file
+ if ($setting = Config::getVar('general', 'time_zone')) {
+ try {
+ $timeZone = (new DateTimeZone($setting))->getName();
+ } catch (Exception $e) {
+ $setting = strtolower($setting);
+ foreach (DateTimeZone::listIdentifiers() as $identifier) {
+ // Backward compatibility identification
+ if ($setting == strtolower(preg_replace(['/^\w+\//', '/_/'], ['', ' '], $identifier))) {
+ $timeZone = $identifier;
+ break;
+ }
+ }
+ }
+ }
+ // Set the default timezone
+ date_default_timezone_set($timeZone ?: ini_get('date.timezone') ?: 'UTC');
+
+ // Synchronize the database time zone
+ if (Application::isInstalled()) {
+ // Retrieve the current offset
+ $offset = (new DateTime())->format('P');
+ $statement = DB::connection() instanceof MySqlConnection
+ ? "SET time_zone = '${offset}'"
+ : "SET TIME ZONE INTERVAL '${offset}' HOUR TO MINUTE";
+ DB::statement($statement);
}
}
@@ -259,13 +296,14 @@ public static function get()
/**
* Return a HTTP client implementation.
*
- * @return \GuzzleHttp\Client
+ * @return Client
*/
public function getHttpClient()
{
$application = Application::get();
$userAgent = $application->getName() . '/';
- if (Config::getVar('general', 'installed') && !defined('RUNNING_UPGRADE')) {
+ if (static::isInstalled() && !static::isUpgrading()) {
+ /** @var \PKP\site\VersionDAO */
$versionDao = DAORegistry::getDAO('VersionDAO');
$currentVersion = $versionDao->getCurrentVersion();
$userAgent .= $currentVersion->getVersionString();
@@ -273,7 +311,7 @@ public function getHttpClient()
$userAgent .= '?';
}
- return new \GuzzleHttp\Client([
+ return new Client([
'proxy' => [
'http' => Config::getVar('proxy', 'http_proxy', null),
'https' => Config::getVar('proxy', 'https_proxy', null),
@@ -416,7 +454,7 @@ public function &getEnabledProducts($category = null, $mainContextId = null)
$settingContext = array_combine($this->getContextList(), $settingContext);
}
- $versionDao = DAORegistry::getDAO('VersionDAO'); /** @var VersionDAO $versionDao */
+ $versionDao = DAORegistry::getDAO('VersionDAO'); /** @var \PKP\site\VersionDAO $versionDao */
$this->enabledProducts[$mainContextId] = $versionDao->getCurrentProducts($settingContext);
}
@@ -511,7 +549,6 @@ public function getDAOMap()
'SubmissionKeywordEntryDAO' => 'PKP\submission\SubmissionKeywordEntryDAO',
'SubmissionSubjectDAO' => 'PKP\submission\SubmissionSubjectDAO',
'SubmissionSubjectEntryDAO' => 'PKP\submission\SubmissionSubjectEntryDAO',
- 'TimeZoneDAO' => 'PKP\i18n\TimeZoneDAO',
'TemporaryFileDAO' => 'PKP\file\TemporaryFileDAO',
'UserGroupAssignmentDAO' => 'PKP\security\UserGroupAssignmentDAO',
'UserGroupDAO' => 'PKP\security\UserGroupDAO',
@@ -782,12 +819,11 @@ public function getCCLicenseBadge($ccLicenseURL, $locale = null)
'|http[s]?://(www\.)?creativecommons.org/licenses/by-sa/3.0[/]?|' => 'submission.license.cc.by-sa3.footer'
];
if ($locale === null) {
- $locale = AppLocale::getLocale();
+ $locale = Locale::getLocale();
}
foreach ($licenseKeyMap as $pattern => $key) {
if (preg_match($pattern, $ccLicenseURL)) {
- PKPLocale::requireComponents(LOCALE_COMPONENT_PKP_SUBMISSION, $locale);
return __($key, [], $locale);
}
}
@@ -843,7 +879,6 @@ public static function getWorkflowTypeRoles()
*/
public static function getWorkflowStageName($stageId)
{
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_SUBMISSION, LOCALE_COMPONENT_APP_SUBMISSION);
switch ($stageId) {
case WORKFLOW_STAGE_ID_SUBMISSION: return 'submission.submission';
case WORKFLOW_STAGE_ID_INTERNAL_REVIEW: return 'workflow.review.internalReview';
@@ -863,7 +898,6 @@ public static function getWorkflowStageName($stageId)
*/
public static function getWorkflowStageColor($stageId)
{
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_SUBMISSION, LOCALE_COMPONENT_APP_SUBMISSION);
switch ($stageId) {
case WORKFLOW_STAGE_ID_SUBMISSION: return '#d00a0a';
case WORKFLOW_STAGE_ID_INTERNAL_REVIEW: return '#e05c14';
@@ -931,6 +965,41 @@ public static function getMetadataFields()
'citations',
];
}
+
+ /**
+ * Retrieves whether the application is installed
+ */
+ public static function isInstalled(): bool
+ {
+ return !!Config::getVar('general', 'installed');
+ }
+
+ /**
+ * Retrieves whether the application is running an upgrade
+ */
+ public static function isUpgrading(): bool
+ {
+ return defined('RUNNING_UPGRADE');
+ }
+
+ /**
+ * Retrieves whether the application is under maintenance (not installed or being upgraded)
+ */
+ public static function isUnderMaintenance(): bool
+ {
+ return !static::isInstalled() || static::isUpgrading();
+ }
+
+ /**
+ * Signals the application is undergoing an upgrade
+ */
+ public static function upgrade(): void
+ {
+ // Constant kept for backwards compatibility
+ if (!defined('RUNNING_UPGRADE')) {
+ define('RUNNING_UPGRADE', true);
+ }
+ }
}
define('REALLY_BIG_NUMBER', 10000);
diff --git a/classes/core/PKPComponentRouter.inc.php b/classes/core/PKPComponentRouter.inc.php
index cbf4b54d6b2..5645acc5063 100644
--- a/classes/core/PKPComponentRouter.inc.php
+++ b/classes/core/PKPComponentRouter.inc.php
@@ -61,7 +61,6 @@
define('COMPONENT_ROUTER_PARTS_MAXLENGTH', 50);
define('COMPONENT_ROUTER_PARTS_MINLENGTH', 2);
-use APP\i18n\AppLocale;
use Exception;
use PKP\config\Config;
@@ -408,11 +407,6 @@ public function handleAuthorizationFailure(
$authorizationMessage,
array $messageParams = []
) {
- // Translate the authorization error message.
- if (defined('LOCALE_COMPONENT_APP_COMMON')) {
- AppLocale::requireComponents(LOCALE_COMPONENT_APP_COMMON);
- }
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_USER);
$translatedAuthorizationMessage = __($authorizationMessage, $messageParams);
// Add the router name and operation if show_stacktrace is enabled.
diff --git a/classes/core/PKPContainer.inc.php b/classes/core/PKPContainer.inc.php
index dcfaeb88f13..68481c2214b 100644
--- a/classes/core/PKPContainer.inc.php
+++ b/classes/core/PKPContainer.inc.php
@@ -29,10 +29,8 @@
use Illuminate\Support\Facades\Facade;
use PKP\config\Config;
use PKP\Domains\Jobs\Providers\JobServiceProvider;
-use PKP\i18n\PKPLocale;
+use PKP\i18n\LocaleServiceProvider;
use PKP\Support\ProxyParser;
-use Sokil\IsoCodes\IsoCodesFactory;
-use Sokil\IsoCodes\TranslationDriver\GettextExtensionDriver;
use Throwable;
@@ -93,13 +91,6 @@ public function renderForConsole($output, Throwable $e)
Kernel::class
);
- // This singleton is necessary to keep user selected language across the application
- $this->singleton(IsoCodesFactory::class, function () {
- $driver = new GettextExtensionDriver();
- $driver->setLocale(PKPLocale::getLocale());
- return new IsoCodesFactory(null, $driver);
- });
-
$this->singleton(
'queue.failer',
function ($app) {
@@ -131,6 +122,10 @@ public function registerConfiguredProviders()
$this->register(new MailServiceProvider($this));
$this->register(new AppServiceProvider($this));
$this->register(new JobServiceProvider($this));
+ $this->register(new \Illuminate\Cache\CacheServiceProvider($this));
+ $this->register(new \Illuminate\Filesystem\FilesystemServiceProvider($this));
+ $this->register(new \ElcoBvg\Opcache\ServiceProvider($this));
+ $this->register(new LocaleServiceProvider($this));
}
/**
@@ -153,8 +148,15 @@ public function registerCoreContainerAliases()
foreach ([
'app' => [self::class, \Illuminate\Contracts\Container\Container::class, \Psr\Container\ContainerInterface::class],
'config' => [\Illuminate\Config\Repository::class, \Illuminate\Contracts\Config\Repository::class],
+ 'cache' => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class],
+ 'cache.store' => [\Illuminate\Cache\Repository::class, \Illuminate\Contracts\Cache\Repository::class, \Psr\SimpleCache\CacheInterface::class],
+ 'cache.psr6' => [\Symfony\Component\Cache\Adapter\Psr16Adapter::class, \Symfony\Component\Cache\Adapter\AdapterInterface::class, \Psr\Cache\CacheItemPoolInterface::class],
'db' => [\Illuminate\Database\DatabaseManager::class, \Illuminate\Database\ConnectionResolverInterface::class],
'db.connection' => [\Illuminate\Database\Connection::class, \Illuminate\Database\ConnectionInterface::class],
+ 'files' => [\Illuminate\Filesystem\Filesystem::class],
+ 'filesystem' => [\Illuminate\Filesystem\FilesystemManager::class, \Illuminate\Contracts\Filesystem\Factory::class],
+ 'filesystem.disk' => [\Illuminate\Contracts\Filesystem\Filesystem::class],
+ 'filesystem.cloud' => [\Illuminate\Contracts\Filesystem\Cloud::class],
'maps' => [MapContainer::class, MapContainer::class],
'events' => [\Illuminate\Events\Dispatcher::class, \Illuminate\Contracts\Events\Dispatcher::class],
'queue' => [\Illuminate\Queue\QueueManager::class, \Illuminate\Contracts\Queue\Factory::class, \Illuminate\Contracts\Queue\Monitor::class],
@@ -240,7 +242,19 @@ protected function loadConfiguration()
$items['mail']['default'] = static::getDefaultMailer();
- $this->instance('config', new Repository($items)); // create instance and bind to use globally
+ // Cache configuration
+ $items['cache'] = [
+ 'default' => 'opcache',
+ 'stores' => [
+ 'opcache' => [
+ 'driver' => 'opcache',
+ 'path' => Core::getBaseDir() . '/cache/opcache'
+ ]
+ ]
+ ];
+
+ // Create instance and bind to use globally
+ $this->instance('config', new Repository($items));
}
/**
diff --git a/classes/core/PKPPageRouter.inc.php b/classes/core/PKPPageRouter.inc.php
index 66a287e7101..23a17200bb5 100644
--- a/classes/core/PKPPageRouter.inc.php
+++ b/classes/core/PKPPageRouter.inc.php
@@ -19,7 +19,7 @@
define('ROUTER_DEFAULT_OP', 'index');
use APP\core\Application;
-use APP\i18n\AppLocale;
+use PKP\facades\Locale;
use PKP\config\Config;
use PKP\db\DAORegistry;
use PKP\plugins\HookRegistry;
@@ -79,10 +79,10 @@ public function getCacheablePages()
*/
public function isCacheable($request, $testOnly = false)
{
- if (defined('SESSION_DISABLE_INIT') && !$testOnly) {
+ if (SessionManager::isDisabled() && !$testOnly) {
return false;
}
- if (!Config::getVar('general', 'installed')) {
+ if (Application::isUnderMaintenance()) {
return false;
}
if (!empty($_POST) || Validation::isLoggedIn()) {
@@ -179,14 +179,14 @@ public function getCacheFilename($request)
if (!isset($this->_cacheFilename)) {
if ($request->isPathInfoEnabled()) {
$id = $_SERVER['PATH_INFO'] ?? 'index';
- $id .= '-' . AppLocale::getLocale();
+ $id .= '-' . Locale::getLocale();
} else {
$id = '';
$application = $this->getApplication();
foreach ($application->getContextList() as $contextName) {
$id .= $request->getUserVar($contextName) . '-';
}
- $id .= $request->getUserVar('page') . '-' . $request->getUserVar('op') . '-' . $request->getUserVar('path') . '-' . AppLocale::getLocale();
+ $id .= $request->getUserVar('page') . '-' . $request->getUserVar('op') . '-' . $request->getUserVar('path') . '-' . Locale::getLocale();
}
$path = Core::getBaseDir();
$this->_cacheFilename = $path . '/cache/wc-' . md5($id) . '.html';
@@ -205,7 +205,7 @@ public function route($request)
// If the application has not yet been installed we only
// allow installer pages to be displayed.
- if (!Config::getVar('general', 'installed')) {
+ if (!Application::isInstalled()) {
if (!in_array($page, $this->getInstallationPages())) {
// A non-installation page was called although
// the system is not yet installed. Redirect to
@@ -226,7 +226,7 @@ public function route($request)
// Redirect requests from logged-out users to a context which is not
// publicly enabled
- if (!defined('SESSION_DISABLE_INIT')) {
+ if (!SessionManager::isDisabled()) {
$user = $request->getUser();
$currentContext = $request->getContext();
if ($currentContext && !$currentContext->getEnabled() && !$user instanceof \PKP\user\User) {
@@ -262,7 +262,7 @@ public function route($request)
}
}
- if (!defined('SESSION_DISABLE_INIT')) {
+ if (!SessionManager::isDisabled()) {
// Initialize session
SessionManager::getManager();
}
diff --git a/classes/core/PKPQueueProvider.inc.php b/classes/core/PKPQueueProvider.inc.php
index 84e3932706a..19af5eee9c4 100644
--- a/classes/core/PKPQueueProvider.inc.php
+++ b/classes/core/PKPQueueProvider.inc.php
@@ -17,6 +17,7 @@
namespace PKP\core;
+use APP\core\Application;
use Illuminate\Queue\WorkerOptions;
use PKP\config\Config;
@@ -28,17 +29,16 @@ public function runJobsAtShutdown(): void
{
$disableRun = Config::getVar('queues', 'disable_jobs_run_at_shutdown', false);
- if ($disableRun || defined('RUNNING_UPGRADE')) {
+ if ($disableRun || Application::isUnderMaintenance()) {
return;
}
- $job = PKPJobModel
- ::isAvailable()
- ->notExceededAttempts()
- ->nonEmptyQueue()
- ->notQueue(PKPJobModel::TESTING_QUEUE)
- ->limit(1)
- ->first();
+ $job = PKPJobModel::isAvailable()
+ ->notExceededAttempts()
+ ->nonEmptyQueue()
+ ->notQueue(PKPJobModel::TESTING_QUEUE)
+ ->limit(1)
+ ->first();
if ($job === null) {
return;
diff --git a/classes/core/PKPRequest.inc.php b/classes/core/PKPRequest.inc.php
index 91ffeb3c4f4..578406f7df0 100644
--- a/classes/core/PKPRequest.inc.php
+++ b/classes/core/PKPRequest.inc.php
@@ -18,9 +18,14 @@
use APP\core\Application;
use APP\facades\Repo;
use PKP\config\Config;
+use PKP\context\Context;
use PKP\db\DAORegistry;
use PKP\plugins\HookRegistry;
+use PKP\security\Validation;
+use PKP\session\Session;
use PKP\session\SessionManager;
+use PKP\site\Site;
+use PKP\user\User;
class PKPRequest
{
@@ -561,63 +566,44 @@ public function isRestfulUrlsEnabled()
/**
* Get site data.
- *
- * @return Site
*/
- public function &getSite()
+ public function getSite(): ?Site
{
$site = & Registry::get('site', true, null);
- if ($site === null) {
- $siteDao = DAORegistry::getDAO('SiteDAO'); /** @var SiteDAO $siteDao */
- $site = $siteDao->getSite();
- // PHP bug? This is needed for some reason or extra queries results.
- Registry::set('site', $site);
- }
-
- return $site;
+ return $site ??= DAORegistry::getDAO('SiteDAO')->getSite();
}
/**
* Get the user session associated with the current request.
- *
- * @return Session
*/
- public function &getSession()
+ public function getSession(): Session
{
$session = & Registry::get('session', true, null);
-
- if ($session === null) {
- $sessionManager = SessionManager::getManager();
- $session = $sessionManager->getUserSession();
- }
-
- return $session;
+ return $session ??= SessionManager::getManager()->getUserSession();
}
/**
* Get the user associated with the current request.
- *
- * @return User
*/
- public function &getUser()
+ public function getUser(): ?User
{
$user = & Registry::get('user', true, null);
-
- $router = $this->getRouter();
- if (!is_null($handler = $router->getHandler()) && !is_null($token = $handler->getApiToken())) {
- if ($user === null) {
- $user = Repo::user()->getByApiKey($token);
- }
- if (is_null($user) || !$user->getData('apiKeyEnabled')) {
- $user = null;
- }
+ if ($user) {
return $user;
}
- if ($user === null) {
- $sessionManager = SessionManager::getManager();
- $session = $sessionManager->getUserSession();
- $user = $session->getUser();
+ // Attempt to load user from API token
+ if (($handler = $this->getRouter()->getHandler())
+ && ($token = $handler->getApiToken())
+ && ($apiUser = Repo::user()->getByApiKey($token))
+ && $apiUser->getData('apiKeyEnabled')
+ ) {
+ return $user = $apiUser;
+ }
+
+ // Attempts to retrieve a logged user
+ if (Validation::isLoggedIn()) {
+ $user = SessionManager::getManager()->getUserSession()->getUser();
}
return $user;
@@ -640,12 +626,7 @@ public function getUserVar($key)
// Get all vars (already cleaned)
$vars = $this->getUserVars();
-
- if (isset($vars[$key])) {
- return $vars[$key];
- } else {
- return null;
- }
+ return $vars[$key] ?? null;
}
/**
@@ -655,12 +636,7 @@ public function getUserVar($key)
*/
public function &getUserVars()
{
- if (!isset($this->_requestVars)) {
- $this->_requestVars = array_map(function ($s) {
- return is_string($s) ? trim($s) : $s;
- }, array_merge($_GET, $_POST));
- }
-
+ $this->_requestVars ??= array_map(fn($s) => is_string($s) ? trim($s) : $s, array_merge($_GET, $_POST));
return $this->_requestVars;
}
@@ -773,11 +749,9 @@ public function redirect($context = null, $page = null, $op = null, $path = null
/**
* Get the current "context" (press/journal/etc) object.
*
- * @return Context
- *
* @see PKPPageRouter::getContext()
*/
- public function &getContext()
+ public function &getContext(): ?Context
{
return $this->_delegateToRouter('getContext');
}
diff --git a/classes/core/PKPString.inc.php b/classes/core/PKPString.inc.php
index 706daa52330..fbc9aaae3d6 100644
--- a/classes/core/PKPString.inc.php
+++ b/classes/core/PKPString.inc.php
@@ -17,7 +17,6 @@
namespace PKP\core;
use PKP\config\Config;
-
use Stringy\Stringy;
class PKPString
@@ -31,18 +30,16 @@ class PKPString
/**
* Perform initialization required for the string wrapper library.
*/
- public static function init()
+ public static function initialize()
{
- $clientCharset = strtolower_codesafe(Config::getVar('i18n', 'client_charset'));
-
- // Check if mbstring is installed
- if (self::hasMBString() && !defined('ENABLE_MBSTRING')) {
- // mbstring routines are available
- define('ENABLE_MBSTRING', true);
-
- // Set up required ini settings for mbstring
- // FIXME Do any other mbstring settings need to be set?
- mb_internal_encoding($clientCharset);
+ static $isInitialized;
+ if (!$isInitialized) {
+ if (self::hasMBString()) {
+ // Set up default encoding
+ mb_internal_encoding('utf-8');
+ ini_set('default_charset', 'utf-8');
+ }
+ $isInitialized = true;
}
}
@@ -65,8 +62,7 @@ public static function hasMBString()
if (ini_get('mbstring.func_overload') && defined('MB_OVERLOAD_STRING')) {
$hasMBString = false;
} else {
- $hasMBString = (
- extension_loaded('mbstring') &&
+ $hasMBString = extension_loaded('mbstring') &&
function_exists('mb_strlen') &&
function_exists('mb_strpos') &&
function_exists('mb_strrpos') &&
@@ -74,8 +70,7 @@ function_exists('mb_substr') &&
function_exists('mb_strtolower') &&
function_exists('mb_strtoupper') &&
function_exists('mb_substr_count') &&
- function_exists('mb_send_mail')
- );
+ function_exists('mb_send_mail');
}
return $hasMBString;
}
@@ -195,11 +190,10 @@ public static function substr_count($haystack, $needle)
*/
public static function encode_mime_header($string)
{
- if (defined('ENABLE_MBSTRING')) {
- return mb_encode_mimeheader($string, mb_internal_encoding(), 'B', MAIL_EOL);
- } else {
- return $string;
- }
+ static::initialize();
+ return static::hasMBString()
+ ? mb_encode_mimeheader($string, mb_internal_encoding(), 'B', Core::isWindows() ? "\r\n" : "\n")
+ : $string;
}
//
@@ -409,7 +403,7 @@ public static function stripUnsafeHtml($input)
static $purifier;
if (!isset($purifier)) {
$config = \HTMLPurifier_Config::createDefault();
- $config->set('Core.Encoding', Config::getVar('i18n', 'client_charset'));
+ $config->set('Core.Encoding', 'utf-8');
$config->set('HTML.Doctype', 'HTML 4.01 Transitional');
$config->set('HTML.Allowed', Config::getVar('security', 'allowed_html'));
$config->set('Cache.SerializerPath', 'cache');
diff --git a/classes/emailTemplate/DAO.inc.php b/classes/emailTemplate/DAO.inc.php
index 119f51a5356..05a6f1ccafe 100644
--- a/classes/emailTemplate/DAO.inc.php
+++ b/classes/emailTemplate/DAO.inc.php
@@ -18,6 +18,7 @@
use Illuminate\Support\LazyCollection;
use PKP\core\EntityDAO;
use PKP\db\XMLDAO;
+use PKP\facades\Locale;
use PKP\facades\Repo;
class DAO extends EntityDAO
@@ -322,11 +323,11 @@ public function installEmailTemplateLocaleData(
->where('locale', $locale)
->delete();
- $keyNotFoundHandler = function ($key) {
- return null;
- };
- $translatedSubject = __($subject, [], $locale, $keyNotFoundHandler);
- $translatedBody = __($body, [], $locale, $keyNotFoundHandler);
+ $previous = Locale::getMissingKeyHandler();
+ Locale::setMissingKeyHandler(fn (string $key): string => '');
+ $translatedSubject = __($subject, [], $locale);
+ $translatedBody = __($body, [], $locale);
+ Locale::setMissingKeyHandler($previous);
if ($translatedSubject !== null && $translatedBody !== null) {
DB::table('email_templates_default_data')->insert([
'email_key' => $attrs['key'],
@@ -373,9 +374,7 @@ public function installEmailTemplateData(
// Translate variable contents
foreach ([&$subject, &$body, &$description] as &$var) {
- $var = preg_replace_callback('{{translate key="([^"]+)"}}', function ($matches) use($locale) {
- return __($matches[1], [], $locale);
- }, $var);
+ $var = preg_replace_callback('{{translate key="([^"]+)"}}', fn($matches) => __($matches[1], [], $locale), $var);
}
if ($emailKey && $emailKey != $emailNode->getAttribute('key')) {
diff --git a/classes/emailTemplate/Repository.inc.php b/classes/emailTemplate/Repository.inc.php
index 3607a242ec5..bb53adfd713 100644
--- a/classes/emailTemplate/Repository.inc.php
+++ b/classes/emailTemplate/Repository.inc.php
@@ -14,7 +14,6 @@
namespace PKP\emailTemplate;
use APP\emailTemplate\DAO;
-use APP\i18n\AppLocale;
use Illuminate\Support\LazyCollection;
use PKP\core\PKPRequest;
use PKP\plugins\HookRegistry;
@@ -102,11 +101,6 @@ public function validate(?EmailTemplate $object, array $props, array $allowedLoc
$this->schemaService->getValidationRules(PKPSchemaService::SCHEMA_EMAIL_TEMPLATE, $allowedLocales)
);
- AppLocale::requireComponents(
- LOCALE_COMPONENT_PKP_MANAGER,
- LOCALE_COMPONENT_APP_MANAGER
- );
-
// Check required fields
ValidatorFactory::required(
$validator,
diff --git a/classes/facades/Locale.inc.php b/classes/facades/Locale.inc.php
new file mode 100644
index 00000000000..5811f36ea88
--- /dev/null
+++ b/classes/facades/Locale.inc.php
@@ -0,0 +1,55 @@
+getHttpClient();
+ $response = $client->request('GET', $filenameOrUrl);
+ return Utils::streamFor($response->getBody());
+ } elseif (file_exists($filenameOrUrl) && is_readable($filenameOrUrl)) {
+ $resource = fopen($filenameOrUrl, 'r');
+ return Utils::streamFor($resource);
+ }
+ return null;
+ }
/**
* Read a file's contents.
@@ -725,7 +750,6 @@ public function compressFile($filePath)
*/
private function _executeGzip($filePath, $decompress = false)
{
- PKPLocale::requireComponents(LOCALE_COMPONENT_PKP_ADMIN);
$gzipPath = Config::getVar('cli', 'gzip');
if (!is_executable($gzipPath)) {
throw new Exception(__('admin.error.executingUtil', ['utilPath' => $gzipPath, 'utilVar' => 'gzip']));
diff --git a/classes/filter/TemplateBasedFilter.inc.php b/classes/filter/TemplateBasedFilter.inc.php
index d80d801fa23..7a8af7a15be 100644
--- a/classes/filter/TemplateBasedFilter.inc.php
+++ b/classes/filter/TemplateBasedFilter.inc.php
@@ -17,7 +17,7 @@
namespace PKP\filter;
use APP\template\TemplateManager;
-
+use PKP\facades\Locale;
class TemplateBasedFilter extends PersistableFilter
{
//
@@ -71,7 +71,7 @@ public function addTemplateVars($templateMgr, &$input, $request, &$locale)
public function &process(&$input)
{
// Initialize view
- $locale = AppLocale::getLocale();
+ $locale = Locale::getLocale();
$request = Application::get()->getRequest();
$templateMgr = TemplateManager::getManager($request);
diff --git a/classes/filter/TypeDescriptionFactory.inc.php b/classes/filter/TypeDescriptionFactory.inc.php
index 837ee879e1f..cc92a02a16d 100644
--- a/classes/filter/TypeDescriptionFactory.inc.php
+++ b/classes/filter/TypeDescriptionFactory.inc.php
@@ -127,10 +127,7 @@ public function _namespaceMap($namespace)
self::TYPE_DESCRIPTION_NAMESPACE_XML => 'lib.pkp.classes.xslt.XMLTypeDescription',
self::TYPE_DESCRIPTION_NAMESPACE_VALIDATOR => 'lib.pkp.classes.validation.ValidatorTypeDescription'
];
- if (!isset($namespaceMap[$namespace])) {
- return null;
- }
- return $namespaceMap[$namespace];
+ return $namespaceMap[$namespace] ?? null;
}
}
diff --git a/classes/form/Form.inc.php b/classes/form/Form.inc.php
index d1ea1344452..9fad62c1169 100644
--- a/classes/form/Form.inc.php
+++ b/classes/form/Form.inc.php
@@ -23,10 +23,11 @@
namespace PKP\form;
use APP\core\Application;
-use APP\i18n\AppLocale;
+use PKP\facades\Locale;
use APP\notification\NotificationManager;
use APP\template\TemplateManager;
+use PKP\session\SessionManager;
use PKP\notification\PKPNotification;
use PKP\plugins\HookRegistry;
@@ -75,15 +76,15 @@ class Form
public function __construct($template = null, $callHooks = true, $requiredLocale = null, $supportedLocales = null)
{
if ($requiredLocale === null) {
- $requiredLocale = AppLocale::getPrimaryLocale();
+ $requiredLocale = Locale::getPrimaryLocale();
}
$this->requiredLocale = $requiredLocale;
if ($supportedLocales === null) {
- $supportedLocales = AppLocale::getSupportedFormLocales();
+ $supportedLocales = Locale::getSupportedFormLocales();
}
$this->supportedLocales = $supportedLocales;
- $this->defaultLocale = AppLocale::getLocale();
+ $this->defaultLocale = Locale::getLocale();
$this->_template = $template;
$this->_data = [];
@@ -299,7 +300,7 @@ public function validate($callHooks = true)
}
}
- if (!defined('SESSION_DISABLE_INIT')) {
+ if (!SessionManager::isDisabled()) {
$request = Application::get()->getRequest();
$user = $request->getUser();
@@ -495,11 +496,11 @@ public function _decomposeArray($name, $value, $stack)
$returner .= $this->_decomposeArray($name, $subValue, $newStack);
}
} else {
- $name = htmlentities($name, ENT_COMPAT, LOCALE_ENCODING);
- $value = htmlentities($value, ENT_COMPAT, LOCALE_ENCODING);
+ $name = htmlentities($name, ENT_COMPAT);
+ $value = htmlentities($value, ENT_COMPAT);
$returner .= '\n";
diff --git a/classes/form/FormBuilderVocabulary.inc.php b/classes/form/FormBuilderVocabulary.inc.php
index e74878da2ab..acf65c39e28 100644
--- a/classes/form/FormBuilderVocabulary.inc.php
+++ b/classes/form/FormBuilderVocabulary.inc.php
@@ -359,7 +359,7 @@ public function _smartyFBVButton($params, $smarty)
case 'disabled':
$smarty->assign('FBV_' . $key, $value);
break;
- default: $buttonParams .= htmlspecialchars($key, ENT_QUOTES, LOCALE_ENCODING) . '="' . htmlspecialchars($value, ENT_QUOTES, LOCALE_ENCODING) . '" ';
+ default: $buttonParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
}
}
@@ -448,7 +448,7 @@ public function _smartyFBVTextInput($params, $smarty)
}
break;
case 'placeholder':
- $textInputParams .= 'placeholder="' . htmlspecialchars(__($value), ENT_QUOTES, LOCALE_ENCODING) . '" ';
+ $textInputParams .= 'placeholder="' . htmlspecialchars(__($value), ENT_QUOTES) . '" ';
break;
case 'disabled':
case 'readonly':
@@ -460,11 +460,11 @@ public function _smartyFBVTextInput($params, $smarty)
$smarty->assign('FBV_' . $key, $value); break;
case 'required':
if ($value) {
- $textInputParams .= 'required="' . htmlspecialchars($value, ENT_QUOTES, LOCALE_ENCODING) . '" ';
+ $textInputParams .= 'required="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
}
break;
default:
- $textInputParams .= htmlspecialchars($key, ENT_QUOTES, LOCALE_ENCODING) . '="' . htmlspecialchars($value, ENT_QUOTES, LOCALE_ENCODING) . '" ';
+ $textInputParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
}
}
@@ -537,7 +537,7 @@ public function _smartyFBVTextArea($params, $smarty)
}
break;
case 'id': break; // if we don't do this, the textarea ends up with two id attributes because FBV_id is also set.
- default: $textAreaParams .= htmlspecialchars($key, ENT_QUOTES, LOCALE_ENCODING) . '="' . htmlspecialchars($value, ENT_QUOTES, LOCALE_ENCODING) . '" ';
+ default: $textAreaParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
}
}
@@ -568,7 +568,7 @@ public function _smartyFBVHiddenInput($params, $smarty)
break;
case 'label': break;
case 'type': break;
- default: $hiddenInputParams .= htmlspecialchars($key, ENT_QUOTES, LOCALE_ENCODING) . '="' . htmlspecialchars($value, ENT_QUOTES, LOCALE_ENCODING) . '" ';
+ default: $hiddenInputParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
}
}
@@ -620,7 +620,7 @@ public function _smartyFBVSelect($params, $smarty)
break;
case 'subLabelTranslate': break;
case 'label': $smarty->assign('FBV_label_content', $this->_smartyFBVSubLabel($params, $smarty)); break;
- default: $selectParams .= htmlspecialchars($key, ENT_QUOTES, LOCALE_ENCODING) . '="' . htmlspecialchars($value, ENT_QUOTES, LOCALE_ENCODING) . '" ';
+ default: $selectParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
}
}
@@ -660,7 +660,7 @@ public function _smartyFBVCheckboxGroup($params, $smarty)
case 'type': break;
case 'inline': break;
case 'subLabelTranslate': break;
- default: $checkboxParams .= htmlspecialchars($key, ENT_QUOTES, LOCALE_ENCODING) . '="' . htmlspecialchars($value, ENT_QUOTES, LOCALE_ENCODING) . '" ';
+ default: $checkboxParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
}
}
@@ -693,7 +693,7 @@ public function _smartyFBVCheckbox($params, $smarty)
case 'disabled':
$smarty->assign('FBV_' . $key, $value);
break;
- default: $checkboxParams .= htmlspecialchars($key, ENT_QUOTES, LOCALE_ENCODING) . '="' . htmlspecialchars($value, ENT_QUOTES, LOCALE_ENCODING) . '" ';
+ default: $checkboxParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
}
}
@@ -731,7 +731,7 @@ public function _smartyFBVRadioButton($params, $smarty)
case 'content':
$smarty->assign('FBV_' . $key, $value);
break;
- default: $radioParams .= htmlspecialchars($key, ENT_QUOTES, LOCALE_ENCODING) . '="' . htmlspecialchars($value, ENT_QUOTES, LOCALE_ENCODING) . '" ';
+ default: $radioParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
}
}
@@ -937,7 +937,7 @@ public function smartyFieldLabel($params, $smarty)
$returner = '';
if (isset($params) && !empty($params)) {
if (isset($params['key'])) {
- $params['label'] = __($params['key'], $params);
+ $params['label'] = __($params['key'], $params ?? []);
}
$form = $this->getForm();
diff --git a/classes/form/validation/FormValidatorLocale.inc.php b/classes/form/validation/FormValidatorLocale.inc.php
index b8ade0adc10..f8065f6b977 100644
--- a/classes/form/validation/FormValidatorLocale.inc.php
+++ b/classes/form/validation/FormValidatorLocale.inc.php
@@ -15,7 +15,7 @@
namespace PKP\form\validation;
-use APP\i18n\AppLocale;
+use PKP\facades\Locale;
class FormValidatorLocale extends FormValidator
{
@@ -36,7 +36,7 @@ public function __construct(&$form, $field, $type, $message, $requiredLocale = n
{
parent::__construct($form, $field, $type, $message, $validator);
if ($requiredLocale === null) {
- $requiredLocale = AppLocale::getPrimaryLocale();
+ $requiredLocale = Locale::getPrimaryLocale();
}
$this->_requiredLocale = $requiredLocale;
}
@@ -53,8 +53,7 @@ public function __construct(&$form, $field, $type, $message, $requiredLocale = n
*/
public function getMessage()
{
- $allLocales = AppLocale::getAllLocales();
- return parent::getMessage() . ' (' . $allLocales[$this->_requiredLocale] . ')';
+ return parent::getMessage() . ' (' . Locale::getMetadata($this->_requiredLocale)->getDisplayName() . ')';
}
//
diff --git a/classes/handler/APIHandler.inc.php b/classes/handler/APIHandler.inc.php
index 31f28cea362..7df76aa675e 100644
--- a/classes/handler/APIHandler.inc.php
+++ b/classes/handler/APIHandler.inc.php
@@ -17,7 +17,6 @@
use APP\core\Application;
use APP\core\Services;
-use APP\i18n\AppLocale;
use PKP\config\Config;
use PKP\core\APIResponse;
use PKP\plugins\HookRegistry;
@@ -30,8 +29,6 @@
use Slim\App;
-AppLocale::requireComponents(LOCALE_COMPONENT_PKP_API, LOCALE_COMPONENT_APP_API);
-
class APIHandler extends PKPHandler
{
protected $_app;
diff --git a/classes/handler/PKPHandler.inc.php b/classes/handler/PKPHandler.inc.php
index b16b85b048d..d2082b0fcff 100644
--- a/classes/handler/PKPHandler.inc.php
+++ b/classes/handler/PKPHandler.inc.php
@@ -17,7 +17,6 @@
namespace PKP\handler;
use APP\core\Application;
-use APP\i18n\AppLocale;
use APP\template\TemplateManager;
use PKP\config\Config;
use PKP\core\Dispatcher;
@@ -29,7 +28,7 @@
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\authorization\HttpsPolicy;
use PKP\security\authorization\RestrictedSiteAccessPolicy;
-
+use PKP\session\SessionManager;
use PKP\security\authorization\UserRolesRequiredPolicy;
use PKP\security\Role;
use PKP\security\Validation;
@@ -328,8 +327,7 @@ public function authorize($request, &$args, $roleAssignments)
// Ensure the allowed hosts setting, when provided, is respected.
$this->addPolicy(new AllowedHostsPolicy($request), true);
-
- if (!defined('SESSION_DISABLE_INIT')) {
+ if (!SessionManager::isDisabled()) {
// Add user roles in authorized context.
$user = $request->getUser();
if ($user instanceof \PKP\user\User || $request->getRouter() instanceof \PKP\core\APIRouter) {
@@ -455,7 +453,7 @@ public static function getRangeInfo($request, $rangeName, $contextData = null)
$context = $request->getContext();
$pageNum = $request->getUserVar(self::getPageParamName($rangeName));
if (empty($pageNum)) {
- $session = & $request->getSession();
+ $session = $request->getSession();
$pageNum = 1; // Default to page 1
if ($session && $contextData !== null) {
// See if we can get a page number from a prior request
@@ -472,7 +470,7 @@ public static function getRangeInfo($request, $rangeName, $contextData = null)
}
}
} else {
- $session = & $request->getSession();
+ $session = $request->getSession();
if ($session && $contextData !== null) {
// Store the page number
$contextHash = self::hashPageContext($request, $contextData);
@@ -522,17 +520,7 @@ public function setupTemplate($request)
}
assert($request instanceof \PKP\core\PKPRequest);
- AppLocale::requireComponents(
- LOCALE_COMPONENT_PKP_COMMON,
- LOCALE_COMPONENT_PKP_USER,
- LOCALE_COMPONENT_APP_COMMON
- );
-
$userRoles = (array) $this->getAuthorizedContextObject(ASSOC_TYPE_USER_ROLES);
- if (array_intersect([Role::ROLE_ID_MANAGER], $userRoles)) {
- AppLocale::requireComponents(LOCALE_COMPONENT_PKP_MANAGER);
- }
-
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign('userRoles', $userRoles);
diff --git a/classes/i18n/Locale.inc.php b/classes/i18n/Locale.inc.php
new file mode 100644
index 00000000000..d33dbe7469d
--- /dev/null
+++ b/classes/i18n/Locale.inc.php
@@ -0,0 +1,437 @@
+translate($key, null, $params, $locale);
+ }
+
+ /**
+ * @copy \Illuminate\Contracts\Translation\Translator::choice()
+ */
+ public function choice($key, $number, array $params = [], $locale = null): string
+ {
+ return $this->translate($key, $number, $params, $locale);
+ }
+
+ /**
+ * @copy \Illuminate\Contracts\Translation\Translator::getLocale()
+ */
+ public function getLocale(): string
+ {
+ if (isset($this->locale)) {
+ return $this->locale;
+ }
+ $request = $this->_getRequest();
+ $locale = $request->getUserVar('setLocale')
+ ?: (SessionManager::hasSession() ? SessionManager::getManager()->getUserSession()->getSessionVar('currentLocale') : null)
+ ?: $request->getCookieVar('currentLocale');
+ $this->setLocale($locale);
+ return $this->locale;
+ }
+
+ /**
+ * @copy \Illuminate\Contracts\Translation\Translator::setLocale()
+ */
+ public function setLocale($locale): void
+ {
+ if (!$this->isLocaleValid($locale) || !$this->isSupported($locale)) {
+ if ($locale) {
+ error_log((string) new InvalidArgumentException("Invalid/unsupported locale \"${locale}\", default locale restored"));
+ }
+ $locale = $this->getPrimaryLocale();
+ }
+
+ $this->locale = $locale;
+ setlocale(LC_ALL, "${locale}.utf-8", $locale);
+ putenv("LC_ALL=${locale}");
+ }
+
+ /**
+ * @copy LocaleInterface::getPrimaryLocale()
+ */
+ public function getPrimaryLocale(): string
+ {
+ if (isset($this->primaryLocale)) {
+ return $this->primaryLocale;
+ }
+ $request = $this->_getRequest();
+ $locale = null;
+ if (SessionManager::isDisabled()) {
+ $locale = $this->getDefaultLocale();
+ } elseif ($context = $request->getContext()) {
+ $locale = $context->getPrimaryLocale();
+ } elseif ($site = $request->getSite()) {
+ $locale = $site->getPrimaryLocale();
+ }
+ return $this->primaryLocale = $this->isLocaleValid($locale)
+ ? $locale
+ : $this->getDefaultLocale();
+ }
+
+ /**
+ * @copy LocaleInterface::registerPath()
+ */
+ public function registerPath(string $path, int $priority = 0): void
+ {
+ $path = new SplFileInfo($path);
+ if (!$path->isDir()) {
+ throw new InvalidArgumentException("${path} isn't a valid folder");
+ }
+
+ // Invalidate the loaded bundles cache
+ $realPath = $path->getRealPath();
+ if (($this->paths[$realPath] ?? null) !== $priority) {
+ $this->paths[$realPath] = $priority;
+ $this->localeBundles = [];
+ $this->locales = null;
+ }
+ }
+
+ /**
+ * @copy LocaleInterface::registerLoader()
+ */
+ public function registerLoader(callable $fileLoader, int $priority = 0): void
+ {
+ // Invalidate the loaded bundles cache
+ if (array_search($fileLoader, $this->loaders[$priority] ?? [], true) === false) {
+ $this->loaders[$priority][] = $fileLoader;
+ $this->localeBundles = [];
+ ksort($this->loaders, SORT_NUMERIC);
+ }
+ }
+
+ /**
+ * @copy LocaleInterface::isLocaleValid()
+ */
+ public function isLocaleValid(?string $locale): bool
+ {
+ return !empty($locale) && preg_match(LocaleInterface::LOCALE_EXPRESSION, $locale);
+ }
+
+ /**
+ * @copy LocaleInterface::getMetadata()
+ */
+ public function getMetadata(string $locale): ?LocaleMetadata
+ {
+ return $this->getLocales()[$locale] ?? null;
+ }
+
+ /**
+ * @copy LocaleInterface::getLocales()
+ */
+ public function getLocales(): array
+ {
+ $key = __METHOD__ . static::MAX_CACHE_LIFETIME . array_reduce(array_keys($this->paths), fn(string $hash, string $path): string => sha1($hash . $path), '');
+ $expiration = DateInterval::createFromDateString(static::MAX_CACHE_LIFETIME);
+ return $this->locales ??= Cache::remember($key, $expiration, function () {
+ $locales = [];
+ foreach (array_keys($this->paths) as $folder) {
+ foreach (new DirectoryIterator($folder) as $cursor) {
+ if ($cursor->isDir() && $this->isLocaleValid($cursor->getBasename())) {
+ $locales[$cursor->getBasename()] ??= LocaleMetadata::create($cursor->getBasename());
+ }
+ }
+ }
+ ksort($locales);
+ return $locales;
+ });
+ }
+
+ /**
+ * @copy LocaleInterface::installLocale()
+ */
+ public function installLocale(string $locale): void
+ {
+ Repo::emailTemplate()->dao->installEmailTemplateLocaleData(Repo::emailTemplate()->dao->getMainEmailTemplatesFilename(), [$locale]);
+
+ // Load all plugins so they can add locale data if needed
+ $categories = PluginRegistry::getCategories();
+ foreach ($categories as $category) {
+ PluginRegistry::loadCategory($category);
+ }
+ HookRegistry::call('Locale::installLocale', [&$locale]);
+ }
+
+ /**
+ * @copy LocaleInterface::uninstallLocale()
+ */
+ public function uninstallLocale(string $locale): void
+ {
+ // Delete locale-specific data
+ Repo::emailTemplate()->dao->deleteEmailTemplatesByLocale($locale);
+ Repo::emailTemplate()->dao->deleteDefaultEmailTemplatesByLocale($locale);
+ }
+
+ /**
+ * Retrieves whether the given locale is supported
+ */
+ public function isSupported(string $locale): bool
+ {
+ static $locales;
+ $locales ??= SessionManager::isDisabled()
+ ? array_keys($this->getLocales())
+ : (($context = $this->_getRequest()->getContext()) ? $context->getSupportedLocales() : $this->_getRequest()->getSite()->getSupportedLocales());
+ return in_array($locale, $locales);
+ }
+
+ /**
+ * @copy LocaleInterface::getSupportedFormLocales()
+ */
+ public function getSupportedFormLocales(): array
+ {
+ return $this->supportedFormLocales ??= (fn(): array => SessionManager::isDisabled()
+ ? array_map(fn(LocaleMetadata $locale) => $locale->locale, $this->getLocales())
+ : (($context = $this->_getRequest()->getContext()) ? $context->getSupportedFormLocaleNames() : $this->_getRequest()->getSite()->getSupportedLocaleNames())
+ )();
+ }
+
+ /**
+ * @copy LocaleInterface::getSupportedLocales()
+ */
+ public function getSupportedLocales(): array
+ {
+ return $this->supportedLocales ??= (fn(): array => SessionManager::isDisabled()
+ ? array_map(fn(LocaleMetadata $locale) => $locale->locale, $this->getLocales())
+ : ($this->_getRequest()->getContext() ?? $this->_getRequest()->getSite())->getSupportedLocaleNames()
+ )();
+ }
+
+ /**
+ * @copy LocaleInterface::setMissingKeyHandler()
+ */
+ public function setMissingKeyHandler(?callable $handler): void
+ {
+ $this->missingKeyHandler = $handler;
+ }
+
+ /**
+ * @copy LocaleInterface::getMissingKeyHandler()
+ */
+ public function getMissingKeyHandler(): ?callable
+ {
+ return $this->missingKeyHandler;
+ }
+
+ /**
+ * @copy LocaleInterface::getBundle()
+ */
+ public function getBundle(?string $locale = null, bool $useCache = true): LocaleBundle
+ {
+ $locale ??= $this->getLocale();
+ $getter = function () use ($locale): LocaleBundle {
+ $bundle = [];
+ foreach ($this->paths as $folder => $priority) {
+ $bundle += $this->_getLocaleFiles($folder, $locale, $priority);
+ }
+ foreach ($this->loaders as $loader) {
+ $loader($locale, $bundle);
+ }
+ return new LocaleBundle($locale, $bundle);
+ };
+ return $useCache ? $this->localeBundles[$locale] ??= $getter() : $getter();
+ }
+
+ /**
+ * @copy LocaleInterface::getDefaultLocale()
+ */
+ public function getDefaultLocale(): string
+ {
+ return Config::getVar('i18n', 'locale');
+ }
+
+ /**
+ * @copy LocaleInterface::getCountries()
+ */
+ public function getCountries(?string $locale = null): Countries
+ {
+ return $this->_getLocaleCache(__METHOD__, $locale, fn () => $this->_getIsoCodes($locale)->getCountries());
+ }
+
+ /**
+ * @copy LocaleInterface::getCurrencies()
+ */
+ public function getCurrencies(?string $locale = null): Currencies
+ {
+ return $this->_getLocaleCache(__METHOD__, $locale, fn () => $this->_getIsoCodes($locale)->getCurrencies());
+ }
+
+ /**
+ * @copy LocaleInterface::getLanguages()
+ */
+ public function getLanguages(?string $locale = null): LanguagesInterface
+ {
+ return $this->_getLocaleCache(__METHOD__, $locale, fn () => $this->_getIsoCodes($locale)->getLanguages());
+ }
+
+ /**
+ * @copy LocaleInterface::getScripts()
+ */
+ public function getScripts(?string $locale = null): Scripts
+ {
+ return $this->_getLocaleCache(__METHOD__, $locale, fn () => $this->_getIsoCodes($locale)->getScripts());
+ }
+
+
+ /**
+ * Translates the texts
+ */
+ protected function translate(string $key, ?int $number, array $params, ?string $locale): string
+ {
+ if (($key = trim($key)) === '') {
+ return '';
+ }
+
+ $locale ??= $this->getLocale();
+ $localeBundle = $this->getBundle($locale);
+ $value = $number === null ? $localeBundle->translateSingular($key, $params) : $localeBundle->translatePlural($key, $number, $params);
+ if ($value ?? HookRegistry::call('Locale::translate', [&$value, $key, $params, $number, $locale, $localeBundle])) {
+ return $value;
+ }
+
+ error_log("Missing locale key \"${key}\" for the locale \"${locale}\"");
+ return is_callable($this->missingKeyHandler) ? ($this->missingKeyHandler)($key) : '##' . htmlentities($key) . '##';
+ }
+
+ /**
+ * Retrieves a cached item only if it belongs to the current locale. If it doesn't exist, the getter will be called
+ */
+ private function _getLocaleCache(string $key, ?string $locale, callable $getter)
+ {
+ if (($locale ??= $this->getLocale()) !== $this->getLocale()) {
+ return $getter();
+ }
+ if (!isset($this->cache[$key][$locale])) {
+ // Ensures the previous cache is cleared
+ $this->cache[$key] = [$locale => $getter()];
+ }
+ return $this->cache[$key][$locale];
+ }
+
+ /**
+ * Given a locale folder, retrieves all locale files (.po)
+ *
+ * @return int[]
+ */
+ private function _getLocaleFiles(string $folder, string $locale, int $priority): array
+ {
+ $files = $this->localeFiles[$folder][$locale] ?? null;
+ if ($files === null) {
+ $files = [];
+ if (is_dir($path = "${folder}/${locale}")) {
+ $directory = new RecursiveDirectoryIterator($path);
+ $iterator = new RecursiveIteratorIterator($directory);
+ $files = array_keys(iterator_to_array(new RegexIterator($iterator, '/\.po$/i', RecursiveRegexIterator::GET_MATCH)));
+ }
+ $this->localeFiles[$folder][$locale] = $files;
+ }
+ return array_fill_keys($files, $priority);
+ }
+
+ /**
+ * Retrieves the request
+ */
+ private function _getRequest(): PKPRequest
+ {
+ return app(PKPRequest::class);
+ }
+
+ /**
+ * Retrieves the ISO codes factory
+ */
+ private function _getIsoCodes(string $locale = null): IsoCodesFactory
+ {
+ return app(IsoCodesFactory::class, $locale ? ['locale' => $locale] : []);
+ }
+}
diff --git a/classes/i18n/LocaleConversion.inc.php b/classes/i18n/LocaleConversion.inc.php
new file mode 100644
index 00000000000..d55ea6b149a
--- /dev/null
+++ b/classes/i18n/LocaleConversion.inc.php
@@ -0,0 +1,122 @@
+ $locale->getIsoAlpha2() === $iso2Letter);
+ return $locale ? $locale->getIsoAlpha3() : null;
+ }
+
+ /**
+ * Translate the ISO 3-letter language string (ISO639-2b) into a ISO compatible 2-letter string (ISO639-1).
+ */
+ public static function get2LetterFrom3LetterIsoLanguage(?string $iso3Letter): ?string
+ {
+ assert(strlen($iso3Letter) === 3);
+ $locale = Arr::first(Locale::getLocales(), fn(LocaleMetadata $locale) => $locale->getIsoAlpha3() === $iso3Letter);
+ return $locale ? $locale->getIsoAlpha2() : null;
+ }
+
+ /**
+ * Translate the PKP locale identifier into an ISO639-2b compatible 3-letter string.
+ */
+ public static function get3LetterIsoFromLocale(?string $locale): ?string
+ {
+ assert(strlen($locale) >= 5);
+ $iso2Letter = substr($locale, 0, 2);
+ return static::get3LetterFrom2LetterIsoLanguage($iso2Letter);
+ }
+
+ /**
+ * Translate an ISO639-2b compatible 3-letter string into the PKP locale identifier.
+ * This can be ambiguous if several locales are defined for the same language. In this case we'll use the primary locale to disambiguate.
+ * If that still doesn't determine a unique locale then we'll choose the first locale found.
+ */
+ public static function getLocaleFrom3LetterIso(?string $iso3Letter): ?string
+ {
+ assert(strlen($iso3Letter) === 3);
+ $primaryLocale = Locale::getPrimaryLocale();
+
+ $candidates = [];
+ foreach (Locale::getLocales() as $identifier => $locale) {
+ if ($locale->getIsoAlpha3() === $iso3Letter) {
+ if ($identifier === $primaryLocale) {
+ // In case of ambiguity the primary locale overrides all other options so we're done.
+ return $primaryLocale;
+ }
+ $candidates[$identifier] = true;
+ }
+ }
+
+ // Attempts to retrieve the first matching locale which is in the supported list, otherwise defaults to the first found candidate
+ return Arr::first(array_keys(Locale::getSupportedLocales()), fn(string $locale) => $candidates[$locale] ?? false, array_key_first($candidates));
+ }
+
+ /**
+ * Translate the ISO 2-letter language string (ISO639-1) into ISO639-3.
+ */
+ public static function getIso3FromIso1(?string $iso1): ?string
+ {
+ assert(strlen($iso1) === 2);
+ $locale = Arr::first(Locale::getLocales(), fn(LocaleMetadata $locale) => $locale->getIsoAlpha2() === $iso1);
+ return $locale ? $locale->getIsoAlpha3() : null;
+ }
+
+ /**
+ * Translate the ISO639-3 into ISO639-1.
+ */
+ public static function getIso1FromIso3(?string $iso3): ?string
+ {
+ assert(strlen($iso3) === 3);
+ $locale = Arr::first(Locale::getLocales(), fn(LocaleMetadata $locale) => $locale->getIsoAlpha3() === $iso3);
+ return $locale ? $locale->getIsoAlpha2() : null;
+ }
+
+ /**
+ * Translate the PKP locale identifier into an ISO639-3 compatible 3-letter string.
+ */
+ public static function getIso3FromLocale(?string $locale): ?string
+ {
+ assert(strlen($locale) >= 5);
+ $iso1 = substr($locale, 0, 2);
+ return static::getIso3FromIso1($iso1);
+ }
+
+ /**
+ * Translate the PKP locale identifier into an ISO639-1 compatible 2-letter string.
+ */
+ public static function getIso1FromLocale(?string $locale): string
+ {
+ assert(strlen($locale) >= 5);
+ return substr($locale, 0, 2);
+ }
+}
diff --git a/classes/i18n/LocaleFile.inc.php b/classes/i18n/LocaleFile.inc.php
deleted file mode 100644
index c06bdc34308..00000000000
--- a/classes/i18n/LocaleFile.inc.php
+++ /dev/null
@@ -1,236 +0,0 @@
-locale = $locale;
- $this->filename = $filename;
- }
-
- /**
- * Get the cache object for this locale file.
- */
- public function _getCache($locale)
- {
- if (!isset($this->cache)) {
- $cacheManager = CacheManager::getManager();
- $this->cache = $cacheManager->getFileCache(
- 'locale',
- md5($this->filename),
- [$this, '_cacheMiss']
- );
-
- // Check to see if the cache is outdated.
- // Only some kinds of caches track cache dates;
- // if there's no date available (ie cachedate is
- // null), we have to assume it's up to date.
- $cacheTime = $this->cache->getCacheTime();
- if ($cacheTime === null || $cacheTime < filemtime($this->filename)) {
- // This cache is out of date; flush it.
- $this->cache->setEntireCache(self::load($this->filename));
- }
- }
- return $this->cache;
- }
-
- /**
- * Register a cache miss.
- */
- public function _cacheMiss($cache, $id)
- {
- return null; // It's not in this locale file.
- }
-
- /**
- * Get the filename for this locale file.
- */
- public function getFilename()
- {
- return $this->filename;
- }
-
- /**
- * Translate a string using the selected locale.
- * Substitution works by replacing tokens like "{$foo}" with the value of
- * the parameter named "foo" (if supplied).
- *
- * @param string $key
- * @param array $params named substitution parameters
- * @param string $locale the locale to use
- *
- * @return string
- */
- public function translate($key, $params = [], $locale = null)
- {
- if ($this->isValid()) {
- $key = trim($key);
- if (empty($key)) {
- return '';
- }
-
- $cache = $this->_getCache($this->locale);
- $message = $cache->get($key);
- if (!isset($message)) {
- // Try to force loading the plugin locales.
- $message = $this->_cacheMiss($cache, $key);
- }
-
- if (isset($message)) {
- if (!empty($params)) {
- // Substitute custom parameters
- foreach ($params as $key => $value) {
- $message = str_replace("{\$${key}}", (string) $value, $message);
- }
- }
-
- // if client encoding is set to iso-8859-1, transcode string from utf8 since we store all XML files in utf8
- if (LOCALE_ENCODING == 'iso-8859-1') {
- $message = utf8_decode($message);
- }
-
- return $message;
- }
- }
- return null;
- }
-
- /**
- * Static method: Load a locale array from a file. Not cached!
- *
- * @param string $filename Filename to locale .po file to load
- *
- * @return array
- */
- public static function &load($filename)
- {
- $localeData = [];
- $loader = new \Gettext\Loader\PoLoader();
- foreach ($loader->loadFile($filename) as $translation) {
- $localeData[$translation->getOriginal()] = $translation->getTranslation();
- }
- return $localeData;
- }
-
- /**
- * Check if a locale is valid.
- *
- * @return bool
- */
- public function isValid()
- {
- return isset($this->locale) && file_exists($this->filename);
- }
-
- /**
- * Test a locale file against the given reference locale file and
- * return an array of errorType => array(errors).
- *
- * @param object $referenceLocaleFile
- *
- * @return array
- */
- public function testLocale(&$referenceLocaleFile)
- {
- $errors = [
- LOCALE_ERROR_MISSING_KEY => [],
- LOCALE_ERROR_EXTRA_KEY => [],
- LOCALE_ERROR_DIFFERING_PARAMS => [],
- LOCALE_ERROR_MISSING_FILE => []
- ];
-
- if ($referenceLocaleFile->isValid()) {
- if (!$this->isValid()) {
- $errors[LOCALE_ERROR_MISSING_FILE][] = [
- 'locale' => $this->locale,
- 'filename' => $this->filename
- ];
- return $errors;
- }
- } else {
- // If the reference file itself does not exist or is invalid then
- // there's nothing to be translated here.
- return $errors;
- }
-
- $localeContents = self::load($this->filename);
- $referenceContents = self::load($referenceLocaleFile->filename);
-
- foreach ($referenceContents as $key => $referenceValue) {
- if (!isset($localeContents[$key])) {
- $errors[LOCALE_ERROR_MISSING_KEY][] = [
- 'key' => $key,
- 'locale' => $this->locale,
- 'filename' => $this->filename,
- 'reference' => $referenceValue
- ];
- continue;
- }
- $value = $localeContents[$key];
-
- $referenceParams = AppLocale::getParameterNames($referenceValue);
- $params = AppLocale::getParameterNames($value);
- if (count(array_diff($referenceParams, $params)) > 0) {
- $errors[LOCALE_ERROR_DIFFERING_PARAMS][] = [
- 'key' => $key,
- 'locale' => $this->locale,
- 'mismatch' => array_diff($referenceParams, $params),
- 'filename' => $this->filename,
- 'reference' => $referenceValue,
- 'value' => $value
- ];
- }
- // After processing a key, remove it from the list;
- // this way, the remainder at the end of the loop
- // will be extra unnecessary keys.
- unset($localeContents[$key]);
- }
-
- // Leftover keys are extraneous.
- foreach ($localeContents as $key => $value) {
- $errors[LOCALE_ERROR_EXTRA_KEY][] = [
- 'key' => $key,
- 'locale' => $this->locale,
- 'filename' => $this->filename
- ];
- }
-
- return $errors;
- }
-}
-
-if (!PKP_STRICT_MODE) {
- class_alias('\PKP\i18n\LocaleFile', '\LocaleFile');
-}
diff --git a/classes/i18n/LocaleMetadata.inc.php b/classes/i18n/LocaleMetadata.inc.php
new file mode 100644
index 00000000000..52de43d9dfa
--- /dev/null
+++ b/classes/i18n/LocaleMetadata.inc.php
@@ -0,0 +1,171 @@
+locale = $locale;
+ return $instance;
+ }
+
+ /**
+ * Retrieves the display name
+ */
+ public function getDisplayName(?string $locale = null): string
+ {
+ $country = $this->getCountry($locale);
+ // Remove parenthesis from the language name and also convert the its character to uppercase (due to the IsoCodes texts)
+ return PKPString::regexp_replace('/\s*\([^)]*\)\s*/', '', PKPString::ucfirst($this->_getLanguage($locale)->getLocalName())) . ($country ? " (${country})" : '');
+ }
+
+ /**
+ * Retrieves the language name
+ */
+ public function getLanguage(?string $locale = null): string
+ {
+ return $this->_getLanguage($locale)->getLocalName();
+ }
+
+ /**
+ * Retrieves the country name
+ */
+ public function getCountry(?string $locale = null): ?string
+ {
+ return $this->_parse()->country ? Locale::getCountries($locale)->getByAlpha2($this->_parse()->country)->getLocalName() : null;
+ }
+
+ /**
+ * Retrieves the script name
+ */
+ public function getScript(?string $locale = null): ?string
+ {
+ $script = ucfirst($this->_parse()->script);
+ return $this->_parse()->script ? Locale::getScripts($locale)->getByAlpha4($script)->getLocalName() : null;
+ }
+
+ /**
+ * Whether the locale expects text on the right-to-left format
+ */
+ public function isRightToLeft(): bool
+ {
+ $locale = $this->_parse();
+ $language = strtolower($locale->language ?? '');
+ $script = strtolower($locale->script ?? '');
+ $rightToLeftLanguages = array_fill_keys(['ar', 'dv', 'fa', 'he', 'ku', 'nqo', 'prs', 'ps', 'sd', 'syr', 'ug', 'ur', 'yi'], true);
+ $languageScriptExceptions = ['sd-deva' => false, 'tzm-arab' => true, 'pa-arab' => true];
+ return $languageScriptExceptions["${language}-${script}"]
+ ?? $rightToLeftLanguages[$language]
+ ?? $languageScriptExceptions[$this->getIsoAlpha3() . "-${script}"]
+ ?? $rightToLeftLanguages[$this->getIsoAlpha3()]
+ ?? false;
+ }
+
+ /**
+ * Compares two locales and retrieves the completeness ratio (source locale keys which are present in the reference)
+ * If a locale isn't supplied, LocaleInterface::DEFAULT_LOCALE will be used as reference
+ */
+ public function getCompletenessRatio(string $referenceLocale = null): float
+ {
+ $destiny = Locale::getBundle($referenceLocale ?? LocaleInterface::DEFAULT_LOCALE)->getTranslator()->getEntries();
+ $source = Locale::getBundle($this->locale, false)->getTranslator()->getEntries();
+ $intersection = array_intersect_key($source, $destiny);
+ return min(1, count($intersection) / max(1, count($destiny)));
+ }
+
+ /**
+ * Retrieves whether the locale can be considered complete respecting a threshold level of completeness
+ */
+ public function isComplete(float $minimumThreshold = 0.9, ?string $referenceLocale = null): bool
+ {
+ return $this->getCompletenessRatio($referenceLocale) >= $minimumThreshold;
+ }
+
+ /**
+ * Retrieves the ISO639-2b representation
+ */
+ public function getIsoAlpha2(): string
+ {
+ return $this->_getLanguage()->getAlpha2();
+ }
+
+ /**
+ * Retrieves the ISO639-3 representation
+ */
+ public function getIsoAlpha3(): string
+ {
+ return $this->_getLanguage()->getAlpha3();
+ }
+
+ /**
+ * Private constructor
+ */
+ private function __construct()
+ {
+ }
+
+ /**
+ * Retrieves the language
+ */
+ private function _getLanguage(?string $locale = null): ?Language
+ {
+ return Locale::getLanguages($locale)->getByAlpha2($this->_parse()->language);
+ }
+
+ /**
+ * Parses the locale string and retrieve its pieces
+ */
+ private function _parse(): object
+ {
+ if (isset($this->_parsedLocale)) {
+ return $this->_parsedLocale;
+ }
+ if (!preg_match(LocaleInterface::LOCALE_EXPRESSION, $this->locale, $matches)) {
+ throw new DomainException("Invalid locale \"{$this->locale}\"");
+ }
+ return $this->_parsedLocale = (object) [
+ 'language' => $matches['language'],
+ 'country' => $matches['country'] ?? null,
+ // Updates our script definitions to match the ISO 15924
+ 'script' => isset($matches['script']) ? str_replace(['cyrillic', 'latin'], ['latn', 'cyrl'], strtolower($matches['script'])) : null
+ ];
+ }
+}
diff --git a/classes/i18n/LocaleServiceProvider.inc.php b/classes/i18n/LocaleServiceProvider.inc.php
new file mode 100644
index 00000000000..a44a55cbb93
--- /dev/null
+++ b/classes/i18n/LocaleServiceProvider.inc.php
@@ -0,0 +1,66 @@
+app->singleton(LocaleInterface::class, fn () => $this->app->make(Locale::class));
+ // Replaces the default Laravel translator
+ $this->app->alias(LocaleInterface::class, 'translator');
+ $this->app->alias(LocaleInterface::class, Translator::class);
+
+ // Reuses the instance and keeps the user selected language across the application
+ $this->app->singleton(
+ IsoCodesFactory::class,
+ fn (Container $container, array $params): IsoCodesFactory => new IsoCodesFactory(
+ null,
+ new IsoCodesTranslationDriver($params['locale'] ?? LocaleFacade::getLocale())
+ )
+ );
+ }
+
+ /**
+ * Get the services provided by the provider.
+ *
+ * @return array
+ */
+ public function provides(): array
+ {
+ return [LocaleInterface::class, Translator::class, 'translator'];
+ }
+}
diff --git a/classes/i18n/PKPLocale.inc.php b/classes/i18n/PKPLocale.inc.php
index c5ff1b409cf..f45b8b7a2e1 100644
--- a/classes/i18n/PKPLocale.inc.php
+++ b/classes/i18n/PKPLocale.inc.php
@@ -15,1032 +15,90 @@
* @class PKPLocale
* @ingroup i18n
*
- * @brief Provides methods for loading locale data and translating strings identified by unique keys
+ * @brief Deprecated class, kept only for backwards compatibility with external plugins
*/
namespace PKP\i18n;
-use APP\i18n\AppLocale;
-use Illuminate\Support\Facades\DB;
-use PKP\cache\CacheManager;
-use PKP\config\Config;
-use PKP\core\Registry;
-use PKP\db\DAORegistry;
-use PKP\db\XMLDAO;
-use PKP\facades\Repo;
-use PKP\plugins\HookRegistry;
-use PKP\plugins\PluginRegistry;
+use PKP\facades\Locale;
-if (!defined('LOCALE_DEFAULT')) {
- define('LOCALE_DEFAULT', Config::getVar('i18n', 'locale'));
-}
-if (!defined('LOCALE_ENCODING')) {
- define('LOCALE_ENCODING', Config::getVar('i18n', 'client_charset'));
-}
-
-// Error types for locale checking.
-// Note: Cannot use numeric symbols for the constants below because
-// array_merge_recursive doesn't treat numeric keys nicely.
-define('LOCALE_ERROR_MISSING_KEY', 'LOCALE_ERROR_MISSING_KEY');
-define('LOCALE_ERROR_EXTRA_KEY', 'LOCALE_ERROR_EXTRA_KEY');
-define('LOCALE_ERROR_DIFFERING_PARAMS', 'LOCALE_ERROR_DIFFERING_PARAMS');
-define('LOCALE_ERROR_MISSING_FILE', 'LOCALE_ERROR_MISSING_FILE');
-
-define('EMAIL_ERROR_MISSING_EMAIL', 'EMAIL_ERROR_MISSING_EMAIL');
-define('EMAIL_ERROR_EXTRA_EMAIL', 'EMAIL_ERROR_EXTRA_EMAIL');
-define('EMAIL_ERROR_DIFFERING_PARAMS', 'EMAIL_ERROR_DIFFERING_PARAMS');
-
-// Shared locale components
-define('LOCALE_COMPONENT_PKP_COMMON', 0x00000001);
-define('LOCALE_COMPONENT_PKP_ADMIN', 0x00000002);
-define('LOCALE_COMPONENT_PKP_INSTALLER', 0x00000003);
-define('LOCALE_COMPONENT_PKP_MANAGER', 0x00000004);
-define('LOCALE_COMPONENT_PKP_READER', 0x00000005);
-define('LOCALE_COMPONENT_PKP_SUBMISSION', 0x00000006);
-define('LOCALE_COMPONENT_PKP_USER', 0x00000007);
-define('LOCALE_COMPONENT_PKP_GRID', 0x00000008);
-define('LOCALE_COMPONENT_PKP_DEFAULT', 0x00000009);
-define('LOCALE_COMPONENT_PKP_EDITOR', 0x0000000A);
-define('LOCALE_COMPONENT_PKP_REVIEWER', 0x0000000B);
-define('LOCALE_COMPONENT_PKP_API', 0x0000000C);
-
-// Application-specific locale components
-define('LOCALE_COMPONENT_APP_COMMON', 0x00000100);
-define('LOCALE_COMPONENT_APP_MANAGER', 0x00000101);
-define('LOCALE_COMPONENT_APP_SUBMISSION', 0x00000102);
-define('LOCALE_COMPONENT_APP_AUTHOR', 0x00000103);
-define('LOCALE_COMPONENT_APP_EDITOR', 0x00000104);
-define('LOCALE_COMPONENT_APP_ADMIN', 0x00000105);
-define('LOCALE_COMPONENT_APP_DEFAULT', 0x00000106);
-define('LOCALE_COMPONENT_APP_API', 0x00000107);
-define('LOCALE_COMPONENT_APP_EMAIL', 0x00000108);
-
-use PKP\session\SessionManager;
-
-// (Let PHPUnit tests define this first if necessary)
-class PKPLocale
-{
- public const MASTER_LOCALE = 'en_US';
- public const LOCALE_REGISTRY_FILE = 'registry/locales.xml';
-
- public static $request;
-
- /**
- * Get all supported UI locales for the current context.
- *
- * @return array
- */
- public static function getSupportedLocales()
- {
- static $supportedLocales;
- if (!isset($supportedLocales)) {
- if (defined('SESSION_DISABLE_INIT')) {
- $supportedLocales = AppLocale::getAllLocales();
- } elseif (($context = self::$request->getContext())) {
- $supportedLocales = $context->getSupportedLocaleNames();
- } else {
- $site = self::$request->getSite();
- $supportedLocales = $site->getSupportedLocaleNames();
- }
- }
- return $supportedLocales;
- }
-
- /**
- * Get all supported form locales for the current context.
- *
- * @return array
- */
- public static function getSupportedFormLocales()
- {
- static $supportedFormLocales;
- if (!isset($supportedFormLocales)) {
- if (defined('SESSION_DISABLE_INIT')) {
- $supportedFormLocales = AppLocale::getAllLocales();
- } elseif (($context = self::$request->getContext())) {
- $supportedFormLocales = $context->getSupportedFormLocaleNames();
- } else {
- $site = self::$request->getSite();
- $supportedFormLocales = $site->getSupportedLocaleNames();
- }
- }
- return $supportedFormLocales;
- }
-
- /**
- * Return the key name of the user's currently selected locale (default
- * is "en_US" for U.S. English).
- *
- * @return string
- */
- public static function getLocale()
- {
- static $currentLocale;
- if (!isset($currentLocale)) {
- if (defined('SESSION_DISABLE_INIT')) {
- // If the locale is specified in the URL, allow
- // it to override. (Necessary when locale is
- // being set, as cookie will not yet be re-set)
- $locale = AppLocale::$request->getUserVar('setLocale');
- if (empty($locale) || !in_array($locale, array_keys(AppLocale::getSupportedLocales()))) {
- $locale = self::$request->getCookieVar('currentLocale');
- }
- } else {
- $sessionManager = SessionManager::getManager();
- $session = $sessionManager->getUserSession();
- $locale = self::$request->getUserVar('uiLocale');
-
- $context = self::$request->getContext();
- $site = self::$request->getSite();
-
- if (!isset($locale)) {
- $locale = $session->getSessionVar('currentLocale');
- }
-
- if (!isset($locale)) {
- $locale = self::$request->getCookieVar('currentLocale');
- }
-
- if (isset($locale)) {
- // Check if user-specified locale is supported
- if ($context != null) {
- $locales = $context->getSupportedLocaleNames();
- } else {
- $locales = $site->getSupportedLocaleNames();
- }
-
- if (!in_array($locale, array_keys($locales))) {
- unset($locale);
- }
- }
-
- if (!isset($locale)) {
- // Use context/site default
- if ($context != null) {
- $locale = $context->getPrimaryLocale();
- }
-
- if (!isset($locale)) {
- $locale = $site->getPrimaryLocale();
- }
- }
- }
-
- if (!AppLocale::isLocaleValid($locale)) {
- $locale = LOCALE_DEFAULT;
- }
-
- $currentLocale = $locale;
- }
- return $currentLocale;
- }
-
- /**
- * Get the stack of "important" locales, most important first.
- *
- * @return array
- */
- public static function getLocalePrecedence()
- {
- static $localePrecedence;
- if (!isset($localePrecedence)) {
- $localePrecedence = [AppLocale::getLocale()];
-
- $context = self::$request->getContext();
- if ($context && !in_array($context->getPrimaryLocale(), $localePrecedence)) {
- $localePrecedence[] = $context->getPrimaryLocale();
- }
-
- $site = self::$request->getSite();
- if ($site && !in_array($site->getPrimaryLocale(), $localePrecedence)) {
- $localePrecedence[] = $site->getPrimaryLocale();
- }
- }
- return $localePrecedence;
- }
-
- /**
- * Retrieve the primary locale of the current context.
- *
- * @return string
- */
- public static function getPrimaryLocale()
- {
- static $locale;
- if ($locale) {
- return $locale;
- }
-
- if (defined('SESSION_DISABLE_INIT')) {
- return $locale = LOCALE_DEFAULT;
- }
-
- $context = self::$request->getContext();
-
- if (isset($context)) {
- $locale = $context->getPrimaryLocale();
- }
-
- if (!isset($locale)) {
- $site = self::$request->getSite();
- $locale = $site->getPrimaryLocale();
- }
-
- if (!isset($locale) || !AppLocale::isLocaleValid($locale)) {
- $locale = LOCALE_DEFAULT;
- }
-
- return $locale;
- }
-
- /**
- * Get a list of locale files currently registered, either in all
- * locales (in an array for each locale), or for a specific locale.
- *
- * @param string $locale Locale identifier (optional)
- */
- public static function &getLocaleFiles($locale = null)
- {
- $localeFiles = & Registry::get('localeFiles', true, []);
- if ($locale !== null) {
- if (!isset($localeFiles[$locale])) {
- $localeFiles[$locale] = [];
- }
- return $localeFiles[$locale];
- }
- return $localeFiles;
- }
-
- /**
- * Add octothorpes to a key name for presentation of the key as missing.
- *
- * @param string $key
- *
- * @return string
- */
- public static function addOctothorpes($key)
- {
- return '##' . htmlentities($key) . '##';
- }
-
- /**
- * Translate a string using the selected locale.
- * Substitution works by replacing tokens like "{$foo}" with the value
- * of the parameter named "foo" (if supplied).
- *
- * @param string $key
- * @param array $params named substitution parameters
- * @param string $locale the locale to use
- * @param callable $missingKeyHandler Callback to be invoked when a key cannot be found.
- *
- * @return string
- */
- public static function translate($key, $params = [], $locale = null, $missingKeyHandler = [__CLASS__, 'addOctothorpes'])
- {
- if (!isset($locale)) {
- $locale = AppLocale::getLocale();
- }
- if (($key = trim($key)) == '') {
- return '';
- }
-
- $localeFiles = & AppLocale::getLocaleFiles($locale);
- $value = '';
- for ($i = count($localeFiles) - 1 ; $i >= 0 ; $i --) {
- $value = $localeFiles[$i]->translate($key, $params);
- if ($value !== null) {
- return $value;
- }
- }
-
- // Add a missing key to the debug notes.
- $notes = & Registry::get('system.debug.notes');
- $notes[] = ['debug.notes.missingLocaleKey', ['key' => $key]];
-
- if (!HookRegistry::call('PKPLocale::translate', [&$key, &$params, &$locale, &$localeFiles, &$value])) {
- // Add some octothorpes to missing keys to make them more obvious
- return $missingKeyHandler($key);
- } else {
- return $value;
- }
- }
-
- /**
- * Initialize the locale system.
- *
- * @param PKPRequest $request
- */
- public static function initialize($request)
- {
- self::$request = $request;
-
- // Use defaults if locale info unspecified.
- $locale = AppLocale::getLocale();
- setlocale(LC_ALL, $locale . '.' . LOCALE_ENCODING, $locale);
- putenv("LC_ALL=${locale}");
-
- AppLocale::registerLocaleFile($locale, "lib/pkp/locale/${locale}/common.po");
-
- // Set site time zone
- $timeZone = self::getTimeZone();
- date_default_timezone_set($timeZone);
-
- if (Config::getVar('general', 'installed')) {
- // Set the time zone for DB
- // Get the offset from UTC
- $now = new \DateTime();
- $mins = $now->getOffset() / 60;
- $sgn = ($mins < 0 ? -1 : 1);
- $mins = abs($mins);
- $hrs = floor($mins / 60);
- $mins -= $hrs * 60;
- $offset = sprintf('%+d:%02d', $hrs * $sgn, $mins);
-
- switch (Config::getVar('database', 'driver')) {
- case 'mysql':
- case 'mysqli':
- DB::statement('SET time_zone = \'' . $offset . '\'');
- break;
- case 'postgres':
- case 'postgres64':
- case 'postgres7':
- case 'postgres8':
- case 'postgres9':
- DB::statement('SET TIME ZONE INTERVAL \'' . $offset . '\' HOUR TO MINUTE');
- break;
- default: assert(false);
- }
- }
- }
-
- /**
- * Build an associative array of LOCALE_COMPOMENT_... => filename
- * (use getFilenameComponentMap instead)
- *
- * @param string $locale
- *
- * @return array
- */
- public static function makeComponentMap($locale)
- {
- $baseDir = "lib/pkp/locale/${locale}/";
-
- return [
- LOCALE_COMPONENT_PKP_COMMON => $baseDir . 'common.po',
- LOCALE_COMPONENT_PKP_ADMIN => $baseDir . 'admin.po',
- LOCALE_COMPONENT_PKP_INSTALLER => $baseDir . 'installer.po',
- LOCALE_COMPONENT_PKP_MANAGER => $baseDir . 'manager.po',
- LOCALE_COMPONENT_PKP_READER => $baseDir . 'reader.po',
- LOCALE_COMPONENT_PKP_SUBMISSION => $baseDir . 'submission.po',
- LOCALE_COMPONENT_PKP_EDITOR => $baseDir . 'editor.po',
- LOCALE_COMPONENT_PKP_REVIEWER => $baseDir . 'reviewer.po',
- LOCALE_COMPONENT_PKP_USER => $baseDir . 'user.po',
- LOCALE_COMPONENT_PKP_GRID => $baseDir . 'grid.po',
- LOCALE_COMPONENT_PKP_DEFAULT => $baseDir . 'default.po',
- LOCALE_COMPONENT_PKP_API => $baseDir . 'api.po',
- ];
- }
-
- /**
- * Get an associative array of LOCALE_COMPOMENT_... => filename
- *
- * @param string $locale
- *
- * @return array
- */
- public static function getFilenameComponentMap($locale)
- {
- $filenameComponentMap = & Registry::get('localeFilenameComponentMap', true, []);
- if (!isset($filenameComponentMap[$locale])) {
- $filenameComponentMap[$locale] = AppLocale::makeComponentMap($locale);
- }
- return $filenameComponentMap[$locale];
- }
-
- /**
- * Load a set of locale components. Parameters of mixed length may
- * be supplied, each a LOCALE_COMPONENT_... constant. An optional final
- * parameter may be supplied to specify the locale (e.g. 'en_US').
- */
- public static function requireComponents()
- {
- $params = func_get_args();
-
- $paramCount = count($params);
- if ($paramCount === 0) {
- return;
- }
-
- // Get the locale
- $lastParam = $params[$paramCount - 1];
- if (is_string($lastParam)) {
- $locale = $lastParam;
- $paramCount--;
- } else {
- $locale = AppLocale::getLocale();
- }
-
- // Backwards compatibility: the list used to be supplied
- // as an array in the first parameter.
- if (is_array($params[0])) {
- $params = $params[0];
- $paramCount = count($params);
- }
-
- // Go through and make sure each component is loaded if valid.
- $loadedComponents = & Registry::get('loadedLocaleComponents', true, []);
- $filenameComponentMap = AppLocale::getFilenameComponentMap($locale);
- for ($i = 0; $i < $paramCount; $i++) {
- $component = $params[$i];
-
- // Don't load components twice
- if (isset($loadedComponents[$locale][$component])) {
- continue;
- }
-
- // Validate component
- if (!isset($filenameComponentMap[$component])) {
- fatalError('Unknown locale component ' . $component);
- }
-
- $filename = $filenameComponentMap[$component];
- AppLocale::registerLocaleFile($locale, $filename);
- $loadedComponents[$locale][$component] = true;
- }
- }
-
- /**
- * Register a locale file against the current list.
- *
- * @param string $locale Locale key
- * @param string $filename Filename to new locale XML file
- * @param bool $addToTop Whether to add to the top of the list (true)
- * or the bottom (false). Allows overriding.
- */
- public static function registerLocaleFile($locale, $filename, $addToTop = false)
- {
- $localeFiles = & AppLocale::getLocaleFiles($locale);
- $localeFile = new LocaleFile($locale, $filename);
-
- if (!HookRegistry::call('PKPLocale::registerLocaleFile::isValidLocaleFile', [&$localeFile])) {
- if (!$localeFile->isValid()) {
- return null;
- }
- }
- if ($addToTop) {
- // Work-around: unshift by reference.
- array_unshift($localeFiles, '');
- $localeFiles[0] = & $localeFile;
- } else {
- $localeFiles[] = & $localeFile;
- }
- HookRegistry::call('PKPLocale::registerLocaleFile', [&$locale, &$filename, &$addToTop]);
- return $localeFile;
- }
-
- /**
- * Get the stylesheet filename for a particular locale.
- *
- * @param string $locale
- *
- * @return string or null if none configured.
- */
- public static function getLocaleStyleSheet($locale)
- {
- $contents = & AppLocale::_getAllLocalesCacheContent();
- if (isset($contents[$locale]['stylesheet'])) {
- return $contents[$locale]['stylesheet'];
- }
- return null;
- }
-
- /**
- * Get the reading direction for a particular locale.
- *
- * A locale can specify a reading direction with the `direction` attribute. If no
- * direction is specified, defaults to `ltr` (left-to-right). The only
- * other value that is expected is `rtl`. This value is used in HTML and
- * CSS markup to present a right-to-left layout.
- *
- * @param string $locale
- *
- * @return string
- */
- public static function getLocaleDirection($locale)
- {
- $contents = & AppLocale::_getAllLocalesCacheContent();
- if (isset($contents[$locale]['direction'])) {
- return $contents[$locale]['direction'];
- }
- return 'ltr';
- }
-
- /**
- * Determine whether or not a locale is marked incomplete.
- *
- * @param string $locale xx_XX symbolic name of locale to check
- *
- * @return bool
- */
- public static function isLocaleComplete($locale)
- {
- $contents = & AppLocale::_getAllLocalesCacheContent();
- if (!isset($contents[$locale])) {
- return false;
- }
- if (isset($contents[$locale]['complete']) && $contents[$locale]['complete'] == 'false') {
- return false;
- }
- return true;
- }
-
- /**
- * Determine whether or not a locale uses family name first.
- *
- * @param string $locale xx_XX symbolic name of locale to check
- *
- * @return bool
- */
- public static function isLocaleWithFamilyFirst($locale)
- {
- $contents = & AppLocale::_getAllLocalesCacheContent();
- if (isset($contents[$locale]) && isset($contents[$locale]['familyFirst']) && $contents[$locale]['familyFirst'] == 'true') {
- return true;
- }
- return false;
- }
-
- /**
- * Check if the supplied locale is currently installable.
- *
- * @param string $locale
- *
- * @return bool
- */
- public static function isLocaleValid($locale)
- {
- if (empty($locale)) {
- return false;
- }
- // variants can be composed of five to eight letters, or of four characters starting with a digit
- if (!preg_match('/^[a-z][a-z]_[A-Z][A-Z](@([A-Za-z0-9]{5,8}|\d[A-Za-z0-9]{3}))?$/', $locale)) {
- return false;
- }
- if (file_exists('locale/' . $locale)) {
- return true;
- }
- return false;
- }
-
- /**
- * Load a locale list from a file.
- *
- * @param string $filename
- *
- * @return array
- */
- public static function &loadLocaleList($filename)
- {
- $xmlDao = new XMLDAO();
- $data = $xmlDao->parseStruct($filename, ['locale']);
- $allLocales = [];
-
- // Build array with ($localKey => $localeName)
- if (isset($data['locale'])) {
- foreach ($data['locale'] as $localeData) {
- $allLocales[$localeData['attributes']['key']] = $localeData['attributes'];
- }
- }
-
- return $allLocales;
- }
-
- /**
- * Return a list of all available locales.
- *
- * @return array
- */
- public static function &getAllLocales()
- {
- $rawContents = & AppLocale::_getAllLocalesCacheContent();
- $allLocales = [];
-
- foreach ($rawContents as $locale => $contents) {
- $allLocales[$locale] = $contents['name'];
- }
-
- // if client encoding is set to iso-8859-1, transcode locales from utf8
- if (LOCALE_ENCODING == 'iso-8859-1') {
- $allLocales = array_map('utf8_decode', $allLocales);
- }
-
- return $allLocales;
- }
-
- /**
- * Install support for a new locale.
- *
- * @param string $locale
- */
- public static function installLocale($locale)
- {
- // Install default locale-specific data
- AppLocale::requireComponents(LOCALE_COMPONENT_APP_EMAIL, $locale);
- Repo::emailTemplate()->dao->installEmailTemplateLocaleData(Repo::emailTemplate()->dao->getMainEmailTemplatesFilename(), [$locale]);
-
- // Load all plugins so they can add locale data if needed
- $categories = PluginRegistry::getCategories();
- foreach ($categories as $category) {
- PluginRegistry::loadCategory($category);
- }
- HookRegistry::call('PKPLocale::installLocale', [&$locale]);
- }
-
- /**
- * Uninstall support for an existing locale.
- *
- * @param string $locale
- */
- public static function uninstallLocale($locale)
- {
- // Delete locale-specific data
- Repo::emailTemplate()->dao->deleteEmailTemplatesByLocale($locale);
- Repo::emailTemplate()->dao->deleteDefaultEmailTemplatesByLocale($locale);
- }
-
- /**
- * Reload locale-specific data.
- *
- * @param string $locale
- */
- public static function reloadLocale($locale)
- {
- AppLocale::installLocale($locale);
- }
-
- /**
- * Given a locale string, get the list of parameter references of the
- * form {$myParameterName}.
- *
- * @param string $source
- *
- * @return array
- */
- public static function getParameterNames($source)
- {
- $matches = null;
- PKPString::regexp_match_all('/({\$[^}]+})/' /* '/{\$[^}]+})/' */, $source, $matches);
- array_shift($matches); // Knock the top element off the array
- if (isset($matches[0])) {
- return $matches[0];
- }
- return [];
- }
-
- /**
- * Translate the ISO 2-letter language string (ISO639-1)
- * into a ISO compatible 3-letter string (ISO639-2b).
- *
- * @param string $iso2Letter
- *
- * @return string the translated string or null if we
- * don't know about the given language.
- */
- public static function get3LetterFrom2LetterIsoLanguage($iso2Letter)
- {
- assert(strlen($iso2Letter) == 2);
- $locales = & AppLocale::_getAllLocalesCacheContent();
- foreach ($locales as $locale => $localeData) {
- if (substr($locale, 0, 2) == $iso2Letter) {
- assert(isset($localeData['iso639-2b']));
- return $localeData['iso639-2b'];
- }
- }
- return null;
- }
-
- /**
- * Translate the ISO 3-letter language string (ISO639-2b)
- * into a ISO compatible 2-letter string (ISO639-1).
- *
- * @param string $iso3Letter
- *
- * @return string the translated string or null if we
- * don't know about the given language.
- */
- public static function get2LetterFrom3LetterIsoLanguage($iso3Letter)
- {
- assert(strlen($iso3Letter) == 3);
- $locales = & AppLocale::_getAllLocalesCacheContent();
- foreach ($locales as $locale => $localeData) {
- assert(isset($localeData['iso639-2b']));
- if ($localeData['iso639-2b'] == $iso3Letter) {
- return substr($locale, 0, 2);
- }
- }
- return null;
- }
-
- /**
- * Translate the PKP locale identifier into an
- * ISO639-2b compatible 3-letter string.
- *
- * @param string $locale
- *
- * @return string
- */
- public static function get3LetterIsoFromLocale($locale)
- {
- assert(strlen($locale) >= 5);
- $iso2Letter = substr($locale, 0, 2);
- return AppLocale::get3LetterFrom2LetterIsoLanguage($iso2Letter);
- }
-
- /**
- * Translate an ISO639-2b compatible 3-letter string
- * into the PKP locale identifier.
- *
- * This can be ambiguous if several locales are defined
- * for the same language. In this case we'll use the
- * primary locale to disambiguate.
- *
- * If that still doesn't determine a unique locale then
- * we'll choose the first locale found.
- *
- * @return string
- */
- public static function getLocaleFrom3LetterIso($iso3Letter)
- {
- assert(strlen($iso3Letter) == 3);
- $primaryLocale = AppLocale::getPrimaryLocale();
-
- $localeCandidates = [];
- $locales = & AppLocale::_getAllLocalesCacheContent();
- foreach ($locales as $locale => $localeData) {
- assert(isset($localeData['iso639-2b']));
- if ($localeData['iso639-2b'] == $iso3Letter) {
- if ($locale == $primaryLocale) {
- // In case of ambiguity the primary locale
- // overrides all other options so we're done.
- return $primaryLocale;
- }
- $localeCandidates[] = $locale;
- }
- }
-
- // Return null if we found no candidate locale.
- if (empty($localeCandidates)) {
- return null;
- }
-
- if (count($localeCandidates) > 1) {
- // Check whether one of the candidate locales
- // is a supported locale. If so choose the first
- // supported locale.
- $supportedLocales = AppLocale::getSupportedLocales();
- foreach ($supportedLocales as $supportedLocale => $localeName) {
- if (in_array($supportedLocale, $localeCandidates)) {
- return $supportedLocale;
- }
- }
- }
-
- // If there is only one candidate (or if we were
- // unable to disambiguate) then return the unique
- // (first) candidate found.
- return array_shift($localeCandidates);
- }
-
- /**
- * Translate the ISO 2-letter language string (ISO639-1) into ISO639-3.
- *
- * @param string $iso1
- *
- * @return string the translated string or null if we
- * don't know about the given language.
- */
- public static function getIso3FromIso1($iso1)
- {
- assert(strlen($iso1) == 2);
- $locales = & AppLocale::_getAllLocalesCacheContent();
- foreach ($locales as $locale => $localeData) {
- if (substr($locale, 0, 2) == $iso1) {
- assert(isset($localeData['iso639-3']));
- return $localeData['iso639-3'];
- }
- }
- return null;
- }
-
- /**
- * Translate the ISO639-3 into ISO639-1.
- *
- * @param string $iso3
- *
- * @return string the translated string or null if we
- * don't know about the given language.
- */
- public static function getIso1FromIso3($iso3)
- {
- assert(strlen($iso3) == 3);
- $locales = & AppLocale::_getAllLocalesCacheContent();
- foreach ($locales as $locale => $localeData) {
- assert(isset($localeData['iso639-3']));
- if ($localeData['iso639-3'] == $iso3) {
- return substr($locale, 0, 2);
- }
- }
- return null;
- }
-
- /**
- * Translate the PKP locale identifier into an
- * ISO639-3 compatible 3-letter string.
- *
- * @param string $locale
- *
- * @return string
- */
- public static function getIso3FromLocale($locale)
- {
- assert(strlen($locale) >= 5);
- $iso1 = substr($locale, 0, 2);
- return AppLocale::getIso3FromIso1($iso1);
- }
-
- /**
- * Translate the PKP locale identifier into an
- * ISO639-1 compatible 2-letter string.
- *
- * @param string $locale
- *
- * @return string
- */
- public static function getIso1FromLocale($locale)
- {
- assert(strlen($locale) >= 5);
- return substr($locale, 0, 2);
- }
-
- /**
- * Translate an ISO639-3 compatible 3-letter string
- * into the PKP locale identifier.
- *
- * This can be ambiguous if several locales are defined
- * for the same language. In this case we'll use the
- * primary locale to disambiguate.
- *
- * If that still doesn't determine a unique locale then
- * we'll choose the first locale found.
- *
- * @param string $iso3
- *
- * @return string
- */
- public static function getLocaleFromIso3($iso3)
- {
- assert(strlen($iso3) == 3);
- $primaryLocale = AppLocale::getPrimaryLocale();
-
- $localeCandidates = [];
- $locales = & AppLocale::_getAllLocalesCacheContent();
- foreach ($locales as $locale => $localeData) {
- assert(isset($localeData['iso639-3']));
- if ($localeData['iso639-3'] == $iso3) {
- if ($locale == $primaryLocale) {
- // In case of ambiguity the primary locale
- // overrides all other options so we're done.
- return $primaryLocale;
- }
- $localeCandidates[] = $locale;
- }
- }
-
- // Return null if we found no candidate locale.
- if (empty($localeCandidates)) {
- return null;
- }
-
- if (count($localeCandidates) > 1) {
- // Check whether one of the candidate locales
- // is a supported locale. If so choose the first
- // supported locale.
- $supportedLocales = AppLocale::getSupportedLocales();
- foreach ($supportedLocales as $supportedLocale => $localeName) {
- if (in_array($supportedLocale, $localeCandidates)) {
- return $supportedLocale;
- }
- }
- }
-
- // If there is only one candidate (or if we were
- // unable to disambiguate) then return the unique
- // (first) candidate found.
- return array_shift($localeCandidates);
- }
-
- //
- // Private helper methods.
- //
- /**
- * Retrieves locale data from the locales cache.
- *
- * @return array
- */
- public static function &_getAllLocalesCacheContent()
- {
- static $contents = false;
- if ($contents === false) {
- $allLocalesCache = & AppLocale::_getAllLocalesCache();
- $contents = $allLocalesCache->getContents();
- }
- return $contents;
- }
-
- /**
- * Get the cache object for the current list of all locales.
- *
- * @return FileCache
- */
- public static function &_getAllLocalesCache()
- {
- $cache = & Registry::get('allLocalesCache', true, null);
- if ($cache === null) {
- $cacheManager = CacheManager::getManager();
- $cache = $cacheManager->getFileCache(
- 'locale',
- 'list',
- ['\APP\i18n\AppLocale', '_allLocalesCacheMiss']
- );
-
- // Check to see if the data is outdated
- $cacheTime = $cache->getCacheTime();
- if ($cacheTime !== null && $cacheTime < filemtime(AppLocale::LOCALE_REGISTRY_FILE)) {
- $cache->flush();
- }
- }
- return $cache;
- }
+if (!PKP_STRICT_MODE) {
/**
- * Create a cache file with locale data.
- *
- * @param CacheManager $cache
- * @param string $id the cache id (not used here, required by the cache manager)
+ * @deprecated The class \PKP\i18n\PKPLocale has been replaced by PKP\facades\Locale
*/
- public static function _allLocalesCacheMiss($cache, $id)
+ class PKPLocale
{
- $allLocales = & Registry::get('allLocales', true, null);
- if ($allLocales === null) {
- // Add a locale load to the debug notes.
- $notes = & Registry::get('system.debug.notes');
- $notes[] = ['debug.notes.localeListLoad', ['localeList' => AppLocale::LOCALE_REGISTRY_FILE]];
-
- // Reload locale registry file
- $allLocales = AppLocale::loadLocaleList(AppLocale::LOCALE_REGISTRY_FILE);
- asort($allLocales);
- $cache->setEntireCache($allLocales);
+ /**
+ * Return the key name of the user's currently selected locale (default
+ * is "en_US" for U.S. English).
+ *
+ * @return string
+ * @deprecated 3.4.0.0 The same method is available at \PKP\facades\Locale::getLocale()
+ */
+ public static function getLocale()
+ {
+ return Locale::getLocale();
}
- return null;
- }
-
- /**
- * Get the sites time zone.
- *
- * @return string Time zone
- */
- public static function getTimeZone()
- {
- $timeZone = null;
- // Load the time zone from the configuration file
- if ($timeZoneConfig = Config::getVar('general', 'time_zone')) {
- $timeZoneDAO = DAORegistry::getDAO('TimeZoneDAO');
- $timeZoneList = $timeZoneDAO->getTimeZones();
- foreach ($timeZoneList as $timeZoneKey => $timeZoneName) {
- if (in_array($timeZoneConfig, [$timeZoneKey, $timeZoneName])) {
- $timeZone = $timeZoneKey;
- break;
- }
- }
+ /**
+ * Retrieve the primary locale of the current context.
+ *
+ * @return string
+ * @deprecated 3.4.0.0 The same method is available at \PKP\facades\Locale::getPrimaryLocale(), but before using this method, try to retrieve the locale directly from a nearby context
+ */
+ public static function getPrimaryLocale()
+ {
+ return Locale::getPrimaryLocale();
}
- // Fall back to the time zone set in php.ini
- if (empty($timeZone)) {
- $timeZone = ini_get('date.timezone');
+ /**
+ * Load a set of locale components. Parameters of mixed length may
+ * be supplied, each a LOCALE_COMPONENT_... constant. An optional final
+ * parameter may be supplied to specify the locale (e.g. 'en_US').
+ * @deprecated 3.4.0.0 All the available locale keys are already loaded
+ */
+ public static function requireComponents()
+ {
}
- // Fall back to UTC
- if (empty($timeZone)) {
- $timeZone = 'UTC';
+ /**
+ * Return a list of all available locales.
+ *
+ * @deprecated 3.4.0.0 Use the \PKP\facades\Locale::getLocales()
+ * @return array
+ */
+ public static function &getAllLocales()
+ {
+ $locales = array_map(fn (LocaleMetadata $locale) => $locale->getDisplayName(), Locale::getLocales());
+ return $locales;
}
-
- return $timeZone;
}
-}
-if (!PKP_STRICT_MODE) {
class_alias('\PKP\i18n\PKPLocale', '\PKPLocale');
- // REGISTRY_LOCALE_FILE excluded because of PHPUnit interaction.
- define('MASTER_LOCALE', PKPLocale::MASTER_LOCALE);
+
+ // Shared locale components
+ define('LOCALE_COMPONENT_PKP_COMMON', 0x00000001);
+ define('LOCALE_COMPONENT_PKP_ADMIN', 0x00000002);
+ define('LOCALE_COMPONENT_PKP_INSTALLER', 0x00000003);
+ define('LOCALE_COMPONENT_PKP_MANAGER', 0x00000004);
+ define('LOCALE_COMPONENT_PKP_READER', 0x00000005);
+ define('LOCALE_COMPONENT_PKP_SUBMISSION', 0x00000006);
+ define('LOCALE_COMPONENT_PKP_USER', 0x00000007);
+ define('LOCALE_COMPONENT_PKP_GRID', 0x00000008);
+ define('LOCALE_COMPONENT_PKP_DEFAULT', 0x00000009);
+ define('LOCALE_COMPONENT_PKP_EDITOR', 0x0000000A);
+ define('LOCALE_COMPONENT_PKP_REVIEWER', 0x0000000B);
+ define('LOCALE_COMPONENT_PKP_API', 0x0000000C);
+
+ // Application-specific locale components
+ define('LOCALE_COMPONENT_APP_COMMON', 0x00000100);
+ define('LOCALE_COMPONENT_APP_MANAGER', 0x00000101);
+ define('LOCALE_COMPONENT_APP_SUBMISSION', 0x00000102);
+ define('LOCALE_COMPONENT_APP_AUTHOR', 0x00000103);
+ define('LOCALE_COMPONENT_APP_EDITOR', 0x00000104);
+ define('LOCALE_COMPONENT_APP_ADMIN', 0x00000105);
+ define('LOCALE_COMPONENT_APP_DEFAULT', 0x00000106);
+ define('LOCALE_COMPONENT_APP_API', 0x00000107);
+ define('LOCALE_COMPONENT_APP_EMAIL', 0x00000108);
}
diff --git a/classes/i18n/TimeZoneDAO.inc.php b/classes/i18n/TimeZoneDAO.inc.php
deleted file mode 100644
index 68fc3d09e21..00000000000
--- a/classes/i18n/TimeZoneDAO.inc.php
+++ /dev/null
@@ -1,98 +0,0 @@
-getFileCache(
- 'timeZone',
- 'list',
- [$this, '_timeZoneCacheMiss']
- );
-
- // Check to see if the data is outdated
- $cacheTime = $cache->getCacheTime();
- if ($cacheTime !== null && $cacheTime < filemtime($this->getFilename())) {
- $cache->flush();
- }
- }
- return $cache;
- }
-
- public function _timeZoneCacheMiss($cache, $id)
- {
- $timeZones = & Registry::get('allTimeZonesData', true, null);
- if ($timeZones === null) {
- // Reload time zone registry file
- $xmlDao = new XMLDAO();
- $data = $xmlDao->parseStruct($this->getFilename(), ['timezones', 'entry']);
- $timeZones = [];
- if (isset($data['timezones'])) {
- foreach ($data['entry'] as $timeZoneData) {
- $timeZones[$timeZoneData['attributes']['key']] = $timeZoneData['attributes']['name'];
- }
- }
- asort($timeZones);
- $cache->setEntireCache($timeZones);
- }
- return null;
- }
-
- /**
- * Return a list of all time zones.
- *
- * @return array
- */
- public function &getTimeZones()
- {
- $cache = & $this->_getTimeZoneCache();
- return $cache->getContents();
- }
-}
-
-if (!PKP_STRICT_MODE) {
- class_alias('\PKP\i18n\TimeZoneDAO', '\TimeZoneDAO');
-}
diff --git a/classes/i18n/interfaces/LocaleInterface.inc.php b/classes/i18n/interfaces/LocaleInterface.inc.php
new file mode 100644
index 00000000000..0200f70c65f
--- /dev/null
+++ b/classes/i18n/interfaces/LocaleInterface.inc.php
@@ -0,0 +1,151 @@
+[a-z]{2})(?:_(?P[A-Z]{2}))?(?:@(?P