PSD To HTML Services

If you’ve reached this page, I assume you know what a PSD file is. Just in case: PSD stands for PhotoShop Document. It is the file extension of the proprietary file format of Adobe Photoshop. Adobe Photoshop is a very useful (and costly) program that can be used to design websites. Very often designers rather produce a PSD file and which they do not convert themselves to a webpage. In this case, they need some web developer to do the job. In case you have a PSD design, do not want to convert it to a webpage yourself and do not work with a dedicated developer, you can revert to companies specialized in creating webpages based on the design in a PSD file.

I was checking what kind of services such companies provide and came up with the idea to create this list.

Note that I’ve found many more companies providing such a service. But I’ve only taken up companies where I could find information about pricing, CMS information and accepted payment methods on the webpage without having to first register or upload a PSD file.

I’ve compiled the following information on this page:

  • Name of the company
  • URL
  • Prices for first and inner pages using a standard desktop, a responsive or a mobile layout
  • Integration with WordPress, Drupal, Joomla!, Typo4, Magento, Shopify
  • Accepted payment options

Also note that I am not affiliated to any of these companies. I haven’t used their services either (as I can perform this kind of things myself). I’ve just been through all these sites out of curiosity and thought I might as well gather the information in case someone is interested.

There are also companies providing services for the automatic conversion of PSD files to HTML. I personally doubt it is a good option, so I did not include any of them in the list.

If you own or work for a company not in the list and would like to be taken up in the list, just drop a comment below. If you are in the list and spot an error, please also add a comment below. I’ve just compiled this list to help so it is neither complete nor am I 100% sure that everything is accurate or still valid when you read it.

Also note that most companies offer a faster delivery of the webpages for extra money. Please also keep in mind that I’ve always selected the cheaper option. Depending of what you need, the price might be higher.

There is no special order in the companies below.

CSSChopper

$49
$29
$49
$29
$199
$399

htmlMafia

$98
$48
$98
$38
$128

PSD to HTML

$129
$54
$228
$114
$91

psdHTML.pro

$117
$88.5

Chop-Chop

$138
$69
$89
$36
$195
$297
$297
$495

PSD2HTML

$159
$80
$228
$114
$179
$90
$195
$295
$295
$595

codemyconcept

$99
$79
$189
$289
$289
$589
$489

Markup Service

$146
$88
$297
$149
$229
$279
$259
$569

W3 Markup

$192
$115
$69
$231
$279
$259

xhtmlchop

$45
$29
$149
$99
$99
$279
$149
$299
$349

htmlBurger

$98
$48
$98
$38
$128

PixelCrayons

$99
$49
$199
$99
$50
$200
$150
$300

htmlBoutique

$138
$68
$138
$48
$168

crazy XHTML

$79
$39
$138
$48
$200

markupcloud

$99
$50
$50
$100
$100
$300

XHTML4U

$138
$69
$198
$298
$298
$598

htmlBlender

$99
$49
$129
$99
$189
$249
$279
$1199
$499
$279

ShopHtml

$79
$39
$139
$69
$116
$116
$116

CodedPSD

$100
$80
$139
$69

HTMLTHIS

$99
$55
$144
$100
$260

Bloody Hell

$189
$94
$228
$113
$229
$229
$289
$549

Embedded Map Generator

I’ve added a new tool to the list of tools I’ve already posted about some time ago. This one is an embedded map generator. It generates some code you can use to embed maps directly into your website. You can find the tool here.

The tool allows you to type in an address (or any location like the Eiffel Tower or the Statue of Liberty) which will be shown in the map. The auto-complete will show you matching locations Google knows. You can also specify a width, height, zoom factor. You can also specify the language in which all texts should be displayed. I guess I got all supported languages. If you find any that Google does support but is not in the list, please leave a comment below.

Additionally, you can also define which type of map is displayed:

  • Google Earth
  • Hybrid
  • Normal Map
  • Satellite
  • Terrain

Your users will be able to change the map type later on but it’s useful to set a meaningful default view.

You can also choose to display or hide the information window (aka the information bubble).

You should experiment with the different parameters until you get the map you want to embed. Then copy the HTML below the preview and paste it in the HTML editor you use to edit your site.

Here are a few things I still want to improve:

  1. Use the geocoding API to provide some auto-complete when you enter the address DONE
  2. Support for more of the parameters supported by the Google Maps API

If you have any suggestion on how to further improve this tool, feel free to leave a comment below !

PHP Localization

When you’re setting up the multi language site, at some point in time, you need to decide which option you will use for static text on the site.

There are a few options available when working with PHP:

When dealing with localization and internationalization, you many need all or some of the following features:

  1. storing and retrieving of strings by locale
  2. message formatting functions including support for variables and placement holders
  3. support for singular, plural and other
  4. support for sorting according to different locales
  5. support for different date, time and currency formats
  6. tools for extraction from source code
  7. tools for having the application translated and for loading the translations
  8. a possibility to add new languages without changing the application
  9. a fallback mechanism i.e. load the de_DE translation in case the de_AT translation is not available.

gettext

gettext™ is the GNU library for internationalization and translation. It is also natively supported by PHP. gettext is quite widely used (most notably in WordPress). It is easy to use and quite powerful. It focuses on the storage, retrieval and formatting of messages.

