Problem/Motivation
Date formats cannot be localized in Drupal 8. There are 3 UIs that lie to you about supporting that, but none of them do.
1. The built-in UI allows you to assign the format to different languages, but not to have different config for the same machine.
2. There is a hidden secret, forgotten UI that allows you to "translate" the formats but then it overwrites the cross-language setting in 1.
3. Config translation has a tab on date formats to translate, but that would only contain the date format label.
Proposed resolution
Use config translation (module). If we mark the date format translatable in the schema, we can expose that on localize.drupal.org and config translation module will pick it up as well. We need to have it as a translatable type, but using an existing type like label would not allow to display a proper widget for it, so defining a new derivative type lets us display a good UI in config_translation.
Remaining tasks
User interface changes
None. The UI change will be
API changes
Related Issues
#2052193: [META] Date format localisation is a huge mess, conflicts, does not work, regressed
#2099997: Use config translation to translate date formats
#2117711: Add tests to date format translation UI from the config_translation module is postponed on this
Comment | File | Size | Author |
---|---|---|---|
#64 | date-formats-2098089-64.patch | 8.71 KB | YesCT |
#64 | interdiff-60-64.txt | 1.68 KB | YesCT |
#60 | date-formats-2098089-60.patch | 9.9 KB | shnark |
#60 | interdiff-56-60.txt | 2.88 KB | shnark |
#59 | interdiff.txt | 2.57 KB | tim.plunkett |
Comments
Comment #1
Gábor HojtsyNeeds quotes.
Comment #2
Gábor HojtsyWups :D
Comment #3
Jose Reyero CreditAttribution: Jose Reyero commentedIt looks to me like a good first step.
Comment #4
webflo CreditAttribution: webflo commentedYes. Lets add this new translatable type to proceed with config translation.
Comment #5
penyaskitoAdded quotes to the schema.
Comment #6
Gábor HojtsyDot missing at end of line.
Comment #7
Gábor HojtsyActually, you know, let's do some tests for this! Once the date format is translatable, the locale_storage() should end up with the source of the date format, it may be good to add some basic test stuff for this. Locale has some tests for strings appearing in the DB when a new language is added / module is enabled, etc. In this case, the source is in system module so it should already be there, easier to test! Let's do that!
Comment #8
penyaskitoWorking on the tests.
Comment #9
penyaskitoThis test should be right, but I think I hit a bug.
Drupal\Core\Datetime\Date::format() is passing the language given to DrupalDateTime::createFromTimestamp, but no to dateFormat(). There, we load the format and apply it, but always the default format and not the translation if any.
Comment #10
penyaskitoAdded context for taking the language into account. Added only-test patch for showing that the current behavior is wrong.
Comment #12
penyaskitoClean up and docblocks. I'm not sure if the failing tests are really related.
Comment #14
YesCT CreditAttribution: YesCT commentedhead is green, BookTest passes on head locally, and fails with the patch. So it's the patch for sure. But I dont know why yet.
we are getting a lot of things here, maybe some are not returning the correct things, or able to access them. I really need to figure out how to debug with step through. But time for dinner now.
Comment #15
penyaskitoNo worries, still working on it.
Comment #16
penyaskitoSilly me, we are trying to load the switch to a different language context no matter if the language module is there or not, so every test that does not activate the language module is failing.
Patch will follow.
Comment #17
penyaskitoTake into account if there is any language given and if the language is given for applying the configuration context.
Comment #18
penyaskitoIn this patch I attempt to redefine the date service in locale, so we can remove the language module existence from the Drupal\Core\DateTime\Date class.
Locally it is not working however.
Comment #19
YesCT CreditAttribution: YesCT commented[edit: posted without seeing 18.]
seems like we could se this later inside the if !isset, it is not used otherwise.
should the moduleHnadler be injected?
if it is, hmm. there is no contructor, so how do we get moduleHandler from the container?
these two lines were already there. I think the recommendation is if it is used more than once, then use a use statement.
maybe we do not need the entityManager here, and instead just pass in the actual format.
I wonder if there is an issue for that.
Comment #20
penyaskitoRenamed LocalizedDateServiceProvider to LocaleServiceProvider because doesn't work if the ServiceProvider has not the same name than the module (thanks Berdir!).
Renamed LocalizedDate to LocaleDate for consistency.
Replying comments in #19:
1 and 2 are not relevant anymore.
3. I don't get it. Do you mean 'use Drupal\language\LanguageConfigContext' and pass only 'LanguageConfigContext'? In any case with the new patch is not relevant, is only used once.
4. No idea.
I took the docblocks from YesCT patch at #19, but my interdiff is with #18.
Comment #22
penyaskitoOops, forgot to remove the contructor parameter.
Comment #23
penyaskitoThe right patches...
Comment #25
penyaskitoFixed the missing import and renaming for consistency.
Comment #26
Gábor HojtsyNot LanguageTest :)
I see you have this child class so that the language context can be used, however that is only defined by language module. Doing this as a class registry replacement sounds a bit hackish to me honestly.
I'd explore two other ways:
- should we inject the date class and inject the proper one?
- should the language context be moved up to the system level (since the language class is already there and language is universally understood on the field, etc. levels)
I think the re-simplification of config that is ongoing is pointing in the direction of the system level language support in config, so that sounds like would be a good interim step here. Then there is no reason to override the class, since the base class can do the same thing. IMHO just move the language context to a global thing like context free and users are :)
It does not really have a specific UI by the virtue of having a separate type, that is not really up to the schema, so I'd leave that ("has a specific UI") out.
Comment #27
Gábor HojtsySo #2098119: Replace config context system with baked-in locale support and single-event based overrides will change how language overrides are accessed / skipped, but the general logic and especially tests in this patch should still apply. I'd encourage doing a simpler language solution with #2098119: Replace config context system with baked-in locale support and single-event based overrides in mind, but not wait on #2098119: Replace config context system with baked-in locale support and single-event based overrides since the other solutions here will still apply 1-1.
Comment #28
Gábor HojtsyThe combination of this with #2099997: Use config translation to translate date formats is currently targeted to provide an actual date translation experience in core. Once config translation is landing in core, we can remove the useless interfaces on language/translation and the very confused backend code as well. There are lots of confused random code that we can remove. Let's do this!
Comment #28.0
Gábor HojtsyAdd meta
Comment #29
Gábor HojtsyDiscussed with @penyaskito. He does not have time for this this week, but indicated we can start from #17. Indeed. Moving the language context to the base config layer instead of in language module would help there, so we can use it without a condition on language module. Someone wants to take a stab at this starting from #17?
Comment #30
penyaskito#17 cleanup. Applied feedback from the other patches+reviews to this one.
Comment #31
penyaskitoMoved the LanguageConfigContext to the base config layer and removing the check for language module.
Comment #32
Gábor HojtsyGetting there. Only one code style and one logic issue :)
Newlines/indent before param descriptions, empty line before @return, etc. missing :)
There is only one problem with this code. It assumes if no language was given, then we should cache the option under the interface language code negotiated, BUT it does not make sure we go into that context.
So if the function was invoked in a different context then the langcode we cache the value under will be different than the language context used to look up the format. So either we need to always go into that context or *better*, attempt to retrieve the language from the context, and if something is set there than use that language, otherwise go into the interface language context.
You can get the context from the config factory by invoking getContext() on it.
Comment #33
penyaskitoAdded the docblock fixes from #32 and the logic for handling if a config context is active at the moment.
Added a config_context() method for avoiding coupling to ConfigFactory.
Comment #34
Gábor HojtsyIf empty($langcode) and these conditions match is the only case when we don't need to explicitly enter a language context later on. So if we are already in some language context and know the langcode from there. If that is not the case, then we need to enter a specific context, since we are not sure if the context we are in matches the langcode we think we get the data in, right? :)
Comment #35
Gábor HojtsyThe config translation module is fully capable now to provide a user interface for the new date_format type, see #2099997: Use config translation to translate date formats and is awaiting more tests to be written as soon as this lands in core (#2117711: Add tests to date format translation UI).
Comment #36
penyaskitoRemoved config_context() from #33 and instead injected the ConfigFactory.
Fixed the pending logic issue.
Comment #37
Gábor HojtsyUse the config factory to do these too and then this all looks like done to me :)
Comment #38
Gábor HojtsySo after talking about this more on IRC, config_context_*() does a lot of utility stuff, like ensuring the class exists/implements the context interface and also reusing context instances instead of instantiating new ones all the time. Those are things we don't get from the injected config factory *at all* because they are only in the procedural function. #1926968: move config_context_enter to a container-aware service was won't fixed earlier, so it is not likely we are getting a solution on the factory or in the form of another service. Finally, #2098119: Replace config context system with baked-in locale support and single-event based overrides may remove the whole context system altogether, so these functions will definitely be looked at again.
So I think we should get back to #33 + the 1 line fix for my comment in #34 that you already included in #36. No injection of the config factory and we can keep config_context() as a global function as you added it. That would be RTBC as in as good as we can do under current APIs.
Comment #39
penyaskitoPatch according to conclusions at #38.
Interdiff with #33.
Comment #40
Gábor HojtsyYay! Thanks. As written in #38 this is as good as we can get with our API now. Code looks good and definitely makes date format translation work and shippable.
Comment #41
tim.plunkettSwitching to injecting config_context_enter/config_context_leave is fine for a followup, but...
Please don't introduce a new procedural wrapper (especially one using drupal_container()!)
Date is a service, please inject this.
Comment #42
Gábor Hojtsy@tim.plunkett: as per #38, the config context functions do not have an injectable equivalent and in fact @alexpott closed down an issue where that was proposed. So we could inject config factory to *get* context but not to enter/leave. That would mean we may use different config factories for different operations. Given the current context API this is the best possible way AFAIS. Please advise in more concrete terms otherwise. More info in #38 :)
Comment #43
tim.plunkettI'm reasonably sure that's BS.
If you're going to add a random procedural wrapper for a service, at least use \Drupal::service() and mark it deprecated.
Comment #44
tim.plunkettBetter yet.
Comment #44.0
tim.plunkettAdded #2099997 to the summary for tracking.
Comment #46
tim.plunkettBefore, config_context() forgot to actually return anything. So it was *always* NULL. Now that I've fixed the bug, the tests fail.
Comment #47
tim.plunkett.
Comment #48
Gábor HojtsyThanks for the update. re #43, the point of injecting the config factory is so it can be different from the global one on the container, so I'd be surprised if sometimes using the global one even though we let it injected would be a good idea. It was explored above and then got discarded.
Now back to fixing the tests/bug :)
Comment #49
penyaskitoI probably won't be able of dedicating more time to this one this week, so unassigning.
Comment #50
YesCT CreditAttribution: YesCT commented[edit]
I'm going to read through this. (in badcamp sprint room)
#2098119: Replace config context system with baked-in locale support and single-event based overrides seems like it would handle the @todo from that last interdiff.
Comment #51
Schnitzel CreditAttribution: Schnitzel commented#44: date-formats-2098089-44.patch queued for re-testing.
Comment #52
Schnitzel CreditAttribution: Schnitzel commentedSo I started to write a test to test if the loading of the langcode via context actually works (which should then fail with the code before #44).
But during writing this I found out that dateFormat() is never called with $langcode = NULL or a non existing $langcode.
So I discussed with @alexpott and he told me that I should remove then the whole Code, which is now in the attached patch.
I'm not sure why we actually have this whole system which checks for !empty($langcode).
Maybe I'm also wrong and there is a case where $langcode = NULL can happen, but I did not found any case.
Comment #54
Schnitzel CreditAttribution: Schnitzel commentedoh, one small change is still necessary, but actually $langcode is used in the same line, so I think this is fine like this.
Comment #55
tim.plunkettI think you just wanted
$format = $this->dateFormat('fallback', $langcode)->getPattern($key);
getPattern has no second param
Comment #56
Schnitzel CreditAttribution: Schnitzel commentedaddressing #55
Comment #57
YesCT CreditAttribution: YesCT commented#52 removed the code that needed tests.
so removing the needs tests tag
Comment #59
tim.plunkettFor anyone curious, here is the interdiff between my patch in #44 and the most recent one, whitespace ignored.
This was RTBC before, I think mine were the only complaints, and that has been resolved. RTBC once more.
EDIT: I crossposted, but also queued for retesting. The error was in the update.module tests, which can time out now and then. Wait for green.
Comment #60
shnark CreditAttribution: shnark commentedI noticed coding style cleanup that could have been done in some of the changed lines of the patch.
None of these are functional changes, just nits.
Comment #61
tim.plunkettComment #62
Gábor HojtsyWorked on this as part of the Spark project as well. Tagging for that.
Comment #63
alexpottUnused variable
$langcode = $langcode?
Comment #64
YesCT CreditAttribution: YesCT commentedhere we go.
addressed those.
also found an un-used use that was added in this patch.
we dont need to "use" it to use it like this.
Comment #65
Schnitzel CreditAttribution: Schnitzel commentednice!
Comment #66
Gábor HojtsyComment #67
webchickThat was pretty straight-forward feedback, and it's all been addressed, so I don't think it needs to go back to alex for a second review.
This looks great to me, thanks for the expanded test coverage.
Committed and pushed to 8.x!
Comment #67.0
webchicknoting config trans has a postponed issue
Comment #68
Gábor HojtsyYay, thanks all! See you in #2127941: Remove two (out of 3) bogus and duplicate date languages UIs.
Comment #69
Gábor Hojtsy