From Developers

Best Practice for Naming String Identifiers

One of the first steps in the internationalization process is also one of the most critical: establishing a naming convention for string identifiers. Identifiers (commonly known as keys) act as placeholders for translated text, providing context for developers and translators. No matter how many languages your project supports, your string identifiers are one of the few components that remain consistent throughout the internationalization process.

Establishing a set of best practices at the start of the internationalization process makes it easier to add new strings and new languages as your project grows. It also serves to help identify localized strings in code and maintain consistency when strings are added, modified, or removed.

This post looks to establish a set of best practices for naming string identifiers. While the exact method will vary based on your localization framework, these practices are general enough to apply to any internationalization project.

1. Use Namespaces

Namespaces are used in software to group related objects, methods, and identifiers. In terms of localization, namespaces perform a similar function while providing additional information about the localized string. For example, a website built using the Model-View-Controller pattern could use namespaces to specify where in the application a particular string appears. For example, a login button on a home page could use the key “user.login_form.login,” where “user.login_form” defines the location of the string and “login” identifies the actual control. Namespaces can be as simple or as verbose as you like, as long as they identify where the string occurs in your project.

2. Be Descriptive

A descriptive identifier accurately reflects the contents of the underlying string, making it easier for developers to recognize the purpose of the string in code.Consider a user login button with two possible identifiers: “user_login_submit,” or simply “submit.” While both represent the same idea, the first option conveys more information about the purpose of the string without being significantly longer.

3. Be Unique

Each identifier in your project is a one-to-one mapping to a string. As a result, using the same identifier for multiple strings can lead to unexpected issues such as translations repeating or appearing in the wrong locations. One possible exception to this rule is when using the same translation in two different locations, although this can be better handled by using Translation Memory to autofill text that’s already been translated.

There are two approaches to generating identifiers: creating a human-readable ID based on the original string, or creating a computer-generated identifier using a hashing algorithm. Take our user login example from the previous section. The ID “user_login_submit” is effective because it reflects the contents of the string. However, another developer working on another login component could accidentally use the same identifier for a completely different element. Human-readable identifiers are easier to recognize in code, but maintaining uniqueness becomes more difficult as projects get larger.

Hashing algorithms, on the other hand, create computer-generated identifiers that are significantly less likely to collide with one another. Hashed identifiers are often generated by combining multiple attributes about the source string, such as the string itself and a description of its context in the program. This way, it’s only possible for two strings to share the same hash if their source strings and contexts are exactly the same.

4. Carefully Consider Using the Source String

Some localization frameworks recommend using the untranslated string as the string identifier. For instance, gettext will let you use “Hello world!” as both the source string and as the identifier. While this approach may seem simpler, not all localization frameworks fully support its use. For instance, .resx files don’t allow spaces when naming string identifiers.

Source strings also limit your ability to modify your translations. What if the original text changes? Not only do you have to update your other translations, but you also have to change each instance of the identifier throughout your project. Also consider that not all languages use the same word for multiple contexts. For instance, the English word “run” could refer to running a marathon just as much as it could refer to executing code. In Spanish, however, you would have to differentiate between “correr” and “ejecutar.” By using “run” as a common identifier, you’ve limited yourself to a single option, even for languages that may use different words depending on context. In this case, you would either need to change the source string to create two different translations or risk using the wrong translation.

It is possible to use source strings successfully in a localization project. For frameworks where it’s the default option, such as gettext, the original string often acts as a fallback if the translated version can’t be found. It’s somewhat more readable than creating a custom identifier, and it removes a layer between the original text and translated text. Check your internationalization framework for the recommended approach to string identifiers.

5. Stick to a Single Language

If it’s supported by your localization framework, your identifiers should be in the same language as your source language. For instance, using Cyrillic characters for your identifiers when your code is written in English is asking for trouble. Sticking to your default language keeps your identifiers readable while eliminating inconsistencies in your code. This practice also applies to characters that might be interpreted by your code as operators, such as single quotes, double quotes, or escape characters.

Building the Foundation for Localization

As one of the first steps in internationalization, creating a naming convention for your identifiers can have a bigger impact on the success of your localization project than expected. A successful naming convention can streamline the localization process, but a poor convention can just as easily hinder it. Taking the time to establish a solid convention today will help prevent hurdles further along in the internationalization process.

Have other questions about localization? Schedule a demo with one of our team members and see how Transifex can help streamline your personal workflow!

Using Grunt and Gulp with Transifex

TL;DR: Both Grunt and Gulp can be used to automate the download of translation files and quickly enable agile localization for your organization. In this post, we’ll cover a few detailed sample configurations and give code examples of how Grunt and Gulp can be used.

What are Grunt and Gulp?

Grunt and Gulp are free, Open Source projects typically used to automate building and deploying of JavaScript applications. They can also be used to help with automating translation tasks for localization. These localization processes can be used for JavaScript projects, or even projects using a different technology stack altogether. The strength of these tools primarily comes from how easy it is to get them up and running.

Before diving into Grunt and Gulp, there are a few prerequisites we’ll need to set up: the NodeJS programming language and the NPM package manager. If you aren’t familiar with these tools, you can find the Getting Started guide for Grunt here and for Gulp here.

After Setting Up Grunt or Gulp

Once you have Grunt and Gulp installed and set up with a default configuration, test and make sure you are able to run them without any task setup (see screenshot below). This is an important step because as you set up your localization process, there will be a number of configuration options. By testing Grunt and Gulp first, you will know that any issues lie in the localization configuration and not the initial project installation.

testing-configuration

Next, you’ll need to identify your source translation files. These could be a number of different formats depending on your project. Often they will be JSON files, but since Grunt and Gulp are merely handling the “grunt work” of building your project, this can easily be extended to other file types such as, XML, PO and HTML formats.

After you have established the source files that need to be translated, you’ll need to decide where to put the resulting translation files from the localization process. Often these files can be located under a subdirectory called “translations.” The important part of this step is to decide a naming convention that includes the locale designation. A locale is usually a 2 or 4 letter code that represents language and region. For example: en is used to indicate the English language generally, and en_US or en_UK is used to indicate the regional use of this language. Your translation files will need to implement this locale designation either in the filename (i.e. index_de.html) or in the directory (i.e. /translations/de/index.html).

Next, we need to decide how our translators are going to receive the source files and then send back the translated files. The easiest way to do this is to use a Translation Management System like Transifex to help manage the linguistic portion of the localization process. Transifex provides plugins to both Grunt and Gulp which make configuration much easier. You can also use other approaches such as file transfer using sFTP or scp. In this post, we will focus on using Transifex for our translation management.

For both Grunt and Gulp, Transifex has a number of community-created plugins. It’s important that you set up a project, upload the initial resource, and assign translation languages and teams in Transifex before continuing the configuration of Grunt and Gulp. If you don’t have a fully setup project in Transifex, be prepared to work through some minor “gotchas.”

Using Grunt-Transifex

When you first use the grunt-transifex package, you’ll be prompted to enter your credentials which will be saved to a “.transifexrc” in your home directory. The rest of the configuration occurs inside the Gruntfile.js. Below is an example of this setup for at test project.