So that gettext can be used in PHP, it must have been built with the –with-gettext option or have to make sure the gettext extension is enabled in php.ini and loaded as a shared/dynamic library.

Windows users should see the following:

extension=php_gettext.dll

Unix/Linux users:

extension=gettext.so

You can check whether your PHP installation supports gettext with the following code:

<?php

if (function_exists("gettext")) {
    echo "gettext installed";
}
else {
    echo "gettext not installed";
}
?>

gettext supports the concept of translation domains. A domain is defining a translation scope which allows to have different translations for the same key in different libraries. Let’s say you use two different libraries which both show the string “I am going to %s” in English. In one library, the placeholder contains an action. In the other one the name of a country. If you now translate the first one let’s say in German, you’ll get “Ich werde %s”. In the second case, “Ich fahre nach %s”. So you need not to mix the two and allows each library to get it’s translation. This is done through domains. Each library will define it’s own domain and will not see the translation of other domains.

A domain also defines a root path for the translations. It is defined like this:

bindtextdomain("mytextdomainname", "mytextdomainlocaleroot");

After that you can also set a default text domain (it defines the text domain used when no text domain is specified):

textdomain("mytextdomainname");

A common choice for the text domain name is the name of the software, library or plugin.

Under the translation root path, you’ll the following directory structure:

/translation_root_path
    /en_US
        /LC_MESSAGES
            messages.po
            messages.mo
    /de_DE
        /LC_MESSAGES
            messages.po
            messages.mo

The messages.po file is a text file containing all translated entries. The messages.mo file is a compiled binary file read by gettext. Getting a messages.mo file involves the following steps:

  1. Extract all strings to be translated from the source code using xgettext. This will create a messages.po file
  2. Translate all string in messages.po
  3. Compile messages.po to messages.mo using msgfmt

The input for xgettext is a bunch of source code files:

xgettext *.php

Your can also use the following options:

  • -n to have the location of the string (which line of which file) written as a comment
  • -j to merge with an existing file
  • -o to specify an output file

You can also merge two .po files like this:

msgmerge old.po new.po --output-file=messages.po

This will merge new.po into old.po and create messages.po. It’s basically similar to:

msgmerge old.po new.po > messages.po

You can also run it in update mode:

msgmerge -U messages.po new.po

This will merge new.po into an existing messages.po. So basically when you make changes, you’ll create a new .po file and merge it with the existing one:

xgettext -o new.po *.php
msgmerge -U messages.po new.po
rm new.po

Translation and messages.po files can be updated and managed using poedit. Poedit is a free GUI tool for computer aided translation of documentation and user interfaces. It’s basically a UI frontend for the tools of GNU gettext used to edit .po files. It provides functionality for managing translation projects with a minimalistic UI. Not yet finished translations are specially marked so that it’s easy to see them, translation catalogs can be imported and unknown translations can be automatically taken over from the catalog.

Once the messages.po file is translated, you can compile it like this:

msgfmt messages.po

This generates the messages.mo file which can then be copied to the appropriate directory as shown above.

To set the language which is then used to load the appropriate messages.mo file, use the following code:

$language = "en_US";
putenv("LANG=" . $language); 
setlocale(LC_ALL, $language);

Now you can use the following to get a translated text:

echo _("Hello World!");

_(…) is a shortcut for gettext(…). Since you’ll be writing many such calls, you’ll soon be glad to have this shortcut :-).

The gettext function returns the translated string from the current text domain (set using the textdomain function). If you need a translation from another domain than the default domain (which is often the case when you work on plugins for an application, where the default text domain is the one of the application and you do not want to change it in a plugin), use dgettext instead:

echo dgettext("mytextdomainname", "mystring");

There are also versions of these two methods which can handle singular/plural: ngettext and dngettext. Here an example with ngettext:

echo ngettext("an apple", "a few apples", $no_of_apples);

ngettext will check whether the third parameter is one (in which case it will consider the first string) or more than one (in which case it will consider the second string). Here another example actually writing the number of apples:

printf(ngettext("%d window", "%d windows", $no_of_apples), $no_of_apples);

Note that gettext .mo files are cached so if you modify a messages.mo file you might notice that the changes are not active. They will only become active after you restart the web server. A few workaround are also described here.

The big advantage of the gettext approach is that you can use gettext without defining any translation at first and then define the translations. Since the strings used as keys are the text in the default language, you will always have a text displayed even if the corresponding translation does not exist yet. The downside is that you basically mix the text for the default language and code which some might find unclean.

Also gettext doesn’t address the conversion of date, time and currencies. It also doesn’t address the language specific sorting of localized entries.

You can find more info on gettext on the gettext GNU project page.

Note that WordPress uses gettext for translation and defines a few additional helper functions:

__('some message') // returns the translation for 'some message'
_e('some message') // echoes the translation for 'some message'
_n('some singular message', 'some plural message', $count ) // returns either the translation for 'some singular message' or 'some plural message' depending on the value of $count

Associative Arrays

This technique basically involves having PHP files containing a big associative array which keys are referenced in the code and values contain the translation in a given language. Based on the language selected in the application, you will load one array or the other one.

Defining such an array is as easy as:

$lang = array(
	"HELLO_WORLD" => "Hallo Welt !",
)

