Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Inside a modules/checkout/commerce_checkout.php there is a following snippet:
function commerce_checkout_completion_message_default() {
return t('Your order is number [commerce-order:order-number]. You can <a href="[commerce-order:url]">view your order</a> on your account page when logged in.')
. "\n\n" . t('<a href="[site:url]">Return to the front page.</a>');
}
The returned string can't be translated, the attempt results in "The submitted string contains disallowed HTML" message.
There are numerous issues with translating strings containing html, for example http://drupal.org/node/1020842
So why not just move html tags out of t() calls?
Comments
Comment #1
rszrama CreditAttribution: rszrama commentedI wasn't aware of translation issues when HTML is included in the string. Is there no way to change which HTML tags are allowed? The problem with taking the a tag out is that I then have to use a token for the whole link, which is much less flexible than just using a token for the URL and putting whatever I want as the link text.
Comment #2
akamaus CreditAttribution: akamaus commentedHi Ryan,
looks like HTML string is not guilty per se. Locale strings get passed through locale_string_is_safe function so tags are allowed.
The problem is probably in filter_xss which doesn't accept the colon inside the attribute . At least, something like
'<a href="[site]">Return to the front page.</a>'
is happily accepted. But'<a href="[xx:yy]">'
triggers the error.Comment #3
tormiSubscribing
Comment #4
marcus_w CreditAttribution: marcus_w commented+1
Comment #5
ducecc CreditAttribution: ducecc commentedsubscribe
Comment #6
kla2t CreditAttribution: kla2t commentedsubscribe
Comment #7
Heine CreditAttribution: Heine commented#928948: _filter_xss_attributes should ignore attribute values for "value" and "flashvars"
Comment #8
svendecabooterWe should find a workable solution for this. It's not really an option to wait for this patch mentioned by Heine to land in D8 (and then hopefully get backported to D7).
Here is my attempt at fixing this. It makes the URL more hardcoded, but that seems the only option.
Comment #9
laraz CreditAttribution: laraz commentedthis not function for me.
Subscribe
Comment #11
ñull CreditAttribution: ñull commentedI don't think this is a core bug. This is just a feature of t(). You need to read the D6 API documentation to find out what is does and why. The proposed patch is the way to go. In URLs no "dangerous" signs should appear like the colon in square brackets. The way to go is to remove the token and replace it by a place holder.
Comment #12
askibinski CreditAttribution: askibinski commentedWhy not just use the l function with a variable_get for the path and leave it to the administrator where to link afterwards (with a default to )?
A link back to the frontpage isn't very helpful anyway imho..
Comment #13
GiorgosK@svendecabooter
why is [commerce-order:order-number] still in the message ?
it applies cleanly but it does not work
still get the message complaining about HTML !!
temporarily I change locale.inc/locale_string_is_safe($string) and add a return true
Comment #14
svendecabooterGiorgosK: which version of Commerce and Drupal core are you using?
The error about HTML I encountered was specifically caused by a : symbol being placed within double quotes (").
Since the [commerce-order:order-number] variable is not within an HTML link ( tag), it works perfectly here.
I'm not running the latest Commerce code though, so maybe something introduced your problems there. I should test this out when I find some time.
Comment #15
ñull CreditAttribution: ñull commentedI applied the patch with the following result:
with the link to the order missing the order id.
For the moment I'll simply avoid the tokens with colons in the translation.
Comment #16
kotnik CreditAttribution: kotnik commentedI think this would solve i18n issue most effectively.
Comment #17
Prague man CreditAttribution: Prague man commentedI had a problem with this phrase, but it solved.
<a href="[site:url]">Return to the front page.</a>
Comment #18
sunAll placeholders in there have to use @.
Comment #19
kotnik CreditAttribution: kotnik commentedBut !orderurl and !fronturl must be inserted as they are, since htmlspecialchars would render link unusable. Agreed for !ordernumber, it can be @ordernumber.
Comment #20
sunAll HTML attribute values need to go through check_plain(), hence you have to use the @-placeholder.
You can find many examples for this throughout Drupal core. (Admittedly not for token_replace(), but your tokens here seem to produce straightforward/raw URIs only, so there's no difference to usage of t().)
Comment #21
kotnik CreditAttribution: kotnik commentedSun, I tested and you are right. Here's fixed patch.
Comment #22
rszrama CreditAttribution: rszrama commentedThe only thing I don't like about this approach is that those tokens are useless; as soon as someone goes to the edit form for the checkout completion message and tweaks it, the tokens will no longer be guiding the message displayed. This could open the doors for unintended side effects in cases of multiple development environments. But it's either that or define our own pseudo-tokens for use in the checkout completion message. Need to weigh risk vs. time to develop.
Comment #23
Jax CreditAttribution: Jax commentedIn this particular case you can just override the checkout message (admin/commerce/config/checkout/form/pane/checkout_completion_message) and set commerce_checkout_completion_message as a language dependent variable enables you to save a value per language.
Just add:
to mymodule.variable.inc and then enable te variable at admin/config/regional/i18n/variable
and translate it at admin/config/system/variable/edit/commerce_checkout_completion_message
#1261012: Add Variable module integration for custom checkout completion messages
Comment #24
mr.baileysMarked #1475132: Default complete message use html links in t function as duplicate.
Comment #25
no2e CreditAttribution: no2e commentedI patched #21 – it works like a charm!
Now I can translate those two strings at
/admin/config/regional/translate/translate
.(refrehsing of all strings and clearing cache didn't worked to get the string in the translation interface; I had do create a new order, so that the string got displayed … after that it appeared in the translation interface)
Thank you, kotnik :)
Comment #26
rszrama CreditAttribution: rszrama commentedActually, the problem here is that this takes you back to storing actual URLs in the message once you provide an alternate checkout message. The purpose of the tokens is to avoid saving hard URLs so you can switch environments without invalidating URLs stored throughout the variable system like this.
Comment #27
navid.kashani CreditAttribution: navid.kashani commented+1 for #21 patch, i hope you apply this path in upcoming commerce 1.4
Comment #28
farrington CreditAttribution: farrington commented+1 for patch #21.
Ryan the patch don't save the URLs hard, it uses tokens just as the original code.
Or, am I misunderstanding you?
Comment #29
rszrama CreditAttribution: rszrama commentedAhh, you know what, I may have misread the patch and how it was using token_replace(). Marking this up for review again, though I can't promise the replacement tokens in the t() string will stay the same for folks who have already used this.
Comment #30
farald CreditAttribution: farald commentedEdit: Patch towards 1.3. Ignore this.
Comment #31
a.milkovskyI separated message in 3 t() strings. This is my code:
file commerce_checkout.module. Line 890
Comment #32
wmostrey CreditAttribution: wmostrey commentedThe idea of the patch in #30 works quite well, although I propose to use the tokens @order-number, @order-url and @site-url since these translations might already be in place.
Comment #33
5n00py CreditAttribution: 5n00py commented...
You cant put html tags while translate strings via locale module.
Need something like this:
'Your order is number @ordernumber. You can @view_link on your account page when logged in.'
'view your order'
Two strings instead one!
Comment #34
Jorrit CreditAttribution: Jorrit commentedIsn't it better to do just:
l(t('Return to the front page.'), '....')
Comment #35
idflood CreditAttribution: idflood commentedRerolled patch in #21 since it wasn't applying anymore. The patch looks good to me, here is what I've tested:
- Translated the strings on a local environment ( localhost/my_shop )
- Pushed the website to a staging environment (example.com/dev/my_shop)
- Checked that the text was translated and the url are correct
What else is missing to have this patch committed?
Comment #36
idflood CreditAttribution: idflood commentedComment #37
5n00py CreditAttribution: 5n00py commentedNo, you can't put html tags in t() function directly! Because locale module just can't validate string with html when you add translation in UI.
Full link tag must be included via "!placeholder".
Like this:
Comment #38
5n00py CreditAttribution: 5n00py commentedAs the message customization.
I think the best way is provide UI for users to change $string and $args arguments of t function:
t($string, array $args = array(), array $options = array())
$string as simple textbox and $args as some constructor for providing any placeholders based on tokens!
If DC commiters agree this (or similar) solution I can start working on this!
Comment #39
5n00py CreditAttribution: 5n00py commentedThe workflow can be:
1. User put english string with the tokens(no html). (raw string)
2. Validator detect and check all tokens.
3. Module collect used tokens.
3.1 assign for each token placeholder
3.2 build own $args array
3.3 replace all tokens in string with placeholders (prepared string that should be used in t()).
4. Now we have customized $string and $args
5. User translate his string via locale module.
6. Show message
6.1 load prepared $string by variable_get
6.2 load prepared $args same way
6.3 do token replace
6.4 do t() call
Notes:
As for me, this solution seems so flexible and can be used not only for this issue!
Better solution is only integration local and token modules(can be done same way)!
Comment #40
5n00py CreditAttribution: 5n00py commentedHm, as is see html tags is really not a broblem, so only tokens is a really problem!
http://drupal.org/node/322732
http://drupal.org/node/322774
Today I will do closer look to the sources...
Comment #41
idflood CreditAttribution: idflood commented@5n00py: Thanks for the review. The token issue has been slightly discussed in #11 and following. The problem with the previous token is that it was putting a possibly dangerous ":" inside html attributes. The patch I've rerolled in #36 should work fine (at least it does on my tests but maybe I've missed something).
Comment #42
5n00py CreditAttribution: 5n00py commented@idflood Yes, now I see!
Sorry for blocking your status!
This is good patch as I see!
Maybe I can provide more flexible addition for your patch...
Just waiting for reviews!
Comment #43
5n00py CreditAttribution: 5n00py commentedOk, take a look and test my patch!
Now we have full token support in checkout complete message!
The idea is described in #39 (some modified).
For example i have this message:
But string that go to t() function looks:
All used tokens automatically goes to t() argument!
And now commerce_checkout_completion_message_default() should return untranslated string!
Comment #44
5n00py CreditAttribution: 5n00py commentedFixed coding standarts!
Comment #45
rszrama CreditAttribution: rszrama commentedSorry, I can't go with this - removing the message string literals from t() makes them invisible to the translation string extractor.
Comment #46
5n00py CreditAttribution: 5n00py commentedOf course @rszrama!
I'll make some trick with this.
Literal contain @placeholders, but return of commerce_checkout_completion_message_default() always return string with [token:placeholders].
And if you don't need translated version just add options array to argument. Tokens saved after this. (Useful for settings form).
Also I found and fix a little related bug. "\n" generates wrong string (including \n signs itself).
"\r\n" fixed this problem.
Comment #48
5n00py CreditAttribution: 5n00py commented#46: token_locale-1196488-46.patch queued for re-testing.
Testbot fail.
Comment #49
Daniel Wentsch CreditAttribution: Daniel Wentsch commentedThanks @5n00py, patch worked for me.
Comment #50
laraz CreditAttribution: laraz commented#46 The patch cannot be applied in the selected context
Comment #51
ArtusamakLet's up this one, @rszrama are you ok with the new approach?
Comment #52
star-szrThanks for the patch, @5n00py! Gave this a review and test, here's some feedback.
@5n00py, let me know and I can roll a new patch with some of these changes.Inline comments need a space between the // and the comment. There are a few more of these in the patch.
I think this description should just be removed, if anything the "Override the default checkout completion message" checkbox should have its label updated to indicate that it's translatable.
This means that if you're using the default message it will go through t() twice since commerce_checkout_completion_message_default() returns a string wrapped in t().
One bug I found: if you're using a custom completion message, you will end up with two strings for translation (default and custom) - this is confusing. This is because commerce_checkout_completion_message_pane_checkout_form() always calls commerce_checkout_completion_message_default(). I would suggest changing the if statement to something like this:
Comment #53
star-szrThis is the solution I ended up using, so here's an updated patch and interdiff to address the above, the only thing it doesn't address is:
Comment #54
bendev CreditAttribution: bendev commented# 53 works as expected, thanks
Comment #55
rszrama CreditAttribution: rszrama commentedI don't understand the custom token replacement code in the bottom hunk; also, any reason to not set the $options array to the default parameter of the function?
Comment #56
5n00py CreditAttribution: 5n00py commentedToken replacement need for converting tokens(with symbols that's can't be translated, e.g. validation error) to @inline_arguments for t() function.
So in the message edition form you see [tokens], but in locale module you see @variables.
This patch use 'string literal' in t() call (for translation parsers), and one t() call with $variable (for token and custom message issue).
Comment #57
georgemastro CreditAttribution: georgemastro commented#53: commerce-1196488-53.patch queued for re-testing.
Comment #58
Jax CreditAttribution: Jax commentedJust FYI. In #1702566: Redirect on checkout complete without using rules module. other approaches are proposed. Instead of providing a translatable message just load the content of a node and show it on the completion page.
Comment #59
candelas CreditAttribution: candelas commentedany news on this? :)
Comment #60
rszrama CreditAttribution: rszrama commentedIf there was, the issue would be updated. On another look, I see that this patch continues to pass variables to t(), which is an improper use of that function.
Comment #61
5n00py CreditAttribution: 5n00py commented@rszrama
Can it be solved using i18n_string/i18n_string_text or not ?
http://drupal.org/node/1114010
If yes I can start work on this?
Or we need to make some custom stuff which interact with core language system?
Give me direction to work & I will do this job.
Comment #62
farald CreditAttribution: farald commentedIf you want to use a completely custom thing, you can create your own simple pane real easy:
Then just disable the standard completion message.
Comment #63
bojanz CreditAttribution: bojanz commentedWe need to start from scratch here :)
The previous patch fixes translation of the default completion message by introducing a custom token format, and accompanying code.
This is not optimal for obvious reasons.
It also doesn't do anything about translation of overridden completion messages (the ones stored in the variable), t() can't be used there, we need i18n_string.
If we use i18n_strings, then the error is gone, and there's no need for a custom token format.
So, the solution is obvious: require i18n_string for any kind of completion message translation (default and overriden).
i18n modules (i18n_field and i18n_string, to be exact) are required anyway to translate some of Commerce strings (and D7 strings in general), and a non-english or multilingual site can't avoid it (they can just kick the ball further down the road with ugly hacks).
This patch:
1) Introduces a default Commerce textgroup.
2) Introduces a commerce_i18n_string() function that provides a fallback in case i18n_string is missing, and makes sure appropriate sanitization is done.
3) Implements translation of the checkout completion message.
4) Changes the format of commerce_checkout_completion_message_default(), so that this:
doesn't cause explosions due to mismatches in formats between the variable and the default.
Comment #64
bojanz CreditAttribution: bojanz commentedForgot to include the changes to commerce_checkout_completion_message_default().
This one is complete.
EDIT: Found a typo and a bug. New patch to follow.
Comment #65
bojanz CreditAttribution: bojanz commentedThis one should be committable.
The sanitization needs to happen after the token replacement, reverted that reordering and the translated text now shows properly.
EDIT: Small nitpick for before committing:
should be
since the text formatting is now applied a few lines down.
Comment #66
5n00py CreditAttribution: 5n00py commentedLooks good, I want to test it. :)
Comment #67
rszrama CreditAttribution: rszrama commentedReads fine to me and worked as normal in a non-translated interface. We'd need to note the API change for commerce_checkout_completion_message_default() on the off chance anyone was using it for something else. One quick regression question, though... by removing the default string from t(), are we not removing the ability for this string to be translated outside of the i18n module? As is, the string could be translated as normal so long as a custom message was not entered.
Patch attached with the updated comment.
Comment #68
bojanz CreditAttribution: bojanz commentedYes, with the latest patch, the default message is definitely not translatable without i18n_string.
Well, it wasn't before either (because of the triggered validation error, since t() can't handle embedded tokens), but we pretended.
Comment #69
rszrama CreditAttribution: rszrama commentedYeah, I was thinking that as I made the comment, but basically we have to decide either to fix it (there's an issue for it) or just remove the possibility. Is there any problem with leaving it in t() and fixing the token issue? Or perhaps we can just remove the whole checkbox thing for overriding the default message. Since we have a way to translate the text via i18n_string, we could get rid of t() and the extra configuration step altogether.
Comment #70
5n00py CreditAttribution: 5n00py commentedt() function can't validate '[]' symbols. For pass tokens to t() we can replace them with placeholders.
$args array can used as t() argument .
I don't know how to use it properly, but maybe it is useful.
Comment #71
5n00py CreditAttribution: 5n00py commentedAlso I think before string customized it just an interface string, that should be translated via t(). And token not required to place order_number in the message.
Comment #72
bojanz CreditAttribution: bojanz commentedI definitely don't like the idea of inventing a custom token format & related code just to support t(), when we need i18n for other Commerce strings anyway.
I like the idea of removing the override checkbox. Patch attached.
Comment #73
bojanz CreditAttribution: bojanz commentedUpdated docs for commerce_i18n_string, to match the style I am adding in #1121722: Improve DX of instance translation.
We should also replace function_exists('i18n_string') with module_exists('i18n_string'), for consistency.
Forgot to include that in the patch, can be done pre-commit.
Comment #75
bojanz CreditAttribution: bojanz commented#73: 1196488-73.checkout_completion_message.patch queued for re-testing.
Comment #76
rszrama CreditAttribution: rszrama commentedJust tried to test this patch but don't see the checkout completion string showing up in the translate interface even though I get the group. Also, thinking afresh about the no default translatable string, I can go either way: while we do need i18n for full field translation on a multilingual site, do we need it for a single language non-English site?
Comment #77
5n00py CreditAttribution: 5n00py commentedLooks like patch fully depends on i18n.
There is no t() call.
As I say before, not-overriden string should be passed through t() because its just an interface string.
Comment #78
bojanz CreditAttribution: bojanz commentedYes.
If you're doing any kind of translation, you need i18n.
As for the string not showing up, did you go to admin/config/regional/translate/i18n_string and click "Refresh strings"?
#77
This message can be user provided, and can contain HTML, therefore, it will never be wrapped in t().
Plus, adding t() would mean translating it twice when i18n is there.
Comment #79
rszrama CreditAttribution: rszrama commentedOk, I think the best way to summarize it is to say "Interface text translation is all managed through i18n." The checkout completion message as is tried to have it both ways, but it's best to just identify it as interface text that must be translated through i18n along with field instance labels, taxonomy terms, menu items, etc.
Committed w/ the change to module_exists().
Oh, and yeah, I had refreshed strings, but I didn't realize I had to check a box to refresh a particular group. : P
Commit: http://drupalcode.org/project/commerce.git/commitdiff/30f4a8e
Comment #81
kadimi CreditAttribution: kadimi commented@rszrama. Ryan, related to your comment #76, maybe you just need to refresh strings of the Drupal Commerce text group
?q=admin/config/regional/translate/i18n_string
Comment #82
FranciscoLuz CreditAttribution: FranciscoLuz commentedI hate to be "that guy" but couldn't get those strings translated.
So, what was the outcome exactly? the 1.8 version should've had it fixed? Is it a bug at i18n? or else?
I made sure the latest version of Drupal Commerce was on, I tried even the latest Dev version, cleared caches and ran cron.
Comment #83
bojanz CreditAttribution: bojanz commentedPlease don't reopen old issues. You are free to open new support requests.
If you don't see the string on admin/config/regional/translate/translate then you must refresh the strings on admin/config/regional/translate/i18n_string.
Comment #84
FranciscoLuz CreditAttribution: FranciscoLuz commentedI do see it there but get the error message saying that HTML is not valid.
Comment #85
a.milkovskyWhen you don't need to use i18n you can do next workaround:
Comment #86
PhilY@a.milkovsky (comment #85): using your tip, the string is exposed to translation but can't be translated because of invalid HTML tags (even by pasting the original English text).
Comment #87
a.milkovskyDo you mean message is not translated when it contains HTML?
Comment #88
PhilYYes,
using Commerce 7.x-1.11 (and filtered HTML text format active), even the original string copied in translation textarea is refused:
Your order number is [commerce-order:order-number]. You can <a href="[commerce-order:url]">view your order</a> on your account page when logged in. <a href="[site:url]">Return to the front page.</a>
but is accepted without HTML tags:
Your order number is [commerce-order:order-number]. You can view your order on your account page when logged in.
Thanks
Comment #90
BramDriesenComment #81 Is actually quite important, strange the variable isn't automatically indexed.
Sorry for reviving this old post.
Comment #91
bjorsen CreditAttribution: bjorsen commentedI'd like to thank BramDriesen for pointing out comment #81.
This did the trick for us, when nothing else would make the string show up in the translation table. Strange indeed.