Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
30812f1
first poc
marco-lengua Jan 27, 2026
adfd13f
ready to test for speed
marco-lengua Jan 27, 2026
4a01e00
added documentation
marco-lengua Jan 29, 2026
1962744
first working and readeable poc
marco-lengua Jan 29, 2026
8cd6dfa
minor variable name change
marco-lengua Jan 29, 2026
c5c8164
add logging and comments
marco-lengua Feb 2, 2026
de025ea
Merge branch 'kitodo:main' into feature_translateHelper
marco-lengua Feb 2, 2026
ba3288b
formatting
marco-lengua Feb 2, 2026
03f77ff
Merge branch 'feature_translateHelper' of https://github.com/marco-le…
marco-lengua Feb 2, 2026
26e1db5
fixed tranlate translation array initialization
marco-lengua Feb 3, 2026
e92aad1
Merge branch 'kitodo:main' into feature_translateHelper
marco-lengua Feb 3, 2026
8f5849e
removed unsued selects and translation entries
marco-lengua Feb 3, 2026
39b185e
Merge branch 'feature_translateHelper' of https://github.com/marco-le…
marco-lengua Feb 3, 2026
1a43791
clean code
marco-lengua Feb 3, 2026
88a068b
clean Code
marco-lengua Feb 3, 2026
5808ef8
Merge branch 'feature_translateHelper' of https://github.com/marco-le…
marco-lengua Feb 4, 2026
8989983
check if table parameter in allowed backend tables else early return
marco-lengua Feb 4, 2026
a1b1a23
formatting fix
marco-lengua Feb 4, 2026
67566b7
more formatting
marco-lengua Feb 4, 2026
7d51826
more codacy formatting fixes
marco-lengua Feb 4, 2026
76efe0c
Merge branch 'main' into feature_translateHelper
sebastian-meyer Feb 4, 2026
6cc72cf
Merge branch 'main' into feature_translateHelper
sebastian-meyer Feb 4, 2026
f398fd0
Merge branch 'main' into feature_translateHelper
marco-lengua Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 29 additions & 105 deletions Classes/Common/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace Kitodo\Dlf\Common;