for the German version and the following for the French version:

$lang = array(
	"HELLO_WORLD" => "Bonjour monde !",
)

In you code, you’d load the appropriate translation like this:

<?php
if(isset($_REQUEST['lang'])) {
	//Explicitely set as URL parameter
	$lang = $_REQUEST['lang']; //read it from the URL parameters
	$_SESSION['lang'] = $lang; //save it in the session
	setcookie('lang', $lang, time() + (3600 * 24 * 20)); //set a cookie for 20 days
}
else if(isset($_SESSION['lang'])) {
	//Fallback: read it from the session information
	$lang = $_SESSION['lang'];
}
else if(isset($_COOKIE['lang'])) {
	//Fallback: read it from the cookie
	$lang = $_COOKIE['lang'];
}
else {
	//Fallback: English is the default
	$lang = 'en';
}

//The files are named lang.LANGUAGE.php i.e. lang.en.php
//Assumption: language files are stored in the langs subdirectory
$lang_ile = 'langs/lang.'.$lang.'.php';
if (!file_exists($lang_file)) {
	//Fallback: if no translation is available for this language, use English
	$lang_file = 'langs/lang.en.php';
}

//Load the selected translation
include_once $lang_file;
?>

Now you’ve loaded the file, you can access the translated string like this:

<?php
	echo $lang['HELLO_WORLD'];
?>

Now this works file if you have no parameter in the translation. Let’s say you want to write “I’ve seen Henri” but implement it so that if $person contains Alex instead of Henri, you can just do something like “I’ve seen $person”. Of course just doing it this way will not work:

<?php
	echo $lang['I_HAVE_SEEN'].' '.$person;
?>

Since in German, it would say: “Ich habe gesehen Henri” instead of “Ich habe Henri gesehen”. So you need to be able to define place holders. You can handle this using such a function:

function translate() {
	global $lang;
	$parameters = func_get_args();
	$parameters[0] = $lang[$parameters[0]];
	return call_user_func_array('sprintf', $parameters);
}

Here a short line by line explanation of the code of the function:

  1. we will use the $lang variable defined outside of the function.
  2. func_get_args returns all parameters of the containing function.
  3. we replace the code used as first parameter by the format string defined in the translation array.
  4. we call sprint with the entries of the array as parameter and return the results.

And you can use this function like this:

echo translate('I_HAVE_SEEN', 'Henri');

Disadvantages of this method:

  • It doesn’t support plural forms i.e. one child vs. two children
  • There is no default. If the translation doesn’t contain the string, you have a problem
  • It only takes care of storing and reading the translation, nothing else

PHP internationalization extension

Intl is the PHP internationalization extension and was introduced in PHP 5.3. It is basically a wrapper for the ICU (International Components for Unicode) library.. It focuses on collation and formatting of dates, times, numbers and currencies.

Translation2

Translation2 is a class to manage internationalization and translations in applications. The main advantages of Translation2 are:

  • It supports different containers for the translations e.g. in a database, using gettext, using XML.
  • It’s very flexible and supports decorators which can provide additional functionality e.g. caching, converting, fallbacks…
  • It supports fallback languages which is not always easy with the other solutions.
  • It has an administration class which makes it easy to manage translations.

More will be added to this post soon…

Post-it cards with CSS

For a Kanban board I needed to create a virtual card on the board which would look like the post-its which are used on analog boards. Here is how to do it very easily with CSS just using a few steps.

First we need a card:

<html>
	<body>
		<div>Buy some milk</div>
	</body>
</html>

It looks like this:
post-it.1

The first step is obvious: a post-it should be yellow, so we’ll start with the following CSS:

background:#ffffa5;

It now looks like this:

post-it.2

Then we need to actually give it the form of a post-it using the following:

width:250px; height:150px;

Now it looks like a yellow rectangular card: post-it.3 Now we need to fix the position of the next: it’s much too close to the border of the card:

padding:15px;

It slowly does look like a real post-it…

post-it.4

So that it looks like the cards on the current board, you need to actually have them look handwritten. Let’s change the font:

font-family: "Lucida Handwriting", cursive;
font-size:15px;

There are many other handwriting fonts available but this one is pretty much available on all modern PCs.

post-it.5

It does already look quite good, but it still doesn’t feel like a paper card… Maybe with some shadow:

box-shadow: 2px 4px 6px #444;
-moz-box-shadow: 2px 4px 6px #444;
-webkit-box-shadow: 2px 4px 6px #444;

This handles the following browsers:

  • Internet Explorer 9.0 and higher
  • Firefox 3.5 and higher
  • Opera 10.5 and higher
  • Safari 3.0 and higher
  • Chrome 1.0 and higher

It then looks like this:

post-it.6

Now this looks good ! The only thing which still looks very computer like and less human like is that the card is perfectly horizontal. This is probably never the case when you stick something on a board. This can be done with some CSS:

transform: rotate(-3deg);
-moz-transform: rotate(-3deg);
-webkit-transform: rotate(-3deg);
-o-transform: rotate(-3deg);
-ms-transform: rotate(-3deg);

And looks like this:
post-it.7

We’re done ! Now you can still improve it by looking at different shadow effects, increasing or decreasing the angle…

Secure your Facebook account – Part 1: Security

