diff --git a/developer_manual/app_development/index.rst b/developer_manual/app_development/index.rst
index 7a969fe7e..69d576256 100644
--- a/developer_manual/app_development/index.rst
+++ b/developer_manual/app_development/index.rst
@@ -11,3 +11,4 @@ App development
info
init
dependency_management
+ translation_setup
diff --git a/developer_manual/app_development/translation_setup.rst b/developer_manual/app_development/translation_setup.rst
new file mode 100644
index 000000000..7761ea089
--- /dev/null
+++ b/developer_manual/app_development/translation_setup.rst
@@ -0,0 +1,154 @@
+.. _Translation setup:
+
+=================
+Translation setup
+=================
+
+Nextcloud's translation system is powered by `Transifex `_. To start translating sign up and enter a group. If your community app should be translated by the `Nextcloud community on Transifex `_ just follow the setup section below.
+
+Translation tool
+----------------
+
+.. note::
+
+ The tool-based translation currently only supports repositories hosted on ``github.com``. If your app is hosted elsewhere, you can try to follow the :ref:`manual-translation` instead.
+
+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
+---------------------------
+
+Transifex configuration ``.tx/config``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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):
+
+.. code-block:: ini
+
+ [main]
+ host = https://www.transifex.com
+ lang_map = hu_HU: hu, nb_NO: nb, sk_SK: sk, th_TH: th, ja_JP: ja, bg_BG: bg, cs_CZ: cs, fi_FI: fi
+
+ [o:nextcloud:p:nextcloud:r:{{APPID}}]
+ file_filter = translationfiles//{{APPID}}.po
+ source_file = translationfiles/templates/{{APPID}}.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.
+
+Branch selection ``.tx/backport``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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
+
+Excluding files ``.l10nignore``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Add one more file called :file:`.l10nignore` in root of the repository and the files and folders to ignore for translations.
+This should be used to exclude files that create false-positive translations, such as:
+
+- Compiled JavaScript files ``js/``
+- 3rd-party PHP dependencies ``vendor/``
+- Non-shipped files and documentation ``docs/``
+
+Validate source strings
+^^^^^^^^^^^^^^^^^^^^^^^
+
+After finishing the setup, you can validate the translation source strings which outlines some common mistakes.
+Clone the `nextcloud/docker-ci `_ repository and afterwards run the following script:
+
+.. code:: sh
+
+ bash translations/validateSyncSetup.sh Owner Repository
+
+Repository permissions
+^^^^^^^^^^^^^^^^^^^^^^
+Now the GitHub account `@nextcloud-bot `_ needs to get ``write`` access to your repository.
+You can invite it from your repository settings::
+
+ https://github.com///settings/access
+
+After sending the invitation, please `open a ticket using the "Request translations" template `_.
+
+.. attention::
+
+ In general you should enable the
+ `protected branches feature `_
+ for default and stable branches. If you do that, you need to grant the
+ `@nextcloud-bot `_ ``admin`` permissions and allow administrators to bypass the protection.
+ This feature however is only possible for repositories owned by organizations, not in repositories owned by individuals!
+ You can `create your own organization `_
+
+If you need help just `open a ticket with the request `_
+and we can also guide you through the steps.
+
+.. _manual-translation:
+
+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 `_
+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 such
+as `Poedit `_. It 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.
diff --git a/developer_manual/basics/front-end/index.rst b/developer_manual/basics/front-end/index.rst
index 925b24570..d075c5cda 100644
--- a/developer_manual/basics/front-end/index.rst
+++ b/developer_manual/basics/front-end/index.rst
@@ -8,5 +8,4 @@ Front-end
templates
js
css
- l10n
theming
diff --git a/developer_manual/basics/index.rst b/developer_manual/basics/index.rst
index 2fbb6d4bb..a59ec9246 100644
--- a/developer_manual/basics/index.rst
+++ b/developer_manual/basics/index.rst
@@ -12,6 +12,7 @@ Basic concepts
middlewares
events
front-end/index
+ translations
backgroundjobs
caching
logging
diff --git a/developer_manual/basics/front-end/l10n.rst b/developer_manual/basics/translations.rst
similarity index 64%
rename from developer_manual/basics/front-end/l10n.rst
rename to developer_manual/basics/translations.rst
index 63503b6cc..5911fb0ad 100644
--- a/developer_manual/basics/front-end/l10n.rst
+++ b/developer_manual/basics/translations.rst
@@ -1,6 +1,8 @@
-===========
-Translation
-===========
+.. _Translations:
+
+============
+Translations
+============
.. sectionauthor:: Bernhard Posselt , Kristof Hamann
@@ -60,27 +62,6 @@ Strings can then be translated in the following way:
}
}
-FIXME
------
-
-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
-
- 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')
-
Language of other users
^^^^^^^^^^^^^^^^^^^^^^^
@@ -121,7 +102,7 @@ In every template the global variable ``$l`` can be used to translate the string
// Date string
l('date', time())); ?>
-JavaScript / Typescript / Vue
+JavaScript / TypeScript / Vue
-----------------------------
There are global functions ``t()`` and ``n()`` available for translating strings in javascript code.
@@ -138,13 +119,15 @@ They differ a bit in terms of usage compared to php:
t('myapp', '{name} is available. Get {linkstart}more information{linkend}', {name: 'Nextcloud 16', linkstart: '', linkend: ''});
n('myapp', 'Import %n calendar into {collection}', 'Import %n calendars into {collection}', selectionLength, {collection: 'Nextcloud'});
-
Guidelines
----------
Please also look through the following hints to improve your strings and make them better translatable by the community
and therefore improving the experience for non-english users.
+Dos and Don'ts
+^^^^^^^^^^^^^^
+
.. list-table::
:header-rows: 1
@@ -191,17 +174,49 @@ and therefore improving the experience for non-english users.
- "Error: %s"
- Instead of concatenating errors or part messages, make them a proper placeholder
+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.
+
+PHP 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 not using any number in the string
+ $title = $l->t('Import calendars')
+
+Opposed to the normal placeholders in javascript, the plural number also uses the ``%n`` syntax:
+
+JS Example:
+
+.. code-block:: js
+
+ /* BAD: Plural without count */
+ n('myapp', 'Import calendar', 'Import calendars', selected.length)
+ /* BETTER: Plural has count, but disrupting to read and unnecessary information */
+ n('myapp', 'Import %n calendar', 'Import %n calendars', selected.length)
+ /* BEST: Simple string with undefined plural not using any number in the string */
+ t('myapp', 'Import calendars')
+
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:
+Starting with the following example, improving it step by step:
.. code-block:: php
t('Select file from')) . ' '; ?>t('local filesystem'));?>t(' or ')); ?>t('cloud'));?>
+Step 1: String split
+""""""""""""""""""""
+
+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.
+
Translators will translate:
* ``Select file from``
@@ -217,13 +232,21 @@ So the following code is a bit better, but suffers from another issue:
t('Select file from local filesystem or cloud'));?>
+Step 2: HTML Markup
+"""""""""""""""""""
+
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
t('Select file from %slocal filesystem%s or %scloud%s', ['', '', '', '']));?>
-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``:
+But there is one last problem with this.
+
+Step 3: Placeholders
+""""""""""""""""""""
+
+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
@@ -234,11 +257,15 @@ This allows translators to have the cloudlink before the browselink in case the
.. _Hints:
Provide context hints for translators
--------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-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:
+In case some translation strings may be translated wrongly because they have multiple meanings.
+Especially translations strings that only contain a single word often result in problems.
+The most famous example in the Nextcloud code base is ``Share`` which can which can be the verb and action ``To share something`` or the noun ``A share``.
+The added hints will be shown in the Transifex web-interface:
-**PHP**
+PHP
+"""
.. code-block:: php
@@ -251,14 +278,16 @@ In case some translation strings may be translated wrongly because they have mul
-**Javascript / Typescript**
+JavaScript / TypeScript
+"""""""""""""""""""""""
.. code-block:: javascript
// TRANSLATORS name that is appended to copied files with the same name, will be put in parenthesis and appended with a number if it is the second+ copy
var copyNameLocalized = t('files', 'copy');
-**Vue**
+Vue
+"""
This covers vue html templates in vue sfc components.
For vue js code, see the javascript section.
@@ -270,21 +299,24 @@ For vue js code, see the javascript section.
{{ t('forms', 'Required') }}
-**C++ (Qt)**
+C++ (Qt) / Desktop client
+"""""""""""""""""""""""""
.. code-block:: c++
//: Example text: "Progress of sync process. Shows the currently synced filename"
fileProgressString = tr("Syncing %1").arg(allFilenames);
-**Android Strings**
+Android
+"""""""
.. code-block:: xml
Boards
-**iOS**
+iOS
+"""
.. code-block:: swift
@@ -294,123 +326,4 @@ For vue js code, see the javascript section.
Adding translations
-------------------
-Nextcloud's translation system is powered by `Transifex `_. To start translating sign up and enter a group. If your community app should be translated by the `Nextcloud community on Transifex `_ 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):
-
-.. code-block:: ini
-
- [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//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 `_ needs to get ``write`` access to your repository.
-You can invite it from your repository settings:
-
- ``https://github.com///settings/access``
-
-After sending the invitation, please `open a ticket using the "Request translations" template `_.
-
-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 `_
- for those branches. If you do that, you need to grant the
- `@nextcloud-bot `_ ``admin`` permissions,
- but that is only possible for repositories owned by organizations.
- You can `create your own organization `_
-
-If you need help just `open a ticket with the request `_
-and we can also guide you through the steps.
-
-.. _manual-translation:
-
-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 `_
-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 such
-as `Poedit `_. It 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.
+The steps how to set up translations for an app have been moved to it's own page in the "App development" chapter: :ref:`Translation setup`
diff --git a/developer_manual/conf.py b/developer_manual/conf.py
index 9859b7fac..24623a67e 100644
--- a/developer_manual/conf.py
+++ b/developer_manual/conf.py
@@ -325,5 +325,7 @@ redirects = {
"core/static-analysis": "../server/static-analysis.html",
"core/unit-testing": "../server/unit-testing.html",
# Removed 2024-09
- "digging_deeper/changelog": "../app_publishing_maintenance/app_upgrade_guide/index.html"
+ "digging_deeper/changelog": "../app_publishing_maintenance/app_upgrade_guide/index.html",
+ # Removed 2025-04
+ "basics/front-end/l10n": "../translations.html"
}
diff --git a/developer_manual/exapp_development/tech_details/Translations.rst b/developer_manual/exapp_development/tech_details/Translations.rst
index e3cd9685f..ad6ca880f 100644
--- a/developer_manual/exapp_development/tech_details/Translations.rst
+++ b/developer_manual/exapp_development/tech_details/Translations.rst
@@ -1,7 +1,7 @@
Translations
============
-ExApps translations work in the :doc:`same way as for PHP apps <../../basics/front-end/l10n>` with a few adjustments and differences.
+ExApps translations work in the :ref:`same way as for PHP apps` with a few adjustments and differences.
In short, you just have to provide the ``l10n/.js`` (for front-end) and ``l10n/.json`` (for back-end) files for your app.
diff --git a/developer_manual/exapp_development/tech_details/api/events_listener.rst b/developer_manual/exapp_development/tech_details/api/events_listener.rst
index 77e562a3c..215540f79 100644
--- a/developer_manual/exapp_development/tech_details/api/events_listener.rst
+++ b/developer_manual/exapp_development/tech_details/api/events_listener.rst
@@ -4,7 +4,7 @@
Events Listener
===============
-This API allows you to listen to :doc:`Nextcloud events <../../../basics/events>`.
+This API allows you to listen to :ref:`Nextcloud events `.
Currently only a **limited** number of events are supported.