Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#163] Added support for translation extractors for JS #238

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

jeremy-richard
Copy link

Improve PR #164

Ping @willdurand, @JANorman, @monteiro @stof and @jcchavezs (thank you for helping me)

@willdurand willdurand self-requested a review January 29, 2018 14:39
@jcchavezs
Copy link

Great work @jeremy-richard!

@jeremy-richard
Copy link
Author

I know CoffeeExtractorTest did not change from the previous PR #164

Is JsExtractorTest OK? Should I do the same modifications to CoffeeExtractorTest, or define dataProviders onto a new parent class aka BaseExtractorTest ?

@jcchavezs
Copy link

I'd recommend to do the abstract class as they behave the same.

Use an abstract class for the tests
@jeremy-richard
Copy link
Author

Done, hopefully this feature will be available soon.

@monteiro
Copy link
Collaborator

@jeremy-richard looks good! Thanks for the great work.

Can you add just the missing break in all js and coffee files. That’s would be great.

Sorry for taking a lot of time.

@willdurand
Copy link
Owner

Do we really need two extractors? Why not allowing users to define the file extensions themselves? I can imagine .ts (typescript) as a useful extension. Thanks for this PR!

@jeremy-richard
Copy link
Author

@monteiro yep, I'll do that.

@willdurand hi, it sounds like a good idea. How would you like to proceed?
I'd like to have a FrontEndExtractor, then set the extension to handle via the key bazinga_js_translation.frontend_extensions with the default value being ['js', 'jsx', 'coffee', 'ts']

Does the sequence will be the same for typescript then? (I've never code with typescript to be honest)

More flexible way to handle extraction from other sources than twig or php
@jeremy-richard
Copy link
Author

@willdurand Hi, I found the way to have only one extractor to handle extraction from frontend files.
Let me know how to improve it.

@@ -34,6 +34,17 @@ public function getConfigTreeBuilder()
->prototype('scalar')
->end()
->end()
->arrayNode('frontend')
Copy link
Contributor

Choose a reason for hiding this comment

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

I would rather name it extractor

@@ -34,6 +34,17 @@ public function getConfigTreeBuilder()
->prototype('scalar')
->end()
->end()
->arrayNode('frontend')
->prototype('array')
->children()
Copy link
Contributor

Choose a reason for hiding this comment

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

indentation should use 4 spaces

Copy link
Author

Choose a reason for hiding this comment

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

OK

@@ -34,6 +34,17 @@ public function getConfigTreeBuilder()
->prototype('scalar')
->end()
->end()
->arrayNode('frontend')
->prototype('array')
Copy link
Contributor

Choose a reason for hiding this comment

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

do we actually need an array of configurations here ?

Copy link
Author

Choose a reason for hiding this comment

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

Based on your comment, no. We can use differents services.

@@ -26,5 +26,10 @@
<argument>%kernel.root_dir%</argument>
<tag name="console.command" command="bazinga:js-translation:dump" />
</service>
<service id="bazinga.jstranslation.translation_frontend_extractor" class="Bazinga\Bundle\JsTranslationBundle\Extractor\FrontendExtractor" public="true">
Copy link
Contributor

Choose a reason for hiding this comment

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

should not be public. Extractors are totally fine as private services as Symfony does not retrieve them dynamically from the container. This would allow inlining the service.

Copy link
Author

Choose a reason for hiding this comment

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

OK

BazingaJsTranslationBundle includes two **extractors** for assets:

- JsExtractor
- CoffeeExtractors
Copy link
Contributor

Choose a reason for hiding this comment

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

this doc is now outdated. Please update it

Copy link
Author

Choose a reason for hiding this comment

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

Done for this part. I'll add how to use the new extractor when all the code will be good to merge.


final class FrontendExtractor extends AbstractFileExtractor implements ExtractorInterface
{
const FUNCTION_STRING_ARGUMENT_REGEX = '\s?[\'"]([^"\'),]+)[\'"]\s?';
Copy link
Contributor

Choose a reason for hiding this comment

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

Spaces between the function call and the string should allow multiple spaces too, not just a single one.

/**
* @var FrontendExtractor
*/
protected $extractor;
Copy link
Contributor

Choose a reason for hiding this comment

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

should be private

Copy link
Author

Choose a reason for hiding this comment

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

OK

public function setUp()
{
$container = $this->getContainer();
$this->extractor = $container->get('bazinga.jstranslation.translation_frontend_extractor');
Copy link
Contributor

Choose a reason for hiding this comment

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

OK, this is the only place where you need the service to be public. But there is a much simpler way to handle all this: don't use a WebTestCase to test the FrontendExtractor but instantiate it directly in the test (the only other service it depends on is the Filesystem, which is dead easy to instantiate)

Copy link
Author

Choose a reason for hiding this comment

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

OK

}

/**
* @dataProvider resourcesWithATransChoiceFunctionUsage
Copy link
Contributor

Choose a reason for hiding this comment

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

no need to create data providers in case you have a single resource to process. Put the name of the file directly in the test.

Copy link
Author

Choose a reason for hiding this comment

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

OK

$messages = $this->getMessagesForSequence($fileContent, $sequence);

foreach ($messages as $message) {
$catalogue->set($message, $this->prefix.$message);
Copy link
Contributor

Choose a reason for hiding this comment

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

Setting them always in the messages domain is wrong. The best solution would be to support extracting the domain properly (but this is hard). But the basic solution is at least to respect the default domain being configured (which may be changed to something else than messages which is very handy when your frontend needs less than 1% of all your translation messages)

Copy link
Author

Choose a reason for hiding this comment

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

Then should we specify the domain in the constructor? (as the TranslationDumper does)
You said we can use the default domain, do you know the way to retrieve it frome this part of the code?

Copy link
Contributor

Choose a reason for hiding this comment

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

The default domain should indeed be injected in the constructor.

@jeremy-richard jeremy-richard force-pushed the 163_addition_of_js_and_coffee_extractor branch from 98494a1 to e6dba39 Compare February 14, 2018 15:05
{
$argumentsRegex = self::REGEX_DELIMITER
.$this->sequence
.self::FUNCTION_STRING_ARGUMENT_REGEX
Copy link
Contributor

Choose a reason for hiding this comment

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

why making the part about the function call configurable, but hardcoding the string argument regex ? Why not making the whole thing configurable instead ? Different cases might have different regexes for string arguments (thus, your own regex about string argument may not be the best one, as it forbids having any quotes inside the string, even when it is not the delimiting quote)

Copy link
Author

Choose a reason for hiding this comment

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

I don't really know. I forked this feature where @jcchavezs left it.

Choose a reason for hiding this comment

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

I would say that when I first wrote this I saw it convenient to make the keywords trans configurable but now I think it is not a problem so I would hardcode all. I don't think having everything configurable is a valid use case.

<argument type="service" id="filesystem"></argument>
<tag name="translation.extractor" alias="frontend" />
<argument></argument> <!-- extensions -->
<argument></argument> <!-- sequence -->
Copy link
Contributor

Choose a reason for hiding this comment

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

you don't have anything configuring this service anymore in the DI extension

@@ -34,6 +34,14 @@ public function getConfigTreeBuilder()
->prototype('scalar')
->end()
->end()
->arrayNode('extractor')
->prototype('array')
Copy link
Contributor

Choose a reason for hiding this comment

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

as you define a prototyped node for extractors, this still requires configuring extractors explicitly, otherwise you would get none. I would rather see a config like that:

bazinga_js_translation:
    extraction:
        js: true # pre-configured extractor for JS, JSX and TS)
        coffee_script: false # separate service from JS with a different regex suited for the Coffee syntax as it may not have braces)
        custom_extractors:
            - { extensions: 'crazy', sequence: '->trans\(' }

This way, we can define a few built-in extractors that users just enable or disable, without having to care about what the regex is (and if they want a different config, they disable the built-in service and register a custom one)

Copy link
Author

Choose a reason for hiding this comment

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

I'm kind of lost here. How can I enable/disable dynamically a service from being instanciate by this config?
Or should I use this config to define the extractors in my own services configuration and delete the default one on this bundle?

Copy link
Contributor

Choose a reason for hiding this comment

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

delete the default one on this bundle

which default one ?

Copy link
Author

Choose a reason for hiding this comment

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

The one with the id bazinga.jstranslation.translation_frontend_extractor from the Resources/config/services.xml

Copy link
Contributor

Choose a reason for hiding this comment

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

all this configuration here is about defining some bazinga.jstranslation.* services (which is missing currently, as you don't process the config anymore).

The issue with your existing configuration tree is that you only have a config corresponding to my custom_extractors proposal, i.e. extractors configured in userland. So by default, there would be no extractor at all if they don't configure anything.

My proposed configuration would allow the bundle to manage itself the extensions and regex for the case of JS and CoffeeScript (and whatever pre-configured extractor we want to expose), while still allowing users to define more of them (I'm not even sure that second part is necessary, as they could just define some services themselves).

We must have some extractor service defined in the bundle. But we must have them working out of the box for users.

Copy link
Author

Choose a reason for hiding this comment

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

I agree. More commits to come to fix this feature.

->arrayNode('extractor')
->prototype('array')
->children()
->scalarNode('extensions')->defaultValue(array('js', 'jsx', 'ts'))->end()
Copy link
Contributor

Choose a reason for hiding this comment

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

this is not a scalar node. This is a prototyped array node. Otherwise, users won't be able to configure multiple values.

@@ -34,6 +34,14 @@ public function getConfigTreeBuilder()
->prototype('scalar')
->end()
->end()
->arrayNode('extractor')
Copy link
Contributor

Choose a reason for hiding this comment

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

this config is currently dead code, as you remove all usage of this config from the DI extension

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.

5 participants