Many people store a lot of private information on Facebook (what they are doing, pictures, list of friends). Facebook lives from your readiness to share. The more you share there, the better. The more is accessible to all, the better. This also means that even though there are quite a few of security and privacy settings in Facebook, most of them are not enabled by default. This means your account is mostly open and can be closed as needed rather than having a secure private account, and have you knowingly make some parts of your life public.

Since it also looks like half of the parents join Facebook to check on what their children write or where they get tagged, Facebook is officially the largest social spying network.

If despite of this all you want to use Facebook but in a private and secure way you need to consider exactly these two aspects. Privacy and security.

Security

Making your account secure is about making sure that unauthorised persons will not take the control of your account or get unlimited access to information.

Password

The first step is to have a secure password protecting your account:

  1. A secure password should be unique i.e. not used on Twitter, Google, Amazon… in addition to Facebook. Once any one of these accounts are hacked, the hacker has access to your data everywhere.
  2. Change the password on a regular basis. You might unknowingly give hints about your password to someone. With enough hints, it might be possible to figure out your password. But if you completely change it every 3 to 6 months, it stays secure. Changing it on a regular basis doesn’t mean moving from “Winter2012” to “Spring2013” to “Summer2013”. It also doesn’t mean changing from “qwertzuio” to “asdfghjk” to “yxcvbnm,” or from “A6Hjfh1$” to “A6Hjfh1%” to “A6Hjfh1&”. If there is an obvious pattern, then you changing the password has almost no effect.
  3. Never tell anyone your password. It’s not that there are not people you can trust. But let’s say you can trust yourself to 100% and very close friends or relatives to 90%. They in turn also trust someone to 90%. This person as well… In the end, sensitive information land in the hands of someone you do not know at all and shouldn’t trust either.
  4. If you cannot remember your password, the chances that someone else will figure it out are very low. So using a password tool is a good idea. Of course it means having a master password and if this one is nor secure and someone gets access to the tool… But as you see it then means that someone also needs to have access to your computer and not just the internet. Also the master password itself should be secure enough.
  5. Don’t save it in browsers. Only save it as suggested above in a place also protected by a master password. Otherwise, it means that anybody with access to your computer has unrestricted access to all accounts including Facebook.
  6. Do not write the password down. If you cannot remember it, use a password vault or tool. More people than you think can manage to look at the back of your keyboard.
  7. Remember that email, sms or any other communication media are not always safe. So do not send your password to anybody as the message might be intercepted.

Use also the following rules to generate a secure password:

  1. A longer password is more difficult to hack. Use at least 8 characters
  2. Use small letters, capital letters, digits, special characters. Of course pay attention, that special characters used are available on all keyboard layouts you use.
  3. Avoid having the password contain parts of your name or email address.
  4. Avoid personal information about you or your family. How many people know the name of your first dog, the maiden name of you wife or the birth date of your children ?
  5. Avoid simple sequences of characters like 123 or abc or qwerty…
  6. Replacing a letter with a similar shape (e.g. an @ to replace an “a”, a zero to replace an “o” or an exclamation mark for an “i”) is a well known way to get special characters and digits in a password. But everybody trying to hack your password also know that, so it might actually not be such a bright idea !
  7. Do not use words from the dictionary, no matter which language (someone trying to find out your password will for sure manage to find out which other language you might be using).
  8. Reversing words is not the answer. Someone trying to find your password will not only check apple, @ppel, appe! and @ppe! but will also reverse the word e.g. elppa.
  9. Doubling words make the password longer but not more secure.

This was about security. In the next post, we’ll focus on the privacy settings available in Facebook.

HTTP redirect, meta refresh, Frame or JavaScript redirect

HTTP 301

A 301 redirect is an HTTP response to a browser request for a page. Here is how the server response looks like:

HTTP/1.1 301 Moved Permanently
Location: http://benohead.com

As shown above, a 301 redirect means that the page has permanently moved to the specified location. All major search engines will

In PHP

You can use the following code to trigger a 301 redirect in PHP:

<?php
header("Status: 301 Moved Permanently");
header("Location:http://benohead.com");
exit;
?>

.htaccess

To redirect a single page using the .htaccess Apache configuration file, just add the following to the file:

Redirect 301 /dir/page.html http://benohead.com/page.html

mod_rewrite

When the Apache module mod_rewrite is active on the server, you can use it to redirect visitors. Here an example:

RewriteEngine On
RewriteRule ^(.*)\.html$ http://benohead.com/$1.html [R=301,L]

This will redirect all visitors from http://olddomain.com/mypage.html to http://benohead.com/mypage.html.

The advantage of this method is that you can write your rules so that the target URL changes based on the source URL e.g. to implement the following mappings:

  • http://olddomain.com/mypage.html to http://benohead.com/mypage.html
  • http://olddomain.com/mycategory/mypage.html to http://benohead.com/mycategory-mypage.html

HTTP 302

A 302 redirect is used to inform the browser/robots that the page has temporarily moved to a different location. So it should only be used when it is planned to host the redirected page back in the original URL after some time.

The redirected page keeps Page Rank, Page Authority and other metrics.  You thus have to pay attention when using it as the new page will not inherit any of it. So if you actually permanently moved the page but used a 302 redirect, you’re starting from scratch.

