-
Notifications
You must be signed in to change notification settings - Fork 176
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
base: master
Are you sure you want to change the base?
[#163] Added support for translation extractors for JS #238
Conversation
…Coffeescript and the inclusion of phpunit for the tests.
…urces/config/services.xml.
…he extractors stateless.
Great work @jeremy-richard! |
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 ? |
I'd recommend to do the abstract class as they behave the same. |
Use an abstract class for the tests
Done, hopefully this feature will be available soon. |
@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. |
Do we really need two extractors? Why not allowing users to define the file extensions themselves? I can imagine |
@monteiro yep, I'll do that. @willdurand hi, it sounds like a good idea. How would you like to proceed? 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
@willdurand Hi, I found the way to have only one extractor to handle extraction from frontend files. |
@@ -34,6 +34,17 @@ public function getConfigTreeBuilder() | |||
->prototype('scalar') | |||
->end() | |||
->end() | |||
->arrayNode('frontend') |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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') |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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.
Resources/config/services.xml
Outdated
@@ -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"> |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
Resources/doc/index.md
Outdated
BazingaJsTranslationBundle includes two **extractors** for assets: | ||
|
||
- JsExtractor | ||
- CoffeeExtractors |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?'; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be private
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
} | ||
|
||
/** | ||
* @dataProvider resourcesWithATransChoiceFunctionUsage |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
Extractor/FrontendExtractor.php
Outdated
$messages = $this->getMessagesForSequence($fileContent, $sequence); | ||
|
||
foreach ($messages as $message) { | ||
$catalogue->set($message, $this->prefix.$message); |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
98494a1
to
e6dba39
Compare
{ | ||
$argumentsRegex = self::REGEX_DELIMITER | ||
.$this->sequence | ||
.self::FUNCTION_STRING_ARGUMENT_REGEX |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 --> |
There was a problem hiding this comment.
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') |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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') |
There was a problem hiding this comment.
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
Improve PR #164
Ping @willdurand, @JANorman, @monteiro @stof and @jcchavezs (thank you for helping me)