0
\$\begingroup\$

I'm currently working on a website which provides content to users based on their region. As a result the website has subdirectories for the appropriate regions. For example, a pricing page would look something like this:

In order to target the appropriate search engines and signify to Google that there are different versions of the page, I've implemented hreflang tags. A good example of a site that uses these would be view-source:https://www.tripadvisor.ie/. If you view the source you will see a long list of hreflang tags to denote multiple regional versions of the page.

So, for my site, I decided to write a PHP script that would output the relevant hreflang tags on the correct page:

<?php $url = $_SERVER['REQUEST_URI']; ?>
<?php if(strpos($url, "pricing") == true): ?>
<link rel="alternate" href="http://www.mydomain.com/ie/pricing/" hreflang="en-ie" />
<link rel="alternate" href="http://www.mydomain.com/gb/pricing/" hreflang="en-gb" />
<link rel="alternate" href="http://www.mydomain.com/fr/pricing/" hreflang="en-fr" />
<link rel="alternate" href="http://www.mydomain.com/pricing/" hreflang="en" />
<link rel="alternate" href="http://www.mydomain.com/pricing/" hreflang="x-default" />
<?php endif; ?>

However, the problem I'm facing is that if the URL contains pricing ANYWHERE it will output the above which is not correct if the URL was something like: http://www.mydomain.com/pricing-types.

To counteract this I added another if statement which checks to ensure that the word types is not present in the URL:

<?php $url = $_SERVER['REQUEST_URI']; ?>
<?php if(strpos($url,"pricing") == true && strpos($url, "types") == false): ?>
<link rel="alternate" href="http://www.mydomain.com/ie/pricing/" hreflang="en-ie" />
<link rel="alternate" href="http://www.mydomain.com/gb/pricing/" hreflang="en-gb" />
<link rel="alternate" href="http://www.mydomain.com/fr/pricing/" hreflang="en-fr" />
<link rel="alternate" href="http://www.mydomain.com/pricing/" hreflang="en" />
<link rel="alternate" href="http://www.mydomain.com/pricing/" hreflang="x-default" />
<?php endif; ?>

Would anyone have any recommendations of how I could improve my script to read the URLs and match them to the appropriate hreflang tags but being more explicit in the URLs I read?

\$\endgroup\$
  • \$\begingroup\$ To whomever downvoted could you please explain why? I thought this place was a good location to ask for reviews of code etc...? I'd just like to know for future posts \$\endgroup\$ – Javacadabra Nov 29 '16 at 22:18
2
\$\begingroup\$

Let's start rewriting your code just to make it more readable:

$url = $_SERVER['REQUEST_URI'];
if(strpos($url,"pricing") == true && strpos($url, "types") == false)) {
?>
    <link rel="alternate" href="http://www.mydomain.com/ie/pricing/" hreflang="en-ie" />
    ... other <link>s
<?php
}

From the few information you showed about how different the URL may be, I don't really understand where is the issue.
Indeed using the simplest (and a bit naive) interpretation possible, it might be merely written like this:

$url = $_SERVER['REQUEST_URI'];
if($url == "http://www.mydomain.com/pricing/") {
?>
    <link rel="alternate" href="http://www.mydomain.com/ie/pricing/" hreflang="en-ie" />
    ... other <link>s
<?php
}

This way it's clearly rid of any ambiguity!
But it lacks taking in account what we guess about your need:

  • the pricing url's "member" might not appear directly next the domain (like in http://www.mydomain.com/foo/bar/pricing/)
  • it even might not be the last "member" in the url (like in http://www.mydomain.com/foo/bar/pricing/baz/
  • BTW even when its the last one it might not be followed by a final /

So to ensure covering all these variations the good way is to use a regex:

$url = $_SERVER['REQUEST_URI'];
if(preg_match("#http://www.mydomain.com/(.*?/)?pricing(/.*)?#", $url, $matches)) {
$before = 'http://www.mydomain.com/' + $matches[1];
$after = '/pricing' . $matches[2];
?>
    <link rel="alternate"
    href="<?php echo $before; ?>ie<?php echo $after; ?>" hreflang="en-ie" />
    ... other <link>s
<?php
}

Note that we had to change the way we build the <link>s, in order them to respect the original url. BTW we slightly reorganized it to keep readability.

Finally we might consider yet enlarging the regex versatility in order to also work with any domain: http://(.*?/)?pricing(/.*)?

Look at https://regex101.com/r/S9N3ZL/3 to see how this regex works with different urls.

\$\endgroup\$
2
\$\begingroup\$

I would think that you would want to decouple the logic you use to determine what localization to display from the logic you are using to actually render the hreflang links in HTML.

With your current approach, you are going to get into a serious mess as you try to localize additional pages in your application.

You would need to hard-code similar links to what you have shown across every page. You would also need to maintain separate hard-coded versions for each page to show the slight variations in these links based on which page the user is currently on. For example, if user is on fr page, they should not see alternate hreflang link for fr.

You also probably need to be thinking about generating an appropriate canonical URL tag as part of this same logic.

Also, over time, you may find the need to send hreflang hints in HTTP headers instead of in HTML <head> element. By decoupling your logic for determining which localizations settings are applicable for a page (more of a routing concern), from how you convey that information to the browser/spider (a display concern), you can make you logic more re-usable across your application.

If you do not already have a central class or library of classes that handle localization logic for your application, you should strongly consider this.

I think what you should be striving for is a usage pattern like the following in your display context.

HTML link example:

<head>
    <!-- other header elements -->
    <?php $localization->displayAltLangLinks(); ?>
</head>

Header example:

<?php
// PHP application bootstrapping here

// send headers
$localization->setAltLangHeaders();

// page output follows

Here I have shown a theoretical Localization class/object where you could encapsulate your logic related to: current localization being requested, localization to be served, outputting localization info to browser, etc.


I think you need a better strategy for determining what language is being requested. Relying on $_SERVER['REQUEST_URI'] will likely make your app fragile to user input. For example, what if I used URI of /PRICING/ in browser? Your current code breaks. What if I put /es/pricing/? Your code would be triggered even though no localization would be served, when perhaps a 404 should be returned, or even a 301 to default page.

You really need to think about handling the logic around URI rewriting/redirecting at the web server and/or application bootstrap level. A common pattern in providing the type of URI's you are looking to use it to put in a rewrite at the web server level to redirect something like:

/fr/pricing/

to

/* for individual PHP scripts */
/pricing.php?lang=fr

/* for front-controller application with central routing */
/index.php?lang=fr

This allows you to pass the language requested as a parameter to your script. Also, if the language segment of the URI doesn't match the languages your application supports perhaps the request falls through your rewrite rules and generates a 404 or 301 redirect depending on how you want to handle these requests.

By handling localization settings early in the response processing cycle you could also do things like look at the user's browser Accept-language request headers, any user preferences settings (cookies, session settings, use preferences stored in database, etc.), to determine when localization should be rendered and performing appropriate redirects for those users.


So to summarize:

  • Handle localization settings and routing for the page much earlier in response process than at display time.
  • Dynamically generate these link tags(and probably canonical tag) in response to what localizations are available for the route as well and what current requested localization is.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.