Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions docs/howto/translations.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Managing translations

A person using the Zulip app can choose from a large number of
languages for the app to present its UI in.

Within the running app, we use a library `react-intl` to get the
appropriate translation for a given string ("message") used in the UI.

To manage the set of UI messages and translations for them, and
provide a nice workflow for people to contribute translations, we use
(along with the rest of the Zulip project) a service called Transifex.


## Maintainers: syncing to/from Transifex

### Setup
Expand Down Expand Up @@ -29,7 +40,56 @@ You'll want Transifex's CLI client, `tx`.

Run `tx push -s`.

This uploads from `static/translations/messages_en.json` to the
set of strings Transifex shows for contributors to translate.
(See `.tx/config` for how that's configured.)


### Downloading translated strings

Run `tools/tx-pull`.

This writes to files `static/translations/messages_*.json`.
(See `.tx/config` for how that's configured.)

Then look at the following sections to see if further updates are
needed to take full advantage of the new or updated translations.


### Updating the languages supported in the code

Sometimes when downloading translated strings we get a file for a new
language. This happens when we've opened up a new language for people
to contribute translations into in the Zulip project on Transifex,
which we do when someone expresses interest in contributing them.

Each messages file in `static/translations/` should be reflected in
three boring, more-or-less mechanical lists:
* `flow-typed/translations.js`
* `src/i18n/locale.js`
* `src/i18n/messages.js`

The first of these has a comment with a trivial command to help
automate updating it. The others are smaller, and are maintained
manually. It'd be good to fully automate all of them; we haven't yet.

So, when a new messages file appears, update those three lists.
Then see if the next section applies too...


### Updating the languages offered in the UI

A fourth list should reflect many but not all of the messages files in
`static/translations/`: only the ones with a significant number of
strings actually translated. This is `src/settings/languages.js`,
which controls the languages offered in the language-picker screen in
our settings UI.

This would also be nice to automate, though a bit more complex, and to
date we haven't.

So, when a language has gone from mostly untranslated, or completely
absent, to significantly translated, update the list in
`src/settings/languages.js` to include it. See there for details on
what counts as "significantly translated", and for an easy way to
check.
28 changes: 28 additions & 0 deletions flow-typed/translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ declare module '../../static/translations/messages_de.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_el_GR.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_el.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_en_GB.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_en.json' {
declare export default {| [string]: string |};
}
Expand Down Expand Up @@ -84,10 +96,18 @@ declare module '../../static/translations/messages_ko.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_lt.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_ml.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_nb_NO.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_nl.json' {
declare export default {| [string]: string |};
}
Expand Down Expand Up @@ -132,10 +152,18 @@ declare module '../../static/translations/messages_uz.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_vi.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_zh-Hans.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_zh-Hant.json' {
declare export default {| [string]: string |};
}

declare module '../../static/translations/messages_zh_TW.json' {
declare export default {| [string]: string |};
}
24 changes: 24 additions & 0 deletions src/i18n/locale.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,74 @@ import ar from 'react-intl/locale-data/ar';
import bg from 'react-intl/locale-data/bg';
import ca from 'react-intl/locale-data/ca';
import cs from 'react-intl/locale-data/cs';
import da from 'react-intl/locale-data/da';
import de from 'react-intl/locale-data/de';
import el from 'react-intl/locale-data/el';
import en from 'react-intl/locale-data/en';
import es from 'react-intl/locale-data/es';
import fa from 'react-intl/locale-data/fa';
import fi from 'react-intl/locale-data/fi';
import fr from 'react-intl/locale-data/fr';
import gl from 'react-intl/locale-data/gl';
import hi from 'react-intl/locale-data/hi';
import hr from 'react-intl/locale-data/hr';
import hu from 'react-intl/locale-data/hu';
import id from 'react-intl/locale-data/id';
import it from 'react-intl/locale-data/it';
import ja from 'react-intl/locale-data/ja';
import ko from 'react-intl/locale-data/ko';
import lt from 'react-intl/locale-data/lt';
import ml from 'react-intl/locale-data/ml';
import nb from 'react-intl/locale-data/nb';
import nl from 'react-intl/locale-data/nl';
import pl from 'react-intl/locale-data/pl';
import pt from 'react-intl/locale-data/pt';
import ro from 'react-intl/locale-data/ro';
import ru from 'react-intl/locale-data/ru';
import sr from 'react-intl/locale-data/sr';
import sv from 'react-intl/locale-data/sv';
import ta from 'react-intl/locale-data/ta';
import tr from 'react-intl/locale-data/tr';
import uk from 'react-intl/locale-data/uk';
import uz from 'react-intl/locale-data/uz';
import vi from 'react-intl/locale-data/vi';
import zh from 'react-intl/locale-data/zh';

[
ar,
bg,
ca,
cs,
da,
de,
el,
en,
es,
fa,
fi,
fr,
gl,
hi,
hr,
hu,
id,
it,
ja,
ko,
lt,
ml,
nb,
nl,
pl,
pt,
ro,
ru,
sr,
sv,
ta,
tr,
uk,
uz,
vi,
zh,
].forEach(locale => addLocaleData(locale));
22 changes: 22 additions & 0 deletions src/i18n/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,72 @@ import ar from '../../static/translations/messages_ar.json';
import bg from '../../static/translations/messages_bg.json';
import ca from '../../static/translations/messages_ca.json';
import cs from '../../static/translations/messages_cs.json';
import da from '../../static/translations/messages_da.json';
import de from '../../static/translations/messages_de.json';
import el from '../../static/translations/messages_el.json';
import en from '../../static/translations/messages_en.json';
import es from '../../static/translations/messages_es.json';
import fa from '../../static/translations/messages_fa.json';
import fi from '../../static/translations/messages_fi.json';
import fr from '../../static/translations/messages_fr.json';
import gl from '../../static/translations/messages_gl.json';
import hi from '../../static/translations/messages_hi.json';
import hr from '../../static/translations/messages_hr.json';
import hu from '../../static/translations/messages_hu.json';
import id from '../../static/translations/messages_id_ID.json';
import it from '../../static/translations/messages_it.json';
import ja from '../../static/translations/messages_ja.json';
import ko from '../../static/translations/messages_ko.json';
import lt from '../../static/translations/messages_lt.json';
import ml from '../../static/translations/messages_ml.json';
import nl from '../../static/translations/messages_nl.json';
import pl from '../../static/translations/messages_pl.json';
import pt from '../../static/translations/messages_pt.json';
import ro from '../../static/translations/messages_ro.json';
import ru from '../../static/translations/messages_ru.json';
import sr from '../../static/translations/messages_sr.json';
import sv from '../../static/translations/messages_sv.json';
import ta from '../../static/translations/messages_ta.json';
import tr from '../../static/translations/messages_tr.json';
import uk from '../../static/translations/messages_uk.json';
import uz from '../../static/translations/messages_uz.json';
import vi from '../../static/translations/messages_vi.json';
import zh from '../../static/translations/messages_zh-Hans.json';

export default {
ar,
bg,
ca,
cs,
da,
de,
el,
en,
es,
fa,
fi,
fr,
gl,
hi,
hr,
hu,
id,
it,
ja,
ko,
lt,
ml,
nl,
pl,
pt,
ro,
ru,
sr,
sv,
ta,
tr,
uk,
uz,
vi,
zh,
};
11 changes: 11 additions & 0 deletions src/settings/languages.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,34 @@ const languages: $ReadOnlyArray<Language> = [
{ locale: 'bg', name: 'Bulgarian', nativeName: 'Български' },
{ locale: 'ca', name: 'Catalan', nativeName: 'Català' },
{ locale: 'zh', name: 'Chinese (Simplified)', nativeName: '中文 (简体)' },
// TODO: migrate `zh` to `zh-Hans`, and add `zh-Hant`
{ locale: 'cs', name: 'Czech', nativeName: 'Čeština' },
{ locale: 'nl', name: 'Dutch', nativeName: 'Nederlands' },
// TODO: add `en_GB` -- it's "100% translated", though that just means
// the one string mentioning "organization" has s/z/s/ in that word
{ locale: 'fi', name: 'Finnish', nativeName: 'Suomi' },
{ locale: 'fr', name: 'French', nativeName: 'Français' },
{ locale: 'gl', name: 'Galician', nativeName: 'Galego' },
{ locale: 'de', name: 'German', nativeName: 'Deutsch' },
{ locale: 'hi', name: 'Hindi', nativeName: 'हिन्दी' },
{ locale: 'hu', name: 'Hungarian', nativeName: 'Magyar' },
{ locale: 'id', name: 'Indonesian', nativeName: 'Bahasa Indonesia' },
{ locale: 'it', name: 'Italian', nativeName: 'Italiano' },
{ locale: 'ja', name: 'Japanese', nativeName: '日本語' },
{ locale: 'ko', name: 'Korean', nativeName: '한국어' },
{ locale: 'lt', name: 'Lithuanian', nativeName: 'Lietuvių' },
{ locale: 'ml', name: 'Malayalam', nativeName: 'മലയാളം' },
{ locale: 'fa', name: 'Persian', nativeName: 'فارسی' },
{ locale: 'pl', name: 'Polish', nativeName: 'Polski' },
{ locale: 'pt', name: 'Portuguese', nativeName: 'Português' },
{ locale: 'ro', name: 'Romanian', nativeName: 'Română' },
{ locale: 'ru', name: 'Russian', nativeName: 'Русский' },
{ locale: 'sr', name: 'Serbian', nativeName: 'Српски' },
{ locale: 'es', name: 'Spanish', nativeName: 'Español' },
{ locale: 'sv', name: 'Swedish', nativeName: 'Svenska' },
{ locale: 'ta', name: 'Tamil', nativeName: 'தமிழ்' },
{ locale: 'tr', name: 'Turkish', nativeName: 'Türkçe' },
{ locale: 'uk', name: 'Ukrainian', nativeName: 'Українська' },
];

export default languages;