Forcing string interpolation in Jade - internationalization

I am trying to use Jade to do some string interpolation + i18n
I wrote a custom tag
mixin unsubscribe
a(title='unsubscribe_link', href='#{target_address}/',
target='_blank', style='color:#00b2e2;text-decoration:none;')
= __("Click here")
Then I got the following to work
p
| #[+unsubscribe] to unsubscribe
However, in order to support i18n I would also like to wrap the the whole string in a translation block the function is called with __().
But when I wrap the string in a code block it no longer renders the custom tag.
p
| #{__("#[+unsubscribe] to unsubscribe")}
p
= __("#[+unsubscribe] to unsubscribe")
will output literally [+unsubscribe] to unsubscribe. Is there a way to force the returned string from the function?
Edit 1
As has been pointed out, nesting the "Click here" doesn't really make sense, since it will be creating separate strings.
My goal with all this is really to create a simplified text string that can be passed off to a translation service:
So ideally it should be:
"#[+unsubscribe('Click here')] to unsubscribe"
and I would get back
"Klicken Sie #[+unsubscribe hier] um Ihr auszutragen"
My reasoning for this is that because using something like gettext will match by exact strings, I would like to abstract out all the logic behind the tag.

What you really want to achieve is this:
<p>
<a href='the link' title='it should also be translated!'
target='_blank' class='classes are better'>Click here</a> to unsubscribe
</p>
And for some reason you don't want to include tags in the translation. Well, unfortunately separating 'Click here' from 'to unsubscribe' will result in incorrect translations for some languages - the translator needs a context. So it is better to use the tag.
And by the way: things like __('Click here') doesn't allow for different translation of the string based on context. I have no idea what translation tool you're using, but it should definitely use identifiers rather than English texts.
Going back to your original question, I believe you can use parametrized mixin to do it:
mixin unsubscribe(title, target_address, click_here, to_unsubscribe)
a(title=title, href=target_address, target='_blank', style='color:#00b2e2;text-decoration:none;')= click_here
span= to_unsubscribe
This of course will result in additional <span> tag and it still does not solve the real issue (separating "Click here" from "to unsubscribe") and no way to re-order this sentence, but... I guess the only valid option would be to have interpolation built-in into translation engine and writing out unescaped tag. Otherwise you'd need to redesign the page to avoid link inside the sentence.

Related

Minitest/Capybara Dropdown Menu Locator Problem

I tried to choose four dropdown menu respectively like this:
find(:xpath, '//select[#id = 'homework_filter_program_id']/option[1]').click_on
find(:xpath, '//select[#id = 'homework_filter_lesson_id']/option[1]').select_option
find(:xpath, '//select[#id = 'homework_filter_unit_id']/option[1]').select_option
find(:xpath, '//select[#id = 'homework_filter_difficulty_level_id']/option[1]').select_option
I tried both click_on and select_option to choose.
I derived the locators from this segment.
enter image description here
But I get these errors:
in block in require
in load_dependency
in <top (required)>
What is the wrong thing in my XPath, it doesn't select. I am newbie in Minitest and Ruby.
I am also open suggestions regarding CSS selectors for dropdown menu.
As Paul points out your quotes are invalid, but beyond that there's no need for XPath here. CSS is easier to read and is generally more efficient for locating elements when it can be used. Something along the lines of
find('select#homework_filter_lesson_id option:first-child').select_option
should work (assuming the select is actually visible on the page). Even easier yet would be to just do
select('text of the first option', from: 'homework_filter_lesson_id')
It seems there's an issue with building the xpath locator string (which you're passing as a second argument to find).
'//select[#id = 'homework_filter_program_id']/option[1]'
This isn't valid ruby code.
What you're looking for is string interpolation. There are two common ways of doing that:
"//select[#id = #{homework_filter_program_id}]/option[1]"
Notice the #{} expression inside the string. The result of the expression is interpolated. Keep in mind that this only works with doubly quoted strings ("like this one")
Or use the string formatting:
'//select[#id = %s]/option[1]' % homework_filter_program_id
Notice the %s placeholder. This works very much like sprintf.
Upd: for some reason as I've read your example I figured you had the local variables called homework_filter_program_id etc. Now that I've also looked at the screenshot I realized that homework_filter_program_id is actually the id of an element you want to find.
Your code will most likely work once you drop the unnecessary quotation marks:
'//select[#id=homework_filter_program_id]/option[1]'
But you probably still want to parameterize this locator, and that's when the interpolation advice will come handy.

