Categories > TinyButStrong general >

Embedded hyperlink in text string

The forum is closed. Please use Stack Overflow for submitting new questions. Use tags: tinybutstrong , opentbs
By: Ian
Date: 2008-12-17
Time: 04:00

Embedded hyperlink in text string

Hi,

I have a question about using TBS with a text string with an embedded anchor ref (hyperlink) tag. 

I have the following string in my original (non-TBS'ed) PHP file:

<p>Check out our new <a href="/posts/cc_launcher.php">"Classroom Corner"</a> feature, where you can stay up-to-date with all the goings-on for your child's classroom!</p>

In the TBS'ed version of this page, I have the following in the .HTML (template) page;

<p>[var.focus_on_text1] <a href="/posts/cc_launcher.php"> [var.focus_on_link_text]</a> [var.focus_on_text2]</p>

and this in the .PHP page:

$focus_on_text1 = _('Check out our new');
$focus_on_link_text = _('Classroom Corner');
$focus_on_text2 = _("feature, where you can stay up-to-date with all the goings-on for your child's classroom!");

As you can see, I've split up the original string into 3 TBS placeholders/vars and left the URL as-is. 

The above approach works just great, but I'm wondering if there is a better way to implement this in TBS.  I'm just beginning with TBS and am in the early stages of developing this site, so I want to make sure I get it right from the beginning... :)

Thanks in advance...

- Ian
By: Ian
Date: 2008-12-17
Time: 04:02

Re: Embedded hyperlink in text string

And in case you're wondering why I've encased the text strings in the PHP page between '_(' and ')' it's because I am using PHP-gettext to dynamically pull the translated versions of these strings at runtime...

- Ian
By: ege
Date: 2008-12-17
Time: 19:19

Re: Embedded hyperlink in text string

Hi Ian,

It seems to me that I see no problem with how you do it, I can't think of a better way if this is all you need.

On the other hand, when using gnu gettext with tbs, you keep the strings to be translated on php side instead of template side, which I believe is kind of negating the use of templates as you can't really see what's going on just by looking at the templates. That approach also requires more coding on the php side.

Following is how I handle multilang sites with gettext. (code taken from a working website, but I deleted some code for the sake of clarity).

$lang=$_GET['lang'];
if ($lang!='e')
  $lang=''; 
else
  $locale='en_US';


if ($locale)
{
  putenv("LC_ALL=$locale");
  setlocale(LC_ALL, $locale);
  bindtextdomain("messages", "./t160908_1628");
  textdomain("messages");
  bind_textdomain_codeset('messages', 'UTF-8');
}

$tbs = new clsTinyButStrong ;
$tbs->LoadTemplate('templates/main.html') ;
//deleted code here that calls MergeBlock function of tbs.
$tbs->MergeField('T','translate',true);
$tbs->Show() ;

function translate($dummy,$params)
{
  return _($params['t']);
}

on template side, strings to be translated would look like this:

<p class='aUserSection'>[T;t=Promosyon filmini seyretmek için sisteme giri&#351; yapman&#305;z gerekiyor.]</p>

As you see, I keep the 1st language's texts (Turkish in this case) in the template. When this page is viewed in English T function translates the text into something like "You must be logged in, in order to watch the promo movie."

The only problem here is about how to create .po and .mo files from templates. You can't use normal tools because they can't parse your your custom [T;t=....] tags. You should write a small php program using regular expressions to extract all those text into _(....) lines contained in a single php file and then use that file to create the .mo file. The code I wrote to do that is not based on regexps but based on tbs, which is not good because it doesn't extract some texts in conditional blocks and throws a lot of errors. That's the reason I don't share that code.

Regards,
ege.
By: Ian
Date: 2008-12-17
Time: 21:44

Re: Embedded hyperlink in text string

Hi Ege,

Great feedback, as always!

I can see your point about using Gettext and how it might be advantageous from a template 'best practices' standpoint to keep the static text strings in the HTML file rather than defining them in the PHP file. However, my issue is that I have a large number of dynamic strings produced from the DB and business logic layers that must be defined in the PHP file, so I felt that moving all strings to the PHP pages and then using TBS placeholders in the template was the most easily-managed method for me.  Otherwise I have a situation where some strings are defined in a PHP file (error messages from the DB class, for instance) and others are defined in the template file.  Additionally, the extra effort needed to build custom scripts using regexp to extract the strings and compile the .mo file is something I don't really have time to tackle right now.

I don't really need to worry about the template itself not exposing the message/text as I am the develper/designer all in one on this project.  I've made liberal use of comments in my code and HTML so if I ever do need to have someone else work on this (doubtful, as it is a pro-bono site I'm building for my kids' PTA organization) it should be pretty straightforward.

The first code snippet you provided is very similar to my own.  I basically first do a check to see if a specific locale value is being passed in the $_GET array (which would happen by the user clicking on one of the language options in a language switcher I have in the header) and set the putenv and setlocale arguments based on that.  I then set a cookie with the lang preference which gets checked with each page to maintain the same language for the duration of the session.  It's pretty basic, but it works like a charm. See the code snippet below:

if (isset($_GET['locale'])) {
$locale = $_GET['locale'];
setcookie('locale','',time()-(60*60));
setcookie('locale', $locale, (time()+(60*60*24*7*2)), '/', '', 0);
} elseif (isset($_COOKIE['locale'])) {
$locale = $_COOKIE['locale'];
} else {
$locale = 'en';
setcookie('locale', $locale, (time()+(60*60*24*7*2)), '/', '', 0);
}

// Gettext parameters...
putenv("LC_ALL=$locale");
setlocale(LC_ALL, $locale);
bindtextdomain("SAM_strings", "../locale");
textdomain("SAM_strings");

Anyway, I really appreciate your help and timely advice on all this stuff. 

Best,

Ian
By: ege
Date: 2008-12-17
Time: 22:01

Re: Embedded hyperlink in text string

Hi again,

You welcome :)

