Problem/Motivation

When you add a language to a site, you'd expect that the language names would be provided with translations automatically. They aren't.

For instance, if I add Spanish to a site installed in English, I would expect that my site would now know at least that Spanish was "Español" and English was "Inglés", in Spanish. These translations are available on localize.drupal.org, but they're not getting into my D8 site automatically. The reason this is not happening is that languages are config entities that are not provided by Core, but are created by users after installation. Much like if you create a field labeled Foo, you don't automatically get "Foo" translated. But languages are a special case -- the language name labels are translated on localize.d.o, because they are part of the "Add a language" dialog (originally sources from LanguageManager.php). So they are somewhere between a shipped configuration entity and a custom one.

Proposed resolution

1. Modify LocaleConfigManager to also fake language entities as if they were in install storage for the scope of locale integration. This integrates perfectly with the interface translation system and will translate built-in languages.
2. Add tests.

Remaining tasks

Review. Commit.

User interface changes

Language config entities would automatically receive translations.

API changes

None.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Gábor Hojtsy’s picture

Status: Active » Needs review
Issue tags: +D8MI, +language-ui, +sprint
FileSize
1.87 KB

I think the solution would be something like this, if the language list is updated at that point is save() already.

Gábor Hojtsy’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Needs at least tests :D Anyone wants to help with that?

jhodgdon’s picture

Can a test get the stuff from localize, and depend on it being there?

Gábor Hojtsy’s picture

Status: Needs work » Active

@jhodgdon: No we should hardcode a .po file that has translations for the languages being tested. Such as LocaleImportFunctionalTest has a de and an xx .po file which are copied to the translations dir. It also has .po files written out from test methods that are manually imported. I think from a results perspective, either should work fine.

The real catch here is my patch will have mixed results, because you can only import translations for languages you added before, so trying to t() a language name to the one you just added will not work. What will work is the newly added language will be translated to languages already on the site as available.

Also doing this in the form is not very nice. It will not happen if you import configuration for example or use the API to add a language. I think instead of doing what the patch does, we'll need to trick the LocaleConfigManager to think that the languages have some default install storage. Ie. add 2 new methods to LocaleConfigManager to wrap getComponentNames/listAll and read() on the install storage and fake the languages as if they were in install storage as afar as LocaleConfigManager is concerned. That should not be very hard hopefully :D

Gábor Hojtsy’s picture

Status: Active » Needs review
FileSize
4.07 KB

So I think the patch would be something like this to work seamlessly with configuration translation creation and update. This is totally untested but may already work :D Basically we simulate configured languages that also have a predefined language as if it was shipped config with the predefined language name.

Gábor Hojtsy’s picture

Status: Needs review » Needs work

Tried this out. Installed site in English, added German and Hungarian. Shipped languages (English, Not specified and Not applicable) were all translated properly to German and Hungarian but German and Hungarian were also. Looks to me like it works perfectly :) I also tested installing in Hungarian out of the gate and then adding Afrikaans and German. While not all languages had translations (I think due to missing language translations on l.d.o), it worked similarly well.

(BTW I found #2407907: Configuration translation entity listings displays items overriden on the way).

Still needs tests. I think I explained in #4 how to add tests for this. Anybody up for this? If you want to use the .po single file import UI in the test, then #2095787: Configuration translations not updated when manually importing a .po file is a pre-requisite for the test to work).

Gábor Hojtsy’s picture

Issue tags: +SprintWeekend2015Queue
rodrigoaguilera’s picture

Issue tags: +#SprintWeekend2015

I will give this a try (add test as I understood) without the .po single import UI

Gábor Hojtsy’s picture

Issue tags: -#SprintWeekend2015 +SprintWeekend2015

That will fail without #2095787: Configuration translations not updated when manually importing a .po file, so if you want to go that way, that needs fixing first.

rodrigoaguilera’s picture

Ok, I'll wait for that fix then

Gábor Hojtsy’s picture

Well, that one actually needs a test written, the fix is there, so if you want :)

DevElCuy’s picture

Issue tags: -SprintWeekend2015Queue
DevElCuy’s picture

Issue tags: +SprintWeekend2015Queue

Removed tag by mistake.

DevElCuy’s picture

Removed tag by mistake.

fran seva’s picture

Assigned: Unassigned » fran seva
Gábor Hojtsy’s picture

#2095787: Configuration translations not updated when manually importing a .po file landed, so should be relatively easy to do a test for this. Easiest to add a language by importing the .po file that has the translation for a language, and then check.

Gábor Hojtsy’s picture

@fran seva: still working on this one?

fran seva’s picture

Status: Needs work » Needs review
FileSize
1.67 KB
39.07 KB

Hi Gabor.
I think I didn't understand what I should check after add the .po that create the language.
What I understood is the test should do:

1. Import a translated string for a language. I chose import from a hardcode .po using importPoFile method
2. Search the the translated string for the "new" language created in the import process.

I made a test with this steps but when I run it in 8.0.x branch the test does not fails. That make me think I'm not checking what is expected.
Attach the test and a snapshot with the test result in 8.0.x branch.

In other hand, reading the issue motivation I tried to import an spanish .po and created the "es" language and then check if the Spanish word were automatically translated but it wasn't.

