3.2 3.1
Jump to table of contents

Localization for Developers

This page describes the motivations and policies that are used in PKP software to maintain consistent and flexible localization, i.e. the ability to translate content and built-in text into numerous languages and support a flexible mix of publishing options.

Locale Files #

Locale files are Gettext PO files stored in one of several directories named for the locale code (i.e. en_US for U.S. English or fr_CA for Canadian French). These contain the key => value associations that map symbolic names for pieces of language (i.e. “navigation.journalHelp”) to translations in various languages (i.e. “Journal Help” in English or “Aide” in French).

There are numerous locale files, organized by function. The major places to find these are:

  • locale/ - Contains the application-specific locale files
  • lib/pkp/locale/ - Contains the generic PKP library locale files
  • plugins/*/*/locale/ - Contains application-specific plugin content
  • lib/pkp/plugins/*/*/locale/ - Contains generic plugin content

Segmentation of content into numerous files serves several purposes:

  • Maintainability: Smaller files, organized roughly by topic, are easier to maintain. A language can be more easily distributed between several translators without requiring merges.
  • Reuse: Any content that is available in the PKP library can be used by any application that needs it. One translation update for OJS therefore may result in useful contributions to OPS or OMP.
  • Portability: Content that is associated with plugins can be packaged easily for distribution.
  • Efficiency: This remains to be proven, but it should be possible to reap some performance and resource usage benefits by only loading a subset of the localized content. Most requests, for example, are for public-facing pages; most of the translated content, however, addresses workflow.

Where should content go? #

  • If content relates only to a single plugin, it needs to be distributed with that plugin.
  • If content is not specific to a single application, put it in the PKP library.
  • Try to group by logical entity. For example, a locale file called “submission.po” dealing with content about submissions is probably more useful a distinction than a locale file called “author.po” dealing with authorship and author workflow, because any page displaying information about a submission will likely include authorship information but will not necessarily care about the author’s workflow.
  • Consider which parts of the system will need to load a locale key. If logically well-structured, it shouldn’t be necessary to load an entire locale file for the sake of a single locale key.

How should I name a locale key? #

  • A locale key should be reasonably short and must uniquely identify a piece of text.
  • Locale keys should be organized for hierarchical grouping, i.e. “submission.id”, “submission.history.clearLog”, “submission.history.viewLog” are all within the “submission” pseudo-namespace, and with history-related locale keys further subgrouped into “submission.history”.
  • As much as possible, related keys should be kept close to each other in the locale file to help translators to stay consistent.
  • Don’t tie the names of locale keys too closely to the user interface. For example, the following should not be used:
msgid "manager.setup.step1.journalDescription"
msgstr "Journal Description"

…instead, use something more like…

msgid "manager.setup.journalDescription"
msgstr "Journal Description"

This keeps the locale files organized in spite of the inevitable user interface rearrangements.

  • When adding a new locale key, particularly for short (1 or 2 word) entries, grep the existing locale files to see whether something already exists:
fgrep -l "\"Title\"" `find . -name \*.po`

(The " sequences surrounding Title are used to mark the beginning and end of the entry so that the word Title isn’t matched in the middle of a long string.)

Semantics #

Do not assume that you can rely on English semantics. Contents may be presented in languages that do not behave the same way.

  • Use replacement variables rather than multiple locale keys to concatenate. The following should /not/ be used:
msgid "some.locale.key1"
msgstr ">You have "

msgid "some.locale.key2"
msstr " unread messages"

Instead, the following should be used:

msgid "some.locale.key"
msgstr "You have {$numberUnreadMessages} unread messages."

This will prevent problems e.g. with right-to-left languages, and keep content more logically organized.

  • Be careful when using the same locale key in different contexts. For example, “active” text, i.e. button labels, may be different than informational labels.