Translation workflow


(Ahmad Khayyat) #1

What is the recommended workflow for translating Discourse?

There are two parts to this issue:

  1. How to add a new language?

    • What new files need to be created?

    • What existing files need to be changed?

  2. How can a translator contribute to a language translation?

    • The Internationalization / localization discussion suggests that translations are managed through GitHub pull requests. Is this an explicit choice, or is it just how things are happening? Pull requests are not translator-friendly.

    • Is there an adopted/recommended translation platform? E.g. Transifex,, Pootle (needs hosting), … etc. And which ones support the translation file format used by Discourse?
      Also, are the strings stable enough by now to upload them to a translation platform? or are they still rapidly changing?

I think a documented workflow is useful for contributors, translators, and project maintainers.

Adding a new language that doesnt exist
(Jeff Atwood) #2

I am a newbie at translation, but what is your data to support the idea that the current process is not working?

There is a big list of English strings in a file ending in .en, create another file with a different language code at the end and translate those English strings to the target language.

I am not saying this process cannot be improved, but it is very simple, and we already have ~12 languages represented with the current process… so why is it “not translator friendly”?

Why add complexity when the current simple process is demonstrably working fine?

(Ahmad Khayyat) #3

I never said it’s not working. I said, it’s not translator-friendly, and I asked whether using pull requests is the “official” translation workflow, or just a defacto one. I’d argue that this process requires knowing how to use git and github. A lot of translators don’t.

It may be simple for developers, but if I want the help of non-developer translators for my language, I may need to adopt an alternative process. I was just wondering if such a process already exists, or if at least there was a recommendation for one. If not, that is all I wanted to know.

I asked about the needed files after I had seen this issue in the Danish translation, which suggests there is another file involved besides what’s in the config/locales directory.

I guess I was hoping to be able to find all of this in one place so I can create my language translation without wondering what to do and having to look around for what people are doing.

A simple statement of: what are all the involved files? and what is the translation process (apparently github pull requests)?

(Jeff Atwood) #4

The only involved files are

  • client.en
  • server.en

Replace the extension with the ISO language code, then translate the strings inside, eg:

      topic: 'share a link to this topic'
      post: 'share a link to this post'


      topic: 'partager un lien vers cette discussion'
      post: 'partager un lien vers ce message'

Also covered in this blog post:

(Ahmad Khayyat) #5

Aha… missed that one… Thanks!

(Régis Hanol) #6

Actually, this is a little more complicated than that…

The main translation work will be done in both config/locales/client.[lang].yml and config/locales/server.[lang].yml files. These are the ones translators should be aware of.

They are pretty standard .yml files but you want to make sure to change the first line to match the language referenced in the name of the file (eg. fr: for the config/locales/ file).

As @akhayyat suggested, this is unfortunately not as simple and other files are involved.

1) Client-side translations

For the translation to work on the client-side, you have to create a file named app/assets/javascripts/locales/[lang].js.erb with the following content:

//= depend_on 'client.[lang].yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:[lang]) %>

This will tell the asset pipeline which client translation file has to be serialized in the response.

2) Pluralization

Discourse uses messageformat.js for handling pluralization. All the rules are defined in lib/javascripts/locale/.
If your language is not supported, you need to create a file named lib/javascripts/locale/[lang].js with the following content:

MessageFormat.locale.[lang] = function(n) {
  if (n === 0) { return 'zero'; }
  if (n === 1) { return 'one'; }
  return 'other';

If you’re not 100% sure of the rules used in your locale, please, check out the Language Plural Rules.

3) Date formatting

Discourse uses moment.js for formatting date on the client-slide.
If your language is not supported, please follow the procedure for adding a language.

That’s all I can think of as of now.

I guess @kuba might have something to add as he did a lot of work in this area.

Discourse Development Contribution Guidelines
(Kuba) #7

I personally think that it’s a good thing that we require translators to use git, GitHub and pull requests. If you can’t handle these things, you probably won’t be a good translator, because translating Discourse needs quite a lot of technical insights into its internals. Being a “translator” is simply not enough.

Note that (and it may be obvious) I am not a fan of crowd-sourced translations. I believe translation should be done by a single person so it keeps a consistent style.

(Kuba) #8

Yeah, @zogstrip got it. But anyway, it’s mostly about the config/locales/client.[lang].yml and config/locales/server.[lang].yml files. If you have those, anyone else can handle the rest, since it’s just “paperwork”.

One thing to add is that we support translations of static pages, like FAQ, TOS and Privacy, they are in discourse/app/views/static at master · discourse/discourse · GitHub. But it seems we only have a Czech translation of these, now.

(Caue Rego) #9

I’m completely new to Discourse and also a translator newbie… So I’m sorry if this is the wrong place to post this.

On my own projects I’ve adopted a few of wordpress “guidelines” by using POEdit and GetText implementations. From the little I could grab on how it’s currently done here, I think there might be something to be learned there.

I’d just like to know from anyone else who knows what I’m talking about if this is a relevant input, or if this have already been considered and disregarded for whatever reason.

(Camilo Hollanda) #10

I am trying to translate to brazilian portuguese pt_BR.
When I do this step:

  1. Client-side translations For the translation to work on the client-side, you have to create a file named app/assets/javascripts/locales/[lang].js.erb with the following content: //= depend_on ‘client.[lang].yml’ //= require locales/i18n <%= JsLocaleHelper.output_locale(:[lang]) %> This will tell the asset pipeline which client translation file has to be serialized in the response

