From c5aa7ada4fa03194221f6064dce935d991dbd2ee Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 29 Jan 2019 10:40:19 +0100 Subject: [PATCH 001/235] Fixed extra v-translate call --- front/src/components/Footer.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/components/Footer.vue b/front/src/components/Footer.vue index 2767167e2..795e9f40a 100644 --- a/front/src/components/Footer.vue +++ b/front/src/components/Footer.vue @@ -3,7 +3,7 @@
-

+

About %{instanceName}

@@ -62,7 +63,8 @@ export default { return { playNow: this.$gettext('Play now'), addToQueue: this.$gettext('Add to current queue'), - playNext: this.$gettext('Play next') + playNext: this.$gettext('Play next'), + startRadio: this.$gettext('Play similar songs') } }, title () { From c16258ed14ab8a33b17cb8bb1f5de894f8e8a733 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Thu, 31 Jan 2019 14:55:05 +0100 Subject: [PATCH 005/235] Removed popularity weight in similar radio, to avoid filter bubbles --- api/funkwhale_api/radios/radios.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/funkwhale_api/radios/radios.py b/api/funkwhale_api/radios/radios.py index 9ccb94e4f..8ca15a026 100644 --- a/api/funkwhale_api/radios/radios.py +++ b/api/funkwhale_api/radios/radios.py @@ -221,7 +221,7 @@ class SimilarRadio(RelatedObjectRadio): next_candidates = [n for n in next_candidates if n[0] in matching_tracks] if not next_candidates: raise NextNotFound() - return weighted_choice(next_candidates) + return random.choice([c[0] for c in next_candidates]) @registry.register(name="artist") From d1d034fa86951a7ee9b31ca8538114f6e47be66f Mon Sep 17 00:00:00 2001 From: Zach Halasz Date: Mon, 4 Feb 2019 06:53:28 +0100 Subject: [PATCH 006/235] Update /usr/bin/nologin to /usr/sbin/nologin --- docs/installation/debian.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation/debian.rst b/docs/installation/debian.rst index 7f6142a64..5da6c0495 100644 --- a/docs/installation/debian.rst +++ b/docs/installation/debian.rst @@ -51,7 +51,7 @@ Create the user and the directory: .. code-block:: shell - sudo useradd -r -s /usr/bin/nologin -d /srv/funkwhale -m funkwhale + sudo useradd -r -s /usr/sbin/nologin -d /srv/funkwhale -m funkwhale cd /srv/funkwhale Log in as the newly created user from now on: From 2fe403ed9a8d6d8f2bf336f6cc3902a09853c443 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 5 Feb 2019 17:59:22 +0100 Subject: [PATCH 007/235] See #662: documentation about i18n / contexts, and first contextualized strings --- CONTRIBUTING.rst | 156 +++++++++++++++++++++- front/src/components/audio/PlayButton.vue | 32 +++-- front/src/components/auth/Signup.vue | 25 ++-- 3 files changed, 185 insertions(+), 28 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 19f034b9f..35a0ebddf 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -353,12 +353,160 @@ Internationalization -------------------- We're using https://github.com/Polyconseil/vue-gettext to manage i18n in the project. -When working on the front-end, any end-user string should be translated -using either ``yourstring`` or ``$gettext('yourstring')`` -function. +When working on the front-end, any end-user string should be marked as a translatable string, +with the proper context, as described below. + +Translations in HTML +^^^^^^^^^^^^^^^^^^^^ + +Translations in HTML use the ```` tag:: + + + +Anything between the `` and `` delimiters will be considered as a translatable string. +You can use variables in the translated string via the ``:translate-params="{var: 'value'}"`` directive, and reference them like this: +``val value is %{ value }``. + +For pluralization, you need to use ``translate-params`` in conjunction with ``translate-plural`` and ``translate-n``: + +- ``translate-params`` should contain the variable you're using for pluralization (which is usually shown to the user) +- ``translate-n`` should match the same variable +- The ```` delimiters contain the non-pluralized version of your string +- The ``translate-plural`` directive contains the pluralized version of your string + + +Translations in javascript +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Translations in javascript work by calling the ``this.$*gettext`` functions:: + + export default { + computed: { + strings () { + let tracksCount = 42 + let playButton = this.$pgettext('Sidebar/Player/Button/Verb, Short', 'Play') + let loginMessage = this.$pgettext('*/Login/Message', 'Welcome back %{ username }') + let addedMessage = this.$npgettext('*/Player/Message', 'One track was queued', '%{ count } tracks were queued', tracksCount) + console.log(this.$gettextInterpolate(addedMessage, {count: tracksCount})) + console.log(this.$gettextInterpolate(loginMessage, {username: 'alice'})) + } + } + } + +The first argument of the ``$pgettext`` and ``$npgettext`` functions is the string context. + +Contextualization +^^^^^^^^^^^^^^^^^ + +Translation contexts provided via the ``translate-context`` directive and the ``$pgettext`` and ``$npgettext`` are never shown to end users +but visible by Funkwhale translators. They help translators where and how the strings are used, +especially with short or ambiguous strings, like ``May``, which can refer a month or a verb. + +While we could in theory use free form context, like ``This string is inside a button, in the main page, and is a call to action``, +Funkwhale use a hierarchical structure to write contexts and keep them short and consistents accross the app. The previous context, +rewritten correctly would be: ``Content/Home/Button/Call to action``. + +This hierarchical structure is made of several parts: + +- The location part, which is required and refers to the big blocks found in Funkwhale UI where the translated string is displayed: + - ``Content`` + - ``Footer`` + - ``Menu`` + - ``Modal`` + - ``Sidebar`` + - ``*`` for strings that are not tied to a specific location + +- The feature part, which is required, and refers to the feature associated with the translated string: + - ``About`` + - ``Admin`` + - ``Album`` + - ``Artist`` + - ``Home`` + - ``Login`` + - ``Moderation`` + - ``Player`` + - ``Playlist`` + - ``Notifications`` + - ``Radio`` + - ``Settings`` + - ``Signup`` + - ``Track`` + - ``Queue`` + - ``*`` for strings that are not tied to a specific feature + +- The component part, which is required and refers to the type of element that contain the string: + - ``Button`` + - ``Card`` + - ``Dropdown`` + - ``Form`` + - ``Header`` + - ``Help text`` + - ``Icon`` + - ``Input`` + - ``Image`` + - ``Label`` + - ``Link`` + - ``List item`` + - ``Message`` + - ``Paragraph`` + - ``Placeholder`` + - ``Tab`` + - ``Table`` + - ``Title`` + - ``Tooltip`` + - ``*`` for strings that are not tied to a specific component + +The detail part, which is optional and refers to the contents of the string itself, such as: + - ``Call to action`` + - ``Verb`` + - ``Short`` + +Here are a few examples of valid context hierarchies: + +- ``Sidebar/Player/Button/Title`` +- ``Content/Home/Button/Call to action`` +- ``Footer/*/Help text`` +- ``*/*/*/Verb, Short`` +- ``Modal/Playlist/Button`` + +It's possible to nest multiple component parts to reach a higher level of detail: + +- ``Sidebar/Queue/Tab/Title`` +- ``Content/*/Button/Title`` +- ``Content/*/Table/Header`` +- ``Footer/*/List item/Link`` +- ``Content/*/Form/Help text`` + +Collecting translatable strings +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you want to ensure your translatable strings are correctly marked for translation, +you can try to extract them. Extraction is done by calling ``yarn run i18n-extract``, which -will pull all the strings from source files and put them in a PO file. +will pull all the strings from source files and put them in a PO files. + +You can then inspect the PO files to ensure everything is fine (but don't commit them, it's not needed). Contributing to the API ----------------------- diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue index 07cb1f585..425d0a1d7 100644 --- a/front/src/components/audio/PlayButton.vue +++ b/front/src/components/audio/PlayButton.vue @@ -7,15 +7,23 @@ :disabled="!playable" :class="buttonClasses.concat(['ui', {loading: isLoading}, {'mini': discrete}, {disabled: !playable}])"> - +
@@ -61,18 +69,18 @@ export default { computed: { labels () { return { - playNow: this.$gettext('Play now'), - addToQueue: this.$gettext('Add to current queue'), - playNext: this.$gettext('Play next'), - startRadio: this.$gettext('Play similar songs') + playNow: this.$pgettext('*/Queue/Dropdown/Button/Title', 'Play now'), + addToQueue: this.$pgettext('*/Queue/Dropdown/Button/Title', 'Add to current queue'), + playNext: this.$pgettext('*/Queue/Dropdown/Button/Title', 'Play next'), + startRadio: this.$pgettext('*/Queue/Dropdown/Button/Title', 'Play similar songs') } }, title () { if (this.playable) { - return this.$gettext('Play...') + return this.$pgettext('*/Queue/Button/Title', 'Play...') } else { if (this.track) { - return this.$gettext('This track is not available in any library you have access to') + return this.$pgettext('*/Queue/Button/Title', 'This track is not available in any library you have access to') } } }, @@ -179,7 +187,7 @@ export default { if (tracks.length < 1) { return } - let msg = this.$ngettext('%{ count } track was added to your queue', '%{ count } tracks were added to your queue', tracks.length) + let msg = this.$npgettext('*/Queue/Message', '%{ count } track was added to your queue', '%{ count } tracks were added to your queue', tracks.length) this.$store.commit('ui/addMessage', { content: this.$gettextInterpolate(msg, {count: tracks.length}), date: new Date() diff --git a/front/src/components/auth/Signup.vue b/front/src/components/auth/Signup.vue index 815f0253a..685be288d 100644 --- a/front/src/components/auth/Signup.vue +++ b/front/src/components/auth/Signup.vue @@ -2,22 +2,22 @@
-

Create a funkwhale account

+

Create a funkwhale account

- Registration are closed on this instance, you will need an invitation code to signup. + Registration are closed on this instance, you will need an invitation code to signup.

-
We cannot create your account
+
We cannot create your account
  • {{ error }}
- +
- +
- +
- +
@@ -94,12 +94,13 @@ export default { }, computed: { labels() { - let title = this.$gettext("Sign Up") - let placeholder = this.$gettext( + let title = this.$pgettext("*/Signup/Title", "Sign Up") + let placeholder = this.$pgettext( + "Content/Signup/Form/Placeholder", "Enter your invitation code (case insensitive)" ) - let usernamePlaceholder = this.$gettext("Enter your username") - let emailPlaceholder = this.$gettext("Enter your email") + let usernamePlaceholder = this.$pgettext("Content/Signup/Form/Placeholder", "Enter your username") + let emailPlaceholder = this.$pgettext("Content/Signup/Form/Placeholder", "Enter your email") return { title, usernamePlaceholder, From 3ca17d3fa1f9717df9b270b9dccee57e5dab8d6b Mon Sep 17 00:00:00 2001 From: jovuit Date: Thu, 7 Feb 2019 14:26:00 +0100 Subject: [PATCH 008/235] Nest multiple component parts by a dot instead of a / --- CONTRIBUTING.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 35a0ebddf..4612ce96d 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -489,13 +489,13 @@ Here are a few examples of valid context hierarchies: - ``*/*/*/Verb, Short`` - ``Modal/Playlist/Button`` -It's possible to nest multiple component parts to reach a higher level of detail: +It's possible to nest multiple component parts to reach a higher level of detail. The component parts are then separated by a dot: -- ``Sidebar/Queue/Tab/Title`` -- ``Content/*/Button/Title`` -- ``Content/*/Table/Header`` -- ``Footer/*/List item/Link`` -- ``Content/*/Form/Help text`` +- ``Sidebar/Queue/Tab.Title`` +- ``Content/*/Button.Title`` +- ``Content/*/Table.Header`` +- ``Footer/*/List item.Link`` +- ``Content/*/Form.Help text`` Collecting translatable strings ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 7d52de5cb4c40d3e9dbb2d040b2c21c5ce4c0b5a Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Thu, 7 Feb 2019 14:33:27 +0100 Subject: [PATCH 009/235] Update CONTRIBUTING.rst --- CONTRIBUTING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 4612ce96d..25ff14c2a 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -483,7 +483,7 @@ The detail part, which is optional and refers to the contents of the string itse Here are a few examples of valid context hierarchies: -- ``Sidebar/Player/Button/Title`` +- ``Sidebar/Player/Button`` - ``Content/Home/Button/Call to action`` - ``Footer/*/Help text`` - ``*/*/*/Verb, Short`` From 3bed93d6c6befe2517d409c5e72aeddf3c524d24 Mon Sep 17 00:00:00 2001 From: Jo Vuit Date: Thu, 7 Feb 2019 16:15:50 +0100 Subject: [PATCH 010/235] Add context --- front/src/components/favorites/TrackFavoriteIcon.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/front/src/components/favorites/TrackFavoriteIcon.vue b/front/src/components/favorites/TrackFavoriteIcon.vue index 690dab21b..7ba3f7fe4 100644 --- a/front/src/components/favorites/TrackFavoriteIcon.vue +++ b/front/src/components/favorites/TrackFavoriteIcon.vue @@ -1,8 +1,8 @@
@@ -87,7 +88,7 @@ export default { computed: { labels() { return { - playlist: this.$gettext("Playlist") + playlist: this.$gettext('"Playlist") } } }, From 56a5a5ed765dc01b365a1336883555b303722acf Mon Sep 17 00:00:00 2001 From: Jo Vuit Date: Thu, 7 Feb 2019 18:14:48 +0100 Subject: [PATCH 012/235] Added context strings for i18n --- front/src/views/playlists/List.vue | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/front/src/views/playlists/List.vue b/front/src/views/playlists/List.vue index 15cc94be4..b6f1e74a6 100644 --- a/front/src/views/playlists/List.vue +++ b/front/src/views/playlists/List.vue @@ -1,21 +1,21 @@