Welcome to mirror list, hosted at ThFree Co, Russian Federation.

l10n.rst « front-end « basics « developer_manual - github.com/nextcloud/documentation.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: bcb45c119fb6d7cb8c48047059730bcef554abb7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
===========
Translation
===========

.. sectionauthor:: Bernhard Posselt <dev@bernhard-posselt.com>, Kristof Hamann

Nextcloud provides mechanisms for internationalization (make an application translatable) and localization (add translations for specific languages). This section provides detailed instructions for both aspects.


Make text translatable
----------------------

In order to make your app translatable (internationalization), you should use Nextcloud's methods for translating strings. They are available for both the server-side (PHP, Templates) as well as for the client-side (JavaScript).


PHP
^^^

If localized strings are used in the backend code, simply inject the ``\OCP\IL10N`` class into your service via type hinting it in the constructor. You will automatically get the language object containing the translations of your app:


.. code-block:: php

    <?php
    namespace OCA\MyApp\Service;

    use \OCP\IL10N;


    class AuthorService {

        /** @var IL10N */
        private $l;

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

        …
    }

Strings can then be translated in the following way:

.. code-block:: php

    <?php
    …

    class AuthorService {

        …

        public function getLanguageCode() {
            return $this->l->getLanguageCode();
        }

        public sayHello() {
            // Simple string
            return $this->l->t('Hello');
        }

        public function getAuthorName($name) {
            // String using a parameter
            return $this->l->t('Getting author %1$s', [$name]);
        }

        public function getAuthors($count, $city) {
            // Translation with plural
            return $this->l->n(
                '%n author is currently in the city %1$s',  // singular string
                '%n authors are currently in the city %1$s',  // plural string
                $count, // number to decide which plural to use
                [$city] // further parameters are possible
            );
        }
    }

Correct plurals
"""""""""""""""

If you use a plural, you **must** also use the ``%n`` placeholder. The placeholder defines the plural and the word without the number preceding is wrong. If you don't know/have a number for your translation, e.g. because you don't know how many items are going to be selected, just use an undefined plural. They exist in every language and have one form. They do not follow the normal plural pattern.

Example:

.. code-block:: php

    // BAD: Plural without count
    $title = $l->n('Import calendar', 'Import calendars', $selectionLength)
    // BETTER: Plural has count, but disrupting to read and unnecessary information
    $title = $l->n('Import %n calendar', 'Import %n calendars', $selectionLength)
    // BEST: Simple string with undefined plural
    $title = $l->t('Import calendars')


Templates
^^^^^^^^^

In every template the global variable **$l** can be used to translate the strings using its methods **t()** and **n()**:

.. code-block:: php

    <div><?php p($l->t('Showing %$1s files', $_['count'])); ?></div>

    <button><?php p($l->t('Hide')); ?></button>

For the right date format use ``<?php p($l->l('date', time()));?>``.



JavaScript
^^^^^^^^^^

There are global functions **t()** and **n()** available for translating strings in javascript code. They differ a bit in terms of usage compared to php:

* First argument is the appId e.g. ``'myapp'``
* Placeholders (apart from the count in plurals) use single-mustache brackets with meaning-full descriptors.
* The parameter list is an object with the descriptors as key.

.. code-block:: js

    t('myapp', 'Hello World!');
    t('myapp', '{name} is available. Get {linkstart}more information{linkend}', {name: 'Nextcloud 16', linkstart: '<a href="...">', linkend: '</a>'});
    n('myapp', 'Import %n calendar into {collection}', 'Import %n calendars into {collection}', selectionLength, {collection: 'Nextcloud'});



Important notes
^^^^^^^^^^^^^^^

Please also look through the following steps to improve your strings and make them better translatable by others

Improving your translations
"""""""""""""""""""""""""""

You shall **never split** sentences and **never concatenate** two translations (e.g. "Enable" and "dark mode" can not be combined to "Enable dark mode", because languages might have to use different cases)! Translators lose the context and they have no chance to possibly re-arrange words/parts as needed.

