Skip to content
Snippets Groups Projects
Commit 262c40bc authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Merge branch '161-i18n-vue-gettext' into 'develop'

Resolve "Industrialize the i18n workflow"

Closes #161 and #167

See merge request funkwhale/funkwhale!286
parents a1b8555c aab048dd
No related branches found
No related tags found
No related merge requests found
Showing
with 4174 additions and 114 deletions
...@@ -91,3 +91,4 @@ data/ ...@@ -91,3 +91,4 @@ data/
po/*.po po/*.po
docs/swagger docs/swagger
_build _build
front/src/translations.json
...@@ -19,9 +19,12 @@ review_front: ...@@ -19,9 +19,12 @@ review_front:
when: manual when: manual
allow_failure: true allow_failure: true
before_script: before_script:
- apt-get update
- apt-get install jq -y
- cd front - cd front
script: script:
- yarn install - yarn install
- yarn run i18n-compile
# this is to ensure we don't have any errors in the output, # this is to ensure we don't have any errors in the output,
# cf https://code.eliotberriot.com/funkwhale/funkwhale/issues/169 # cf https://code.eliotberriot.com/funkwhale/funkwhale/issues/169
- INSTANCE_URL=$REVIEW_INSTANCE_URL yarn run build | tee /dev/stderr | (! grep -i 'ERROR in') - INSTANCE_URL=$REVIEW_INSTANCE_URL yarn run build | tee /dev/stderr | (! grep -i 'ERROR in')
...@@ -175,11 +178,11 @@ build_front: ...@@ -175,11 +178,11 @@ build_front:
stage: build stage: build
image: node:9 image: node:9
before_script: before_script:
- apt-get update
- apt-get install jq -y
- cd front - cd front
script: script:
- yarn install - yarn install
- yarn run i18n-extract
- yarn run i18n-compile - yarn run i18n-compile
# this is to ensure we don't have any errors in the output, # this is to ensure we don't have any errors in the output,
# cf https://code.eliotberriot.com/funkwhale/funkwhale/issues/169 # cf https://code.eliotberriot.com/funkwhale/funkwhale/issues/169
......
...@@ -289,8 +289,9 @@ Typical workflow for a contribution ...@@ -289,8 +289,9 @@ Typical workflow for a contribution
Internationalization 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 When working on the front-end, any end-user string should be translated
using either ``<i18next path="yourstring">`` or the ``$t('yourstring')`` using either ``<translate>yourstring</translate>`` or ``$gettext('yourstring')``
function. function.
Extraction is done by calling ``yarn run i18n-extract``, which Extraction is done by calling ``yarn run i18n-extract``, which
......
...@@ -26,4 +26,9 @@ Contribute ...@@ -26,4 +26,9 @@ Contribute
---------- ----------
Contribution guidelines as well as development installation instructions Contribution guidelines as well as development installation instructions
are outlined in `CONTRIBUTING <CONTRIBUTING>`_ are outlined in `CONTRIBUTING <CONTRIBUTING>`_.
Translate
^^^^^^^^^
Translators willing to help can refer to `TRANSLATORS <TRANSLATORS>`_ for instructions.
Translating Funkwhale
=====================
Thank you for reading this! If you want to help translate Funkwhale,
you found the proper place :)
Translation is done via our own Weblate instance at https://translate.funkwhale.audio/projects/funkwhale/front/.
You can signup/login using your Gitlab account (from https://code.eliotberriot.com).
Translation workflow
--------------------
Once you're logged-in on the Weblate instance, you can suggest translations. Your suggestions will then be reviewer
by the project maintainer or other translators to ensure consistency.
Guidelines
----------
Respecting those guidelines is mandatory if you want your translation to be included:
- Use gender-neutral language and wording
Requesting a new language
-------------------------
If you'd like to see a new language in Funkwhale, please open an issue here:
https://code.eliotberriot.com/funkwhale/funkwhale/issues
New translation workflow (#161, #167)
...@@ -23,6 +23,7 @@ Funkwhale is a self-hosted, modern free and open-source music server, heavily in ...@@ -23,6 +23,7 @@ Funkwhale is a self-hosted, modern free and open-source music server, heavily in
api api
third-party third-party
contributing contributing
translators
changelog changelog
Indices and tables Indices and tables
......
.. include:: ../TRANSLATORS.rst
...@@ -14,8 +14,6 @@ var webpackConfig = process.env.NODE_ENV === 'testing' ...@@ -14,8 +14,6 @@ var webpackConfig = process.env.NODE_ENV === 'testing'
? require('./webpack.prod.conf') ? require('./webpack.prod.conf')
: require('./webpack.dev.conf') : require('./webpack.dev.conf')
require('./i18n')
// default port where dev server listens for incoming traffic // default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port var port = process.env.PORT || config.dev.port
var host = process.env.HOST || config.dev.host var host = process.env.HOST || config.dev.host
......
const fs = require('fs');
const path = require('path');
const { gettextToI18next } = require('i18next-conv');
const poDir = path.join(__dirname, '..', '..', 'po')
const outDir = path.join(__dirname, '..', 'static', 'translations')
if (!fs.existsSync(outDir) || !fs.statSync(outDir).isDirectory()) {
fs.mkdirSync(outDir)
}
// Convert .po files to i18next files
fs.readdir(poDir, (err, files) => {
if (err) {
return console.log(err)
}
for (const file of files) {
if (file.endsWith('.po')) {
const lang = file.replace(/\.po$/, '')
const output = path.join(outDir, `${lang}.json`)
fs.readFile(path.join(poDir, file), (err, content) => {
if (err) {
return console.log(err)
}
gettextToI18next(lang, content).then(res => {
fs.writeFile(output, res, err => {
if (err) {
console.log(err)
} else {
console.log(`Wrote translation file: ${output}`)
if (lang === 'en') {
// for english, we need to specify that json values are equal to the keys.
// otherwise we end up with empty strings on the front end for english
var contents = fs.readFileSync(output)
var jsonContent = JSON.parse(contents)
var finalContent = {}
Object.keys(jsonContent).forEach(function(key) {
finalContent[key] = key
})
fs.writeFile(output, JSON.stringify(finalContent))
}
}
})
})
})
}
}
})
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -5,11 +5,11 @@ ...@@ -5,11 +5,11 @@
"author": "Eliot Berriot <contact@eliotberriot.com>", "author": "Eliot Berriot <contact@eliotberriot.com>",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "node build/dev-server.js", "dev": "scripts/i18n-compile.sh && node build/dev-server.js",
"start": "node build/dev-server.js", "start": "scripts/i18n-compile.sh && node build/dev-server.js",
"build": "node build/build.js", "build": "node build/build.js",
"i18n-extract": "find src/ -name '*.vue' | xargs vendor/vue-i18n-xgettext/index.js > ../po/en.po", "i18n-extract": "scripts/i18n-extract.sh",
"i18n-compile": "node build/i18n.js", "i18n-compile": "scripts/i18n-compile.sh",
"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
"unit-watch": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js", "unit-watch": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js",
"e2e": "node test/e2e/runner.js", "e2e": "node test/e2e/runner.js",
...@@ -21,9 +21,6 @@ ...@@ -21,9 +21,6 @@
"axios": "^0.17.1", "axios": "^0.17.1",
"dateformat": "^2.0.0", "dateformat": "^2.0.0",
"django-channels": "^1.1.6", "django-channels": "^1.1.6",
"i18next": "^11.1.1",
"i18next-conv": "^6.0.0",
"i18next-fetch-backend": "^0.1.0",
"js-logger": "^1.3.0", "js-logger": "^1.3.0",
"jwt-decode": "^2.2.0", "jwt-decode": "^2.2.0",
"lodash": "^4.17.4", "lodash": "^4.17.4",
...@@ -34,6 +31,7 @@ ...@@ -34,6 +31,7 @@
"semantic-ui-css": "^2.2.10", "semantic-ui-css": "^2.2.10",
"showdown": "^1.8.6", "showdown": "^1.8.6",
"vue": "^2.5.16", "vue": "^2.5.16",
"vue-gettext": "^2.0.31",
"vue-lazyload": "^1.1.4", "vue-lazyload": "^1.1.4",
"vue-masonry": "^0.10.16", "vue-masonry": "^0.10.16",
"vue-router": "^2.3.1", "vue-router": "^2.3.1",
...@@ -61,6 +59,7 @@ ...@@ -61,6 +59,7 @@
"cross-env": "^4.0.0", "cross-env": "^4.0.0",
"cross-spawn": "^5.0.1", "cross-spawn": "^5.0.1",
"css-loader": "^0.28.0", "css-loader": "^0.28.0",
"easygettext": "^2.5.0",
"es6-promise": "^4.2.2", "es6-promise": "^4.2.2",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-standard": "^6.2.1", "eslint-config-standard": "^6.2.1",
...@@ -104,7 +103,6 @@ ...@@ -104,7 +103,6 @@
"sinon-chai": "^2.8.0", "sinon-chai": "^2.8.0",
"sinon-stub-promise": "^4.0.0", "sinon-stub-promise": "^4.0.0",
"url-loader": "^0.5.8", "url-loader": "^0.5.8",
"vue-i18n-xgettext": "^0.0.4",
"vue-loader": "^12.1.0", "vue-loader": "^12.1.0",
"vue-style-loader": "^3.0.1", "vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.3.3", "vue-template-compiler": "^2.3.3",
......
#!/bin/bash -eux
locales=$(tail -n +2 src/locales.js | sed -e 's/export default //' | jq '.locales[].code' | xargs echo)
find locales -name '*.po' | xargs $(yarn bin gettext-extract)/gettext-compile --output src/translations.json
#!/bin/bash -eux
locales=$(tail -n +2 src/locales.js | sed -e 's/export default //' | jq '.locales[].code' | xargs echo)
locales_dir="locales"
sources=$(find src -name '*.vue' -o -name '*.html' 2> /dev/null)
js_sources=$(find src -name '*.vue' -o -name '*.js')
touch $locales_dir/app.pot
# Create a main .pot template, then generate .po files for each available language.
# Extract gettext strings from templates files and create a POT dictionary template.
$(yarn bin gettext-extract)/gettext-extract --attribute v-translate --quiet --output $locales_dir/app.pot $sources
xgettext --language=JavaScript --keyword=npgettext:1c,2,3 \
--from-code=utf-8 --join-existing --no-wrap \
--package-name=$(node -e "console.log(require('./package.json').name);") \
--package-version=$(node -e "console.log(require('./package.json').version);") \
--output $locales_dir/app.pot $js_sources
# Fix broken files path/lines in pot
sed -e 's|#: src/|#: front/src/|' -i $locales_dir/app.pot
# Generate .po files for each available language.
echo $locales
for lang in $locales; do \
po_file=$locales_dir/$lang/LC_MESSAGES/app.po; \
echo "msgmerge --update $po_file "; \
mkdir -p $(dirname $po_file); \
[ -f $po_file ] && msgmerge --lang=$lang --update $po_file $locales_dir/app.pot || msginit --no-translator --locale=$lang --input=$locales_dir/app.pot --output-file=$po_file; \
msgattrib --no-wrap --no-obsolete -o $po_file $po_file; \
done;
...@@ -2,14 +2,14 @@ ...@@ -2,14 +2,14 @@
<div id="app"> <div id="app">
<div class="ui main text container instance-chooser" v-if="!$store.state.instance.instanceUrl"> <div class="ui main text container instance-chooser" v-if="!$store.state.instance.instanceUrl">
<div class="ui padded segment"> <div class="ui padded segment">
<h1 class="ui header">{{ $t('Choose your instance') }}</h1> <h1 class="ui header">{{ $gettext('Choose your instance') }}</h1>
<form class="ui form" @submit.prevent="$store.dispatch('instance/setUrl', instanceUrl)"> <form class="ui form" @submit.prevent="$store.dispatch('instance/setUrl', instanceUrl)">
<p>{{ $t('You need to select an instance in order to continue') }}</p> <p>{{ $gettext('You need to select an instance in order to continue') }}</p>
<div class="ui action input"> <div class="ui action input">
<input type="text" v-model="instanceUrl"> <input type="text" v-model="instanceUrl">
<button type="submit" class="ui button">{{ $t('Submit') }}</button> <button type="submit" class="ui button">{{ $gettext('Submit') }}</button>
</div> </div>
<p>{{ $t('Suggested choices') }}</p> <p>{{ $gettext('Suggested choices') }}</p>
<div class="ui bulleted list"> <div class="ui bulleted list">
<div class="ui item" v-for="url in suggestedInstances"> <div class="ui item" v-for="url in suggestedInstances">
<a @click="instanceUrl = url">{{ url }}</a> <a @click="instanceUrl = url">{{ url }}</a>
...@@ -27,20 +27,20 @@ ...@@ -27,20 +27,20 @@
<div class="ui container"> <div class="ui container">
<div class="ui stackable equal height stackable grid"> <div class="ui stackable equal height stackable grid">
<div class="three wide column"> <div class="three wide column">
<i18next tag="h4" class="ui header" path="Links"></i18next> <h4 v-translate class="ui header">Links</h4>
<div class="ui link list"> <div class="ui link list">
<router-link class="item" to="/about"> <router-link class="item" to="/about">
<i18next path="About this instance" /> {{ $gettext('About this instance') }}
</router-link> </router-link>
<a href="https://funkwhale.audio" class="item" target="_blank">{{ $t('Official website') }}</a> <a href="https://funkwhale.audio" class="item" target="_blank">{{ $gettext('Official website') }}</a>
<a href="https://docs.funkwhale.audio" class="item" target="_blank">{{ $t('Documentation') }}</a> <a href="https://docs.funkwhale.audio" class="item" target="_blank">{{ $gettext('Documentation') }}</a>
<a href="https://code.eliotberriot.com/funkwhale/funkwhale" class="item" target="_blank"> <a href="https://code.eliotberriot.com/funkwhale/funkwhale" class="item" target="_blank">
<template v-if="version">{{ $t('Source code ({% version %})', {version: version}) }}</template> <translate :translate-params="{version: version}" v-if="version">Source code (%{version})</translate>
<template v-else>{{ $t('Source code') }}</template> <translate v-else>Source code</translate>
</a> </a>
<a href="https://code.eliotberriot.com/funkwhale/funkwhale/issues" class="item" target="_blank">{{ $t('Issue tracker') }}</a> <a href="https://code.eliotberriot.com/funkwhale/funkwhale/issues" class="item" target="_blank">{{ $gettext('Issue tracker') }}</a>
<a @click="switchInstance" class="item" > <a @click="switchInstance" class="item" >
{{ $t('Use another instance') }} {{ $gettext('Use another instance') }}
<template v-if="$store.state.instance.instanceUrl !== '/'"> <template v-if="$store.state.instance.instanceUrl !== '/'">
<br> <br>
({{ $store.state.instance.instanceUrl }}) ({{ $store.state.instance.instanceUrl }})
...@@ -49,14 +49,26 @@ ...@@ -49,14 +49,26 @@
</div> </div>
</div> </div>
<div class="ten wide column"> <div class="ten wide column">
<i18next tag="h4" class="ui header" path="About funkwhale" /> <h4 v-translate class="ui header">About Funkwhale</h4>
<p> <p>
<i18next path="Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!"/> <translate>Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!</translate>
</p> </p>
<p> <p>
<i18next path="The funkwhale logo was kindly designed and provided by Francis Gading."/> <translate>The funkwhale logo was kindly designed and provided by Francis Gading.</translate>
</p> </p>
</div> </div>
<div class="three wide column">
<h4 v-translate class="ui header">Options</h4>
<div class="ui form">
<div class="ui field">
<label>{{ $gettext('Change language') }}</label>
<select class="ui dropdown" v-model="$language.current">
<option v-for="(language, key) in $language.available" :value="key">{{ language }}</option>
</select>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -115,7 +127,7 @@ export default { ...@@ -115,7 +127,7 @@ export default {
}) })
}, },
switchInstance () { switchInstance () {
let confirm = window.confirm(this.$t('This will erase your local data and disconnect you, do you want to continue?')) let confirm = window.confirm(this.$gettext('This will erase your local data and disconnect you, do you want to continue?'))
if (confirm) { if (confirm) {
this.$store.commit('instance/instanceUrl', null) this.$store.commit('instance/instanceUrl', null)
} }
...@@ -144,6 +156,9 @@ export default { ...@@ -144,6 +156,9 @@ export default {
'$store.state.instance.instanceUrl' () { '$store.state.instance.instanceUrl' () {
this.$store.dispatch('instance/fetchSettings') this.$store.dispatch('instance/fetchSettings')
this.fetchNodeInfo() this.fetchNodeInfo()
},
'$language.current' (newValue) {
this.$store.commit('ui/currentLanguage', newValue)
} }
} }
} }
......
...@@ -3,21 +3,23 @@ ...@@ -3,21 +3,23 @@
<div class="ui vertical center aligned stripe segment"> <div class="ui vertical center aligned stripe segment">
<div class="ui text container"> <div class="ui text container">
<h1 class="ui huge header"> <h1 class="ui huge header">
<template v-if="instance.name.value">{{ $t('About {%instance%}', { instance: instance.name.value }) }}</template> <template v-if="instance.name.value" :template-params="{instance: instance.name}">
<template v-else="instance.name.value">{{ $t('About this instance') }}</template> About %{ instance }
</template>
<template v-else="instance.name.value">{{ $gettext('About this instance') }}</template>
</h1> </h1>
<stats></stats> <stats></stats>
</div> </div>
</div> </div>
<div class="ui vertical stripe segment"> <div class="ui vertical stripe segment">
<p v-if="!instance.short_description.value && !instance.long_description.value"> <p v-if="!instance.short_description.value && !instance.long_description.value">
{{ $t('Unfortunately, owners of this instance did not yet take the time to complete this page.') }} {{ $gettext('Unfortunately, owners of this instance did not yet take the time to complete this page.') }}
</p> </p>
<router-link <router-link
class="ui button" class="ui button"
v-if="$store.state.auth.availablePermissions['settings']" v-if="$store.state.auth.availablePermissions['settings']"
:to="{path: '/manage/settings', hash: 'instance'}"> :to="{path: '/manage/settings', hash: 'instance'}">
<i class="pencil icon"></i>{{ $t('Edit instance info') }} <i class="pencil icon"></i>{{ $gettext('Edit instance info') }}
</router-link> </router-link>
<div <div
v-if="instance.short_description.value" v-if="instance.short_description.value"
......
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
<div class="ui vertical center aligned stripe segment"> <div class="ui vertical center aligned stripe segment">
<div class="ui text container"> <div class="ui text container">
<h1 class="ui huge header"> <h1 class="ui huge header">
{{ $t('Welcome on Funkwhale') }} {{ $gettext('Welcome on Funkwhale') }}
</h1> </h1>
<p>{{ $t('We think listening to music should be simple.') }}</p> <p>{{ $gettext('We think listening to music should be simple.') }}</p>
<router-link class="ui icon button" to="/about"> <router-link class="ui icon button" to="/about">
<i class="info icon"></i> <i class="info icon"></i>
{{ $t('Learn more about this instance') }} {{ $gettext('Learn more about this instance') }}
</router-link> </router-link>
<router-link class="ui icon teal button" to="/library"> <router-link class="ui icon teal button" to="/library">
{{ $t('Get me to the library') }} {{ $gettext('Get me to the library') }}
<i class="right arrow icon"></i> <i class="right arrow icon"></i>
</router-link> </router-link>
</div> </div>
...@@ -22,9 +22,9 @@ ...@@ -22,9 +22,9 @@
<div class="row"> <div class="row">
<div class="eight wide left floated column"> <div class="eight wide left floated column">
<h2 class="ui header"> <h2 class="ui header">
{{ $t('Why funkwhale?') }} {{ $gettext('Why funkwhale?') }}
</h2> </h2>
<p>{{ $t('That\'s simple: we loved Grooveshark and we want to build something even better.') }}</p> <p>{{ $gettext('That\'s simple: we loved Grooveshark and we want to build something even better.') }}</p>
</div> </div>
<div class="four wide left floated column"> <div class="four wide left floated column">
<img class="ui medium image" src="../assets/logo/logo.png" /> <img class="ui medium image" src="../assets/logo/logo.png" />
...@@ -35,26 +35,26 @@ ...@@ -35,26 +35,26 @@
<div class="ui middle aligned stackable text container"> <div class="ui middle aligned stackable text container">
<div class="ui hidden divider"></div> <div class="ui hidden divider"></div>
<h2 class="ui header"> <h2 class="ui header">
{{ $t('Unlimited music') }} {{ $gettext('Unlimited music') }}
</h2> </h2>
<p>{{ $t('Funkwhale is designed to make it easy to listen to music you like, or to discover new artists.') }}</p> <p>{{ $gettext('Funkwhale is designed to make it easy to listen to music you like, or to discover new artists.') }}</p>
<div class="ui list"> <div class="ui list">
<div class="item"> <div class="item">
<i class="sound icon"></i> <i class="sound icon"></i>
<div class="content"> <div class="content">
{{ $t('Click once, listen for hours using built-in radios') }} {{ $gettext('Click once, listen for hours using built-in radios') }}
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<i class="heart icon"></i> <i class="heart icon"></i>
<div class="content"> <div class="content">
{{ $t('Keep a track of your favorite songs') }} {{ $gettext('Keep a track of your favorite songs') }}
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<i class="list icon"></i> <i class="list icon"></i>
<div class="content"> <div class="content">
{{ $t('Playlists? We got them') }} {{ $gettext('Playlists? We got them') }}
</div> </div>
</div> </div>
</div> </div>
...@@ -62,28 +62,31 @@ ...@@ -62,28 +62,31 @@
<div class="ui middle aligned stackable text container"> <div class="ui middle aligned stackable text container">
<div class="ui hidden divider"></div> <div class="ui hidden divider"></div>
<h2 class="ui header"> <h2 class="ui header">
{{ $t('Clean library') }} {{ $gettext('Clean library') }}
</h2> </h2>
<p>{{ $t('Funkwhale takes care of handling your music') }}.</p> <p>{{ $gettext('Funkwhale takes care of handling your music') }}.</p>
<div class="ui list"> <div class="ui list">
<div class="item"> <div class="item">
<i class="download icon"></i> <i class="download icon"></i>
<div class="content"> <div class="content">
{{ $t('Import music from various platforms, such as YouTube or SoundCloud') }} {{ $gettext('Import music from various platforms, such as YouTube or SoundCloud') }}
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<i class="tag icon"></i> <i class="tag icon"></i>
<div class="content"> <div class="content">
<i18next path="Get quality metadata about your music thanks to {%0%}"> <template v-translate>
<a href="https://musicbrainz.org" target="_blank">{{ $t('MusicBrainz') }}</a> Get quality metadata about your music thanks to
</i18next> <a href="https://musicbrainz.org" target="_blank">
MusicBrainz
</a>
</template>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<i class="plus icon"></i> <i class="plus icon"></i>
<div class="content"> <div class="content">
{{ $t('Covers, lyrics, our goal is to have them all ;)') }} {{ $gettext('Covers, lyrics, our goal is to have them all ;)') }}
</div> </div>
</div> </div>
</div> </div>
...@@ -91,20 +94,20 @@ ...@@ -91,20 +94,20 @@
<div class="ui middle aligned stackable text container"> <div class="ui middle aligned stackable text container">
<div class="ui hidden divider"></div> <div class="ui hidden divider"></div>
<h2 class="ui header"> <h2 class="ui header">
{{ $t('Easy to use') }} {{ $gettext('Easy to use') }}
</h2> </h2>
<p>{{ $t('Funkwhale is dead simple to use.') }}</p> <p>{{ $gettext('Funkwhale is dead simple to use.') }}</p>
<div class="ui list"> <div class="ui list">
<div class="item"> <div class="item">
<i class="book icon"></i> <i class="book icon"></i>
<div class="content"> <div class="content">
{{ $t('No add-ons, no plugins : you only need a web library') }} {{ $gettext('No add-ons, no plugins : you only need a web library') }}
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<i class="wizard icon"></i> <i class="wizard icon"></i>
<div class="content"> <div class="content">
{{ $t('Access your music from a clean interface that focus on what really matters') }} {{ $gettext('Access your music from a clean interface that focus on what really matters') }}
</div> </div>
</div> </div>
</div> </div>
...@@ -112,26 +115,26 @@ ...@@ -112,26 +115,26 @@
<div class="ui middle aligned stackable text container"> <div class="ui middle aligned stackable text container">
<div class="ui hidden divider"></div> <div class="ui hidden divider"></div>
<h2 class="ui header"> <h2 class="ui header">
{{ $t('Your music, your way') }} {{ $gettext('Your music, your way') }}
</h2> </h2>
<p>{{ $t('Funkwhale is free and gives you control on your music.') }}</p> <p>{{ $gettext('Funkwhale is free and gives you control on your music.') }}</p>
<div class="ui list"> <div class="ui list">
<div class="item"> <div class="item">
<i class="smile icon"></i> <i class="smile icon"></i>
<div class="content"> <div class="content">
{{ $t('The plaform is free and open-source, you can install it and modify it without worries') }} {{ $gettext('The plaform is free and open-source, you can install it and modify it without worries') }}
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<i class="protect icon"></i> <i class="protect icon"></i>
<div class="content"> <div class="content">
{{ $t('We do not track you or bother you with ads') }} {{ $gettext('We do not track you or bother you with ads') }}
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<i class="users icon"></i> <i class="users icon"></i>
<div class="content"> <div class="content">
{{ $t('You can invite friends and family to your instance so they can enjoy your music') }} {{ $gettext('You can invite friends and family to your instance so they can enjoy your music') }}
</div> </div>
</div> </div>
</div> </div>
......
...@@ -5,13 +5,14 @@ ...@@ -5,13 +5,14 @@
<h1 class="ui huge header"> <h1 class="ui huge header">
<i class="warning icon"></i> <i class="warning icon"></i>
<div class="content"> <div class="content">
<strike>{{ $t('Whale') }}</strike> {{ $t('Page not found!') }} <strike>{{ $gettext('Whale') }}</strike> {{ $gettext('Page not found!') }}
</div> </div>
</h1> </h1>
<p>{{ $t('We\'re sorry, the page you asked for does not exists.') }}</p> <p>{{ $gettext('We\'re sorry, the page you asked for does not exist:') }}</p>
<i18next path="Requested URL: {%0%}"><a :href="path">{{ path }}</a></i18next> <a :href="path">{{ path }}</a>
<div class="ui hidden divider"></div>
<router-link class="ui icon button" to="/"> <router-link class="ui icon button" to="/">
{{ $t('Go to home page') }} {{ $gettext('Go to home page') }}
<i class="right arrow icon"></i> <i class="right arrow icon"></i>
</router-link> </router-link>
</div> </div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment