Problem
Calling the function locale_get_plural (file modules/locale/locale.module) for language 'en' will always return -1
Reproduce:
locale_get_plural(1, 'en');
locale_get_plural(2, 'en');
Expected:
0
1
Actual:
-1
-1
Note that:
- The only current use (format_plural()) will not be touched by this bug as it contains some (ugly) code to work around this problem???
- Issues #1220964: Number field prefix/suffix get t()'ed through format_plural() and #1221208: i18n_field should translate prefix/suffix of number fields do depend on this issue being solved. Thus this issue should have at least the same priority as those.
Proposed resolution
The cause for this behaviour is the fact that the languages table does not contain a formula for English as there will normally no .po file being imported for English. To solve it this function could hardcode the formula for English as a default fall-back if the formula field is empty for a given languages table record.
| Comment | File | Size | Author |
|---|---|---|---|
| #51 | interdiff.txt | 519 bytes | kalman.hosszu |
| #51 | 1221276-51-d7.patch | 10.66 KB | kalman.hosszu |
| #48 | interdiff.txt | 2.61 KB | kalman.hosszu |
| #48 | 1221276-48-d7.patch | 10.65 KB | kalman.hosszu |
| #46 | 1221276-45-d7.patch | 10.8 KB | gumanist |
Comments
Comment #1
fietserwinComment #2
fietserwinI took the patch of #1221254: locale_get_plural() only works for a single language within a request and changed it slightly to also solve this issue. Thus this patch will solve both issues. However, I'm not sure if format_plural will still work with this change.
Note: I'm not yet into the testing framework, but putting the above steps to reproduce the problem in a test method should probably suffice.
Comment #3
sunPlease do not merge other issues/patches. The patch from #1221254: locale_get_plural() only works for a single language within a request was bogus, so this one is too.
In case this patch depends on the other issue, then we need to mark this issue as postponed.
Comment #4
fietserwinBoth patches are not bogus, they just implement this function in a more comprehensible and elegant way (IMHO :)). Thus, yes they do a bit more than strictly solving the issue, but in terms of functionality the result is the same. This patch solves this issue but happens to also solve the other issue. (IMO Drupal is already full of incomprehensible code that gets less and less readable by each patch because it only should solve the error at hand.)
Note I did change a usage of array_key_exists back to isset (I had changed that because I started my solution differently in a way that didn't work out).
Comment #5
sunIt seems you are missing the point of what's happening in the individual conditions of this function. I can see how not understanding functionality can be frustrating, but that's not a reason to make utterly wrong claims and bash on the general quality of code in Drupal core.
Comment #6
sunThis is what you intended to do.
Comment #7
sun#1221254: locale_get_plural() only works for a single language within a request has been marked as duplicate, since this patch is superior.
Comment #8
sunForgot parenthesis here.
Fixed in attached patch.
Comment #9
fietserwinMy patch left all the caching intact. It did change the -1 return to always return "an index" (calling functions expect an index not a failure) and treated English and other languages without a configured formula as having the default formula.
Aside: your performance worry did make me curious, so I did some timing. It looks like PHP caches the eval'ed code, so the performance hit is not extreme (10000 calls (same language, different count, language_list() already called): 0.1 s with formula hardcoded; 0.19 s with eval(); 0.21 s with @eval(). The performance hit is not only in the first call to eval);
But that should not be part of this patch.
I reviewed and tested the patch from #8, no further comments on that.
Comment #10
gábor hojtsyLooks like good progress here. This is a pretty forgotten place of the API I must say...
Comment #11
fietserwinI added tests to the patch of #8. It's my first time I add tests and I did not find a logical class to add them, so I placed it in a class on its own. Perhaps it should be added to LocaleTranslationFunctionalTest?
Comment #12
alduya commentedI tested the patch in #8 on multilingual Drupal 7.10 and it works. Thank you.
Comment #13
gábor hojtsy- I think you can remove the comment on why we import .po files to set the plural form. At least don't include the alternate PHP code :)
- hr is Croatian, not Hungarian
- not sure the naming of the PO file helper functions is good in this context (I guess you copied from somewhere else). Most of their content have no usefulness for the test and whether they have or not have contexts is irrelevant also, right?
Comment #14
gábor hojtsyTagging as current focus.
Comment #15
gábor hojtsy@fietserwin: can you help with updating the tests? I think this looks like it would be pretty close otherwise.
Comment #16
fietserwinWell, patch needs to be redone. due to large changes in D8
- Move to /core (simple)
- Introduction of variable locale_translation_plurals (at least, I've never seen that one before)
- Introduction of another error in the code: testing a non-existing variable (line 749):
- I still consider returning -1 to be an error. It is not documented as so (function doc says that it returns an index, not -1) and the only current usage (format_plural() in common.inc) has a comment "// Backwards compatibility." before the check on -1.
- Test can indeed be adapted as per #13.
I'm very busy the coming weeks and won't do much patch writing. Soif anyone else wants to change the patch, feel free to do so.
Comment #17
gábor hojtsyAnybody else want to look into this?
Comment #18
gábor hojtsyAll right, I thought this would be simple for someone to take, but apparently not. Some notes for this update:
1. The issue that locale_get_plural() cannot be used with multiple languages in the same request was solved earlier by pulling in the whole list of plural configurations. That is not a bug to solve here anymore. However, the non-existent English plural formula is a bug for the 80% case where you start translating English but obviously just start on the UI, you don't have a plural form to import from a .po file.
2. Renamed the variables inside locale_get_plural() for clarity. There was probably too much dark magic going on in the function names, hopefully now much better. Also took all comment from the above patch and then added some :)
3. Took the test, updated it to the current API and improved it. Almost only left the relevant parts of the .po files plus added a non-existent Hungarian language as well so we can test for the -1 error condition (and fixed the code comment in format_plural() to better explain why it does what it does). Fixed comments and test feedback to be better English (hopefully).
Please review!
Comment #19
gábor hojtsyAdding UI language translation tag.
Comment #20
kalman.hosszu commentedHi,
I reviewed and tested the #18 patch and it seems OK!
I change the status to RTBC.
Kálmán
Comment #21
xjmAll the summaries should start with third-person verbs. ;)
Comment #22
gábor hojtsy@xjm: sampling of comments from locale.test outside of this patch:
In short it is VERY hard to find anything in this file that conforms to that rule. Even the two examples I cited above which do start with a third person verb are at error. The first one was above 80 chars, the second is two lines of summary. Both are invalid, right?
So (a) I don't seem to find examples of how to word test class descriptions, helper function descriptions, class variable descriptions, etc. starting with 3rd person verbs to apply to the patch and (b) I don't know if it makes sense if we fix it in the 3 comments added in this patch and not elsewhere. I do want to keep this backportable though because the bug exists in Drupal 7 and before and it would be useful to backport. (Edit: Also keep the issue scope focused instead of turning into a doc cleanup task).
Any tips?
Comment #23
gábor hojtsyAlso @catch previously communicated that we should mark issues where others depend on solutions as major. The following two bugs were marked as postponed on this issue earlier, so this should be major with that logic: #655048: Plural formula information blanked when importing a poorly-formed .po file and #1220964: Number field prefix/suffix get t()'ed through format_plural()
Comment #24
xjm#1326618: Clean up API docs for locale module will take care of correcting docs in the rest of the module. We just need to make sure that new docs we add or change follow the standards in:
http://drupal.org/node/1354#functions
http://drupal.org/node/1354#classes
Comment #25
gábor hojtsyHere is a reroll with the docs changed as suggested. Interdiff attached. Please help move this forward again. Two issues depend on it, and its a pretty small issue at that.
Comment #26
xjmYay! Thanks Gábor.
Comment #27
andypostThis is a wrong way to test this functionality.
Import is happen in different process but asert() is happen within old instance operationg with old variables and statics
By the same way we have broken test within testStandalonePoFile()
At least 3 statics should be reseted
Comment #28
gumanist commentedGot two issues related to this on D7:
1. Incorrect plural form when using
format_plural(3, '@count drupaler', '@count drupalers', array(), array('context' => 'Multilingual', 'langcode' => 'ru'))2. Notices about incorrect language parameter.
These patches includes next changes:
1. Updated tests with separate page implementation
2. Backported to D7
Comment #29
gumanist commentedD8 patch without suffix to allow testing
Comment #30
gábor hojtsy@andypost, @gumanist: can you elaborate on why did it pass before for the validation of those complex formulas if it was using the wrong data? I'd rather not do this whole proxy webpage thing for testing if possible (we can clear/reinitialize variables/caches for testing if we need to).
@gumanist: can you elaborate on the two errors you got on D7 and what you added to the patch to overcome them?
Comment #31
gábor hojtsy@andypost, @gumanist: I gave more thought to this test data issue. If you put simple debug() statements on the variable in the test before and after the .po files are initialized, you'll see that there are no plural formulas before so the default array is used, and then the plurals are properly initialized. I think the test can assume that it starts from a blank slate and that there is no locale_get_plural() cache to clear since it was not cached (neither the variable). Also, locale_get_plural() does not use language_list in the D8 version so no need to touch that either.
All-in-all looks like you are trying to inflict extra pain on the D8 patch/test for something you've experienced with D7, but this issue is not about D7 at the moment. Why do we need to complicate this so much when we can assume the environment we are in. We don't have any languages at first in this test, then add two, so we can assume what kind of environment is available and don't need to do all this extra stuff you suggested AFAIS.
Comment #32
jose reyero commentedAfter some running the tests and debug I think I have some explanation to all of these questions. My conclussions first:
- Though D8 tests in Gabor's patch work and they do test what they try to test (that assert happens in the same db instance, the only difference are static variables) ...
- it wouldn't be bad some drupal_static_reset() to make sure we start from a clean state for these variables -we actually don't, but we are simply lucky the variable is not yet set to a non empty value (#25), that empty($plural_formulas). -
This is what is happening:
- The locale_get_plural() function is called during test initialization. Don't ask me why but it is just called, you can see it adding some debug. It may be the po import.
- For D7 as we rely on language_list(), which has only English the first time the function is called, $plural_formulas is initialized to some non empty value, array ( 'en' => ''), so it doesn't get the right value later.
- For D8, $plural_formulas is initialized with some variable and it gets an empty value the first time it's called, so it is initialized again on next call using the new imported languages.
I think this explains why tests work for D8 and not for D7. Also tests without further page requests are ok though to be safe about static variables maybe using some more page requests could be better, Which doesn't mean we should use tests in #28 because these don't really tests requests with multiple languages as they just try a single language per request.
So I'd leave this to 'needs work' (minimal, just some static resets to make sure we have a clean start and we are better future-wise in case something changes later with test initializations that lets us with some 'polluted' static variables).
And, but maybe for another issue, we should add some function that can be reused for other tests to fully reset locale variables because this kind of things just happen too often when testing locale features, and that's because normal test initialization starts the locale system too.
Comment #33
gábor hojtsyOk here is a new version of the patch with the locale_get_plural() caches reset before they are tested (although as discussed above this does not actually do anything in the test, since they were not yet invoked). If their static caches would contain outdated data, the majority of the tests would fail, which is clearly not the case.
Since this patch is the same as was RTBC for 10 days minus some docs fixes plus a minor cache clears added that do not change the behavior of the tests at all, I think this should be back to RTBC once it proves it passes (still).
Comment #34
gábor hojtsyBack to RTBC as per above.
Comment #35
catchI don't understand why we're static caching the plural_formulas, since that's just coming from variable_get() anyway?
Having said that this pre-exists the patch and it just happens those lines are being moved around, so we can look at that in a followup (and it'll need reconsidering with CMI anyway).
Committed/pushed to 8.x, moving to 7.x for backport.
Comment #36
gábor hojtsyRight, the variable/static issue is going to be revisited IMHO when we do the CMI migration. This commit unblocked #655048: Plural formula information blanked when importing a poorly-formed .po file and #655048: Plural formula information blanked when importing a poorly-formed .po file so following up on those.
Comment #37
fietserwinIn D7 the formula comes from language_list(). Though that function executes a SQL query, it also caches the result of it. So, using a static for that saves a function call, at the expense of calling another function: drupal_static()?! So, I think it is better to follow up and clean this function up a bit further.
Comment #38
gábor hojtsy#655048: Plural formula information blanked when importing a poorly-formed .po file is marked postponed on this one in D7 too as it needs this backported first to apply tests just like in D8.
Comment #39
gábor hojtsy@fietserwin or @kalman.hosszu: can you help with backporting the functional change and the test for now? I think cleanup can be done later once we ensure the bug is fixed.
Comment #40
gábor hojtsyUnfortunately I don't have hordes of people to do D7 backports. Getting the D8 patches done (and committed) is hard enough. Removing sprint tag.
Comment #41
gumanist commented@Gábor Hojtsy:
About #1
Don't know why, but I couldn't reproduce it now. Hope this is good and it was something with configuration :)
About #2
Example:
When you don't have a russian language (ru) in system this code
or this
returns notices
About tests: imho you are creating 'synthetic' enviroment by clearing static variables, etc. Changing anything with static variables will cause incorect tests, which could pass, but maybe not. My tests are slower and more 'resource eater', but they test same as user gets. And it is up to you which approach to use.
My patch is rewritten version of yours, so it could be used as backported version or as a base for it.
Comment #42
gábor hojtsy@gumanist: your test does not test the original problem, that when calling format_plural() with multiple languages in the same request, it will not take the plural formula for the second, third, etc. languages anymore. It only takes the first in D7.
Comment #43
gumanist commentedWill it be enough if I will just add a page with that number of calls to the test? Or you have a suggestions to the patch itself too?
Comment #44
gábor hojtsyI guess if you do all the plural generation on one page and then do all the checking of the HTTP output in the test, that would also do it. But this is essentially a very slim API function, so I still think that looks like big overkill just for the tests to test it via HTTP.
Comment #45
gumanist commentedI'm agree with you on one hand, but on other the only one HTTP request is nothing compare to Drupal installation from setUp() method.
Patch is updated and all requests done on one page.
Comment #46
gumanist commentedChanging status to run tests
Comment #47
andypostPatch looks good except code-style, probably it's ok because this arrays are long. But please format this same way
this hard to read
same
add new line
Comment #48
kalman.hosszu commentedThe modified patch is attached.
Comment #49
gábor hojtsyLooks good to me, and the actual bugfix looks well ported from D8. Any more concerns @andypost?
Comment #50
andypostGood except minor code-style
@return requires blank line before
Comment #51
kalman.hosszu commented@andypost Thanks for your feedback! Anything else?
Comment #52
andypostYes, good to go
Comment #53
webchickGreat, folks. Thanks!
Committed and pushed to 7.x.
Comment #55
Georgii commentedSubmitted a follow up issue regarding to this fix #1567662: The 1st plural index on a page is not calculated correctly
Comment #55.0
Georgii commentedChanged the proposed solution part.