// Project configuration.
  grunt.initConfig({
  ...
    transifex: {
      "grunt-test": {
        options: {
          targetDir: "./translations", 
          resources: ["samplejson"],
          languages: ["fr"],
          filename: "sample-_lang_.json"
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-transifex');

Our project directory structure looks like this:

├── Gruntfile.js
├── package.json
├── sample.json
└── translations
    └── sample-fr.json

Don’t forget to load the grunt-transifex task at the end of the Gruntfile.js !

Now, our example task can be run like this:

$ grunt transifex
Running "transifex:grunt-test" (transifex) task
>> Successfully downloaded samplejson | fr strings into translations/sample-fr.json

Done, without errors.

And our Transifex project looks like this:

grunt-test-in-transifex

Using Gulp-Transifex

For the ‘gulp-transifex’ package, we will add our credentials to a “config.json” folder stored outside of the project directory. The rest of the configuration occurs inside the Gulpfile.js. Below is an example of this setup for a test project.

var config = require('../config.json');
var env = config.env
var options = {
    user: config.transifex[env].user,
    password: config.transifex[env].password,
    project: 'gulp-test',
    local_path: './translations/'
}

var transifex = require('gulp-transifex').createClient(options)

gulp.task('upstream', function(){
    return gulp.src('./sample.json')
        .pipe(transifex.pushResource())
});

gulp.task('downstream', function(){
    return gulp.src('./sample.json')
        .pipe(transifex.pullResource())
});

Our project directory structure looks like this:

├── gulpfile.js
├── package.json
├── sample.json
└── translations
    └── fr
        └── sample.json

Now our example tasks can be run like this:

$ gulp upstream
[22:24:01] Using gulpfile ~/wip/node-v4.2.1/gulpjs/gulpfile.js
[22:24:01] Starting 'upstream'...
[22:24:01] updating: sample.json
[22:24:02] no changes done
[22:24:02] ✔ sample.json Uploaded successful
[22:24:02] Finished 'upstream' after 1.74 s

$ gulp downstream
[22:24:35] Using gulpfile ~/wip/node-v4.2.1/gulpjs/gulpfile.js
[22:24:35] Starting 'downstream'...
[22:24:36] Downloading file: sample.json
[22:24:36] ✔ fr sample.json Downloaded:
[22:24:36] File saved
[22:24:36] Finished 'downstream' after 1.46 s

And our Transifex project looks like this:

gulp-test-in-transifex

Now, you’ll be able to automate the download of translation files.

Thanks to Marcos Hernández and tonyskn for their contributions to these projects.

Localization Process for Javascript Web Applications

Last week, Transifex software engineer, Matt Jackowski (@mjjacko) spoke at Bitmatica’s Code and Cookies event in San Francisco, sharing his insights about Javascript development. For readers who couldn’t make the event, Matt recaps the localization story shared at Code and Cookies!

“Localization can often be a scary process, especially if you haven’t done it before. In this post I’ll walk through the a basic agile process that can be used for any Javascript web application.”

An Agile process for Javascript development

When we are building Javascript applications, we move from ideation to application very quickly. Therefore, we need an agile approach that can be used to quickly enable us.

Plan

We often skip this step and go directly to coding. But it’s important to pause a minute and consider the decisions we must make before building our app.

  1. Internationalization Support – We’ll want to leverage existing i18n functionality either in our framework, or pick a library that supports multilingual functionalities. Unfortunately, support for i18n is not as mature as most other languages. Depending on the structure of your application, it might be difficult to find a good supporting i18n library. ReactJS and AngularJS currently have the best support, however this support comes from external libraries. Some integration will be needed.
  2. Linguistic Guides – During planning, it’s a great time to define style guides for your translated copy. A typical translation style guide contains the application’s standards and expectations that must be followed when writing copy.

Javascript-Localization-Planning

1: NodeJS i18n will allow you to process formatting on the server-side. This is great from a performance perspective, but can be tricky with client-side views.

2: Build tools can be used in place for a more formal i18n approach. However, this approach primarily works well for static content.

Build

There are 2 key parts to build step:

  1. Tagging the code– For most frameworks, we can use our templating language to designate the text that needs translations. Similarly, dates and numbers should be passed to an internationalization format before displayed in the view.

  1. Translation – As soon as we start tagging strings for our text, we also want to start sending this to our translators. Waiting until the entire site is complete and THEN doing translation, isn’t very efficient and certainly isn’t agile.

Integrate

Now we want to bring the code and the translations together into a working application.
Reliable automation is the key in this step.

We can accomplish much of this automation with Grunt and if we integrate with a translation management tool (like Transifex), that setup allows us to run our application in other languages before translation is fully completed.

Integration-Iteration

Finalize

The last step in this process is our final quality check. Here we can run any time consuming acceptance tests, keeping in mind that we’ll likely need to run these multiple times for each language. Also on the linguistic side, it’s recommend that a quality check should be performed by professional translators who really understand our application.

Next Steps

Here are some additional resources to help you get started:

Matt shared some great tips for companies interested in taking their web apps to a global audience. For more information about localizing Javascript web apps, don’t hesitate to visit our website at www.transifex.com or request a personalized demo with one of our team members today!

Designing a Localization Friendly User-Interface: Managing Locale Specific Content (Part 2)

Continuing from our previous post about best practices for global UI design, we wanted to share insight about managing locale specific content. We’ve also opened up the comment section so feel free to share your stories relating to the early stages of application design and localization.

Identify and Plan for Locale Specific Variations

There are a number of locale specific variations that must be addressed in the user interface so your users can connect with your software in a language and locale that feels native to them. These include:

Calendar, Date, Time, Currency and Numbers

Numerical data, such as date, time, and currency, as well as calendar-related events are displayed differently from language to language. When users arrive at your website or use your application, the last thing they want to do is convert prices into local values to make decisions. Your user-interface must be designed to display locale specific data correctly in order to offer a positive user experience.

The example below shows how, based on the user’s chosen locale, calendar and currency are displayed. In this example, it is achieved by defining language specific javascripts.

Localize-Time-and-Date

Sorting Rules for Different Languages

Languages have their own sorting rules, so if your application’s user interface displays data in a sorted view, it should be in a locale-aware manner. This implies that the proper locale must be passed to the “sort” API used by your application and the sorting algorithm should handle locale specific rules. For instance, your menu items, if sorted alphabetically, may not hold the same value for all languages. It would be better if you have them sorted by functionality.

The below examples will help you understand how different sorting rules followed by different locales can affect your application.

Example 1: An application that sorts the names Ändrè and Andrew produces different results depending on the locale.
German: Ändrè is at the beginning
Swedish: Andrew is first

Example 2: Sorting is even more complicated in Japanese since it has thousands of characters and four different types of written characters. The special nature of kanji characters and their pronunciations make it extremely hard if not impossible to sort accurately. It is not possible to digitally guess the pronunciation, so it is a good idea to ask the user for the pronunciation. You will find that most of the software applications in Japanese adopt this approach.

The snapshots below show how amazon.com addresses this issue in the Japanese version of their website. In their new user registration form, there is an additional field for Japanese which captures the pronunciation in katakana for the name field from the user. With the kanji name and the correct pronunciation, it becomes easier to sort the user information in Japanese.

Amazon-Localization

Icons and Imaging

Icons may hold different meanings in different cultures. Icons that are accepted in a certain culture may even be offensive in another, so design should aim to include universally understood and accepted icons wherever possible.

You could choose to localize icons for every target locale (culture) or internationalize them by restricting your icon usage to universal symbols. The icons in the example below have become standards because of their widespread usage, but are not necessarily approved or licensed by any international committee.

Localization-Images

There are few icons standardized by ISO and you can find them under their user interface standards.

Images with Embedded Text

When localizing content, one of the most commonly overlooked texts are those embedded within images. To ensure that embedded text is not displayed in the source language, and instead in the language of the user, use as little embedded text as possible. If you must have text embedded within images, using SVG files for your graphics may come in handy because SVG supports text that can be easily localized.

Embedded-Text-Images-Localization

SVG files are XML, which makes the text strings embedded within the graphics to be easily accessed for translation using XSLT. Since XLIFF files are XML, the translated text strings can be assembled back into SVG files using XSLT. The diagram below shows the localization workflow for translating text within SVG image files.

Implement Responsive Design

With the increasing number of mobile users worldwide, developers are compelled to design applications and websites that can be viewed and used on a variety of mobile devices like smartphones and tablets. This necessitates design practices that allow the user interface to expand and contract in size based on the user’s screen resolution, also known by the term “Responsive Design.”

Enabling Proper Localization

The responsive design technique can complement the localization requirements of your user-interface because the goals are similar – handle fluctuations in size. When you design the UI to accommodate multiple device sizes and resolutions, your user interface is designed to be flexible for dynamic expansion and contraction. It is now becoming increasingly popular to look at “Responsive Design” as tied up with “Localizable UI Design”.

You can verify the responsiveness of your design and get a good idea of how your design looks on multiple screen sizes by using emulators and virtual machines or testing services and tools like BrowserStack where you can generate screenshots at actual device sizes. There are also community efforts available like OpenDeviceLab where you will find shared community pools of internet connected devices for testing applications and websites.

Have other tips about designing a localization friendly user interface? Comment below or learn more about localizing your product by contacting our localization team and request a personalized demo!

Designing a Localization-Friendly User Interface (Part 1)

More and more businesses are expanding into new foreign markets, and are realizing that in order to be successful, the target market’s local customs must be recognized, respected, and reflected in any digital content. In this two part series, we’ll discuss the challenges involved in creating a user-friendly interface and present a few best practices that will meet (and exceed) the needs of your multilingual users. Part 1 will focus specifically on the importance of a flexible and dynamic layout for overall consistency.

Design Your Layout to be Flexible and Dynamic

Some languages are more verbose than others, meaning your design must account for text expansion and contraction in translated languages. The general rule is to plan for an average of 35% text expansion. The example below illustrates how the character length of a text string will vary from language to language.

 

EnglishTransifex makes localization easy.
DutchTransifex maakt lokalisatie gemakkelijk.
Chinese SimplifiedTransifex 使得本地化容易。
HindiTransifex स्थानीयकरण आसान बनाता है।

Development frameworks provide features that can help you to program dynamic UI expansion and contraction. Although the intricacies may vary based on the framework, the design considerations detailed below are mostly common.

1. Sizing Metrics

Your user interface window and elements should be laid out relative to each other without fixed positions or sizes in order to allow them to realign as required for every language.

  • Container: Your design should let the main container/wrapper adjust to the size of the contained elements that may change with every language.
  • Elements: Avoid using fixed width/height constraints for buttons, labels, text fields, images, menus, dialog boxes and all other elements of the interface. Setting fixed sizes may lead to your text appearing cropped or create excessive empty space in some translated languages.

2. Right to Left Flip

In certain languages like Arabic and Hebrew, text is read from right-to-left (RTL) necessitating your entire design to be flipped. A modular design approach will come in handy while accommodating RTL languages. For example, the homepage of Facebook is designed to flip neatly for Arabic and other RTL languages as shown below:

Facebook-EnglishFacebook-Arabic

When localizing, the below elements should not be mirrored in RTL languages:

  • Images, except when they correspond to direction (example: arrows)
  • Graphs (x– and y–axes are always shown in the same orientation in all languages)
  • Music notes and sheet music
  • Clocks
  • Video controls and timeline indicators

Most of the recent native frameworks are now mirroring aware, making it easier to create a mirrored layout with very limited code changes.

Example 1: iOS Support for RTL

Starting from iOS 9, there is comprehensive support for a mirrored user interface as listed below:

  • Make use of base internationalization and Auto Layout
  • Standard UIKit controls automatically flip in a right-to-left context
  • UIView defines semantic content attributes that allow you to specify how particular views should appear in right-to-left context. You can get the layout direction of an instance of UIView by calling the userInterfaceLayoutDirectionForSemanticContentAttribute, method as shown: if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:view.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) { … }
  • In iOS, using natural text alignment aligns text on the left in a left-to-right language, and automatically mirrors the alignment for right-to-left languages. For example, if you set the alignment of an NSMutableParagraphStyle object using the setAlignment: method, you should pass NSNaturalTextAlignment as the parameter.
  • UIImage offers the method imageFlippedForRightToLeftLayoutDirection, which makes it simple to flip an image programmatically when appropriate.

You can refer to the for more details on iOS support for internationalization.

Example 2: MSDN support for RTL (mirroring awareness)

You can activate mirroring and also have full control over when to enable mirroring – such as per process, per window or per device context, using the new mirroring APIs offered. You can refer to the developer reference guidefor full details.

3. Line Breaking and Word Wrapping

Latin and most Western languages use spaces to separate words. However, East Asian languages such as Japanese, Chinese, and Korean may not use spaces to separate words. Instead they rely on syllable boundaries. This implies that text wrapping may not always be done at spaces. Consider an example English sentence “I am a student.” The white space between words is often considered a delimiter for word-wrapping, but when translated to Chinese, sentences are written as strings of Chinese characters without any delimiters between them, as shown below:

 

EnglishI am a student.
Chinese Simplified我是一名学生。

For character-based languages, your application cannot rely on the usual line-breaking and word-wrapping algorithms for text parsing or display. Your routines must be able to handle text without spaces or punctuations, especially when wrapping for layout. This is easier said than done because in many cases you will find that linguistic expertise is required to handle this correctly.

For example, your text parsing routine for Chinese will require a specific Chinese word segmentation algorithm. There are many widely used algorithms and you can choose based on your application’s needs. Most of the word segmentation algorithms can be categorized under two types: lexical knowledge based methods (faster but less accurate) and linguistic knowledge based methods (highly accurate).

Select Fonts for Global Consistency

Although it seems like a minor step in the localization process, font choice can dramatically impact the layout and readability of your localized user interface and can also result in an inconsistent look and feel. To avoid picking the wrong font for your product, keep the following font-related notes in mind.

Fonts Must Provide Multi-Lingual Support

First and foremost, you need to choose a font that is Unicode-compliant, meaning the characters and text of your application have been encoded in a way that enables the exchange of text data internationally. It’s risky to assume that all fonts claiming to be Unicode will address all font-related issues in your multilingual interface. Some Unicode fonts may only support characters at the correct code-point and may not have usable characters for all code-points.

To prevent issues with Unicode fonts, refer to the recently introduced Google Noto Fonts, which aim to provide pan-language harmony and can be used for web as well as desktop applications. There are many resources* available online where you can search for a list of fonts that are Unicode compatible and the characters they support.

Font Sizes Vary from Language to Language

A 12px font may be readable in English with no issues, while the same font may be extremely difficult to see when translated to Japanese. That leaves us with the question, is there an ideal multilingual font size? Unfortunately, there isn’t an ideal font size that works for all languages across the globe, but one option is to implement a dynamic layout to handle varying text size. If you wish to maintain a globally consistent layout, you could also adopt a variable font size option to give a better user experience across languages and devices. We’ll use the following example to explain further.

If you are designing a website, then it is common practice to use separate language-specific style sheets and define specific styles to suit every target language. In the example below, the font-size for the text “I am a student” will vary, based on the language, as specified in 2 different stylesheets style.en-US.css and style.ja-JP.css.

 

English (style.en-US.css)Japanese (style.ja-JP.css)
p { font-size: 14px; }p { font-size: 16px; }
I am a student我是一名学生。

Manage Content for Different Global Markets

Now that we’ve discussed how to lay out your website to create a localization friendly user interface, we must address how to manage your content for different global markets. Because that’s going to take more than a few sentences, we’re going to save it for later this week, so check back for part 2 of our designing a localization friendly user-interface series!

Automating Transifex Updates with Jenkins

Jenkins, an Open Source continuous integration server, supports Transifex projects using a few simple shell commands. In this post, we’ll show you how to configure Jenkins to automatically synchronize with a Transifex project.

Jenkins Basics

Jenkins automates the process of running repeating tasks such as executing scripts or building software packages from source. Jenkins also monitors the status of running jobs, provides alerts, and integrates with external build services such as Maven and Ant.

Each job is assigned a workspace, which is a directory where copies of the job’s source files are stored. For instance, in Ubuntu, these directories fall under /var/lib/jenkins/workspace/. If your project is managed by source control, Jenkins will automatically pull the latest revision from your source repository. Otherwise, you’ll need to copy your source files to the job’s workspace after creating the new job in Jenkins.

Integrating Transifex

One of the benefits of Jenkins is that it can execute shell scripts during the build cycle. This makes it easy to integrate Transifex by simply calling the Transifex Client. Using tx push and tx pull, you can have Jenkins automatically update your source and translation files as the project is built.

Configuring Your Project

Before Jenkins can use the Transifex Client, it has to be able to read your project’s configuration. By default, the Transifex Client stores its configuration settings on a per-user basis. On a Linux environment, this is typically /home/<user>/.transifexrc. However, Jenkins executes commands as the Jenkins user. For instance, in Ubuntu, this means that any user-specific configuration files for Jenkins should be stored in /var/lib/jenkins/.

To configure your project for Jenkins, copy your transifexrc file to /var/lib/jenkins/.transifexrc. If you manage multiple jobs with Jenkins, you can store .transifexrc in the job’s workspace. Keep in mind that .transifexrc contains sensitive account information and shouldn’t be viewable by anyone but the Jenkins user.

You’ll also need to make sure that your project is configured to use the Transifex Client. Running tx init in your project folder will generate a .tx directory containing a configuration file. This configuration file tells the Transifex Client how to connect to your localization repository. Unlike the .transifexrc file, we recommend checking the .tx directory and config file into your source control repository. That way, when Jenkins checks out the project, it has everything it needs to connect to Transifex.

Configuring the Jenkins Job

Once you’ve granted Jenkins permission to access your Transifex project, you can configure your Jenkins job. As the job executes, Jenkins will call the Transifex client and update the project’s localization files.

Jenkins project

Navigate to the job’s configuration page. Under the “Build” section, click “Add build step”, and select “Execute shell”. A text box appears, allowing you to enter commands. Enter the following commands and click Save:

tx push -s
tx pull -a

Jenkins configuration

The next time a build occurs, Jenkins uses the Transifex Client to not only push your source files to Transifex, but also to pull the most recent translations into the build.

Modifying Downloaded Translation Files
If you need to further modify your language files before including them in the build, you can run additional commands by either adding new statements or by adding a new build step. You can perform any command-line task supported by the operating system running Jenkins, such as converting between file types or moving files to a separate directory. For instance, if you haven’t set up your project using a file expression, you’ll have to manually move your translation files into the target directory.

Handling Problems

Integrating Transifex with Jenkins is fairly straightforward, but problems can arise during the process. Two issues in particular are extremely common: file permissions, and build failures due to the Transifex Client.

File Permissions

As we mentioned earlier, the Jenkins user must have access to project files. If permissions aren’t properly assigned, this could result in opening Jenkins or your Transifex project to outside access. When copying files to a Jenkins workspace, make sure those files can only be modified by the Jenkins user. If you’re copying a .transifexrc file to the Jenkins workspace, make sure the file can only be read by the Jenkins user.

If you manage your project using version control, Jenkins will automatically fetch and store files as the Jenkins user. However, this does mean providing Jenkins with access to your source repository. Some version control systems such as Git allow you to use file permissions to prevent users from modifying checked out files. Alternatively, hosting providers such as GitHub provide advanced controls for limiting users’ ability to push to repositories.

Build Failures

Common causes of build failures are a missing .transifexrc file, or Jenkins not having the right permissions to read your project’s files. Make sure your .transifexrc file is not only stored in Jenkin’s configuration directory, but is also readable by the Jenkins user.

You should also make sure that your project configuration and source files can be read by Jenkins. If you copied your source files manually, you’ll need to grant permission to the Jenkins user to read and modify files in the project directory.

If the build continues to fail, try running the Transifex Client as the Jenkins user and viewing the immediate output. After logging into the Jenkins server, use the sudo command to temporarily switch to the Jenkins user:

$ sudo -iu jenkins cd /path/to/workspace && tx push -s

Any output from the Transifex Client will appear directly in the console. You can also view output from the Transifex Client by opening Jenkins, navigating to your project’s build history, and clicking on Console Output for the last build.

Additional Resources

Using the Transifex Client through Jenkins is the easiest way to ensure your builds are using the latest translations. You can also install the Transifex Plugin for Jenkins, which simply adds a hyperlink to the Transifex project page on the Jenkins job page.

Bridging GitHub and Transifex with Txgh

We previously showcased Txgh, a tool for automatically bridging GitHub with Transifex. Txgh is an Open Source Sinatra server that uses webhooks to trigger updates between GitHub and Transifex. It allows both developers and translators to share localization updates in an automated and seamless way. Changes to either a GitHub repository or a Transifex project are pushed to the Txgh server, which forwards them to Transifex or GitHub respectively.

Txgh has seen a lot of updates since its earlier release. Txgh can now run as a Heroku dyno for quick and easy deployment. The new release also supports branches, rather than limiting changes to master. Transifex will manage future updates to the project, but the project will remain open sourced. In this post, we’ll walk you through setting up the latest version of Txgh.

Getting Started with Txgh

Txgh requires an existing Heroku account. If you don’t already have one, you can sign up for a free account here. Txgh requires Ruby and Bundler, which you can install using this guide. Note that the Txgh project uses Ruby version 2.0.0, which we recommend installing using a version manager.

Download Txgh

To download Txgh, simply clone the latest version from GitHub:

$ git clone https://github.com/matthewjackowski/txgh.git
Cloning into 'txgh'...
remote: Counting objects: 162, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 162 (delta 0), reused 0 (delta 0), pack-reused 159
Receiving objects: 100% (162/162), 34.75 KiB | 0 bytes/s, done.
Resolving deltas: 100% (68/68), done.
Checking connectivity... done.

Configure Txgh

Txgh needs access to a GitHub account, a Transifex account, and information about the Transifex project being managed. Earlier versions of Txgh used a YAML file to store user credentials. With the latest version of Txgh, this information is stored as environment variables in Heroku. We’ll add these environment variables after deploying the server to Heroku.

In the meantime, we can provide Txgh with our Transifex project’s configuration. Copy the config file stored in your project’s .tx directory to Txgh’s config directory and rename it to tx.config. You should see three files in the directory: key_manager.rb, key_manager_ruby.rb, and tx.config. For more information on setting up your project configuration, click here.

Deploying to Heroku

Heroku provides a command-line tool for interacting with applications. When you create a new application, Heroku creates a remote Git repository (with a branch named heroku), which you can then push your code to. Change your current directory to the Txgh project’s root directory and enter the following command:

$ heroku create
Creating nameless-eyrie-4025... done, stack is cedar-14
https://nameless-eyrie-4025.herokuapp.com/ | https://git.heroku.com/nameless-eyrie-4025.git
Git remote heroku added

By default, Heroku provides a randomly generated name, but you can supply one as a parameter. Once the new application has been created, you can deploy your app by using git:

$ git push heroku master
Counting objects: 156, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (71/71), done.
Writing objects: 100% (156/156), 33.84 KiB | 0 bytes/s, done.
Total 156 (delta 65), reused 155 (delta 65)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Ruby app detected
remote: -----> Compiling Ruby/Rack
remote: -----> Using Ruby version: ruby-2.0.0
remote: -----> Installing dependencies using bundler 1.9.7
...
remote: -----> Compressing... done, 18.3MB
remote: -----> Launching... done, v4
remote:        https://nameless-eyrie-4025.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy.... done.
To https://git.heroku.com/nameless-eyrie-4025.git
 * [new branch]      master -> master

You can verify the success of the deployment by opening the Heroku dashboard in your web browser and navigating to the newly created dyno:

Heroku dyno

Updating the Configuration

Before you can start pushing updates between GitHub and Transifex, you’ll need to provide the Heroku app with information on how to access each service. Txgh uses a set of environment variables to manage connections between each service. The name and description of these variables is shown in the table below:

VariableDescriptionExample
transifex_project_config_tx_configLocation of your Transifex project's configuration file relative to Txgh's root folder../config/tx.config
transifex_project_config_api_usernameYour Transifex username.txuser
transifex_project_config_api_passwordPassword to your Transifex account.1324578
transifex_project_config_push_translations_toName of the GitHub repository that Txgh will push updates to.ghuser/my_repository
transifex_project_config_push_translations_to_branchGitHub branch to update.heads/master
github_repo_config_api_usernameYour GitHub username.ghuser
github_repo_config_api_tokenA personal API token created in GitHub.489394e58d99095d9c6aafb49f0e2b1e
github_repo_config_push_source_toName of the Transifex project that Txgh will push updates to.my_project

There are two ways to apply these to your Heroku app:

  1. Add the environment variables through Heroku’s web interface.
  2. Create a local file containing your environment variables and apply it using rake.

Add Environment Variables Through the Heroku Dashboard
Open the Heroku dashboard in your web browser. Click on the Settings tab and scroll down to the Config Variables section. Click Reveal Config Vars, then click Edit. You’ll have access to the application’s existing variables, but more importantly you can add new variables as shown in the screenshot below. Add the variables listed above and click Save.

Config vars

Note that the RACK_ENV variable defaults to production, but in order for it to work with Txgh we need to set it to test.

Add Environment Variables Using txgh_config.rb
The txgh_config.rb file stores our environment variables inside of the Txgh folder. To create the file, copy and paste the following into a new text file. Replace the placeholder values with your actual values and save the file in the config directory as txgh_config.rb.

# 'test' only ENV['RACK_ENV']
config_env :test do
    set 'transifex_project_config_tx_config', './config/tx.config'
    set 'transifex_project_config_api_username', 
    set 'transifex_project_config_api_password', 
    set 'transifex_project_config_push_translations_to', 
    set 'transifex_project_config_push_translations_to_branch', 'heads/master'
    set 'github_repo_config_api_username', 
    set 'github_repo_config_api_token', 
    set 'github_repo_config_push_source_to', 
end

To apply the changes to your Heroku dyno, use the rake command:

$ rake config_env:heroku
Running echo $RACK_ENV on nameless-eyrie-4025... up, run.2376
Configure Heroku according to config_env[test]

=== nameless-eyrie-4025 Config Vars
LANG:                                          en_US.UTF-8
RACK_ENV:                                      test
github_repo_config_api_token:                  489394e58d99095d9c6aafb49f0e2b1e
github_repo_config_api_username:               ghuser
github_repo_config_push_source_to:             nodejs-test
transifex_project_config_api_password:         12345678
transifex_project_config_api_username:         txuser
transifex_project_config_push_translations_to: ghuser/nodejs-test
transifex_project_config_tx_config:            ./config/tx.config

This command updates the configuration of your Heroku app with the values specified in txgh_config.rb. If you have any issues running the rake command, run bundle install in the Txgh project’s root directory. This compiles and installs the Ruby gems required by Txgh. Once the install completes, run the rake command again.

Since this file contains sensitive information, you should avoid committing it to your Heroku repository or to your GitHub repository.

Once the rake command has completed successfully, open the Heroku dashboard, navigate to the application’s settings and click Reveal Config Vars.

Final Configuration Steps
The last step is to change the value of the RACK_ENV variable. By default, Heroku sets the value of RACK_ENV to production. However, we recommend testing Txgh by setting this value to test. If you haven’t already, open your application’s environment variables in a web browser and change the value of RACK_ENV from production to test. When you’re ready to deploy, you can change this value back to production.

config-vars-fixed

Meanwhile, check the values of your other variables. If any values seem incorrect, you can edit them in your browser or edit and re-apply the txgh_config.rb file using rake. Once everything looks good, you can add your webhooks to Transifex and GitHub.

Connecting Transifex and GitHub to Txgh

Txgh synchronizes your Transifex and GitHub projects using webhooks, allowing Txgh to respond immediately to changes in either service. The webhook URLs follow the format https://.herokuapp.com/hooks/, where is the name of your deployed Heroku app and is either “transifex” or “github.” For instance, we’ll use the following URL with Transifex:

https://nameless-eyrie-4025.herokuapp.com/hooks/transifex

and the following URL with GitHub:

https://nameless-eyrie-4025.herokuapp.com/hooks/github

Connecting Your Transifex Project
Open your project in Transifex. Under More Project Options, click Manage.

Transifex manage

In the Features section at the bottom of the screen is a text box titled Web Hook URL. Enter in the URL you created from your Heroku app, then click Save Project. Secret keys are currently unsupported, so leave the field blank for now.

Transifex webhook

Connecting Your GitHub Repository
Connecting a GitHub repository is similar. Open your repository in a web browser and click Settings.

GitHub repository

Under Webhooks & services, click to add a webhook. You may be asked to confirm your password. Enter the Heroku app URL for the Payload URL and change the Content type to application/x-www-form-urlencoded. Just like with Transifex, keep the Secret token field blank.

GitHub add webhook

Click Add webhook to create your new webhook. GitHub will ping the URL to test its validity. You can check whether the ping was successful by reloading the page.

Next, we’ll test out the integration by moving translations between GitHub and Transifex.

Testing It Out

To test the integration, we’ll push a new commit to GitHub, then we’ll use the new commit to update translations in Transifex.

First, add a new string to the language source file in your Transifex project. Save your changes, then push the code to your GitHub repository. The push will automatically trigger the webhook. You can verify that webhook was successful by opening GitHub in a browser, navigating to the Webhooks & services, clicking on the webhook URL, and reviewing Recent Deliveries.

Recent deliveries

If successful, you should see the new source strings in your Transifex project:

Transifex new strings

Update the translations in Transifex. Back in your GitHub repository, review the latest commits. You should see a commit from Transifex with the latest updates to the target language:

GitHub results

Going Forward

This project represents an early look into automated integration with GitHub. Going forward, we plan on making it easier to setup automated integration with code repositories and other third-party services. If you have any questions, feel free to contact a Transifex team member for a personalized demo or leave a comment below.

Integrating Transifex with Bamboo (Part 2)

Earlier this week, we shared a post about integrating Transifex with Bitbucket. In addition to Bitbucket, Atlassian offers a continuous integration server called Bamboo. Bamboo lets you automatically setup, build, and deploy projects. Integrating Transifex with Bamboo lets you update your project’s localization files in an automatic and seamless way.

The Bamboo workflow is split into five components:

  • Tasks are the individual work units in a Bamboo project. A task is any discrete action such as checking out a project from source, executing a script, or, in our case, calling an external program such as the Transifex Client.
  • Jobs are used to control multiple tasks. They specify task execution order, enforce requirements, and collect artifacts created by tasks.
  • Stages represent steps within a build process. As an example, you might use three separate stages for compiling, testing, and deploying code.
  • Plans organize work units, builds, and tests. They also allow you to link to code repositories, generate notifications, configure permissions, and specify variables.
  • Projects organize multiple plans under a single logical unit.

For more information on Bamboo’s structure, click here to go to the Atlassian documentation site.

Installing Bamboo

Bamboo can be installed on a server or hosted on a cloud instance. This article assumes Bamboo is being installed on a server.

Navigate to Bamboo’s download page and download the executable for your server operating system. From there, navigate to Atlassian’s documentation site to find more information on installing Bamboo for your particular OS. When the installation is finished, access Bamboo by opening a web browser and navigating to http://<server IP address>:8085. You may need to open the port in your server’s firewall.

Bamboo requires a valid license before it can start. If you haven’t already, generate a trial license by logging into your Atlassian account and requesting an evaluation license. After Bamboo verifies the license, it will ask you to create a new administrator account for the Bamboo agent. Once the account setup is complete, you’ll be greeted by the Bamboo home screen.

Creating a New Project

To create a new project, click the Create button at the top of the screen, then click “Create a new plan.” This brings you to the plan configuration screen where you can enter details about the new project. We’ll create a new project for our Node.js app:

Make New Project in Bamboo

 

Click “Configure plan” to create the new project. Along with the new project and plan, Bamboo creates a default stage and a default job. Since we specified a Bitbucket repository, Bamboo automatically creates a task to retrieve the source code from the repository. Next, we’ll add tasks that synchronize the source code with Transifex using the Transifex Client.

Adding a Task Command to Bamboo

To run the Transifex client during the build process, we need to add a new task to our default job. Bamboo supports a variety of tasks for generating logs, deploying to cloud services, or even executing Maven or Ant tasks. In this case, we’ll use the Command task to run a command that calls the Transifex Client.

Before we do this, we need to register the Transifex Client as an executable. Navigate to the Bamboo administration page by clicking the gear icon in the top-right corner of the screen, then click “Overview”. Under the “Build Resources” section on the left-hand navigation menu, click “Server capabilities.” This will show you the actions available to the Bamboo server, including the executables available to your build plan.

Scroll down until you see the “Add capability” section. Under “Capability type,” select “Executable,” then select “Command” for the type. Enter an identifier for the command, followed by the path to the executable that you want to run (in Ubuntu Linux, the Transifex Client executable is found at /usr/local/bin/tx). Click the “Add” button to register the new executable with Bamboo:

Adding Transifex Client to Bamboo

Navigate back to your project by clicking “Build” at the top of the screen, then “All build plans.” Edit the project by clicking the pencil icon on the right-hand side of the screen, across from the project name. Under “Plan Configuration”, click on the default job. Switch to the “Tasks” tab, then click the “Add task” button. Bamboo prompts you for the type of task to add:

Add a New Task

Click the “Command” task and select the “Transifex Client” executable we defined earlier. In this task, we’ll push the latest source files to Transifex. Under arguments, type “push -s.” Add a small description, then click Save. Repeat this process to create a new command that pulls the latest translations from transifex using the command tx pull -a.

Note that you may need to specify the “Working Sub Directory” field before the command will successfully execute. The working sub directory tells Bamboo to run the command in a folder relative to the project’s root folder. If your Transifex configuration is stored somewhere other than in the project’s root directory, you’ll need to specify the directory here. The best way to determine this is to run the Transifex Client in your project, note which subfolder you ran the command in, then enter that subfolder as the working sub directory.

How to Create a New Command

Next, we’ll run the plan and generate a new build.

Run the Plan

To generate a new build, click the “Run” button in the top-right corner of the screen. Your build will begin in the background, and the results will be displayed in the Build Dashboard.

Build Dashboard on Bamboo

Click into the build to see the results. If it was successful, you should be able to see output from the Transifex Client in the Logs tab.

Client Output in Tx

You can use this same process to integrate Transifex into an existing Bamboo project. Once the Transifex Client is registered as an executable, add two tasks to your project that call the tx push and tx pull commands. Make sure you do this earlier enough in your build process so that you can reliably test and package the localization files with the rest of your project.

For more information on integrating Transifex into your projects, visit the Integrate Transifex documentation page.

Integrating Transifex and Bitbucket (Part 1)

Bitbucket is a service that lets you host and share code with other users. Bitbucket supports version control using Git and Mercurial. In this post, we’ll show you how to synchronize changes between your projects hosted in Bitbucket and your localization projects in Transifex. We’ll also post a blog on how to integrate Transifex with Bamboo, a popular continuous integration service, so don’t forget to check out our blog later this week!

What is Version Control?

If you’re unfamiliar with version control, we recommend reading our previous post on version controlled translations with Git. Version control systems (VCS) track changes to files across multiple users. Most version control systems are based around three core concepts:

  • Repositories, or directories that hold code files.
  • Commits, or changes to source code that are applied to a repository.
  • Branches, or deviations in the code base.

Version control helps developers coordinate code changes while reducing the chances of conflicts or data loss. While there are multiple version control systems available, the examples in this article use Git simply due to its popularity.

Getting Started with Bitbucket

To start, we’ll create a new repository in Bitbucket via the web UI. After logging into your account (new accounts are free), click on the “Create” dropdown button, then click “Create Repository.”

Enter the details of your repository. You can modify the visibility settings of your project and allow other users to “fork” your project, which lets them create and work on a copy of your repository. You can also add bug tracking, add a Wiki, or select a base language for the codebase. When you’re ready, click “Create repository.”

How To Make New Repository in BitBucket

By default, your new repository will be empty. You can use a version control system to push project files to Bitbucket, or you can import an existing repository from another code hosting service. In this example, we’ll use an existing Git repository for a NodeJS project stored locally on a Linux desktop. We’ll change the working directory to the project folder, use git remote add to add the remote repository, then push our project to the remote repository.

$ cd /home/transifex/projects/i18n
$ git remote add origin https://bitbucket@bitbucket.org/bitbucket/i18n.git
$ git push -u origin --all

Once the command completes, you should be able to browse your Git repository through the Bitbucket website.

Syncing Changes with Bitbucket

As you make changes to your local code files, you’ll need to update the Bitbucket repository. Imagine we have a file named “i18n.js,” which contains a list of all the locales used in the project. We decide to change a locale, so we update i18n.js. With Git, you can view changes between the repository’s current state and the last commit using the command git status:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

	modified:   i18n.js

no changes added to commit (use "git add" and/or "git commit -a")

Git uses a staging area to temporarily store files before committing them, allowing you to customize a commit by including or excluding certain changes. We’ll add i18n.js to the staging area, then create a new commit:

$ git add i18n.js
$ git commit -m "Added new locale: es_ES"
[master 6a9f8d5] Added new locale es_ES
 1 file changed, 1 insertion(+), 1 deletion(-)

To update the Bitbucket repository, use the git push command. origin specifies the name of the remote destination, while master specifies the name of the local branch being pushed. You may be prompted for your Bitbucket account password:

$ git push origin master
Password for 'https://bitbucket@bitbucket.org': 
Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 298 bytes | 0 bytes/s, done.
Total 3 (delta 2), reused 0 (delta 0)
To https://bitbucket@bitbucket.org/bitbucket/i18n.git
   45cff79..6a9f8d5  master -> master

If you need to pull changes from a remote repository into a local repository, for instance, to incorporate changes from another developer, use the git pull command:

$ git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://bitbucket.org/bitbucket/i18n
   6a9f8d5..776aa6d  master     -> origin/master
Updating 6a9f8d5..776aa6d
Fast-forward
 i18n-node-http.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

You can use git log to review the latest changes:

$ git log
commit 776aa6d5375037f009f4d4dc8acbe06bd228c214
Author: Other Developer 
Date:   Mon Aug 24 16:41:40 2015 -0400

    Changed default locale to es_ES

commit 6a9f8d57dab0db5fb50d4be1b863307bd10c9c0c
Author: bitbucket
Date:   Mon Aug 24 16:25:08 2015 -0400

    Added new locale es_ES

Syncing with the Transifex Client

You can use the Transifex Client to push source changes to Transifex. The benefit to this approach is that it ensures everyone has access the latest translations. However, it could potentially make it more difficult to control who updates the latest translations and when. Features that are still in development can change entirely, and having your localizers work on text that might not appear in the final product would be a waste of time and money. Developers will also need to remember to update localization files when committing their code changes. Enforcing a push policy ensures everyone knows exactly when to sync their changes to the Transifex project.

To update your Transifex project from a local Git repository, make sure your repository is up to date with the remote Bitbucket repository by using git pull. Install the Transifex Client if it’s not already installed. The Transifex Client is based off of the Git client and uses a similar command structure. For instance, use the tx init command inside of your project’s root folder to create a new Transifex configuration:

$ tx init
Creating .tx folder...
Transifex instance [http://wpdev.transifex.com]: 
Creating skeleton...
Creating config file...
Done.

You should now have a .tx folder inside of your project. Inside of this folder is a configuration file, which contains the information used to identify the project on a Transifex server. For instance, this Node.js app has the project name “i18n” and stores its localization resources in the “locales” directory as standard JSON files.

[main]
host = http://wpdev.transifex.com

[i18n.enjson]
file_filter = locales/.json
source_file = locales/en.json
source_lang = en
type = KEYVALUEJSON

You can add the .tx folder to your Git repository by using git add. When other developers pull your changes, they can use the same configuration file to connect their Transifex clients to the Transifex project.

When you’re ready to push your updated files to Transifex, use the tx push command. The -s flag pushes source files, while the -t flag pushes translation files:

$ tx push -st
Pushing translations for resource i18n.enjson:
Pushing source file (locales/en.json)
Pushing 'de' translations (file: locales/de.json)
Pushing 'es' translations (file: locales/es.json)
Done.

To pull changes into your local project folder, use tx pull:

$ tx pull -a
New translations found for the following languages: de, es
Pulling new translations for resource i18n.enjson (source: locales/en.json)
 -> de: locales/de.json
 -> es: locales/es.json
Done.

From here, simply stage, commit, then push the updated localization files to your Bitbucket repository.

Again, don’t forget to check in for part 2 of this post, Integrating Transifex with Bamboo! And until then, check out the Transifex localization website for more information about localizing digital content!

Localization 101: A Beginner’s Guide to Software Localization

If you’re a developer who’s been handed the responsibility of software localization and you’re unfamiliar with the process, the road ahead may seem daunting. While localization can be complex, learning about localization in general and planning ahead can ensure the efficient and cost-effective localization of your product. In today’s article, we’ll share key information about software localization as well as steps for how to localize software from start to finish.

What is software localization?

Software Localization (also referred to as l10n) is the process of adapting or translating software to a specific locale’s language, culture, and legal requirements. In many cases, localization will require modifications to the user-visible components of software such as the user interface, images, documentation, etc.

Effectively localized software will allow your users to connect with your software in a language and locale that feels native to them.

For a software developer, i18n means designing a localizable user-interface and abstracting all localizable elements (user-visible strings, locale-specific data like date, time, and currency formats, keyboard usage, etc.) out of your application source code and into external files that can be made available for translations.

What are the best practices for localization?

There are no hard and fast rules or specific global standards governing localization, but you’ll find that adhering to 4 best practices will result in a hassle-free localization process:

  1. View localization as an extension of product development. Rather than looking at software localization as an isolated task in the final stages of your product release, view localization-related tasks as things that arise during each step of the software development process, from release to maintenance. This mindset can ensure that you plan accordingly for localization and that your software can be translated into other languages without causing you extra work or time.
  2. Keep your source language simple. Using industry-related or complicated words and phrases in your user interface and documentation may appeal to a select audience, failing to elicit positive responses in other languages or another culture. It also makes translation difficult or even impossible in some cases, so keep your source language as simple as possible.
  3. Focus on your strings. As a developer, much of your localization efforts will revolve around your strings. Along with externalizing all user-visible strings, localization best practices include never hard coding strings, avoiding concatenated strings, and always providing explanatory comments which will discuss later in this article.
  4. Find a comprehensive localization platform. The localization process goes beyond software development, requiring a robust localization platform. Localization platforms designed specifically for developers often offer a higher level of customization and can include integrations with tools such as GitHub, Python, and Django. A handful of localization platforms also provide integrated translation providers so you don’t have to spend time creating and managing a team of translators.

How is localization done?

Localization involves two key phases; the first being internationalization, and the second being the actual process of localization. The diagram below outlines the steps involved in both phases.

Phase 1: Internationalization

While localization is the actual process of adapting your software to another language, internationalization includes design and development practices that enable a simple and straightforward localization. During the internationalization phase, you’ll need to:

Review Application Framework

Your ability to internationalize your software will be dependent on your application framework. The diagram below outlines the key features to look for to determine if your software can support internationalization efforts.

Plan for Text in Other Languages

Translated text may take up more space, or in some cases, less space, causing your neat and perfectly laid-out design to appear crowded or even indecipherable when translated. The design of your user interface must allow room for expansion and contraction in translated languages.

To ensure that your content is viewed how you intended, we recommend programming dynamic UI expansion into your software. For example, if you are developing an iOS app, you should use Auto Layout to ensure that your views realign suitably for every locale. For an Android app, you can build a dynamic UI with Fragments.

Make use of features supported by your application framework to program dynamic UI expansion.

Another point to consider: In languages such as Arabic, text is read from right-to-left (RTL) so your design will also need to supports RTL script.

Code Strings with Global Expansion in Mind

During the internationalization phase, strings must be extracted, localized, and reinserted back into the code. Coding string codes under Unicode/UTF-8 will generally be the best option, unless you are working with Asian languages that require UTF-16.

Externalize Your Locale Specific Data

Strings that have been prepped for localization must be externalized, meaning you will have to save your strings to a translation file. We’ll get into resource files in more depth below, but know that it’s common to have multiple versions of your resources files for every target language.

Resource Files

Resource files in the use case of localization contain resources specific to a given language. Recent application frameworks typically define a key-delimiter-value syntax for resource files, which will determine how you create pairs of keys and values for all user-visible strings in your application. Then, based on the user’s locale, appropriate values for these strings will be fetched and rendered in the user interface.

Example 1: Properties Files

Below, you can see how to make use of properties files to externalize your localizable strings. It is common to create multiple .properties files, one for each target language.

messages_en.propertiesmessages_de.properties
label.name = Your name

label.greeting = Welcome

label.name = Ihren namen

label.greeting = willkommen

Example 2: Localizable.Strings files

For an iOS app, the files that contain localizable strings are referred to as the Strings file and you must make use of these and the NSLocalizedString macro. Your application should have specific locale directories and you must localize the Localizable.strings file in each language folder.

en.lproj/Localizable.stringsde.lproj/Localizable.strings
/* Label item Your name */

“name” = “Your name”;

/* Label item Greeting*/

“greeting”=”Welcome”;

/* Label item Your name */

“name” = “Ihren namen”;

/* Label item Greeting*/

“greeting”=” willkommen”;

Example 3: Strings.xml files

For an Android app, all your localizable strings will go to the strings.xml file and into appropriate locale directories as shown below:

res/values/strings.xmlres/values-de/strings.xml
<?xml version=”1.0″ encoding=”utf-8″?>

<resources>

<string name=”name”>Your name</string>

<string name=”greeting”>Welcome</string>

</resources>

<?xml version=”1.0″ encoding=”utf-8″?>

<resources>

<string name=”name”> Ihren namen </string>

<string name=”greeting”>willkommen </string>

</resources>

Resource Bundle

Resource bundle is a general term, but when used in regards to localization, typically means a set of resource files sharing a common base name and having an additional language-specific component in its name to identify specific locales.

  • In example 1 above, messages_en.properties and messages_de.properties will be joined together into a Resource Bundle.
  • In example 2 above, en.lproj/Localizable.strings and de.lproj/Localizable.strings will be put together into an Application Bundle.
  • In example 3 above, res/values/strings.xml and res/values-de/strings.xml will be joined into a Resource Bundle.

Focus On Your Strings

The final thing you’ll need to do before the actual translation process revolves around your strings. Try to:

  • Avoid hard-coded strings. All user-visible strings must be externalized appropriately. Avoiding hard coded strings will make your life easier, and when unsure, perform pseudo localization to root out hard coded strings. Pseudo-localization is often performed in a separate localization testing branch, allowing you to replace your strings using a regular expression. Then, when you run your software, any hard-coded string will be clearly visible.
  • Avoid concatenation of strings. Two or more strings are sometimes concatenated by developers for the purpose of saving space. However, word order varies significantly in each language and string concatenation will most likely result in translation errors in the localization process.
  • Provide self-explanatory comments. Providing explanatory comments for your strings to define context wherever possible will go a long way in assuring better translation accuracy with less back and forth communication efforts. This means time savings for you and fewer headaches.

Internationalization should not be treated as a separate step in your software development process, but rather a fundamental thought in every stage of your design and development process.

Phase 2: Localization

In the past, people often localized content using spreadsheets, requiring developers to copy and paste strings and source content into the spreadsheets before sending it off to a single or multiple translators. Translators would then have to access the spreadsheet, put in their translations, and some sort of quality assurance measure would be implemented to ensure translations were correct before pushing the new strings back into the software. While this is just one translation option, the remainder of our article will focus on using a localization platform, a more commonly adopted way of localizing content that helps developers save time, while ensuring overall quality of translations.  

Select a localization platform that provides a good editor, supports multiple source formats, allows your translation cycles to integrate well with your build cycles, and offers additional localization features designed for developers.

Extract Resource Files

To start the localization process, you’ll need to extract your resource files for translation. Localization platforms like Transifex support a variety of source file formants and you’ll be able to directly upload your resource files. In some cases, you’ll have to export your resource files into standard XLIFF (XML Localization Interchange File Format) files or other such localization file formats to make them suitable for translation into multiple languages.

If you are using Transifex, all you need to do is directly upload the resource files of your project in one of the supported resource file formats. The platform will automatically extract all your source strings and make them available for translation.

Translate

When it comes to translating your content, it’s crucial to take the time to select the right translator for the job – ideally a native language speaker who has experience with translations. If you decide to use a localization platform, you’ll likely be provided with an integrated translation provider or you can invite translators of your choice from an outside translation agency. Whether through the platform or personal invite, your translators will have access to your source strings, can view them in the platform’s editor, and translate them appropriately and within the context of your content.

Review

All translations must be reviewed for accuracy, language quality, terminology, and any other requirements you may be particular about. The translators, based on feedback from you, a translation administrator, or project manager, must make any necessary modifications. If you’re using Transifex, the platform has built-in features for review as well as options to keep your communication with your translators efficient and quick.

Copy Translated Files into Code Structure

After your translations are completed, you’ll need to copy the translated files into your code structure. Quality translation platforms will provide an option to pull the translated files that are ready for use with your application. The next step is to import these translated files into your application and deploy your localized application with the new translations.

If you push content out on a regular basis, a localization platform will be most beneficial because it can make continuous localization a part of every release cycle. Then, when you add or modify strings in your source files or there is new content to be localized, you can push them for translation and merge the new translations back into the code.

Test Your Software for Every Translated Locale

You must test your software to ensure both functional and linguistic acceptability for every target locale and language and that translated content makes sense in the context of your product. In addition to translation accuracy, specifically look for layout issues, display errors, formatting issues, and locale specific settings.

When is the localization process complete?

Once you have made sure that your product is bug-free and sufficiently documented, announce a “string freeze” on your product. This means you can no longer change the code in a way that affects the source strings (except for specific improvements). The string freeze will allow translators to work on a stable set of strings and ensure adequate time is available to translate and review. Before the final release, you can obtain the translation for all your target languages, compile them into your product and release it.

Have other questions about localization? Visit our website at www.transifex.com!