Although Google accepts 302 redirects and will not create major issues, Bing states the following:

We do not index any pages that have been 302 redirected by design.

So you basically have the risk that your pages will be indexed twice and you will be penalized for duplicate contents.

HTTP 307

The HTTP return code 307 is the successor of the return code 302 and was introduced in HTTP 1.1. This means that old systems only supporting HTTP 1.0 will not understand it.

This is also temporary redirect. It tells the calling user agent to redirect to a different page but not to forget this URL since it is only temporarily redirected.

To figure out whether to use a 302 or a 307 redirect, you need to check your web server log files. If you see that there are few HTTP 1.0 requests, then 307 is a good option. Otherwise you should probably stick to the 302 redirects.

Meta refresh

The meta refresh tag is a meta http-equiv tag to be inserted in the <head> section of web page. It will cause the browser to automatically refresh the webpage after a set period of time using the provided URL. It basically redirects the visitor to a new page.

It looks like this:

<meta http-equiv="refresh" content="...;url=..." />

Here’s what the Google Webmaster Tools say about the meta refresh tag:

This meta tag sends the user to a new URL after a certain amount of time, and is sometimes used as a simple form of redirection. However, it is not supported by all browsers and can be confusing to the user. The W3C recommends that this tag not be used. We recommend using a server-side 301 redirect instead.

And Google’s John Mueller:

In general, we recommend not using meta-refresh type redirects, as this can cause confusion with users (and search engine crawlers, who might mistake that for an attempted redirect). This is likely where the warning you saw came from. This is currently not causing any problems with regards to crawling, indexing, or ranking, but it would still be a good idea to remove that.

So although a meta refresh tag (also known as “the poor man’s 301 redirect”) with 0 seconds basically has the same goal as a 301 redirect (to immediately redirect the visitor), if possible, it makes more sense to use a 301 redirect.

The way meta refresh tags are interpreted vary across search engines:

  • Yahoo and Google treats zero seconds meta refreshes just like a 301 redirect.
  • Bing will not follow meta refreshes and will just skip the page.
Basically, you should avoid such redirects except if you do not have full access to the server and cannot setup a 301 redirect. In this case do not forget to also use a rel=”canonical” tag to tell the search engines where to find the new location of the page.
Also remember to only had a meta refresh tag but also remove the actual content from the redirected page (replacing by something saying the content is now somewhere else) just in case.

JavaScript redirect

JavaScript can also be used to redirect the user to a different page.

Here is how to redirect the user immediately:

<html>
  <body>
    <script language="javascript" type="text/javascript">
<!--
      window.location.href = 'http://benohead.com';
// -->
    </script>
  </body>
</html>

As soon as the page is loaded, the JavaScript will be executed and replace the current URL by the new one.

You can also use the replace function instead of setting the href attribute:

window.location.replace('http://benohead.com');

instead of:

window.location.href = 'http://benohead.com';

Instead of this you may want to inform your visitors that they are being redirected and thus only activate the redirection once the visitor has had a chance to read the message:

<script language="javascript" type="text/javascript">
<!--
  window.setTimeout ('redirect_to ("http://benohead.com")', 5000);

  function redirect_to (destination) {
    window.location.href = destination;
  }
// -->
</script>

This basically instructs the browse to wait 5 seconds (5000 milliseconds) and call the function redirect_to which will update the current location in the browser.

So additionally to the JavaScript code you will want to display some kind of message (possibly with a link to the new URL in case the redirection doesn’t work).

Frame redirect

You can also achieve a kind of redirect by displaying the new page in an HTML frame. The advantage is that the visitor still sees the old URL in the address bar of the browser. The disadvantage is that it is a kind of cloaking method often used for phishing and spoofing. Another disadvantage is that the visitor then further navigates within the frame and the address bar in the browser doesn’t change. So it is not possible to bookmark a linked page after such a redirect.

The code looks like this:

<html>
  <frameset cols="100%,*" rows="*" frameborder=0 border=0>
    <frame src="http://benohead.com/">
  </frameset>
</html>

Useful 404 pages

The web keeps changing, pages come and go. Another development in recent years is that the keys of keyboards (virtual or not) seem to get smaller even though our fingers do not shrink. This all leads to the situations where a user requests a non-existing web page from a server. For such case, the HTTP protocol defines an error return code: 404. When a web server returns such an error code, the browser will display a standard page telling the user the page is not available.

Of course this standard page doesn’t look very nice and very often doesn’t really help either. So most servers will not return a 404 HTTP code but return a special web page (usually named after the 404 HTTP return code). Now, what do you need on such a page? Well it depends on how you want to handle this unexpected user visit.

First, you have to keep in mind that such a 404 traffic is still traffic and can be used to your advantage. Now let’s see why the user got to the 404 page:

Misspelled domain name

Well in this case, the user actually didn’t want to come to your site at all. Does it mean that your site is of no interest to him? Maybe not… Actually I’m pretty sure he has. For example, if there is a web site called linux-blog.com with a page called kernel.html. But instead of typing linux-blog.com, the user typed linuxblog.com, this site belongs to you but you do not have a kernel.html page… If the user is looking for a page on the Linux kernel, you blog (linuxblog.com) is probably a place where he might find the info he needs.

Lesson 1: Welcome visitors knocking at the wrong door since they might still enjoy what they find in here.