Bad example:

.. code-block:: php

  <?php p($l->t('Select file from')) . ' '; ?><a href='#' id="browselink"><?php p($l->t('local filesystem'));?></a><?php p($l->t(' or ')); ?><a href='#' id="cloudlink"><?php p($l->t('cloud'));?></a>

Translators will translate:

* Select file from
* local filesystem
* ' or '
* cloud

Translating these individual strings results in  ``local filesystem`` and ``cloud`` losing case. The two white spaces surrounding ``or`` will get lost while translating as well. For languages that have a different grammatical order it prevents the translators from reordering the sentence components.

So the following code is a bit better, but suffers from another issue:

.. code-block:: php

  <?php p($l->t('Select file from <a href="#" id="browselink">local filesystem</a> or <a href="#" id="cloudlink">cloud</a>'));?>

In this case the translators can re-arrange as they like, but have to deal with your markup and can mess it up easily. It is better to **keep the markup out** of your code, so the following translation is even better:

.. code-block:: php

  <?php p($l->t('Select file from %slocal filesystem%s or %scloud%s', ['<a href="#" id="browselink">', '</a>', '<a href="#" id="cloudlink">', '</a>']));?>

But there is one last problem with this. In case the language has to turn things around, your code will still insert the parameters in the given order and they can not re-order them. To prevent this last hurdle simply **use positioned placeholders** like ``%1$s``:

.. code-block:: php

  <?php p($l->t('Select file from %1$slocal filesystem%2$s or %3$scloud%4$s', ['<a href="#" id="browselink">', '</a>', '<a href="#" id="cloudlink">', '</a>']));?>

This allows translators to have the cloudlink before the browselink in case the language is e.g. right-to-left.

