1.6 A String Manager Module
An Overview of Our Prototype String Manager and Description of Usage in Various Projects
The Story So Far - Strings!
In the previous articles in this series, I've described some of the reasons why User Narratives are critical to building a successful user experience. I've also talked about some of the technical factors that are involved with gaining control over the words that can form these narratives.
The key concept here is 'strings' - the technical term used to describe the readable characters that can appear on screen or are used internally in the computer code. There are several types of strings and understanding this is critical to establishing a clear idea of how to communicate effectively with users on screen.
Here's a quick review of how strings can be classified:
External Content Strings - Huge amounts of words that provide the real substance of a web site. These are blog posts, tweets, comments, articles, etc. For applications such as MS Word, this is everything the user writes into their files. I'm just going to call these strings 'content'.
External User Interface Strings - Strings you can read on the screen that are created by the developers and provide the cues about how to interact with the application. These are what I will refer to as 'UI strings'.
Internal Code Strings - Hidden strings that are structural elements of computer code. They are inflexible (changing them can break the code) and are used to connect diverse components of the system logic so I prefer to call them 'code nails'. I am of the firm belief that you should never see these nails on screen.
The thrust of my arguments here is that the middle set, the UI strings, are extremely important to defining User Narratives and hence, user experience. When I use the words 'string' or 'ui string' or 'screen string', I'm referring strictly to these UI strings.
Stop the Insani-t() !
In the previous article I used the example of how current usage of the t() function on the Drupal platform systematically prevents the UX designer from having adequate control over User Narratives. This has broader and deeper implications than people might realize. In my opinion, current practices are a wasted opportunity to vastly improve the overall quality of user experience for Drupal applications.
In this article I'm presenting a high level description of an alternative Drupal module that I developed to help me gain back control of User Narrative design. For the record, I've used the same code in non-Drupal contexts also.
Before I get into the details I want to point out that, although this is a technical topic, I will try to explain it in non-technical terms as much as possible. Where I use geek-speak I'll explain what I mean. Apologies also to developers for perhaps not covering certain issues that a purely technical article should touch on and for going slowly over some of the basic ideas that you are very familiar with.
The General Idea
The primary concept behind string management is being able to associate external, human readable, strings with the internal code that is responsible for displaying them on screen. Notice I said 'associate'. That means a strong linkage can be found but they are also easily separated. String management not only requires us to specify when and where a human readable string should be displayed but also requires that these strings are easily modified by the UX designer who generally does not have control over the code.
My critique of how Drupal's t() function is used is that it involves placing these external strings directly into the code which makes them harder to control from the UX design point of view. In other words UI strings are created as 'code nails' which are exposed on the screen. I consider this to be the UX equivalent to exposed nails on a wooden floor and they can cause suffering - which is my definition of non-usability.
Why are these 'embedded' strings like sharp pointy objects of non-usability? Because when they are embedded into the code it is very likely that:
- They are authored by a developer who is typically not in a position to consider the usage contexts very deeply
- They are harder to modify in a systematic way that can support the multiple usage contexts that a UX design must support (there's rarely just one usage context).
Drupal's current answer to this problem is to place copies of these strings into the database so that they can be modified by a string editor. This is primarily done to allow the strings to be translated into a different languages - the t() function was named as a shortcut for 'translation' and it does that task reasonably well.
But the language of the user is just one usage context. So what is available for handling others? Well there is a clever module called 'String Overrides' which squeezes more juice out of the standard t() approach. However this is aimed at special exceptions rather than the entire dimension of textual communication manifest as UI strings - so I don't consider it a practical means of systematic control over User Narrative design.
The database approach merely 'softens' the nails making them 'sort of flexible' which, frankly, is not good enough for my needs. (Is a soft nail useful to anyone?)
So, once again, here's the main point: because the current practices around the t() function do not allow developers to specify the string's usage context to a User Narrative designer, it prevents the designer from speaking to users through idioms (variations of expression that can occur within a given language).
Idiomatic language can make a huge difference in a user's comfort level. Metaphorically, the difference is like sleeping on a bed of nails versus sleeping on memory foam. Idiomatic speech adapts to the mindset and terms that are familiar to a particular user group. In the next sections, I'll describe a technique that I've used to accomplish this.
One Alternative - A String Manager Module
In order to gain proper control over User Narrative design I've written a Drupal module that I call 'String Manager'. This module creates a 'decoupled' association between internal code nails and external UI strings.
To illustrate its usage, I'll use an example from a recent application I've been prototyping that shows how users can register for special exams. The application is designed to allow three kinds of users to submit requests for taking exams: students, their parents and their teachers.
Each of these types of users do exactly the same process for exam registration. The same sequence of input forms appears for each user type:
- specify the course that the exam is for
- verify the student's address
- specify the location where the exam could be taken (this is a nation-wide program)
- specify the date and time that the exam would be booked
In order to make this a smoother process, I was concerned about getting the 'language' just right for each of these user types. It seems absurd to me to provide a prompt to a teacher that says "Please verify your address" when what we mean is "Please verify your student's address".
This is an example of how subtle the craft of User Narratives and string management can be. Saying things in an 'objective' voice is not an option either. Harsh phrases like 'Verify address' contribute to the discomfort people have with computers. Speaking more precisely is how we achieve the 'memory foam' effect.
My perspective on this is that brittle language and other User Narrative failures can lead to siginificant costs for a company or organization in terms of customer support calls, lost business, missed growth opportunities and the consequences of various 'user malfunctions'.
So how do we move from the brittleness of "Verify address" to the user targetted "Please verify your student's address"? I'll describe the technique.
A Look Inside to Compare Techniques
The standard 'Drupal way' of specifying this UI string would typically have code like this:
$ui_string = t('Verify address');
Even if you don't speak PHP, you can easily see the screen string embedded right into this snippet of code.
The string manager method, on the other hand, would look like this:
$str_mgr = get_string_manager('my_module', 'forms');
$ui_string = $str_mgr->t('PROMPT_EXAM_REGISTER_VERIFY_ADDRESS');
In the second line you can see this string:
PROMPT_EXAM_REGISTER_VERIFY_ADDRESS
This is an inflexible nail version of our UI string that is called a 'key'. The key has been constructed to offer some semantic clues about the usage context - i.e. what task the user is doing. It is a PROMPT that appears in the EXAM_REGISTER area where the user will VERIFY their ADDRESS.
As I mentioned earlier, conveying usage context to the UX Designer will be critical for them when shaping the User Narrative. This key is UX designer friendly not user friendly.
How It Works
Let's follow the string manager to find out how it can get the right words up onto the screen for that usage context. The first line of the code snippet was:
$str_mgr = get_string_manager('my_module', 'forms');
$str_mgr is the workhorse here. As far as the code is concerned, $str_mgr is the string manager. We create it using the function get_string_manager.
The information passed into this function (known as arguments) is 'my_module' and 'forms'. This tells the string manager where to find the screen strings. 'my_module' is essentially the directory where the UI string files are kept. In Drupal this can be the folder of any given module. The second argument 'forms' is the type of UI strings that we want to get. In this case the page is showing a form so we've placed its strings into a file that defines all the form strings for 'my_module'.
get_string_manager() is a 'wrapper' function that provides a simple way to create or retrieve the string manager object. The insides of that function are a bit more complex.
function get_string_manager($module='string_mgr', $type='common') {
global $language;
static $_str_mgr = null;
if(!isset($_str_mgr)){
$_str_mgr = new string_mgr($language->language, $module, $type);
}
return $_str_mgr;
}
(Note: alert Drupal developers will notice that the name of this wrapper function is a bit random. I've adjusted it for illustration purposes. The real function name is 'string_mgr_obj' to match my module name 'string_mgr'.)
If you're not a developer, don't worry about what this all means. I'm just showing it to point out that the web site language is also factored into the process. The name of the file that contains the screen strings also takes into account which language its strings are written in.
The real work happens when we call the string manager's own version of t().
$ui_string = $str_mgr->t('PROMPT_EXAM_REGISTER_VERIFY_ADDRESS');
And this version of t() looks like this:
public function t($key) {
$string_array = $this->get_string_resource();
if(!isset($string_array)){
return 'No resource file for: ' . $key;
}
$ui_string = $string_array[$key];
if(empty($ui_string)) {
return ($key);
}
else {
return $ui_string;
}
}
Let's look at the first line:
$string_array = $this->get_string_resource();
In plain English this means "find the resource file that this string manager is using and then give me its string data". The resource file is named 'string_data_forms_en.inc' - you can see that 'forms' (the type specifier) and 'en' (the language specifier) are integrated into it.

Now we have to check that we were able to find our strings:
if(!isset($string_array)){
return 'No resource file for: ' . $key;
}
If we can't find our resource file or if the file doesn't provide the data we want, we tell the developer by sending them a warning string that appears where the UI string should go. It says 'No resource file for: PROMPT_EXAM_REGISTER_VERIFY_ADDRESS'. (Granted this is a brutally geeky bit of text to throw up on the screen but it is basically an error condition that indicates the application is still under development. It is a very large and sharp nail sticking out of the floorboards and end users should never see such a thing!)
Now we attempt to find the part in the array that matches the key that we're using:
$ui_string = $string_array[$key];
if(empty($ui_string)) {
return ($key);
}
How does this work? For non-technical readers, let me explain a bit about how information is passed around in code. Computer code uses chunks of the computer's memory to store information. These bits of memory are like boxes. The trick to knowing how to get the contents of these boxes back is to remember where the box is. Typically this is done by giving the box a label. The code that says '$ui_string' is a box that labelled in a way to say that we're going to put our UI string there. Likewise '$key' is the box where we put 'PROMPT_EXAM_REGISTER_VERIFY_ADDRESS'.
The '=' means we're going to put whatever we get on the right side into the box on the left.
What goes in the ui_string box? Let's look at what's in the resource file - it has lines that look like this:
'MAIN_PROMPT_LOGGED_OUT' => "To register for an exam, please login.",
'MAIN_TITLE_REGISTER_SELECT' => "Summer 2011 Session: Exam Selection",
'PROMPT_EXAM_REGISTER_VERIFY_NAME' => "Please verify your name",
'PROMPT_EXAM_REGISTER_VERIFY_ADDRESS' => "Please verify your address",
'PROMPT_EXAM_REGISTER_VERIFY_PHONE' => "",
(Click here to see an image version)
These lines are the contents of an 'array' which is another way to create a group of boxes with labels on them. Here we see three boxes and each has its own label on the left and contents on the right. So this piece of code:
$string_array[$key]
means give me the contents of the box with the label that can be found inside the '$key' box. It's the equivalent of saying:
$string_array['PROMPT_EXAM_REGISTER_VERIFY_ADDRESS'].
So that means the screen string "Please verify your address" is placed inside the box called '$ui_string'.
But there is the possibility that the key is wrong or the UI string doesn't exist in the resource file. How do we handle that situation? We need to check the contents of $ui_string to make sure that something useful got in there.
if(empty($ui_string))
If the ui_string box is empty then we send back another error indicator string which is displayed on the screen. What is this error indicator? Well, it's the key itself which is 'PROMPT_EXAM_REGISTER_VERIFY_ADDRESS'.
Again this is not a pretty string to have on the screen - it's another exposed nail. But it does tell the developers and the testers that something is missing - so go call the UX designer and tell them to make a UI string for that usage context.
If everything goes right and we have real data for the given key, we send back that UI string:
else {
return $ui_string;
}
Here's a flow chart to show the overall logic of how we arrive at the string that appears on the screen:
More UX Design Control
So the UX designer can gain control of the language that appears on the screen simply by defining the contents of those boxes in the string resource file. Look at this line:
'PROMPT_EXAM_REGISTER_VERIFY_PHONE' => "",
This is a way for the developer to create a 'placeholder' that tells the designer a string is needed on the screen. (You could make a 'to do' list for UX designers this way.) The box exists in the resource file but it is empty. If this happens the string manager will again show the key on the screen. It's an error condition.
However this is slightly different:
'PROMPT_EXAM_REGISTER_VERIFY_PHONE' => " ",
In this case the space character will 'appear' on the screen. This has interesting implications. It means that the developer can go ahead and build default placeholders on behalf of the UX designer but the UX designer does not have to use them. They can effectively turn them off by setting them to be a simple space character (" "). (Note to themers: you could use these empty strings as the logical basis for modifying the HTML markup.)
A Quick Aside to Developers About Technique
The string management approach that I'm describing here is all about how to deliver control over UI string content to the UX designer. I want to mention that it is the key technique, not the use of a file rather than a database for defining the UI strings, that is most important here.
Having said that, I do prefer to put these string definitions into resource files. I'll describe in the next section how the file based method offers a simple way to provide greater idiomatic control of the UI strings.
On the other hand, I can't, however, verify that the file method and the use of static variables to store string manager instances is a scalable solution for web applications. I have no metrics about the impact on memory usage or performance or how it compares to a database method. This is still in the early phases. Anyone who has thoughts or comments on this, please fire them back!
But, whether a file or a database is used, the point is that internal and external strings should be decoupled in order to provide the UX designer with systematic control over the User Narrative. The key is the key!
Getting to the Level of Idioms
The String Manager can greatly assist the UX designer to fine tune the User Narrative because it uses a key system to distinguish between the internal nails and the external UI strings. The construction of the keys can also offer some very useful techniques for reaching highly targeted users - and that was what I said I wanted to do at the outset. Remember, I had three kinds of users: students, their parents and their teachers. How do we speak to them in ways that recognize their role?
The method I use is remarkably simple. First let me point out that another condition that could occur is that a given user may be a student and a parent and a teacher - so that makes things a bit more interesting. My general solution to this is to recognize that at any given time they are playing just one of these Roles. They can change hats.
Given that, I have designed the interface so that they 'changes hats' by clicking on the appropriate tab in their profile. If a user clicks on their profile tab 'My Students' it tells the system that they are wanting to do Teacher related tasks. This means that the application can make modifications accordingly.
In my prototype I harnessed this usage context indicator to modify the UI string key. Here's how:
$mp = $str_mgr->t('MAIN_PROMPT_BOOKING'.'['.$profile_tab.']');
The key is constructed by joining various semantic components together:
'MAIN_PROMPT_BOOKING' is a prefix that describes the general usage context
.'['.$profile_tab.']' adds a further specification to describe the active profile tab.
So the resulting key would look like this:
'MAIN_PROMPT_BOOKING[profile_students]'
And the resource file would have lines like this:
'MAIN_PROMPT_BOOKING[profile_my_exams]' => "To start the exam application process, complete the exam selection form.",
'MAIN_PROMPT_BOOKING[profile_family]' => "To start the exam application process, choose a child's name and then complete the exam selection form.",
'MAIN_PROMPT_BOOKING[profile_students]' => "To start the exam application process, choose a student's name and then complete the exam selection form. You can return here before payment to add another student exam request to your shopping cart.",
You can see that the things we say to each type of user are finely tuned to speak in easily understood terms as well as offering additional information where needed. In the case of the 'profile_my_exams' the student is registering themselves. In the case of 'profile_students' the teacher is registering on behalf of one or more students so we also offer a hint about how to handle multiple students more efficiently.
In fact this key targeting is even more specific in my prototype. The process of registering for exams is actually done in a wizard style with a sequence of forms that the user completes. I actually specify the wizard state (which page are we on) in the keys. They really look like this:
'MAIN_PROMPT_BOOKING_select-exam[profile_my_exams]'
'MAIN_PROMPT_BOOKING_select-exam[profile_family]'
'MAIN_PROMPT_BOOKING_select-exam[profile_students]'
'MAIN_PROMPT_BOOKING_address[profile_my_exams]'
'MAIN_PROMPT_BOOKING_address[profile_family]'
'MAIN_PROMPT_BOOKING_address[profile_students]'
'MAIN_PROMPT_BOOKING_schedule[profile_my_exams]'
'MAIN_PROMPT_BOOKING_schedule[profile_family]'
'MAIN_PROMPT_BOOKING_schedule[profile_students]'
Which means that I can use the same wizard 'container' code to host all the forms for the various steps and dynamically define the key based on the state of the wizard. So in that code we'd see this:
$mp = $str_mgr->t ('MAIN_PROMPT_BOOKING_'.$arg_map[$state].'['.$profile_tab.']');
With this degree of specification of keys, we can say vastly different things to the right user at the right time - all through a common piece of code.
Verticals and Major User Target Shifts
I want to end this (rather lengthy!) article by talking about 'verticals'. This is a term used by product managers and marketers to describe defined target markets. An application might be aimed to meet the needs of particular groups of people - e.g. banking or retail or environmentalists or teachers or even something as specific as 'XYZ Foundation'.
It is often the case that these groups of people will use very specific terms and modes of expression on a daily basis. These are idioms. And as I've said, proper string management and User Narrative design should allow this idiomatic level of expression to be supported. Doing so will reduce the effort that users in these groups will have to make in order to understand how to use the system. It will feel like it was shaped for their needs - like memory foam!
We can use a descriptive label for our target groups as way to pinpoint specific resource files that are tuned to each group. Here's how: remember that 'type' value that we passed to the function that retrieves the string manager? It was the one called 'forms':
$str_mgr = get_string_manager('my_module', 'forms');
By factoring in the targeted vertical we can modify the type parameter appropriately.
$type = 'forms'. '['.$vertical.']'
Where $vertical can be such things as 'banking', 'retail' or for that matter 'dog owners'.
The type parameter is what guided the String Manager to a specific file which, in my original example, was 'string_data_forms_en.inc'. Alternatively we could create different versions of this file for individual markets named like this:
'string_data_forms[banking]_en.inc'
'string_data_forms[retail]_en.inc'
'string_data_forms[dog owners]_en.inc'
So if we wanted to create a general purpose registration module (for example) that can speak in marvelously precise ways to vastly different groups of people we could supply sets of individual UI strings for each of them through their individual string resource files.
An Example of Idioms in Action
I used a technique like this on another application that involved registration of projects for Science Fairs. We found out very late in the cycle that the target students were much younger than we had assumed. It also had to support teachers who were registering on behalf of students. When the client saw the UI strings on the screen they were quite concerned that it was too 'adult' and their kids wouldn't be able to follow the instructions.
Our solution was to create a targeted resource file where we could modify the strings on the basis of knowing that the user was a student. That did the trick.
Conclusions: For UX Designers
Although this is a long article, it only touches the surface of what I consider to be the very important topic of providing a systematic way of giving control of user facing strings to UX designers. The registration tasks that I've described here are just one of countless usages contexts where this principle is applicable, if not desperately needed.
When you are designing your user interfaces, I think it is worthwhile to consider the concept of User Narratives and to imagine effective ways to talk to users. If this concept seems worthwhile you might want to raise this in future discussions about UI strategy.
Conclusions: For Developers
Other developers I've spoken with about this seem to generally agree that it is an interesting and potentially worthwhile approach. It does not yet answer all the questions especially in the context of Drupal. I haven't touched on many details about how to properly integrate this into the Drupal context - e.g. handling placeholders in strings, identifying places where we might use hooks, how we might use overrides to avoid duplicating all string definitions, overall performance metrics and potential issues.
There is also the matter of how this could be done. I think it is something that would work best if addressed in core - which then leads to questions about how to change the legacy code. Frankly I think changing the code is a relatively easy matter. Changing our current practices and favoured techniques as developers is another matter.
My hope is developers will recognize that UX control of User Narrative is an essential component of a platform such as Drupal and that the development community will consider making this adjustment.


