Problem/Motivation
Plurals are not exported correctly when exporting source translations.
STR:
- Install the "Interface Translation" (locale) module.
- Go to /admin/config/regional/language and add a language. I chose Spanish.
- Go to /admin/config/regional/translate/export
- Choose "source text only, no translations"
- Click export
- Open PO file, you'll find:
msgid "@count contextual link"
msgid_plural "@count contextual links"
msgstr[0] ""
As the source language is English and the header says "Plural-Forms: nplurals=2; plural=(n > 1);, it should be:
msgid "@count contextual link"
msgid_plural "@count contextual links"
msgstr[0] ""
msgstr[1] "" <<<<<<<<
Reported originally by Dale Eggett @ Lingotek
Proposed resolution
Investigate
Remaining tasks
Investigate. Create patch.
User interface changes
None
API changes
None
Data model changes
None
Comments
Comment #2
penyaskitoMaybe related? #2882617: String version of plural formula is not available, exported .po files contain an incorrect default.
I'm not sure if the
PoDatabaseReader::loadStringsshould be modified to create as many translations in the array as needed, or ifPoItem::formatPluralshould take care of this.None of them have access to the plural formula AFAIK.
Feedback appreciated!
Comment #3
gábor hojtsyHm, are translations properly exported when you choose that method (not the template)?
Comment #4
penyaskitoIf they are untranslated the same issue occurs.
If they are translated (e.g. translated them to German -1 plural-), it works as expected.
Interestingly, I tried with Polish (2 plurals) and Slovenian (3 plurals). I translated only some strings (1/2 plural for Polish), (2/3 plurals for Slovenian) and it worked well.
Created a new clean site again, and started over. Without any translations it doesn't work well for Slovenian or Polish.
Translated only 2nd plural for Slovenian, works well.
Translated only singular for Polish, works well.
So the problem is only when there is no translations at all for this string.
Comment #5
penyaskitoThe reason that behavior happens is that saving the translations in the form saves the empty translations:
in
\Drupal\locale\Form\TranslateEditForm::submitFormComment #6
gábor hojtsyNot clear if this is broken only for saved translations or stuff that was never yet touched too? The code cited from the save seems like it would add the needed amount of delimiters(?)
Comment #7
mglamanI reported this to LingoTek. It was an export of untranslated strings I wanted to upload so we could get a translation of strings in our Twig templates.
Comment #8
penyaskitoIt's broken for stuff that was never yet touched, as the form submit handler fixes it for us by saving the space delimiter the right amount of times.
Here I worked on a fix. Depending on the locale.plural.formula service in PoItem won't be acceptable here I guess, but as far of my manual tests have gone this works for the case at hand.
Any ideas of how to make this without depending on the service are much than appreciated.
Comment #9
penyaskitoCrossposted with both of you, sorry for the bad patch naming!
I confirm @mglaman originally discovered the issue I reported. Hoping we can give him attribution once this is solved.
Comment #10
mglamanManually tested the patch and can confirm it fixes the issue.
Before patch
After patch
The following UI screen for export. And I was able to upload to LingoTek without errors in parsing the .po
Items which have been translated, as stated, do not have this problem.
Comment #11
penyaskito@Gábor do you think it would be acceptable the conditional check for the locale module?
Comment #12
penyaskitoAdded a test for exporting the POT template with a plural string.
Comment #14
mglamanNow there are tests, and this fixes the bug. +1 and RTBC from me.
Comment #15
alexpottHmmm... a component probably should not be reaching out into \Drupal::service land. I think we should add a param to this method to set the number of plurals. Or add a new method to set it. Tricky.
We need a followup to decouple this component from LOCALE_PLURAL_DELIMITER - or to move the definition of this to the component. The dependencies are wrong way around.
As above - calling out to \Drupal::service is not great here.
Comment #18
andypostIt makes sense to check all string/array flip/flops and decide about home for delimiter definition
Faced in #1637662-14: Clean up the Gettext PO parser
Surely component's space should not use services and common.inc constant
PS: reroll
Comment #19
andypostFix for 15.3 in #2911606: Replace usage of the deprecated LOCALE_PLURAL_DELIMITER constant so this issue partially blocked on it
It looks totally wrong that
PoItemshould know language and number of plurals - it is used to store strings only and language mostly unusedI think it needs deeper refactor to store string & translation as arrays and implode/explode them where it actually expected.
Core using delimiter in following places
- .po files reader/writer - conditionally flipping source and translation depending on number of plurals - #2918489: Plurals are not exported correctly when exporting source translations
- storing plurals in config - also using flip/flop on export and to create translations- #2545730: Misuse of formatPlural() in Numeric field prefix/suffix
- UI elements and controls to edit translations
Comment #23
penyaskitoClosed #2882667: [PP-1] Interface translations export contains incorrect plural formula in favor of this one even if that's older, as this one has a patch and some more discussion. Please credit @idebr here.
Comment #24
penyaskitoRe-rolled this by hand, not sure why those tests failed in #19 but they are working for me locally.
Fixed underscored variables, did that for consistency but that was already fixed everywhere.
Comment #25
penyaskitoTagging for the DrupalCon Global 2020 sprint
Comment #26
hedrickbt commentedTesting the patch during DrupalCon Global.
Comment #27
penyaskitoRE: #19: I agree that PoItem shouldn't know about language, but I'm not that sure about plurals number. A PoItem must be responsible of knowing the number of plurals (msgstr) to generate even when the translations are not set.
It should get that value injected somehow and not obtain it by itself, but I don't think that logic should be moved anywhere else.
Comment #28
hedrickbt commentedComment #29
hedrickbt commentedComment #30
hedrickbt commentedComment #31
hedrickbt commentedComment #32
hedrickbt commentedComment #33
hedrickbt commentedI have tested #24 via a local ddev environment using the 9.1.x branch. I am also seeing the additional 'msgstr[1] ""' items for plurals. There were no other changes to the pot file - except the POT-Creation-Date and PO-Revision-Date lines, which I would expect.
Not sure if this should be moved to RTBC due to the additional discussion.
Comment #34
penyaskitoI think this solves everything in #15, but I would love to hear if anyone has a better idea than mine.
This patch adds a
PoPluralCountAwareInterface, and makesPoStreamReader,PoStreamWriterandPoItemimplement it. I would have added those methods to their interfaces, but that would make a BC break.So the responsibility of looking at the plural count goes to
\Drupal\locale\Form\ExportForm, which can depend on that service without adding the dependency from the component to the locale module.But in this way we are moving that responsibility to any callers, which means that e.g.
\Drush\Drupal\Commands\core\LocaleCommandswould need to find the plurals count too and inject them. I guess potx module will need to do the same. So I would love to hear if there's anything better than this, but I think this needs to be good enough for the component sake.Comment #35
penyaskitoThanks @hedrickbt for the updates to the issue summary, fixing my vague steps to reproduce and testing the patch, and sorry we crossposted :-/
Would you like to test the new patch and ensure we are not breaking anything else? Thanks again!
Comment #36
penyaskitoFixing the parameter name and other coding standards complaints.
Comment #37
andypostFix interface docblock and added types to new interface
Comment #38
samiullah commented@andypost I tried to manually apply the patch on 9.1x. After site refresh I am getting website encountered error.
Comment #39
samiullah commentedDblog showed the php error:
Comment #40
andypostIt's strange, it works for me
Comment #42
gábor hojtsyCrediting @idebr as per #23.
Comment #43
penyaskitoReviewed #37 patch and the interface is there, and in the correct place. The name and filename have the same upper case/lower case (I was wondering if that could be an error only affecting MAMP or alike).
Tested #37 with simplytest.me. Patch applied correctly. No errors navigating the site.
Did the steps to reproduce from the issue summary and the result PO file was the expected one.
The only thing I found is, for Russian, the
@count[2]param which I'm not sure if it's correct:But I don't think we are introducing that here.
Comment #44
penyaskitoif I go to https://localize.drupal.org/translate/languages/ru/translate?project=dru... I see that the translations are saved as that, so it looks it's ok
Comment #45
andypostIn related it was fixed to handle this suffixes
Comment #46
alexpottWhy private?
This are not inheriting anything.
Comment #47
penyaskitoRE: 1. Why private?
For consistency. The other properties there are already private. If we want to make them protected, we could do that, but IMHO it's better having consistency inside the same class.
RE: 2,3: PoDatabaseWriter is not implementing these methods. Should the writer and reader interfaces extend the new PoPluralCountAwareInterface? That feels disruptive.
I would totally do that, or even not having a separate interface, but didn't want to do a BC break. If you think that's low impact and acceptable, I would do it.
Or should we just change the {@inheritdoc} there?
Comment #50
gábor hojtsyI am not surprised that there is not wider impact. IMHO we can coordinate with locale_translation_context to fix on their side as well.
Comment #52
hmdnawaz commentedRerolled the patch 37.
Comment #53
hmdnawaz commentedComment #54
gábor hojtsyWhile #2882667: [PP-1] Interface translations export contains incorrect plural formula was closed as a duplicate, this does not deal with the export of the plural formula itself in the .po file. Which is also still broken. I think that would be fine to be in scope for #2882617: String version of plural formula is not available, exported .po files contain an incorrect default though.
Comment #58
robincsRecap from DrupalCon Lille 2023:
The functionallity of the latest patch #37 looks fine. If the reroll of the patch passes, we put this back to Needs Review.
Comment #60
andypostMoved last patch to MR and fixed following to make the test case to pass
- https://www.drupal.org/node/3168858
- https://www.drupal.org/node/3129738
Comment #61
andypostNW to add
should be typed to int, so default value will be 0
Probably should return :self as of PHP 8.0 https://php.watch/versions/8.0/static-return-type
Comment #62
hmdnawaz commentedPatch for Drupal 10.3