Hints
"""""

In case some translation strings may be translated wrongly because they have multiple meanings, you can add hints which will be shown in the Transifex web-interface:

**PHP**

.. code-block:: php

    <ul id="translations">
        <li id="add-new">
            <?php
                // TRANSLATORS Will be shown inside a popup and asks the user to add a new file
                p($l->t('Add new file'));
            ?>
        </li>
    </ul>

**Javascript**

.. code-block:: javascript

    // TRANSLATORS name that is appended to copied files with the same name, will be put in parenthesis and appened with a number if it is the second+ copy
    var copyNameLocalized = t('files', 'copy');

**Vue**

Currently, commenting in Vue files is not possible.
Vue files are not read via gettext and synchronized with Transifex.
As a small help, the file in question as well as the line of code is written in the translation comment (``// TRANSLATORS``).

**C++ (Qt)**

.. code-block:: c++

    //: Example text: "Progress of sync process. Shows the currently synced filename"
    fileProgressString = tr("Syncing %1").arg(allFilenames);

**Android Strings**

.. code-block:: xml

    <!-- TRANSLATORS List of deck boards -->
    <string name="simple_boards">Boards</string>

**iOS**

.. code-block:: swift

    /* The title on the navigation bar of the Scanning screen. */
    "wescan.scanning.title"             = "Scanning";

Adding translations
-------------------

Nextcloud's translation system is powered by `Transifex <https://www.transifex.com/nextcloud/>`_. To start translating sign up and enter a group. If your community app should be translated by the `Nextcloud community on Transifex <https://www.transifex.com/nextcloud/nextcloud/dashboard/>`_ just follow the setup section below.



Translation tool
^^^^^^^^^^^^^^^^

The translation tool scrapes the source code for method calls to  **t()**
or **n()** to extract the strings that should be translated. If you check
in minified JS code for example then those method names are also quite
common and could cause wrong extractions. For this reason we allow to
specify a list of files that the translation tool will not scrape for
strings. You simply need to add a file named :file:`.l10nignore` into
the root folder of your app and specify the files one per line::

    # compiled vue templates
    js/bruteforcesettings.js



Setup of the transifex sync
^^^^^^^^^^^^^^^^^^^^^^^^^^^

To setup the transifex sync within the Nextcloud community you need to add first the
transifex config to your app folder at :file:`.tx/config` (please replace **MYAPP** with your apps id)::

    [main]
    host     = https://www.transifex.com
    lang_map = th_TH: th, ja_JP: ja, bg_BG: bg, cs_CZ: cs, fi_FI: fi, hu_HU: hu, nb_NO: nb, sk_SK: sk

    [o:nextcloud:p:nextcloud:r:MYAPP]
    file_filter = translationfiles/<lang>/MYAPP.po
    source_file = translationfiles/templates/MYAPP.pot
    source_lang = en
    type        = PO

Then create a folder :file:`l10n` and a file :file:`l10n/.gitkeep` to create an
empty folder which later holds the translations.

Add one more file called :file:`.l10nignore` in root of the repository and the files and folders to ignore for translations.
Mostly used to ignore packed js files.

Now the GitHub account `@nextcloud-bot <https://github.com/nextcloud-bot>`_ needs to get ``write`` access to your repository.
You can invite it from your repository settings:

    ``https://github.com/<user-name>/<repo-name>/settings/access``

After sending the invitation, please `open a ticket <https://github.com/nextcloud/docker-ci/issues>`_ requesting access.

The bot will run every night and only push commits to the following branches branch once there is an update to the translation:

* main
* master
* stableX (X being the recent 3 versions of Nextcloud Server)

You can overwrite this list by creating a file ``.tx/backport`` in your repository with the following content::

    develop stable

That would sync the translations for the branches (``main`` and ``master`` are added automatically):

* main
* master
* develop
* stable


.. note::

    In general you should enable the
    `protected branches feature <https://help.github.com/articles/configuring-protected-branches/>`_
    for those branches. If you do that, you need to grant the
    `@nextcloud-bot <https://github.com/nextcloud-bot>`_ ``admin`` permissions,
    but that is only possible for repositories owned by organizations.
    You can `create your own organization <https://docs.github.com/en/organizations/collaborating-with-groups-in-organizations/creating-a-new-organization-from-scratch>`_

For the sync job there is a `configuration file <https://github.com/nextcloud/docker-ci/blob/master/translations/config.json>`_
available in our docker-ci repository. Adding there the repo owner and repo name
to the section named **app** via pull request is enough. Once this change is in
one member of the sysadmin team will deploy it to the sync server and the job
will then run once a day.

If you need help then just `open a ticket with the request <https://github.com/nextcloud/docker-ci/issues/new>`_
and we can also guide you through the steps.


Manual translation
^^^^^^^^^^^^^^^^^^

If Transifex is not the right choice or the app is not accepted for translation,
generate the gettext strings by yourself by executing our
`translation tool <https://github.com/nextcloud/docker-ci/tree/master/translations/translationtool>`_
in the app folder::


    cd /srv/http/nextcloud/apps/myapp
    translationtool.phar create-pot-files

The translation tool requires **gettext**, installable via::

    apt-get install gettext

The above tool generates a template that can be used to translate all strings
of an app. This template is located in the folder :file:`translationfiles/template/` with the
name :file:`myapp.pot`. It can be used by your favored translation tool which
then creates a :file:`.po` file. The :file:`.po` file needs to be placed in a
folder named like the language code with the app name as filename - for example
:file:`translationfiles/es/myapp.po`. After this step the tool needs to be invoked to
transfer the po file into our own fileformat that is more easily readable by
the server code::

    translationtool.phar convert-po-files

Now the following folder structure is available::

    myapp/l10n
    |-- es.js
    |-- es.json
    myapp/translationfiles
    |-- es
    |   |-- myapp.po
    |-- templates
        |-- myapp.pot

You then just need the :file:`.json` and :file:`.js` files for a working localized app.