I'm currently doing internationalization with gettext using PHP. I was wondering if there were any good methods for this example:
By using this website, you accept the Terms of Use.
For en_US, this sentence would follow such a format. However, in another language, the link "Terms of Use" could be at the beginning of the sentence. Is there an elegant way to do this? Thanks for your time.
For simple internationalization, I'll simply create an array per language, include the proper file, and access that array rather than do what you are doing.
en.php:
$text = array(
'footer' => 'By using this website, you accept the Terms of Use.',
'welcome' => 'Welcome to our website!'
);
index.php:
$langdir = '/path/to/languages';
$lang = ( $_GET['lang'] ) ? $langdir . '/' . $_GET['lang'] . '.php' : $langdir . '/en.php';
if( dirname($lang) != $langdir || !require_once( $lang ) )
exit('Language does not exist');
echo '<p class="footer">' . $text['footer'] . '</p>';
The dirname() call is critical; otherwise users get unvetted access to any php file on your filesystem.
Here's how I'd do it. The phrase to be translated would be
By using this website, you accept the <a>Terms of Use</a>.
Then I'd replace the link after the phrase is localised, e.g.
$str = _('By using this website, you accept the <a>Terms of Use</a>.');
$str = preg_replace('#<a>(.*?)</a>#', '$1', $str);
Of course you can replace <a> and </a> with whatever makes sense to you and your translators. Come to think of it, since you have to escape your output to prevent translators from messing up with your HTML (intentionally or not) I'd probably go with something like
$str = htmlspecialchars(_('By using this website, you accept the [tos_link]Terms of Use[/tos_link].'));
$str = preg_replace('#\\[tos_link\\](.*?)\\[/tos_link\\]#', '$1', $str);
Related
I've updated a website and found an annoying problem in my pages
I resolved it, but i'm not realy convinced it should look like this.
To me i'm not an PHP expert its strange behaviour could someone explain me whats going on.
I had this code:
if($Menu == "index"){
{if ($Language == "UK"){echo "<td><h1>Welcome</h1>";}
{if ($Language == "NL"){echo "<td><h1>Welkom </h1>";}
}
else
// if $menu was not index it displayed a hyperlink to Welcome page
The string thing here that while the checking for language worked fine.
The comparison for $Menu did not work, even if it contained the word index
As the whole page was generated on the fly and some string operations where done before
I assumed that maybe, despite i also tested it with
echo "dump Menu variable " . $Menu
Which resulted in jut displaying the word index on the page. So maybe there would go something wrong in string types or something like that
So i experimented with
if($Menu === "index")
No luck
Well i finally solved it like this
if (strpos($Menu,'index' !==false)
Is that really the way it should be done???, I don't feel really comfortable with it.
As to me its strange that for $Language it just works as it should (in my opinion).
Is there is some type problem here, or maybe unwanted endings \n could i perhaps ehm normalize the string to do a content of readable string comparions or, a different type of equal operator. As it feels to me as $Menu could be handled more easily. Maybe a reformat or but i'm not sure here.
Looks indeed like your index-String is containing whitespaces, instead of strpos you could use trim( $Menu ) to get rid of them.
But the best would be to prevent their occurence. You could try echo "dump Menu variable |" . $Menu ."|"; or just var_dump( $Menu ) to identify the additional characters.
Maybe you could post, your code-segment where $Menu is filled.
(sorry can't just comment)
You can try something like this
$Menu = trim($Menu);
$Language = trim($Language);
$langAllowed = ["UK", "NL"]; // zero element is default;
if(!in_array($Language, $langAllowed )) $Language = $langAllowed[0];
$menuLocales=[
"index" => [
"UK"=>"Welcome",
"NL"=>"Welkom "
],
"default"=>[
"UK"=>"Welcome default (no index)",
"NL"=>"Welkom default (no index)"
]
];
$MenuIndex = isset($menuLocales[ $Menu ]) ? $Menu : "default";
echo "<td><h1>".$menuLocales[ $MenuIndex ][ $Language ]."</h1>";
I am trying to create file links based a variable which has a "prefix" and an extension at the end.
Here's what I have:
$url = "http://www.example.com/mods/" . ereg("^[A-Za-z_\-]+$", $title) . ".php";
Example output of what I wish to have outputted (assuming $title = testing;):
http://www.example.com/mods/testing.php
What it currently outputs:
http://www.example.com/mods/.php
Thanks in advance!
Perhaps this is what you need:
$title = "testing";
if(preg_match("/^[A-Za-z_\-]+$/", $title, $match)){
$url = "http://www.example.com/mods/".$match[0].".php";
}
else{
// Think of something to do here...
}
Now $url is http://www.example.com/mods/testing.php.
Do you want to keep letters and remove all other chars in the URL?
In this case the following should work:
$title = ...
$fixedtitle=preg_replace("/[^A-Za-z_-]/", "", $title);
$url = "http://www.example.com/mods/".$fixedtitle.".php";
the inverted character class will remove everything you do not want.
OK first it's important for you to realize that ereg() is deprecated and will eventually not be available as a command for php, so to prevent an error down the road you should use preg_match instead.
Secondly, both ereg() and preg_match output the status of the match, not the match itself. So
ereg("^[A-Za-z_\-]+$", $title)
will output an integer equal to the length of the string in $title, 0 if there's no match and 1 if there's a match but you didn't pass it another variable to store the matches in.
I'm not sure why it's displaying
http://www.example.com/mods/.php
It should actually be outputting
http://www.example.com/mods/1.php
if everything was working correctly. So there is something going on there, and it's definitely not doing what you want it to. You need to pass another variable to the function that will store all the matches found. If the match is successful (which you can check using the return value of the function) then that variable will be an array of all matches.
Note that with preg_match by default only the first match will be returned. but it will still generate an array (which can be used to get isolated portions of the match) whereas preg_match_all will match multiple things.
See http://www.php.net/manual/en/function.preg-match.php for more details.
Your regex looks more or less correct
So the proper code should look something like:
$title = 'testing'; //making sure that $title is what we think it is
if (preg_match('/^[A-Za-z_\-]+$/',$title,$matches)) {
$url = "http://www.example.com/mods/" . $matches[0] . ".php";
} else {
//match failed, put error code in here
}
I am creating an OpenCart extension where the admin can change his email templates using the user interface in the admin panel.
I would like the user to have the option to add variables to his custom email templates. For example he could put in:
Hello $order['customer_firstname'], your order has been processed.
At this point $order would be undefined, the user is simply telling defining the message that is to be sent. This would be stored to the database and called when the email is to be sent.
The problem is, how do I get "$order['customer_firstname']" to become a litteral string, and then be converted to a variable when necessary?
Thanks
Peter
If I understand your question correctly, you could do something like this:
The customer has a textarea or similar to input the template
Dear %NAME%, blah blah %SOMETHING%
Then you could have
$values = array('%SOMETHING%' => $order['something'], '%NAME%' => $order['name']);
$str = str_replace(array_keys($values), array_values($values), $str);
the user will be using around 40 variables. Is there a way I can set it to do that for each "%VARIABLE%"?
Yes, you can do so for each variable easily with the help of a callback function.
This allows you, to process each match with a function of your choice, returning the desired replacement.
$processed = preg_replace_callback("/%(\S+)%/", function($matches) {
$name = $matches[1]; // between the % signs
$replacement = get_replacement_if_valid($name);
return $replacement;
},
$text_to_replace_in
);
From here, you can do anything you like, dot notation, for example:
function get_replacement_if_valid($name) {
list($var, $key) = explode(".", $name);
if ($var === "order") {
$order = init_oder(); // symbolic
if(array_key_exists($key, $order)) {
return $order[$key];
}
}
return "<invalid key: $name>";
}
This simplistic implementation allows you, to process replacements such as %order.name% substituting them with $order['name'].
You could define your own simple template engine:
function template($text, $context) {
$tags = preg_match_all('~%([a-zA-Z0-9]+)\.([a-zA-Z0-9]+)%~', $text, $matches);
for($i = 0; $i < count($matches[0]); $i++) {
$subject = $matches[0][$i];
$ctx = $matches[1][$i];
$key = $matches[3][$i];
$value = $context[$ctx][$key];
$text = str_replace($subject, $value, $text);
}
return $text;
}
This allows you to transform a string like this:
$text = 'Hello %order.name%. You have %order.percent%% discount. Pay a total ammount of %payment.ammount% using %payment.type%.';
$templated = template($text, array(
'order' => array(
'name' => 'Alex',
'percent' => 20
),
'payment' => array(
'type' => 'VISA',
'ammount' => '$299.9'
)
));
echo $templated;
Into this:
Hello Alex. You have 20% discount. Pay a total ammount of $299.9 using VISA.
This allows you to have any number of variables defined.
If you want to keep the PHP-syntax, then a regex would be appropriate to filter them:
$text = preg_replace(
"/ [$] (\w+) \[ '? (\w+) \'? \] /exi",
"$$1['$2']", # basically a constrained eval
$text
);
Note that it needs to be executed in the same scope as $order is defined. Else (and preferrably) use preg_replace_callback instead for maximum flexibility.
You could also allow another syntax this way. For example {order[customer]} or %order.customer% is more common and possibly easier to use than the PHP syntax.
You can store it as Hello $order['customer_firstname'] and while accessing make sure you have double-quotes "" to convert the variable to its corresponding value.
echo "Hello $order['customer_firstname']";
Edit: As per the comments, a variation to Prash's answer,
str_replace('%CUSTOMERNAME%', $order['customer_name'], $str);
What you're looking for is:
eval("echo \"" . $input . "\";");
but please, PLEASE don't do that, because that lets the user run any code he wants.
A much better way would be a custom template-ish system, where you provide a list of available values for the user to drop in the code using something like %user_firstname%. Then, you can use str_replace and friends to swap those tags out with the actual values, but you can still scan for any sort of malicious code.
This is why Markdown and similar are popular; they give the user control over presentation of his content while still making it easy to scan for HTML/JS/PHP/SQL injection/anything else they might try to sneak in, because whitelisting is easier than blacklisting.
Perhaps you can have a template like this:
$tpl = "Hello {$order['customer_firstname']}, your order has been processed.".
If $order and that specific key is not null, you can use echo $tpl directly and show the content of 'customer_firstname' key in the text. The key are the curly braces here.
I am storing HTML layouts within a MySQL database. These layouts may contain tags within the HTML as show below..
{site.poll="fred,joe,john"}
and
{site.layout.header}
Currently i am searching the HTML template by executing multiple preg_matches to identify the tags, looping through the array then executing a str_replace(), replacing with another partial html template also pulled back from the db.. Example below..
if (preg_match_all('/{site\.layout\.(.)*}/', $data, $match) != FALSE)
{
foreach($match[0] as $value)
{
$value = trim($value, '{}');
$tmp_store = explode('.', $value);
$tmp_partial = $this->parse($this->get_layout(end($tmp_store)));
$data = str_replace('{'. $value .'}', $tmp_partial, $data);
}
}
I would need to execute a regex for each tag i required, then execute a str_replace on each instance of that tag.. The same again would need doing for each required partial template..
To me, this is all seeming to get heavy..
Is there a better way of doing this?
Thanks in advance..
Edit: I do not want to use an existing library, i would like to do this task myself and learn in the process..
Well you could use preg_replace to find and replace your tags in one shot.
The best approach in my opinion would be to use an existing template system such as Twig or Smarty. I know for sure that you can read data into Smarty (it doesn't have to be from a file). I'm sure Twig has something similar.
Twig and Smarty also provide caching options so you aren't rebuilding your template on every request. However they work best if the templates are stored in files.
If you really must roll your own template system you should build some kind of parser that actually checks the content character by character. This will likely be faster and more accurate than regular expressions (though more complex)
I don't expect this to answer your question as such, but thought it might give you something else to think about. Some code I've got for when my template class is overkill.
function replace_tags(&$xhtml, $tags) {
if( is_array($tags) && count($tags) > 0 )
foreach ($tags as $tag => $data) {
$xhtml = str_replace("{{" . $tag . "}}", $data, $xhtml);
}
if( $xhtml ) return $xhtml;
}
$tpl = "/templates/index.xhtml";
$tags = array(
"css" => null,
"js" => null,
"main_content" => null
);
$tags['main_content'] = file_get_contents("/home/main.xhtml");
echo replace_tags(file_get_contents($tpl), $tags);
edit
Thought I'd clarify on the reason the function receives $xhtml by reference, and also returns $xhtml. Basically just to make it dual purpose.
//Usage at the end of a page
echo replace_tags(file_get_contents($tpl), $tags);
//Usage for template in template
$tags['menu'] = file_get_contents($menu_tpl);
replace_tags($tags['menu'], $tags);
echo replace_tags(file_get_contents($tpl), $tags);
I'm writing a mail class that pulls content stored in a database and loads it into a template before sending it as a HTML e-mail. However, because each e-mail contains PHP variables and dynamic content, I've decided to use delimiters. So instead of the content looking like:
Hello $username, welcome to the site.
It'll look like:
Hello {{username}}, welcome to the site.
So far I'm using these methods:
function load($name,$content)
{
// preps the template for HTML
}
function content($template_id)
{
$template = $this->db->get_where('email_templates',array('id'=>$template_id));
return $template->content;
}
function new_email($email,$name,$user_type)
{
$msg = $this->load($name,$this->content(1));
$this->send($email,'Thanks for your application',$msg,1);
}
The trouble I'm having is how to convert a {{variable}} into a $variable so that it will parse - I don't want it to just be loaded as $username in the e-mail template. Is it just a case of using regular expressions and escaping the string so that it'll parse? Something like:
$content = str_replace("{{","'.$",$template->content);
$content = str_replace("}}",".'",$template->content);
Or is this flawed? Does anybody know what's the best thing to do?
I would not create my own templating system, because there are existing ones out there.
The most popular is probably Smarty, but there is an another one which has the same format as you created, that is mustache.
Update:
The problem with your code is that you're replacing the {{ to a .$ and store that in $content variable, then replacing }} to . and overwrite this replaced $content variable.
A possible working solution could be:
if (preg_match_all("/{{(.*?)}}/", $template, $m)) {
foreach ($m[1] as $i => $varname) {
$template = str_replace($m[0][$i], sprintf('$%s', $varname), $template);
}
}
But then you would also need to eval your code somehow.
So after converting {{variable}} to $variable in your email template, you will use eval to get it replaced by the actual contents of that variable?
Why not just replace {{variable}} with the contents of $variable straight away?
Perhaps have a function that takes the template text and an array of placeholder => "text to replace it with". Then it's as simple as making up the placeholders' exact strings by adding {{ and }} around that array's key and doing str_replace.
foreach ($replacements as $placeholder => $value) {
$placeholder = "{{" . $placeholder . "}}" ;
$text = str_replace($placeholder, $value, $text) ;
}
Couple this with (class) constants for the placeholders and you have a very solid and typo-repelant templating system. It will not be as elegant or easy to use as a full blown templating solution, and it might require extra work from whoever writes code that uses it, but they will not make mistakes during development due to mis-named variables.
If you are going to do it yourself it is probably best to just be explicit with str_replace. If you try to convert the curly bracers to $ you'll then need to eval() which is a potential security hole.
This would be my approach with str_replace - this becomes difficult to maintain as you add more variables but it really doesn't get much simpler either.
$content = str_replace(
array('{{username}}','{{var2}}'),
array($username,$var2),
$template->content
);
use preg_replace_callback , see : http://codepad.org/EvzwTqzJ
<?php
$myTemplateStr = "Hello {{username}} , this is {{subject}} ,and other string {{example}}";
$tagRegex = "|{{(.*?)}}|is";
$result = preg_replace_callback($tagRegex,"myReplaceFunc",$myTemplateStr);
echo $result ;
/* output :
Hello $username , this is $subject ,and other string {{example}}
*/
function myReplaceFunc($matches)
{
$validTags = array('username','subject','name');
$theFull = $matches[0];
$theTag = $matches[1];
if(in_array($theTag,$validTags) == true)
return '$'.$theTag;
return $theFull ;
}
?>
$template = "Hello {{username}} , this is {{subject}} ,and other the answer is on page {{example}}";
$replacements = array(
'username' => 'Jeffrey',
'subject' => 'your final notice',
'page' => 43
);
function bind_to_template($replacements, $template) {
return preg_replace_callback('/{{(.+?)}}/',
function($matches) use ($replacements) {
return $replacements[$matches[1]];
}, $template);
}
echo bind_to_template($replacements, $template);
Credit to https://www.labnol.org/code/19266-php-templates