Misspelled the resource ID

The resource ID is the part coming after the protocol and server ID (i.e. domain name) in the URL. E.g. for http://benohead.com/useful-404-pages:
protocol=http
server ID=benohead.com
resource ID=useful-404-pages

So if they got the resource ID wrong, they are still on the wrong server. They were looking for something which is in there but are just looking in the wrong corner. This is an easy one. The best solution is to help them find the right corner.

Lesson 2: Be fault-tolerant and help lost visitors get to their planned destination.

Requesting a deleted page

First deleting a page is never a good idea. If you change the path to a page, it’s always a good idea to have the old URL redirect to the new one. If you really want to get rid of a page, it might make more sense to keep the page, remove the content and maybe write an explanation why the content is gone. You can also let the search engines know that this page should not be indexed.

Lesson 3: Do not forget that bookmarking exists and once something is out there, it stays out there.

Broken internal link

To avoid broken internal link, once you put a page out in the wild, you should check that the links in the page are actually working. Especially when you type in links, there are good chances that you’ll misspell something.

If the internal page you refer to doesn’t exist anymore, first refer to the previous lesson. Then there many tools out there to find such broken links. If your site is powered by WordPress, there is a plugin for that: The broken link checker. Otherwise you can use some online tool like:

Lesson 4: Visitors will find your 404 page themselves without you providing broken links.

How to make the 404 useful

There are a few practices which will help you create a useful 404 page:

Avoid error messages

Though error messages make it easier for you to figure out what the root cause of an issue is, displaying it to visitors will scare them and make sure they leave your site at once. It’s fine to say something went wrong because it obviously was the case and anybody will understand it but avoid cryptic error messages like the plague. Here are a few examples of messages which are easy to understand and will not scare visitors:

  • We can’t find what you want.
  • The page you are trying to reach is unavailable.
  • This is not the web page you are looking for.
  • The page you are looking for cannot be found.
  • The page you were looking for appears to have been moved, deleted or does not exist.

Keep the visitor in there

Once the visitor is on your site (whether he meant to visit it or not), keep him there. Do not send him back with messages like:

  • You took the wrong turn. Turn around.
  • It looks like you found a dead link.
  • The page that you are looking for does not exist, go back.
  • Hit the back button.

All those are used on actual web sites but just make sure those visitors leave as soon as possible. Some of those messages (like the suggestion to hit the back button) actually try to help the visitor but it’s a waste for your site.

Actually say something

Ok, someone took a wrong turn, broken URL, misspelled something and landed on your 404 page. Now we’re all civilized people and you should at least tell them what happened and what their options are. There are too many examples of nice looking 404 pages which actually do not help the visitors get back on their feet. Displaying a funny picture, a crazy animation or some video might entertain the visitor for a few seconds but it’s not helping and will drive them away.

Something like this does actually look good but what’s the purpose of this page ? Will someone even bother trying to see whether this site might be relevant for him ? Probably not…
oops

This one would have been useful if it actually told you how to find “other sections featured on this page”. I personally couldn’t find a link anywhere and left.
img404

This one doesn’t even tell you what happened or where you are:
404

This one is actually funny, it also tells you what happened but doesn’t even try to show you a way out:
404_tinsanity_net

This one has a clean 404 page, telling you what happened but seems to be determined to get you out of the site asap:
404_jackfish_com

This one also looks interesting but just gets the visitor wondering where he got and hit the back button as fast as possible:
404_d20srd_org

This one doesn’t even say what happened. If you know what 404 means, then you’ll understand, otherwise you’ll have to guess. It does provide a link to the homepage (by clicking on the image) but it doesn’t say anything and it’s not even visible that there is a link on the image:
404_amorphia-apparel_com

The next one is actually a very nice idea. The problem is only that it is a completely useless 404 page…
404_ook_co_uk

This one is funny too but I couldn’t even find a link to the homepage:
404

Of course there are sometimes exceptions to this. If your site is very specialized and you know visitors who might want to stay will also not require any support from you side, then it’s fine. Here an example from www.acme.com:
404_acme_com

Useful options for the visitor

It should now be clear that it’s very important to give your unexpected visitor a few options in order to keep him on your site. Here are a few things which might be useful:

Provide useful links

Provide links to some pages which probably are of interest to most visitors. A nice example is the 404 page of GitHub:
404_github

It provides 3 links to the most probable places where you could find the content you wanted to access.

Here another example basically providing an overview of the navigation menu:
404_suspendedanimations_com

If you’re using WordPress, you may want to display a list of recent posts:

<?php the_widget( 'WP_Widget_Recent_Posts', array( 'number' => 10 ), array( 'widget_id' => '404' ) ); ?>

Or a list of the categories with the highest number of posts:

<div class="widget">
	<h2 class="widgettitle"><?php _e( 'Most Used Categories', 'twentyeleven' ); ?></h2>
	<ul>
	<?php wp_list_categories( array( 'orderby' => 'count', 'order' => 'DESC', 'show_count' => 1, 'title_li' => '', 'number' => 10 ) ); ?>
	</ul>
</div>

Display a search form

The visitor might be able to search himself for the content he wanted to get to. GitHub does provide a link to the Search page but it’s even better if a search form is embedded on the 404 page e.g.:
404_jhuskisson_com