Discourse stops compiling assets and give this error:

** [out :: localhost] rake aborted!
** [out :: localhost]
** [out :: localhost] undefined method delete' for nil:NilClass ** [out :: localhost] ** [out :: localhost] (in /home/deployer/apps/myforum/releases/20130619061718/app/assets/javascripts/locales/pt_BR.js.erb) ** [out :: localhost] ** [out :: localhost] /home/deployer/apps/myforum/releases/20130619061718/lib/js_locale_helper.rb:14:inoutput_locale’
** [out :: localhost]
** [out :: localhost] /home/deployer/apps/myforum/releases/20130619061718/app/assets/javascripts/locales/pt_BR.js.erb:3:in block in singleton class' ** [out :: localhost] ** [out :: localhost] /home/deployer/apps/myforum/releases/20130619061718/app/assets/javascripts/locales/pt_BR.js.erb:-6:ininstance_eval’
** [out :: localhost]
** [out :: localhost] /home/deployer/apps/myforum/releases/20130619061718/app/assets/javascripts/locales/pt_BR.js.erb:-6:in singleton class' ** [out :: localhost] ** [out :: localhost] /home/deployer/apps/myforum/releases/20130619061718/app/assets/javascripts/locales/pt_BR.js.erb:-8:in__tilt_5316220’

(Charles-Pierre) #11

Are those instructions still up to date?

I’m trying to add ff as a language, but all I get is the following error:

NoMethodError in List#latest

Showing /home/cpa/discourse/app/views/common/_discourse_javascript.html.erb where line #19 raised:

undefined method `delete' for nil:NilClass
  (in /home/cpa/discourse/app/assets/javascripts/locales/ff.js.erb)

Here is what I’ve done:

  1. Create and translate client.ff.yml and server.ff.yml
  2. Create ff.js.erb with the following contents:
//= depend_on 'client.ff.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:ff) %>
  1. Added to date_locales.js:
Date.getLocale('ff').short_no_year = '{d} {Mon}';

// create [lang] locale, because it's not supported by Sugar at all
// TODO: currently just English, needs to be translated and localized
Date.addLocale('[lang]', {  
  'plural': true,
  'capitalizeUnit': false,
  'months': 'January,February,March,April,May,June,July,August,September,October,November,December',
  'weekdays': 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday',
  'units': 'millisecond:|s,second:|s,minute:|s,hour:|s,day:|s,week:|s,month:|s,year:|s',
  'short': '{Month} {d}, {yyyy}',
  'short_no_year': '{Month} {d}',
  'long': '{Month} {d}, {yyyy} {h}:{mm}{tt}',
  'full': '{Weekday} {Month} {d}, {yyyy} {h}:{mm}:{ss}{tt}',
  'past': '{num} {unit} {sign}',
  'future': '{num} {unit} {sign}',
  'duration': '{num} {unit}'

And all I get is the aforementioned error.

What makes me think that this doc is not up to date is the fact the all lines in date_locales are commented out (as of commit dca216), so it feels very weird to add stuff there.

(Régis Hanol) #12

You’re right, the instructions are not up to date due to the recent removal of the sugar.js library.

Unfortunately, the ff locale is not supported by moment.js and adding it requires a bit more work. Here’s a boilerplate commit you can use to start localizing Discourse in fula:

(Saud) #13

Please mention that for who want to add a new language or anyone want to edit some phrases in current language:
He must have to change the config file to (true):

stuff should be pre-compiled
config.assets.compile = true

(Stan Lun) #14

Is there any way to compare [en] and [ru] translations to understand which string translations are missing, without hassle of comparing both files by myself?

(Erlend Sogge Heggen) #15

With specialised tools tools there is a way. See this conversation:

(Daniel Luca) #16

@zogstrip I am unsure what I need to do after I create the files. The languages did not appear in the admin interface. In the end I restarted the whole server and restarted the docker instance.

But right now I have the problem that I created the file app/assets/javascripts/locales/[lang].js.erb and it doesn’t seem to go through the pipeline. Specifically i created the file app/assets/javascripts/locales/ro.js.erb (for Romanian) and I get a /javascripts/locales/ro.js 404 (Not Found) error in my browser.

I must say I am not familiar with Ruby and I don’t know the exact process.

My question is
After I create the files and change the content what do I need to do to restart the discourse platform to have the assets compiled?

EDIT: I found this on your forum Restart docker instance and rebuild translation cache
Will try this when I have the time.

(Régis Hanol) #17

That PR is a year old, so I’m pretty sure stuff has changed since… :frowning:

(Caue Rego) #18

It sure has. Though I have barely read the old messages, now it’s better to use:

(Jakub Ryška) #19

Is there a guide, how to get the context of the translated term? When I open the transifex, I can see the terms that needs to be translated, but I’m unaware where the term is actually used in discourse. Is there some easy way, how to get it? I have only managed to think of grepping the discourse sources and trying to find the term there, but since some of the keys are constructed from strings, simply trying to find the context by searching for the key is not an approach that works in every case. And if I manage to find the line of code used, it’s still not clear, where it is used on the discourse. I’d be happy if someone would share his take on things here.