Page MenuHomePhabricator

Translatable Maniphest Task Priorities
Closed, ResolvedPublic

Description

We're currently thinking about bringing our clients into our Phabricator install, the one major issue being that we have a lot of "Amusing" task priorities in maniphest. (For instance 'Scorchio'[1] for High and 'Normil'[2] for Normal).

One thing I've been thinking of doing is having a "Silly" and a "Sensible" translation that can be enabled in the User Preferences.

As the task priorities don't run through pht, I'm guessing this isn't possible?

[1]: https://www.youtube.com/watch?v=MuSPdsPWit0
[2]: https://www.youtube.com/watch?v=WPUZ_JaO-go

Event Timeline

nateguchi2 raised the priority of this task from to Needs Triage.
nateguchi2 updated the task description. (Show Details)
nateguchi2 added a subscriber: nateguchi2.

This isn't currently possible.

A configuration file or a subclass of PhabricatorConfigSiteSource can use pht() to generate configuration, but configuration is evaluated at the beginning of the request, before we authenticate the user. It is not re-evaluated later, so it can't differ from user to user.

So I'm guessing something like the following is a bad idea then.

public static function getTaskStatusName($status) {
  $name = self::getStatusAttribute($status, 'name', 'Unknown Status');
  return pht($name);
}

(as it breaks the 'Only pass static strings as the first parameter to pht().' rule on https://secure.phabricator.com/book/phabcontrib/article/internationalization/).

I know my use case is a bit silly, but is this worth thinking about in the future?

nateguchi2 renamed this task from Translatable Maniphest Task Statuses to Translatable Maniphest Task Priorities.Aug 10 2015, 10:37 AM
nateguchi2 updated the task description. (Show Details)

Messed up Status + Priority, I meant Priorities - Must be monday morning!

That will technically work as a local patch, it just isn't extractable by translation tools.

It's possible you can do something sort of goofy like this with a subclass of PhabricatorConfigSiteSource (this will be more complicated, but not require you to patch anything, potentially):

class DelayedTranslation {

  private $string;

  public function __construct($string) {
    $this->string = $string;
  }

  public function __toString() {
    return pht($this->string);
  }

}

Then build your config value using new DelayedTranslation('High') in place of pht('High').

I'd like to come up with a stronger use case before pursuing anything for this in the upstream, since I think we either need to do something like that or flag some configuration values to be reloaded after authenticating the user, and either of those approaches are fairly complicated.

Broadly, I'm not sure that having, e.g., "Esperanto" and "Esperanto (Silly)" translations is especially good in general, since they also interact with phabricator.serious-business, and that flag has some effects beyond changing strings. Doing a "silly mode" through translations means that you need four versions of the strings (Serious Business on or off, Silly on or off).

A conceptually cleaner fix for this would be be something like adding name.silly (or name.serious) to the config:

public static function getTaskStatusName($status) {
  $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
  if (!$is_serious) {
    $silly_name = self::getStatusAttribute($stauts, 'name.silly');
    if ($silly_name !== null) {
      return $silly_name;
    }
  }

  return self::getStatusAttribute($status, 'name', 'Unknown Status');
}

But I'd hesitate to pursue this in the upstream, and it wouldn't be very useful anyway without making "serious-business" a preference for each user and possibly implementing T4103 so you could change defaults.

Basically, I'd say:

  • Easiest fix with local patches: just pht($name), as in your patch.
  • Easiest fix without local patches: provide config via config file or SiteSource, return object which defers evaluation of pht(), not 100% sure this will work.
  • Most-acceptable upstream fix: probably adding name.silly, but wouldn't solve your problem on its own, and isn't something I want to pursue in the upstream, at least right now.

We may eventually need to solve this properly in the upstream in some way (e.g., an install with Spanish-speaking and French-speaking users who are separated by Spaces and don't regularly with each other). I think it's rare that users of an install don't have a language in common, but this isn't completely inconceivable.

I'm not exactly sure how we'd pursue this. Even if we get the values extracting and passing through pht(), it's very difficult to translate a small number of strings into a bunch of languages right now (you have to create a file per language), and I think we'd have to add some kind of priority mechanism to specify that the user strings should override other strings. Maybe that's fine (it seems OK to create 2-3 supplementary translation files, and presumably installs with multilingual users are more likely to have 2-3 languages than 100).

nateguchi2 claimed this task.

Have implemented using local patch, working well.

I guess this is resolved with us anyway, Thanks.

I suppose we could do something like this, hypothetically:

/**
 * Unbundles a configured, translated value.
 *
 * If passed a string from pht(), just returns that string unmodified.
 *
 * If passed a map of strings, chooses the string for the current locale if an entry exists,
 * and the first entry otherwise.
 */
function pht_config($config) {
  if (is_array($config)) {
    // We have a map of strings.
    $locale = get_current_locale();
    if (issset($config[$locale])) {
      // The current locale has a translation.
      return $config[$locale];
    }

    // No translation for current locale, use default (first) value.
    return head($config);
  }

  // This is just a normal pht() string or a static string, so
  // return it unmodified.
  return $config;
}

Then you'd specify values like this:

// Just one translation.
"name": "High"

// Values per locale, first is default.
"name": {
  "en_US": "High",
  "es_ES": "El High-o",
  "fr_FR": "Hiuette"
}

That seems reasonably good/easy/flexible?

I'd still like to wait for someone to hit the, e.g., Russian + Spanish problem in case more complications arise between now and then, but that allow-a-map solution seems relatively upstreamable to me when we do.

The only problem I can see with it offhand is that it's not flexible enough on its own if we need to do strings with variables, although even that is solvable by letting the value run through the translation engine rather than just using it directly.