Xpath to find an element using partial class name and exact text with capybara in ruby

I have html code with div having same matching text in class name as menu1 and text like
Berlin
and
Berlin Germany
for which when i use below code returns ambiguous elements
find(:xpath, "//div[contains(text(), \"Berlin\") and contains(#class, \"menu1\")]")
Note: I want both class and text to be in my xpath
Suggestions will be appreciated, thanks in advance.
If by partial class name you mean something like <div class="blah menu1 other">Berlin</div> then you could just do it in a readable way with something like
find('div.menu1', exact_text: 'Berlin')
or
find('div.menu1', text: 'Berlin', exact: true)
If it's more like <div class="blah menu1_part other">Berlin</div> you can still do it with a more readable CSS selector like
find('div[class*=menu1]', exact_text: 'Berlin')
If you actually need to do it all in one XPath for performance reasons (a LOT of div.menu1 elements on the page, where you can't scope to a limited section of the page for some crazy reason) then you could do something like
find(:xpath, './/div[text()="Berlin"][contains(#class, "menu1")]')
Note the leading . in the XPath expression. 99.9% of the time when using Capybara, and manually writing your own XPath expressions, you want to start your XPath expressions with .//, otherwise you are defeating any scoping you have done - see https://github.com/teamcapybara/capybara#beware-the-xpath--trap
Another option is to use the xpath gem Capybara uses internally for generating XPaths, which would be something like
find(:xpath, XPath.css('div.menu1')[XPath.string.n.is('Berlin')], exact: true)
or
find(:xpath, XPath.css('div[class *= "menu1"]')[XPath.string.n.is('Berlin')], exact: true)
depending on exactly what you mean by partial class name. The benefit of doing something like that is the meaning of the is method can be changed from contains to equals depending on the value of the exact option, and it also handles all the normalizing and escaping of strings as necessary if your strings weren't as simple as 'Berlin'

How does empty start tag work in HTML4?

The HTML4 specification mentions various SGML shorthand markup constructs. While I understand what others do, with a help of HTML validator, I cannot find understand why anyone would want an empty start tag. It cannot even have attributes, so it's not a shorter <span>.
The SGML definition of HTML4 enables the empty start feature. In it, there is an interesting section with features.
FEATURES
MINIMIZE
DATATAG NO
OMITTAG YES
RANK NO
SHORTTAG YES
LINK
SIMPLE NO
IMPLICIT NO
EXPLICIT NO
OTHER
CONCUR NO
SUBDOC NO
FORMAL YES
APPINFO NONE
The important section of features is MINIMIZE section. It enables OMITTAG which is a standard feature of HTML, which allows start or end tags to be ommited. This is particular allows you to write code like <p> a <p> b, without closing paragraphs.
The more important part is SHORTTAG feature, which is actually a category. However, because it's not expanded, the SGML automatically assumed YES for all entries in it. It has the following categories in it. Feel free to skip this list, if you aren't interested in other shorthand features in SGML.
ATTRIB, which deals with attributes, and has following options.
DEFAULT - defines whether attributes can contain default values. This allows writing <p> without defining every single attribute. Nobody would want to write <p id="" class="" style="" title="" lang="en" dir="ltr" onclick="" ondblclick="" ...></p> after all. Hey, I even gave up trying to write all that. This is a commonly supported feature.
OMITNAME - if the attribute and value have the same name, the value is optional. This allows writing <input type="checkbox" checked> for instance. This is a commonly supported feature (although, HTML5 defines default to be empty string, not an attribute name).
VALUE - allows writing values without quotes. This allows writing code like <p class=warning></p> for instance. This is a commonly supported feature.
ENDTAG, which is a category for end tags containing the following options.
UNCLOSED - allows starting a new tag before ending the previous tag, allowing code like <p><b></b</p>.
EMPTY - allows unnamed end tags, such as <b>something</>. They close most recent element which is still open.
STARTTAG, which is a category for start tags containing the following options.
NETENABL - allows using Null End Tag notation. It's worth noting this notation is incompatible with XHTML. Anyway, the feature allows writing code like <b/<i/hello//, which means the same thing as <b><i>hello</i></b>.
UNCLOSED - allows starting a new tag before ending the previous tag, allowing code like <p<b></b></p>.
EMPTY - this is the asked feature.
Now, it's important to understand what EMPTY does. While <> may appear useless at first (hey, how could you determine what it does, when nothing aside of Validator supports it), it's actually not. It opens the previous sibling, allowing code like the following.
<ul>
<li class=plus> hello world
<> another list element
<> yet another
<li class=minus> nope
<> what am I doing?
</ul>
In this example, the list has two classes, plus and minus for positive and negative arguments. However, the webmaster was lazy (and doesn't care about that HTML4 doesn't support this), and decided to use empty start tag in order to not specify the class for next elements. Because <li> has optional end tag, this automatically closed previous <li> tag.

writing a short script to process markdown links and handling multiple scans

I'd like to process just links written in markdown. I've looked at redcarpet which I'd be ok with using but I really want to support just links and it doesn't look like you can use it that way. So I think I'm going to write a little method using regex but....
assuming I have something like this:
str="here is my thing [hope](http://www.github.com) and after [hxxx](http://www.some.com)"
tmp=str.scan(/\[.*\]\(.*\)/)
or if there is some way I could just gsub in place [hope](http://www.github.com) -> <a href='http://www.github.com'>hope</a>
How would I get an array of the matched phrases? I was thinking once I get an array, I could just do a replace on the original string. Are there better / easier ways of achieving the same result?
I would actually stick with redcarpet. It includes a StripDown render class that will eliminate any markdown markup (essentially, rendering markdown as plain text). You can subclass it to reactivate the link method:
require 'redcarpet'
require 'redcarpet/render_strip'
module Redcarpet
module Render
class LinksOnly < StripDown
def link(link, title, content)
%{#{content}}
end
end
end
end
str="here is my thing [hope](http://www.github.com) and after [hxxx](http://www.some.com)"
md = Redcarpet::Markdown.new(Redcarpet::Render::LinksOnly)
puts md.render(str)
# => here is my thing hope and ...
This has the added benefits of being able to easily implement a few additional tags (say, if you decide you want paragraph tags to be inserted for line breaks).
You could just do a replace.
Match this:
\[([^[]\n]+)\]\(([^()[]\s"'<>]+)\)
Replace with:
\1
In Ruby it should be something like:
str.gsub(/\[([^[]\n]+)\]\(([^()[]\s"'<>]+)\)/, '\1')

page name, or argumentless URL string in smarty?

I have a search form, and I want to create a "Start Over" link that just returns users to the page without any arguments. What Smarty code can I use? For example, if they are at http://website.com/search.php?value1=x&value2=y, I want to get http://website.com/search.php or just search.php, so I can construct <a href='{$string}'>Start Over</a>.
I would just fill a variable in your php that you send to smarty? you could get that in smarty I guess, but that's not really a good way. just do something like this:
$yourSmartyVar->assign("string",$_SERVER[’PHP_SELF’])
That should give you the scriptname without querystring if I remember correctly. If it doesn't you could use $_SERVER[’SCRIPT_NAME’] if that suits you better, or even use http://php.net/manual/en/function.parse-url.php

Resources