It's really nice to save the locale in session in developer's point of view, as you don't have to build url's to accommodate different languages in all of your templates. Normally, however, this is something I usually avoid as from search engine optimization point, alternative language version of the same site is never fully or correctly indexed by them, as search engine bots don't have facilities to keep the session information. But if the project in hand doesn't require SEO then it's the best method I guess.
By: Ian
Date: 2008-12-17
Time: 22:32

Re: Embedded hyperlink in text string

Ege,

Out of curiosity, how do you manage error messages and other more strings that are produced in external classes (like DB wrapper) from a Gettext point of view?  Do you end up having some strings defined in your PHP files and the rest in your template files?  Or is there another way you've figured out to manage all these strings in the same place?

Thanks again,

Ian
By: ege
Date: 2008-12-18
Time: 02:24

Re: Embedded hyperlink in text string

Sorry Ian, but I don't think I understood your question. If there is an error in the system it should be sorted out, so that result won't contain any error messages. You say "More strings that are produced" in external classes, what are they? If you mean something like db connection error, I usually don't translate them because such a thing usually mean a total disaster, no db connection means there is no website so, for me, it really doesn't matter if you translate it or not as the visitors will not be pleased anyway, even if they are told what happened in their language. I treat those cases as if they will never happen.

I hope this is what you meant, if not, could you please clarify what you meant?

ege.
By: Ian
Date: 2008-12-18
Time: 04:15

Re: Embedded hyperlink in text string

Hi Ege,

For example, I have a form validator class that uses the built-in PHP filter functions to validate and sanitize data entered into the forms on my site.  That class has numerous message strings that indicate whether the data entered meets the validation criteria (e.g.: if the field validation is set to only accept a text string between 3 and 20 chars, and the user enters a string of only 2 chars, a message will be produced informing the user that the data entered is invalid).  The way the class is set up is that any validation errors like this are added to an "errors" array and I merely output any field-specific errors next to the form field it pertains to. 

Since the error messages themselves are defined in the Validator class, and those errors have to be translated to be understood by all users, I don't see any reasonable way to "move" them out of that class and maintain them in the HTML (template) file, as you suggest.  I agree that it makes no sense to translate "behind the scenes" or catastrophic error messages, but, in this case, these messages are vital to ensure the user knows what they did wrong on a form.

Anyway, you may not have a similar situation with your sites, but that is one example of where some text strings in my site really must be maintained in the source PHP file rather than the template.

I'm looking into your suggestion about keeping the strings in the template and then using regexp to extract the strings in more depth in the meantime as I do like the idea of having a single PHP text file (excepting the Validator messages described above) for all my strings, not to mention use TBS as it was designed. As it stands now, I have a core set of strings that I add to every page and then have a subset of strings that are unique to each page.  Gettext has no problem finding all these strings as I just direct it to search recursively in my web root for strings marked with _( and ).

Hope this helps clarify,

Ian
By: ege
Date: 2008-12-18
Time: 22:15