If your site is powered by WordPress, you just need to add the following to the 404 page template:

<?php get_search_form(); ?>

Try to find the missing content

Similar to the “Did you mean” functionality in Google Search, you could also try and find the page the visitor was actually looking for or at least a similar page on your site. This of course only works fine if you resource IDs actually contain keywords/titles.

That’s what the Smart 404 WordPress plugin did. Unfortunately, this plugin hasn’t been maintained for years.

The Permalink Finder WordPress Plugin goes one step further and before the visitor is redirected, tries to find the page he was looking for and redirects him there. I personally it’s going one step too far. The visitor then lands somewhere totally different than he’d have expected and I guess it’ll just get him confused.

Another useful WordPress is the True Google 404 plugin.

Forget about 404

You’ve seen that many of the screens above actually tell the visitor is a 404 page. In many cases, the visitor doesn’t even know what you mean with 404. And what’s the added value to say it’s a 404 page ? Just keep it simple and easy and just tell the user what the page is about and do not try to confuse him with HTTP return codes.

A few more things…

You should also keep the following in mind:

  • The 404 page should actually look like a page of your site i.e. have the same look and feel.
  • It’s an error scenario but making the page look friendly (or funny) makes it easier on the visitor.

Rich snippets: microformats for reviews

Note: Eine deutsche Version des Artikels findet ihr bei amazingweb

Microformats define classes that can be used in any HTML tag to provide semantic information to non-human readers. Among the different ways to reach this goal, microformats have the advantage that they do not represent a modification of the HTML specification, but only use existing artefacts and further specify their use.

There are various microformats to encode many different types of semantic information:

  • Addresses
  • Geographic coordinates
  • Atom information
  • Services or product lists
  • Media Information
  • News
  • Recipes
  • Resumes
  • Reviews of services and products
  • And more …

A common example of this is the hCard microformat. With a nice CSS stylesheet the following might look good:

<div id="contact-data-Henri-Benoit">
    <a href="http://www.amazingweb.de">Henri Benoit</a>
    amazingweb GmbH
    <a href="mailto:hb@amazingweb.de">hb@amazingweb.de</a>
    Ölmühlweg 37
    Königstein, Hessen, 61462
    Germany
    +49 6174 297365
</div>

While it is understandable for human readers, it is difficult to interpret for machines. With the hCard microformat, these contact details would be encoded like this:

<div id="contact-data-Henri-Benoit" class="vcard">
    <a class="url fn" href="http://www.amazingweb.de">Henri Benoit</a>
    <div class="org">amazingweb GmbH</div>
    <a class="email" href="mailto:hb@amazingweb.de">hb@amazingweb.de</a>
    <div class="adr">
        <div class="street-address">Ölmühlweg 37</div>
        <span class="locality">Königstein</span>, <span class="region">Hessen</span>, <span class="postal-code">61462</span>
        <span class="country-name">Germany</span>
    </div>
    <div class="tel">+49 6174 297365</div>
</div>

It looks exactly like the previous code for a human reader:

Henri Benoit

amazingweb GmbH

Ölmühlweg 37

Königstein, Hessen, 61462
Germany

+49 6174 297365

But all the data contained can be processed by machines. Google understands the following:

hcard

fn: Henri Benoit
person-name:
family-name: Benoit
given-name: Henri
org:
organization-name: amazingweb GmbH
adr:
street-address:
Ölmühlweg 37
locality: Königstein
region: Hessen
postal-code: 61462
country-name: Germany
tel:
value: +49 6174 297365
email:
value: hb@amazingweb.de
url:

Microformats are also used by Google to present search results in a more useful way. When looking for a product, you will find many web sites listing this product together with a note representing user feedback and review comments. When these data are encoded with microformats, Google can extract them and show directly in the search results which average note was given to this product, how many users gave feedback… An entry in Google’s search results then looks like this:

In this example, the HTML code looks like this:

<html>
    <head>
        <title>amazingweb GmbH</title>
    </head>
    <body>
        <span class="hreview-aggregate">
            <span class="item">
                <span class="fn">amazingweb GmbH</span>
                <span class="url">http://www.amazingweb.de</span>
            </span>
            <span class="rating">
                Bewertung :
                <span class="average">98</span> von <span class="best">100</span>
            </span>
            bei
            <span class="count">457</span> Bewertungen.
            <span class="summary">Just the best Internet agency !</span>
        </span>
    </body>
</html>

 

You can check on this page how you microformats will then look like for Google. You can either type in a URL if the page is already visible on the Internet, or just enter some HTML code.

To use microformats you must do the following:
Add a special class to the tag containing the whole information. This tells what for a micro-format is used e.g.:

<span class="hreview-aggregate">

or:

<div class="vcard">

hreview aggregates is the microformat for consolidated review results. vcard the microformat for contact details.
You can see above, that it is irrelevant which HTML tag is marked with the class. Of course, it actually only makes sense to use container tags such as div or span.

Additionally, all data will be marked individually with classes as defined in the hreview aggregates specification:

  • item for the product itself. item is used as a container and can contain the following:
    • fn for the name of the product
    • url for the web address
  • rating for rating. rating is used as a container and can contain the following:
    • average for the average rating
    • best for the highest possible rating
    • worst for the lowest rating
  • votes for the number of votes taken into account
  • count for the number of reviews into account
  • photo can be used to show an image of the product
  • summary for summary reviews

