Skip to content

Commit 941ab71

Browse files
authored
[FEATURE] Skip rewriting XLF files when only the timestamp changed (#810)
* Skip rewriting XLF files when only the date attribute changed When regenerating extension code, XLF files were always overwritten even if no labels changed. This caused VCS noise because the date= attribute in the <file> element is set to the current timestamp on every run. FileGenerator now compares the normalized content (date= attribute stripped) of the new and existing XLF file before writing. If they are equal, the write is skipped and the file retains its original mtime. The existing staticDateInXliffFiles workaround remains fully functional. * Remove staticDateInXliffFiles setting XLF files are now only rewritten when label content actually changes, so a static date workaround is no longer necessary. Remove the setting from the XLF template, the settings template, test fixtures, documentation, and the changelog entry documents the change under 13.1.0. Also removes a now-stale PHPStan baseline entry for FileGenerator.php.
1 parent 679446c commit 941ab71

8 files changed

Lines changed: 145 additions & 22 deletions

File tree

Classes/Service/FileGenerator.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,12 +1515,39 @@ protected function writeFile(string $targetFile, string $fileContents): void
15151515
if (empty($fileContents)) {
15161516
return;
15171517
}
1518+
1519+
// Skip writing XLF files when only the date= attribute has changed to avoid VCS noise
1520+
if (
1521+
strtolower(pathinfo($targetFile, PATHINFO_EXTENSION)) === 'xlf'
1522+
&& $this->xlfContentIsUnchanged($targetFile, $fileContents)
1523+
) {
1524+
return;
1525+
}
1526+
15181527
$success = GeneralUtility::writeFile($targetFile, $fileContents);
15191528
if (!$success) {
15201529
throw new Exception('File ' . $targetFile . ' could not be created!');
15211530
}
15221531
}
15231532