use Exception;
use TYPO3\CMS\Core\Configuration\ConfigurationManager;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Core\Environment;
Expand Down Expand Up @@ -650,120 +651,43 @@ public static function timeCodeToSeconds(string $timeCode): float
*/
public static function translate(string $indexName, string $table, string $pid): string
{
// Load labels into static variable for future use.
static $labels = [];
// Sanitize input.
$pid = max((int) $pid, 0);
if (!$pid) {
self::warning('Invalid PID ' . $pid . ' for translation');
//Check if this table is allowed for translation.
if (!in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_metadatasubentries', 'tx_dlf_structures'])) {
return $indexName;
}
/** @var PageRepository $pageRepository */
$pageRepository = GeneralUtility::makeInstance(PageRepository::class);

//Get LanguageId (sys_language_uid in Backend) from context
$languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
$languageContentId = $languageAspect->getContentId();

// Check if "index_name" is a UID.
if (MathUtility::canBeInterpretedAsInteger($indexName)) {
$indexName = self::getIndexNameFromUid((int) $indexName, $table, $pid);
}
/* $labels already contains the translated content element, but with the index_name of the translated content element itself
* and not with the $indexName of the original that we receive here. So we have to determine the index_name of the
* associated translated content element. E.g. $labels['title0'] != $indexName = title. */

$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable($table);

// First fetch the uid of the received index_name
$result = $queryBuilder
->select(
$table . '.uid AS uid',
$table . '.l18n_parent AS l18n_parent'
)
->from($table)
->where(
$queryBuilder->expr()->eq($table . '.pid', $pid),
$queryBuilder->expr()->eq($table . '.index_name', $queryBuilder->expr()->literal($indexName)),
self::whereExpression($table, true)
)
->setMaxResults(1)
->executeQuery();

$row = $result->fetchAssociative();

if ($row) {
// Now we use the uid of the l18_parent to fetch the index_name of the translated content element.
$result = $queryBuilder
->select($table . '.index_name AS index_name')
->from($table)
->where(
$queryBuilder->expr()->eq($table . '.pid', $pid),
$queryBuilder->expr()->eq($table . '.uid', $row['l18n_parent']),
$queryBuilder->expr()->eq($table . '.sys_language_uid', $languageContentId),
self::whereExpression($table, true)
)
->setMaxResults(1)
->executeQuery();

$row = $result->fetchAssociative();

if ($row) {
// If there is a translated content element, overwrite the received $indexName.
$indexName = $row['index_name'];
}
}

// Check if we already got a translation.
if (empty($labels[$table][$pid][$languageContentId][$indexName])) {
// Check if this table is allowed for translation.
if (in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_metadatasubentries', 'tx_dlf_structures'])) {
$additionalWhere = $queryBuilder->expr()->in($table . '.sys_language_uid', [-1, 0]);
if ($languageContentId > 0) {
$additionalWhere = $queryBuilder->expr()->and(
$queryBuilder->expr()->or(
$queryBuilder->expr()->in($table . '.sys_language_uid', [-1, 0]),
$queryBuilder->expr()->eq($table . '.sys_language_uid', $languageContentId)
),
$queryBuilder->expr()->eq($table . '.l18n_parent', 0)
);
}

// Get labels from database.
$result = $queryBuilder
->select('*')
->from($table)
$languageId = $languageAspect->getContentId();
//static Array to save all already queried Translations
static $translations = [];
//Query Backend for translations for one table and PID combination - only query if not yet done
if (empty($translations[$table][$pid])) {
try {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable($table);

$rows = $queryBuilder
->select(
$table . '.label AS label',
$table . '.index_name AS index_name',
$table . '.sys_language_uid As sys_language_uid'
)
->where(
$queryBuilder->expr()->eq($table . '.pid', $pid),
$additionalWhere,
self::whereExpression($table, true)
$queryBuilder->expr()->eq($table . '.deleted', 0)
)
->setMaxResults(10000)
->executeQuery();

if ($result->rowCount() > 0) {
while ($resArray = $result->fetchAssociative()) {
// Overlay localized labels if available.
if ($languageContentId > 0) {
$resArray = $pageRepository->getLanguageOverlay($table, $resArray, $languageAspect);
}
if ($resArray) {
$labels[$table][$pid][$languageContentId][$resArray['index_name']] = $resArray['label'];
}
}
} else {
self::notice('No translation with PID ' . $pid . ' available in table "' . $table . '" or translation not accessible');
->from($table)
->executeQuery()
->fetchAllAssociative();
foreach ($rows as $row) {
$translations[$table][$pid][$row['index_name']][(int) $row['sys_language_uid']] = ['label' => $row['label']];
}
} else {
self::warning('No translations available for table "' . $table . '"');
} catch (Exception $e) {
self::error('Error querying backend pool: ' . $e->getMessage());
}
}

if (!empty($labels[$table][$pid][$languageContentId][$indexName])) {
return $labels[$table][$pid][$languageContentId][$indexName];
} else {
return $indexName;
}
//return translation based on parameters table, PID, indexName and languageId, else return indexName
return isset($translations[$table][$pid][$indexName][$languageId]) ? $translations[$table][$pid][$indexName][$languageId]['label'] : $indexName;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion Classes/Controller/TableOfContentsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ private function getMenuEntry(array $entry, bool $recursive = false): array
}
$entryArray['year'] = $entry['year'];
$entryArray['orderlabel'] = $entry['orderlabel'];
$entryArray['type'] = $entry['type'];
$entryArray['type'] = $this->getTranslatedType($entry['type']);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change disables the option to display document type icons. This functions is used for e.g. in DFG Viewer <span class="meta-type-icon meta-type-{child.type}">{child.type}</span>. It is necessary that type is in English instead be translated here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for your feedback, i was not aware of this. What i don't understand is why in the current version (branch 6) it is the same way. I think we made the change in one PR and now wanted to revert it. The purpose of this is a batch translation and a speed gain. It was like this before. Could you explain why it would not be working anymore now.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use case is as mentioned above, the template contains span with the meta-type-{child.type}, then there is css for this class e.g.

&.meta-type-article {
    background-position: -60px -30px;
}

If this is translated than css class is not working anymore.

$entryArray['pagination'] = htmlspecialchars($entry['pagination']);
$entryArray['_OVERRIDE_HREF'] = '';
$entryArray['doNotLinkIt'] = 1;
Expand Down
2 changes: 1 addition & 1 deletion Resources/Private/Partials/TableOfContents/Title.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<f:format.crop maxCharacters="55" append="&nbsp;..."><f:format.htmlspecialchars doubleEncode="false">{child.title}</f:format.htmlspecialchars></f:format.crop>
</f:then>
<f:else>
<f:format.crop maxCharacters="55" append="&nbsp;..."><f:format.htmlspecialchars doubleEncode="false"><f:translate key="LLL:EXT:dlf/Resources/Private/Language/locallang_structure.xlf:structure.{child.type}" /></f:format.htmlspecialchars><f:if condition="{child.volume}"> &nbsp;<f:format.htmlspecialchars doubleEncode="false">{child.volume}</f:format.htmlspecialchars></f:if></f:format.crop>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Translation should be kept here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have the translated "entry.Type" we can use that translation in this fluid template, because it makes the page load much faster. That was the purpose of this PR. Do you know any case this is not feasible? Could you please provide me with more information on this topic. Please see also the GitHub Discussion for our usecases

Copy link
Collaborator

@beatrycze-volk beatrycze-volk Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Here is the example how this icons are used.
You can see next to the year the small newspaper icon.

<f:format.crop maxCharacters="55" append="&nbsp;..."><f:format.htmlspecialchars doubleEncode="false">{child.type}</f:format.htmlspecialchars><f:if condition="{child.volume}"> &nbsp;<f:format.htmlspecialchars doubleEncode="false">{child.volume}</f:format.htmlspecialchars></f:if></f:format.crop>
</f:else>
</f:if>
</span>
Expand Down
Loading