Gábor Hojtsy’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php
    @@ -626,4 +643,23 @@ public function getPoFileWithConfigDe() {
    +  ¶
    

    Extra spaces :)

  2. +++ b/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php
    @@ -626,4 +643,23 @@ public function getPoFileWithConfigDe() {
    +msgid "Anonymous user"
    +msgstr "Névtelen felhasználó"
    

    The goal of this issue is to translate the languages themselves. So the .po file should contain the string "English" and "Hungarian" and check that those are properly translated. (Feel free to use Spanish or another language of your choosing BTW :)

    You the test with $override = \Drupal::languageManager()->getLanguageConfigOverride('es', 'language.entity.es'); to check if the override was created properly. Checking for the anonymous user name does not prove anything about this issue unfortunately.

fran seva’s picture

Status: Needs work » Needs review
FileSize
1.15 KB
5.22 KB
1.15 KB

The test should work :p

The last submitted patch, 20: 2397281-language-translations-20-TEST-ONLY.patch, failed testing.

Gábor Hojtsy’s picture

Assigned: fran seva » Unassigned
Issue summary: View changes

Yup, the test looks straightforward and makes sense :) @jhodgdon: what do you think?

Updated issue summary.

jhodgdon’s picture

Status: Needs review » Needs work

Yes, the test looks good to me!

One nitpick is that the comment says:

+    // Import a .po file to add hu language.

but the code is adding "de". ;)

Looking at the code... Wow, that's a lot of extra code just for this special case. I wish I'd looked at this patch sooner!

So the config system has this concept of defining "storages", of which there are already a few on the LocaleConfigManager. I'm wondering if it would actually make more sense (and really cut the lines of code down) to write out the default language config items to config files, perhaps in core/modules/locale/config/language, and define a storage that would read that one directory? Then for instance you'd have something like:

  protected function installStorageRead($name) {
    if ($this->installStorage->exists($name)) {
      return $this->installStorage->read($name);
    }
    elseif ($this->languageStorage->exists($name)) {
      return $this->languageStorage->read($name);
    }

That seems a LOT cleaner than what's in the patch now, and similar things would be done in those other methods.

I think all you'd need to do is use the \Drupal\Core\Config\ExtensionInstallStorage class and do something like

$this->languageStorage = new ExtensionInstallStorage($this->configStorage, 'config/language')

Does that make sense? I guess it would technically make the patch bigger, since we'd need to create all the language config files, but it really seems like the code would be a lot cleaner and clearer that way to me. The way it's written now, it's a mashup of reading stuff from disk like normal and checking the default languages; using a config storage, it would just be reading stuff from disk like normal plus looking in one other place.

Anyway setting to Needs Work for the nitpick comment.

Gábor Hojtsy’s picture

@jhodgdon: so we maintain this built-in language list now in LanguageManager::getStandardLanguageList. We could have a shipped config file for each, though if its in a special directory, we need to make the potx module have special support for it. Then we need to redo LanguageManager::getStandardLanguageList() to read from that directory or we have two copies of the list (one in there and one in YML files). At that point I am not sure that solution is much of an improvement, it is a lot more special case code in multiple places, instead of the self-contained solution here.

One more way to solve this would be to ship the language files actually as default config, but then remove them in the installer after they are all imported, so they are actually kept as default config but not in active config. Then we need to redo the LanguageManager::getStandardLanguageList() and/or the add language process to read from that special install storage.

While there are other options, I don't see those as simpler than what we do in this patch or more self-contained in any way.

Gábor Hojtsy’s picture

If we think outsourcing the storage to some other service would be good, then we can subclass the install storage to an "install storage with languages" and put the special case code there and inject that to the locale config manager. That would also be more code because we'd set up our own storage and inject it here but would be very similar to what we do here. I did not think we would need the overall list of the entries or their contents elsewhere other than in the locale config manager, so I did not do that, but that may be an option I could stand behind.

jhodgdon’s picture

OK. It was just an idea... I've been doing stuff with the ExtensionInstallStorage class lately in https://www.drupal.org/project/config_update and got familiar with it, so it just struck me that the code would be more symmetrical like this, but I see your point that you have other code already for the default languages.

But the code isn't actually too bad as it is. It's very understandable, at least -- it feels a bit like a kludge, but that's because it's merging two concepts, which is probably what it needs to do given #24.

I think if that one comment about 'hu' in the test is fixed it's probably ready to go.

fran seva’s picture

fran seva’s picture

Status: Needs work » Needs review

The last submitted patch, 27: 2397281-27-language-translations-ONLY-TEST.patch, failed testing.

Gábor Hojtsy’s picture

Issue tags: -Needs tests

@jhodgdon: what do you think?

jhodgdon’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me!

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Me too. This issue is a normal bug fix, and doesn't include any disruptive changes, so it is allowed per https://www.drupal.org/core/beta-changes. Committed b9819fa and pushed to 8.0.x. Thanks!

  • alexpott committed b9819fa on 8.0.x
    Issue #2397281 by fran seva, Gábor Hojtsy: Languages not translated when...
Gábor Hojtsy’s picture

Issue tags: -sprint

Yay, magic, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.