Re: Embedded hyperlink in text string

Hi Ian,

Ok, now I understood what you meant. However, I have never used any third party libraries of that sort, that's why I haven't encountered such situation. It's probably because, if I know that the site is going to be multi lingual I show reluctance to use such components that don't support my multilang infrastructure. I actually started to wonder how you cope with that. Did you modify the component's code to change all ".." to _("..")? Really can't think of anything else right now.
By: Ian
Date: 2008-12-18
Time: 23:02

Re: Embedded hyperlink in text string

Hi Ege,

Yes, I wrote the validation wrapper class to make my DB calls a bit more manageable and less verbose, but it does pose an issue when it comes to multi-lang considerations.  Since I have all my text strings in my .PHP files, I did actually wrap the strings in the validation class in "_(...)" and they get pulled just fine with POEdit (my po file editor of choice).  However, if I were to go the route of putting those strings back in the template, I would then have strings in the PHP validation class file and the HTML files...

Decisions, decisions... :)

- Ian
By: Ian
Date: 2008-12-18
Time: 23:05

Re: Embedded hyperlink in text string

Sorry for the reference in my prior response to the "DB".  I meant form validation... For some reason I have the DB on my mind today!

- Ian
By: ege
Date: 2008-12-19
Time: 05:18

Re: Embedded hyperlink in text string

Ok, yes I see. I think not much can be done about it when you use libraries that spit out some predefined text to clients. The way you handled things as you coded in the original post seems very reasonable to me.

Maybe you could have managed to design the validation library so that it wouldn't throw any explicit texts but some constants to be interpreted later on in templates but this would deny the creation of the library altogether as you would have to type all the validation messages into the templates on every project. The templates would probably be very confusing and ugly as well. Really bad idea.

In my opinion, I guess it's ok to keep some or all text in libraries in such cases and let the text be cluttered in templates and php files. Thinking in terms of template use, I probably would search for a way to put normal strings in my templates so that they would look handsome and let some really dynamic stuff reside in php pages, even though that would make it harder to maintain .po files. From a code design perspective, I'm just more inclined to think that way now, I can't say it's a better one until I try both approaches.

Regards.
ege.
By: Ian
Date: 2008-12-19
Time: 05:59

Re: Embedded hyperlink in text string

Hi Ege,

Yep.  This is one of those "gray areas" in design that can be challenging when it comes to trying to apply a single approach.  I think I'll stick with my current tack of keeping the strings in the PHP file just from a maintainability perspective. 

I'll definitely be more cognizant of these types of considerations on the next project and can hopefully come up with a more elegant implementation.

Thanks again for your help!

- Ian
By: Ian
Date: 2008-12-23
Time: 00:44

Re: Embedded hyperlink in text string

Hi Ege,

Just for kicks, I've created a test PHP and HTML file to implement the multi-lang/gettext solution you outlined above and it appears to be working fine with the basic setup you outlined.  I created a script with regexp to identify all the strings encased in "_(" and ") in my HTML files and write those to a single PHP file, which I then run POEdit (gettext front-end) against to create my .PO and .MO files. 

One thing that I'm still not clear on is how you would pass the TBS params via the MergeField method for these fields.

As an example, I have the following TBS placeholder in my HTML file:

[trans;user='Username']

and the MergeField method in the PHP file like this:

$tbs->MergeField('trans','f_translate',true);

and the custom function "f_translate" looks like this:

function f_translate($dummy,$params)
{
  return gettext($params['user']);
}

Let's imagine I wanted to enable the field params ";magnet=p;noerr;" for the Username field outlined above.  What is the syntax in the MergeField method for passing these params?  If you could provide an example, that would be great.

Thanks for your help,

Ian
By: Ian
Date: 2008-12-23
Time: 01:58

Re: Embedded hyperlink in text string

Sorry, small correction in my previous post...

Where I said

I created a script with regexp to identify all the strings encased in "_(" and ") in my HTML files and write those to a single PHP file

I meant "I created a script with regexp to identify all the strings encased by "[trans" and "]"...

- Ian
By: ege
Date: 2008-12-23
Time: 06:53

Re: Embedded hyperlink in text string

Hi again,

I hope I understood what you're asking correctly. Well, I guess all of the parameters are passed automatically with "trans" field and can be accessible from within f_translate function, ie, like $params['user'], you should be able to reach "magnet" parameter with $params['magnet']. I never tried this but I don't see any reason why it shouldn't work. Don't really know about what value it returns for $params['noerr'] though, but you can easily check that out.