1533+
/**
1534+
* Checks whether an existing XLF file has the same content as $newContent,
1535+
* ignoring differences in the date= attribute.
1536+
* Used to avoid rewriting XLF files when only the timestamp changed.
1537+
*/
1538+
private function xlfContentIsUnchanged(string $targetFile, string $newContent): bool
1539+
{
1540+
if (!file_exists($targetFile)) {
1541+
return false;
1542+
}
1543+
$existing = file_get_contents($targetFile);
1544+
if ($existing === false) {
1545+
return false;
1546+
}
1547+
$normalize = static fn(string $content): string => (string)preg_replace('/\bdate="[^"]*"/', 'date=""', $content);
1548+
return $normalize($existing) === $normalize($newContent);
1549+
}
1550+
15241551
/**
15251552
* @param $destinationFile
15261553
*

Documentation/ChangeLog/Index.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
Change log
77
==========
88

9+
Version 13.1.0
10+
--------------
11+
12+
* [FEATURE] XLF files are no longer rewritten when only the ``date=`` attribute changed — avoids VCS noise on every regeneration. The ``staticDateInXliffFiles`` setting is removed as it is no longer needed.
13+
914
Version 12.0.0
1015
--------------
1116

Documentation/Configuration/Index.rst

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,20 +168,11 @@ This is an example of the :file:`settings.yaml` file:
168168
Miscellaneous
169169
-------------
170170

171-
There are more options both for the timestamps of the language files and for
172-
working with the Extension Builder itself.
171+
There are more options for working with the Extension Builder itself.
173172

174173
+----------------------------+-------------------------------------------------------------------------------+
175174
|**Setting** |**Description** |
176175
+----------------------------+-------------------------------------------------------------------------------+
177-
|staticDateInXliffFiles |By default, the date attribute in language files is updated every time you |
178-
| |save in the Extension Builder. |
179-
| |This can be confusing in a version control system if all language files are |
180-
| |marked as changed even if no labels have been added or changed. |
181-
| |To prevent this effect, you can set a static date – |
182-
| |although this is not recommended because the modification date can be useful |
183-
| |in the translation context. |
184-
+----------------------------+-------------------------------------------------------------------------------+
185176
|ignoreWarnings |Some modeling configurations result in warnings. |
186177
| |For example, if you configure a show action as a default action, you are |
187178
| |warned that you need to define a parameter of a domain object to be shown. |
@@ -195,7 +186,5 @@ This is an example of the :file:`settings.yaml` file:
195186

196187
.. code-block:: yaml
197188
198-
staticDateInXliffFiles: 2021-11-18T12:37:00Z
199-
200189
ignoreWarnings:
201190
503

Resources/Private/CodeTemplates/Extbase/Configuration/ExtensionBuilder/settings.yamlt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ overwriteSettings:
6868
## add declare strict types in php files
6969
declareStrictTypes: true
7070

71-
## use static date attribute in xliff files
72-
#staticDateInXliffFiles: '{f:format.date(format:'Y-m-d\TH:i:s\Z',date:'now')}'
73-
7471
## skip docComment (license header)
7572
#skipDocComment: false
7673

Resources/Private/CodeTemplates/Extbase/Resources/Private/Language/locallang.xlft

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{namespace k=EBT\ExtensionBuilder\ViewHelpers}<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
22
<xliff version="1.0">
3-
<file source-language="{extension.sourceLanguage}" datatype="plaintext" original="{fileName}" date="{f:if(condition:extension.settings.staticDateInXliffFiles, then:'{extension.settings.staticDateInXliffFiles}', else: '{f:format.date(format:\'Y-m-d\\TH:i:s\\Z\',date:\'now\')}')}" product-name="{extension.extensionKey}">
3+
<file source-language="{extension.sourceLanguage}" datatype="plaintext" original="{fileName}" date="{f:format.date(format:'Y-m-d\TH:i:s\Z',date:'now')}" product-name="{extension.extensionKey}">
44
<header/>
55
<body><f:for each="{labelArray}" as="label" key="index">
66
<trans-unit id="{index}" resname="{index}">

Tests/Fixtures/TestExtensions/eb_astrophotography/Configuration/ExtensionBuilder/settings.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ overwriteSettings:
6868
## add declare strict types in php files
6969
declareStrictTypes: true
7070

71-
## use static date attribute in xliff files
72-
#staticDateInXliffFiles: '2026-04-06T19:20:13Z'
73-
7471
## skip docComment (license header)
7572
#skipDocComment: false
7673

Tests/Fixtures/TestExtensions/test_extension/Configuration/ExtensionBuilder/settings.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,6 @@ overwriteSettings:
7070

7171
# ext_tables.sql: merge
7272

73-
## use static date attribute in xliff files ##
74-
#staticDateInXliffFiles: ###YEAR###-01-01T01:00:00Z
75-
7673
## list of error codes for warnings that should be ignored ##
7774
#ignoreWarnings:
7875
#503
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the TYPO3 CMS project.
7+
*
8+
* It is free software; you can redistribute it and/or modify it under
9+
* the terms of the GNU General Public License, either version 2
10+
* of the License, or any later version.
11+
*
12+
* For the full copyright and license information, please read the
13+
* LICENSE.txt file that was distributed with this source code.
14+
*
15+
* The TYPO3 project - inspiring people to share!
16+
*/
17+
18+
namespace EBT\ExtensionBuilder\Tests\Unit\Service;
19+
20+
use EBT\ExtensionBuilder\Service\ClassBuilder;
21+
use EBT\ExtensionBuilder\Service\FileGenerator;
22+
use EBT\ExtensionBuilder\Service\LocalizationService;
23+
use EBT\ExtensionBuilder\Service\Printer;
24+
use EBT\ExtensionBuilder\Service\RoundTrip;
25+
use org\bovigo\vfs\vfsStream;
26+
use org\bovigo\vfs\vfsStreamDirectory;
27+
use ReflectionClass;
28+
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
29+
use TYPO3\CMS\Core\View\ViewFactoryInterface;
30+
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
31+
32+
class FileGeneratorXlfTest extends UnitTestCase
33+
{
34+
private FileGenerator $fileGenerator;
35+
private vfsStreamDirectory $vfsRoot;
36+
37+
protected function setUp(): void
38+
{
39+
parent::setUp();
40+
41+
$this->vfsRoot = vfsStream::setup('root');
42+
43+
$this->fileGenerator = new FileGenerator(
44+
$this->createMock(ClassBuilder::class),
45+
$this->createMock(RoundTrip::class),
46+
$this->createMock(Printer::class),
47+
$this->createMock(LocalizationService::class),
48+
$this->createMock(ViewFactoryInterface::class),
49+
$this->createMock(ExtensionConfiguration::class),
50+
);
51+
}
52+
53+
private function callXlfContentIsUnchanged(string $targetFile, string $newContent): bool
54+
{
55+
$ref = new ReflectionClass($this->fileGenerator);
56+
$method = $ref->getMethod('xlfContentIsUnchanged');
57+
return $method->invoke($this->fileGenerator, $targetFile, $newContent);
58+
}
59+
60+
private function xlf(string $date, string $label = 'Hello'): string
61+
{
62+
return <<<XML
63+
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
64+
<xliff version="1.0">
65+
<file source-language="en" datatype="plaintext" original="locallang.xlf" date="{$date}" product-name="my_ext">
66+
<header/>
67+
<body>
68+
<trans-unit id="general.yes" resname="general.yes">
69+
<source>{$label}</source>
70+
</trans-unit>
71+
</body>
72+
</file>
73+
</xliff>
74+
XML;
75+
}
76+
77+
/**
78+
* @test
79+
*/
80+
public function returnsFalseWhenFileDoesNotExist(): void
81+
{
82+
$path = vfsStream::url('root/locallang.xlf');
83+
self::assertFalse($this->callXlfContentIsUnchanged($path, $this->xlf('2025-01-01T00:00:00Z')));
84+
}
85+
86+
/**
87+
* @test
88+
*/
89+
public function returnsTrueWhenOnlyDateAttributeDiffers(): void
90+
{
91+
$path = vfsStream::url('root/locallang.xlf');
92+
vfsStream::newFile('locallang.xlf')
93+
->withContent($this->xlf('2020-01-01T00:00:00Z'))
94+
->at($this->vfsRoot);
95+
96+
self::assertTrue($this->callXlfContentIsUnchanged($path, $this->xlf('2026-04-11T12:00:00Z')));
97+
}
98+
99+
/**
100+
* @test
101+
*/
102+
public function returnsFalseWhenLabelChanged(): void
103+
{
104+
$path = vfsStream::url('root/locallang.xlf');
105+
vfsStream::newFile('locallang.xlf')
106+
->withContent($this->xlf('2020-01-01T00:00:00Z', 'Hello'))
107+
->at($this->vfsRoot);
108+
109+
self::assertFalse($this->callXlfContentIsUnchanged($path, $this->xlf('2026-04-11T12:00:00Z', 'Goodbye')));
110+
}
111+
}

0 commit comments

Comments
 (0)