It should also be noted that if you use count instead votes, then you should theoretically list all reviews. But it is apparently not an issue for Google if it is not the case.

You cannot see in the search results all of the data that are marked with microformat but it still makes sense to mark them, because they are visible in other applications.

JavaScript: Detecting the caps lock key

caps lockOn most web pages where a user needs to register / login, there is a password field where the input is masked. One issue there is that a user cannot see whether what he’s typing is actually what he thinks he’s typing. When logging in, it’s not such a big issue, since if you mistyped your password, you’ll see that you’re not authenticated.

When registering, it’s more of an issue. The common solution is to ask the user to type his password again. If a user pressed a wrong key once, the second time the chances are high that he won’t press the exact same key. Of course there are a few exceptions:

  1. Wrong keyboard layout: German keyboard on a US English OS.
  2. Num Lock on, on a notebook.
  3. Caps Lock on.

For the first one, there isn’t much you can do about it, except taking a snapshot of the keyboard with the webcam and figuring out which layout it is ;-).
For the second one, you can’t handle it in JavaScript. To read the num lock state, you’d need some ActiveX objects or VBScript.
It leaves us the caps lock thing. This one can be handled in JavaScript. If you google for it, you’ll find many posts, articles and forum answers about it. All kind of look like this:

<html>
<body>
<script language=javascript>
function checkCapsLock( e ) {
	var myKeyCode = e.which ? e.which : ( e.keyCode ? e.keyCode : ( e.charCode ? e.charCode : 0 ) );
	var myShiftKey = e.shiftKey || ( e.modifiers && ( e.modifiers & 4 ) );
	var charStr = String.fromCharCode(myKeyCode);
	if ( ( ( myKeyCode >= 65 && myKeyCode <= 90 ) && !myShiftKey ) || ( ( myKeyCode >= 97 && myKeyCode <= 122 ) && myShiftKey ) ) {
		alert( "caps lock on" );
	}
}
</script>
<form>
<input type="password" name="password" size=20 onkeypress="checkCapsLock( event )" />
</form>
</body>
</html>

Basically, it does the following:

  • Checks which character was typed using the which, keycode or charcode property of the keyboard event (whichever is provided by this particular browser.
  • It checks whether the Shift key was pressed either using the shiftKey property or the modifiers flags.
  • It then checks whether the character was between A and Z (capital letters) and shift wasn’t pressed or between a and z and the shift key was pressed. If it’s the case it notifies the user.

This check with the shift key is required because the behavior in most browser is that the shift key turns a capital letter into a non-capital one and vice versa. So if you have caps lock on, you’d get capital letters except if you press shift. So the check whether we get capital letters without shift pressed or non-capital letters with shift pressed.

This method has 3 major problems:

  1. It assumes that you’re actually typing the password (and e.g. not copy&pasting).
  2. Safari (at least on macs) ignores the shift key if the caps lock is on.
  3. It assumes that all letters are between A and Z.

For the first point, you can detect that the user pasted something (vs. typed it in), by using an onpaste on the input box. You could also forbid copy and pasting using:

<input type="password" name="password" size=20 onkeypress="checkCapsLock( event )" onpaste="return false;" />

For the second point, you should probably detect whether you’re running on Safari and disable the check (especially since Safari already shows you with a small icon that caps lock is on). You can detect it this way:

var ua = navigator.userAgent.toLowerCase(); 
if ( ua.indexOf ( 'safari' ) != -1 ) { 
	if ( ua.indexOf( 'chrome' ) == -1 ) {
		// It's Safari.
	}
}

Or this way (assuming Apple doesn’t have a second browser line when you read this post ;-)):

var vendor = navigator.vendor.toLowerCase();
if ( vendor.indexOf('apple') != -1 ) {
	// It's Safari
}

Now let’s come to the third point. If you have a German or French keyboard, you’ll have many more keys available to type in a password. The code above will not notice that caps lock is on if you type Ö or Ä. This is because ö and ä are not between a and z (in terms of character code) but afterwards. So just defining ranges will not work for all languages.

My first idea was to compare the typed character with it’s upper case version. If they are the same, then it’s an upper case character. This works for letters but makes the script think that all numeric and non-alphanumeric characters are upper case letters. So you need an additional condition to determine whether it’s an upper case letter. That’s kind of obvious once you’ve used the trick of converting the character to uppercase: convert it to lower case. If the character is now different, then it was an upper case character.

Here the sample code:

<html>
<body>
<script language=javascript>
function checkCapsLock( e ) {
	var myKeyCode = e.which ? e.which : ( e.keyCode ? e.keyCode : ( e.charCode ? e.charCode : 0 ) );
	var myShiftKey = e.shiftKey || ( e.modifiers && ( e.modifiers & 4 ) );
	var charStr = String.fromCharCode(myKeyCode);
	if ( (charStr.toUpperCase() == charStr ) && ( charStr.toLowerCase() != charStr ) && !myShiftKey ) {
		alert( "caps lock on" );
	}
}
</script>
<form>
<input type="password" name="password" size=20 onkeypress="checkCapsLock( event )" />
</form>
</body>
</html>