On the other hand I never needed to access those additional params in the custom translation function and wondered why you do. If the reason is to conditionally display some text I usually prefer a different approach. Off of my head:
<p>
[onshow;block=p;when var._SESSION.userId!='';noerr]
[trans;user='Username']
</p>
That way I avoid trying to do more than one thing in one expression, if onshow block's condition is not satisfied the whole "trans" field would be removed. This looks somewhat clumsy but I didn't think of a more elegant solution to this. I hope a more knowledgeable tbs user would provide one. If you have another reason I apologize for the noise.

Finally, the above code explains why my tbs based creation of php file that contains _() texts fails. The trans field within the p block is removed when I run the _() generator, therefore 'Username' string is missing in the resulting php file. I would be happy if you would share the regexp code as it would take too long time for me to do it myself since my regexp experience is close to non-existent.

Thanks.
ege.

By: Ian
Date: 2008-12-23
Time: 19:38

Re: Embedded hyperlink in text string

Hi Ege,

I've included the PHP script and REGEX I'm using for pulling the strings to be translated below.  Modify the REGEX as you need to based on the naming conventions for your custom tags...

The regex itself is:
\[trans;.+?='(.+?)'[;\]]

Note that I have added in a character grouping clause at the end to allow for the TBS placeholder field to contain one or more additional params (like 'magnet=p;noerr' as in my earlier example).  It doesn't sound like you implement your placeholders this way, but rather use the "onshow;block=p" usage instead, but that shouldn't make any difference to how this regex works.  Additionally, this regex will return the text string without the single-quotes.  If you want it to return the single-quotes as part of the string, use the regex below:

\[trans;.+?=(.+?)[;\]]

The PHP function I use to identify and pull the text strings is below.  Please see my comments at the bottom of the script, as I haven't had time to comment the script in-line yet...

<?php

searchDir("c:\\wamp\\www\\recursion_strings",'#\[trans;.+?=\'(.+?)\'[;\]]#', array('images','nbproject', 'stylesheets', 'tbs','tinymce', 'recursive_search2.php'));

function searchDir( $pathToDirectory, $regex, $blacklist ) {
if (!isset($results)) $results = array();

     // Get list of files in directory
     //
     if ( $fileList = opendir( $pathToDirectory ) ) {
          while ( $aFile = readdir( $fileList ) ) {

               if ( $aFile == "." || $aFile == ".." )
                    continue;

               // Skip directory entries - files only
               //
               if ( @filetype( $pathToDirectory . "\\" . $aFile ) == "dir" )
                         {
                            if (!in_array($aFile, $blacklist))
                            searchDir( $pathToDirectory . "\\" . $aFile, $regex, $blacklist);
                         }


               if ( @filetype( $pathToDirectory . "\\" . $aFile ) != "file" )
                         continue;

               // Read the file into a string
               //
               $fileText = file_get_contents( $pathToDirectory . "\\" . $aFile );
              
               // Search for test string
               //
               if (preg_match_all("$regex", $fileText, $matches, PREG_PATTERN_ORDER)); {
              // echo '<pre>';
              // print_r($matches);
              // echo '</pre>';


                //$fp = fopen("test.txt","w");
                foreach ($matches[1] as $val ) {
               
                   //fwrite($fp, $val . "\r\n");
                   $matchesCombined .=  '_(' . $val . ')' . " \n";
                    }

                   // echo "$aFile<br>";
                    $results[] = $pathToDirectory . "\\" . $aFile;

               }
          }

          closedir( $fileList );
     }
   
     $fp = fopen("test.php","w");
     fwrite($fp, '<?php' . "\n" . $matchesCombined . '?>');
     return true;
}

?>

Script Comments:

Note that I have this script set up for use on a Windows machine running WAMP, so you will need to alter the directory backslashes into forward slashes if you are working on UNIX/LINUX.  The "blacklist" array is just an array containing those directories I want the script to skip when it recurses, as there are no applicable HTML files in those directories.  For the PHP PREG-MATCH_ALL function, I am using the PREG_PATTERN_ORDER flag and am only interested in the $matches[1] array as that is the array that contains the captured text that I am interested in translating. 

Hopefully, the above helps you out.  As I said, I'm a pretty novice PHP programmer, so use at your own risk!  ;)  I'm definitely happy to share any knowledge I have but want to clarify that I am still at a pretty basic level...

Best,

Ian