Skip to content

IBX-12091: Added TypeScript translation extraction support#1967

Open
mikadamczyk wants to merge 6 commits into
6.0from
feature/IBX-12091-typescript-translation-extraction
Open

IBX-12091: Added TypeScript translation extraction support#1967
mikadamczyk wants to merge 6 commits into
6.0from
feature/IBX-12091-typescript-translation-extraction

Conversation

@mikadamczyk

@mikadamczyk mikadamczyk commented Jul 16, 2026

Copy link
Copy Markdown
Contributor
🎫 Issue IBX-12091

Description:

This PR adds TypeScript translation extraction support for Translator.trans() and Translator.transChoice() calls, including translation descriptions from @Desc comments. The extractor uses a persistent Node.js process with runtime validation, response timeouts, error handling, and automatic restart after failures. The implementation separates the JMS visitor from process management and installs frontend dependencies in the backend CI job so the tests can run correctly.

For QA:

Documentation:

@mikadamczyk
mikadamczyk force-pushed the feature/IBX-12091-typescript-translation-extraction branch from 5198656 to 8b44786 Compare July 16, 2026 08:51
@mikadamczyk
mikadamczyk marked this pull request as ready for review July 20, 2026 10:19
Comment thread .github/workflows/backend-ci.yaml Outdated
Comment thread src/lib/Translation/Extractor/typescript_translation_extractor.mjs Outdated
Comment thread src/lib/Translation/Extractor/typescript_translation_extractor.mjs Outdated
Comment thread src/lib/Translation/Extractor/typescript_translation_extractor.mjs
Comment thread src/lib/Translation/Extractor/typescript_translation_extractor.mjs
Comment thread src/lib/Translation/Extractor/typescript_translation_extractor.mjs Outdated
@albozek
albozek requested a review from GrabowskiM July 20, 2026 13:31
Comment thread src/lib/Translation/Extractor/typescript_translation_extractor.mjs
@albozek
albozek force-pushed the feature/IBX-12091-typescript-translation-extraction branch from 26d6cc2 to 8b9bbb0 Compare July 20, 2026 13:49
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

💡 Need a hand with PR review? Try Gitar by Sonar!

@mikadamczyk
mikadamczyk requested a review from a team July 21, 2026 11:40
@ibexa-workflow-automation-1
ibexa-workflow-automation-1 Bot requested review from Steveb-p, ViniTou, alongosz, barw4, bnowak, ciastektk, konradoboza, tbialcz and wiewiurdp and removed request for a team July 21, 2026 11:40

@konradoboza konradoboza left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice one!

use Symfony\Component\Process\InputStream;
use Symfony\Component\Process\Process;

final class TypeScriptExtractorClient

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
final class TypeScriptExtractorClient
final readonly class TypeScriptExtractorClient


final class TypeScriptExtractorClient
{
private const DEFAULT_RESPONSE_TIMEOUT_SECONDS = 30.0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
private const DEFAULT_RESPONSE_TIMEOUT_SECONDS = 30.0;
private const float DEFAULT_RESPONSE_TIMEOUT_SECONDS = 30.0;

$this->parserScriptPath = $parserScriptPath ?? __DIR__ . '/typescript_translation_extractor.mjs';
}

public function __destruct()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can this have a return type?

use SplFileInfo;
use Twig\Node\Node as TwigNode;

final class TypeScriptFileVisitor implements FileVisitorInterface, LoggerAwareInterface

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
final class TypeScriptFileVisitor implements FileVisitorInterface, LoggerAwareInterface
final readonly class TypeScriptFileVisitor implements FileVisitorInterface, LoggerAwareInterface

* @param array<mixed> $ast
*/
public function visitPhpFile(SplFileInfo $file, MessageCatalogue $catalogue, array $ast): void
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we provide a comment why it is empty? 😉 Same goes for the Twig below.


public function testServeModeProcessesMultipleFilesSequentiallyAndSurvivesPerFileErrors(): void
{
$goodFile = tempnam(sys_get_temp_dir(), 'ts_extractor_') . '.ts';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

tempnam() creates the file immediately. Appending .ts only modifies the returned path, so file_put_contents() creates a second file and the original temporary file is never removed. Could we rename the file instead, as done in testExtractsExpectedMessages()? The same applies to the other tempnam(...).'.ts' occurrences.


foreach ($warnings as $warning) {
if (is_string($warning)) {
$this->logger?->error($warning);

@barw4 barw4 Jul 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nitpick but shouldn't it be warning per method's name and variables' names?

$extractionResponse['error'],
));

return null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would it make sense to narrow down the return type to an empty array since the method's name implies messages? In this case we won't need check from line 40.

@alongosz alongosz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

SonarQube analysis is relevant to the changes and should be addressed. It doesn't mean it's correct. In some cases (maybe even most) it's false-positive, but then it needs to be marked as such, so we don't miss a relevant one.

For that reason putting this in "request changes" state.

Some of the issues might require Front-end Team input.

Summary of that + my own findings:

satis-network-key: ${{ secrets.SATIS_NETWORK_KEY }}
satis-network-token: ${{ secrets.SATIS_NETWORK_TOKEN }}

- uses: actions/setup-node@v4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

outdated action version

Suggested change
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7

satis-network-key: ${{ secrets.SATIS_NETWORK_KEY }}
satis-network-token: ${{ secrets.SATIS_NETWORK_TOKEN }}

- uses: actions/setup-node@v4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7

satis-network-key: ${{ secrets.SATIS_NETWORK_KEY }}
satis-network-token: ${{ secrets.SATIS_NETWORK_TOKEN }}

- uses: actions/setup-node@v4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7

node-version: '24'

- name: Install frontend dependencies
run: yarn install

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

SonarQube:

Lifecycle scripts are enabled by default in Yarn v2+.
See more on https://sonarcloud.io/project/issues?id=ibexa_admin-ui&issues=AZ9_yweGtPeG6EAUOR9u&open=AZ9_yweGtPeG6EAUOR9u&pullRequest=1967

This looks relevant to the case, but if it's false positive, please mark it as false positive. I'm not able to assess this one on my own.

(you can use GitHub to log in to Sonar, so you can access full report, lmk if you have questions)

};

const extractFromFile = (filePath) => {
const source = fs.readFileSync(filePath, 'utf8');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

SonarQube:

LLMs running this code with faulty CLI arguments can escape file system restrictions. Refactor this code to validate the constructed path before accessing the file system.

This is an interesting one, I think we might LLM assessment on that :D

namespace Ibexa\AdminUi\Translation\Extractor;

use JsonException;
use RuntimeException;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If this is run in CLI, then one of Symfony\Console\ exceptions should be used.

Comes from SonarQube analysis, but I find this valid.

{
if ($this->isRuntimeReady) {
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Architecture: is there a better way of checking readiness? For e.g., existence of some file?

  1. This creates stateful service, which tends to cause problems and is not reliable really, as it lives in the current process memory only
  2. it doesn't check if the runtime did its job.

I don't have a solution, because I don't know what this runtime does, but maybe it's worth looking into.

Comment on lines +123 to +124
{
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add a nested comment explaining why this method is empty, throw an Exception or complete the implementation.

Not just SonarQube idea, we've been actually practicing this elsewhere, if spotted.

Comment on lines +126 to +128
public function visitTwigFile(SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast): void
{
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants