OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openproject/docs/development/concepts/translations/README.md

130 lines
5.2 KiB

---
sidebar_navigation:
title: Translations
description: Get an overview of how translations are being used in OpenProject, and how they end up in the frontend
robots: index, follow
keywords: translations, I18n, localization, locales, languages
---
# Development Concept: Translations
OpenProject is currently being translated in over 40 languages. We use a cloud translation service called [Crowdin](https://crowdin.com/project/openproject) for allowing the community and professional translators to contribute translations to OpenProject.
Not all translations are fully translated, and they will fallback to english strings for the untranslated portions.
![Overview of translation and their progress](translations-overview.png)
## Key takeaways
*Translations in OpenProject...*
- depend on the [I18n yaml-based Rails standard](https://guides.rubyonrails.org/i18n.html) for translation files
- are split into backend and frontend strings
## I18n definitions
The OpenProject localizable strings are stored in the [Rails-standard I18n YAML files](https://guides.rubyonrails.org/i18n.html). The core only manages english source strings in `en.yml` and `js-en.yml` files. The can be found at `config/locales/en.yml` and `config/locales/js-en.yml`, respectively.
Additionally, modules can define their own translations, such as `modules/budgets/config/locales/en.yml`.
The `js-en.yml` are not special on their own, but are simply prefixed with the `js:` key at the beginning of the while. This means all translations within are prefixed with the `js.` key. This is picked up by [`I18n.js`](https://github.com/fnando/i18n-js), a Ruby gem and frontend library that helps outputting javascript objects for the frontend. Only strings that are prefixed with `js.` and some internals will end up in the frontend due to the config we applied in [`config/i18n-js.yml`](https://github.com/opf/openproject/blob/dev/config/i18n-js.yml). The translations are output by the take task `./bin/rails i18n:js:export` and are output to `frontend/src/locales/{language}.js`.
## Using I18n in your Rails code
You are likely familiar already with the `I18n.t` helper provided by Rails. We simply use this helper to localize strings. Translations are simply loaded by Rails and available everywhere in the application.
Let's say want to add a specific label to a work package page and use that in a HTML ruby file, you should go through this way:
You have to add the below rows to the appropriate `en.yml` file. If you're editing a module, add the string to the module's `config/locales/en.yml` file.
You should prefix your string unless its something very generic:
```yaml
# ...
work_packages:
my_specific_page:
my_button: "My localized button label"
```
You can then use the `t()` helper method in the Rails erb view to translate the string:
```html
<%= styled_button_tag t('work_packages.my_specific_page.my_button') %>
```
Outside of the views and controllers, you need to use the module function `I18n.t` to be used, for example in Ruby tests:
```ruby
expect(page).to have_selector('button', text: I18n.t('work_packages.my_specific_page.my_button'))
```
The Rails guide on translations will give advanced examples on variable interpolation and the like: https://guides.rubyonrails.org/i18n.html
## Using I18n in your frontend code
The frontend receives all translations prefixed with `js.` (see above) through compiled javascript files from `frontend/src/locales/{language}.js` folder generated by I18n.js. These locales are loaded early on in the application in [frontend/src/main.ts](https://github.com/opf/openproject/blob/dev/frontend/src/main.ts) to ensure they are present when the page loads. The import happens dynamically so only the language the user has selected is going to be loaded by webpack.
If you want to create an angular component named ‘news’ and translate the text of a "save news" button and author of the news, you should go through this way:
You have to add the below (exemplary) entry to the `js-en.yml` file
```yaml
js:
# ... other values
news:
button_save: "Save news"
label_added_by: "Added by %{author}"
```
You will then need to access the Angular [ `I18nService`](https://github.com/opf/openproject/blob/dev/frontend/src/app/modules/common/i18n/i18n.service.ts) that provides access to the `I18n.js` helper. Right now, there is also a window global `I18n` that works, but it is not recommended to use that wherever you can inject the service instead.
In the `news.component.ts`, you should import the `I18nService`. Then you have to define a property named ‘text’ used for all text in your component, like below:
```typescript
public text = {
saveButton: this.I18n.t('js.news.button_save'),
addedBy: (news:NewsResource) => this.I18n.t('js.news.label_added_time_by', { author: news.author}
};
```
Then you can use them as below in your template:
```html
<button type="submit" class="button -highlight">
<span class="button—text" [innerText]=text.saveButton></span>
</button>
<div *ngIf="news.author" class="news-author">
<span [innerText]="text.addedBy(news)"></span>
</div>
```
For more information on formatting and other helpers of the `I18n.js` library, see https://github.com/fnando/i18n-js