Add ability to change language

This commit is contained in:
Chocobozzz 2020-09-02 10:17:50 +02:00
parent 581f3a6fa2
commit fa06aaea30
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
15 changed files with 198 additions and 42 deletions

View File

@ -7,9 +7,9 @@ $ git submodule update --init --recursive
$ yarn install --pure-lockfile $ yarn install --pure-lockfile
``` ```
The database (Elastic Search) is automatically created by PeerTube on startup. The database (Elastic Search) is automatically created by PeerTube at startup.
Run simultaneously (for example with 2 terminals): Run simultaneously (for example with 3 terminals):
```terminal ```terminal
$ tsc -w $ tsc -w
@ -19,7 +19,11 @@ $ tsc -w
$ node dist/server $ node dist/server
``` ```
Then open http://localhost:3234. ```
$ cd client && npm run serve
```
Then open http://localhost:8080.
## Production ## Production

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" transform="scale(1.2)" viewBox="0 0 200 200">
<defs></defs>
<path stroke="#000" stroke-width="3" d="M93 155H42a18 18 0 01-18-18V29a5 5 0 015-5h89a5 5 0 015 6L98 151a5 5 0 01-5 4zM34 34v103a8 8 0 008 8h47l22-111z"></path>
<path stroke="#000" stroke-width="3" d="M171 176H75a5 5 0 01-5-6l4-21a5 5 0 0110 2l-3 15h85V63a8 8 0 00-8-8h-45a5 5 0 010-10h45a18 18 0 0118 18v108a5 5 0 01-5 5zM50 92h0a5 5 0 01-5-5V63a17 17 0 0135 0v24a5 5 0 01-10 0V62a7 7 0 00-15 0v25a5 5 0 01-5 5z"></path>
<path stroke="#000" stroke-width="3" d="M75 76H50a5 5 0 010-10h25a5 5 0 010 10zM120 155a5 5 0 01-3-9l21-21h-18a5 5 0 010-10h30a5 5 0 014 9l-30 30a5 5 0 01-4 1z"></path>
<path stroke="#000" stroke-width="3" d="M150 155a5 5 0 01-4-1l-14-15a5 5 0 017-7l15 14a5 5 0 01-4 9zM143 110h-15a5 5 0 110-10h15a5 5 0 010 10z"></path>
</svg>

After

Width:  |  Height:  |  Size: 860 B

View File

@ -2,7 +2,7 @@
<footer id="main-footer"> <footer id="main-footer">
<div class="header"> <div class="header">
<div class="title">PeerTube</div> <img src="/img/bottom-peertube-logo.svg" alt="">
<div v-translate class="description">A free software to take back control of your videos</div> <div v-translate class="description">A free software to take back control of your videos</div>
</div> </div>
@ -11,19 +11,23 @@
<div> <div>
<div v-translate class="subtitle">Open your own videos website with PeerTube!</div> <div v-translate class="subtitle">Open your own videos website with PeerTube!</div>
<a v-translate href="">Install PeerTube</a> <a target="_blank" v-translate href="https://docs.joinpeertube.org/#/install-any-os">Install PeerTube</a>
<a v-translate href="">Why should I have my own PeerTube website?</a> <a target="_blank" v-translate href="https://joinpeertube.org#what-is-peertube">Why should I have my own PeerTube website?</a>
</div> </div>
<div> <div>
<div v-translate class="subtitle">Create an account to take back control of your videos</div> <div v-translate class="subtitle">Create an account to take back control of your videos</div>
<a v-translate href="">Open an account on a PeerTube website</a> <a target="_blank" v-translate href="https://joinpeertube.org/instances">Open an account on a PeerTube website</a>
<a v-translate href="">Create playlists</a> <a target="_blank" v-translate href="https://docs.joinpeertube.org/#/use-library?id=playlist">Create playlists</a>
</div> </div>
</div> </div>
<div class="footer">
<a href="https://joinpeertube.org" target="_blank" v-translate> &gt;&gt; Check all guides on joinpeertube.org &lt;&lt; </a>
</div>
</footer> </footer>
</template> </template>
@ -43,14 +47,17 @@
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
}
.title { img {
font-size: 36px; width: 100%;
max-width: 500px;
margin-bottom: 20px;
}
} }
.description { .description {
color: $grey; color: $grey;
text-align: center;
} }
.columns { .columns {
@ -89,6 +96,17 @@
} }
} }
.footer {
margin-top: 50px;
a {
font-size: 20px;
font-weight: bold;
text-decoration: none;
text-align: center;
}
}
@media screen and (max-width: $container-width) { @media screen and (max-width: $container-width) {
width: 100%; width: 100%;
} }
@ -103,6 +121,10 @@
.subtitle { .subtitle {
font-size: 16px; font-size: 16px;
} }
.footer a {
font-size: 16px;
}
} }
} }
</style> </style>

View File

@ -1,11 +1,13 @@
<template> <template>
<div> <div>
<header> <header>
<interface-language-dropdown class="interface-language-dropdown"></interface-language-dropdown>
<h1>{{indexName}}</h1> <h1>{{indexName}}</h1>
<h4 v-translate>A video index developed by <a href="https://framasoft.org" target="_blank">Framasoft</a></h4> <h4 v-translate>A video index developed by <a href="https://framasoft.org" target="_blank">Framasoft</a></h4>
<img src="/img/search-home.png" alt=""> <img class="search-home" src="/img/search-home.png" alt="">
</header> </header>
</div> </div>
</template> </template>
@ -21,7 +23,14 @@
margin-bottom: 30px; margin-bottom: 30px;
font-family: monospace; font-family: monospace;
img { .interface-language-dropdown {
width: 20px;
position: absolute;
right: 10px;
top: 10px;
}
.search-home {
width: 300px; width: 300px;
margin: 30px 0 0 0; margin: 30px 0 0 0;
} }
@ -61,8 +70,12 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue' import Vue from 'vue'
import InterfaceLanguageDropdown from './InterfaceLanguageDropdown.vue'
export default Vue.extend({ export default Vue.extend({
components: {
'interface-language-dropdown': InterfaceLanguageDropdown
},
props: { props: {
indexName: String indexName: String
} }

View File

@ -0,0 +1,71 @@
<template>
<div class="interface-language-dropdown">
<img :title="imgTitle" v-on:click="toggleShow()" class="interface-language" src="/img/interface-languages.svg" alt="Change interface language">
<div v-if="showMenu" class="menu">
<a v-for="(lang, locale) in $language.available" :key="locale" :href="'/' + locale + '/'" class="menu-item">
{{lang}}
</a>
</div>
</div>
</template>
<style lang="scss">
.interface-language-dropdown {
img {
cursor: pointer;
}
.menu {
background-color: #fff;
background-clip: padding-box;
border: 1px solid rgba(0,0,0,.15);
border-radius: .25rem;
color: #212529;
cursor: pointer;
display: flex;
flex-direction: column;
font-size: 1rem;
list-style: none;
margin: .125rem 0 0;
padding: .5rem 0;
position: absolute;
text-align: left;
}
.menu-item {
color: #212529;
padding: .25rem 1.5rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.menu-item:hover {
background-color: #F4F6F6;
cursor: pointer;
}
}
</style>
<script lang="ts">
import Vue, { PropType } from 'vue'
export default Vue.extend({
data () {
return {
showMenu: false
}
},
computed: {
imgTitle () {
return this.$gettext('Change interface language')
}
},
methods: {
toggleShow () {
this.showMenu = !this.showMenu;
}
}
})
</script>

View File

@ -3,7 +3,11 @@ import Vue from 'vue'
import GetTextPlugin from 'vue-gettext' import GetTextPlugin from 'vue-gettext'
import VueMatomo from 'vue-matomo' import VueMatomo from 'vue-matomo'
import App from './App.vue' import App from './App.vue'
import router from './router' import Router from 'vue-router'
import Search from './views/Search.vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
Vue.config.productionTip = process.env.NODE_ENV === 'production' Vue.config.productionTip = process.env.NODE_ENV === 'production'
@ -63,6 +67,27 @@ p.catch(err => {
Vue.config.language = currentLanguage Vue.config.language = currentLanguage
// Routes
let routes = []
for (const locale of [ '' ].concat(allLocales)) {
const base = locale
? '/' + locale + '/'
: '/'
routes = routes.concat([
{
path: '/' + locale,
component: Search
}
])
}
const router = new Router({
mode: 'history',
routes
})
// Stats Matomo // Stats Matomo
if (!(navigator.doNotTrack === 'yes' || if (!(navigator.doNotTrack === 'yes' ||
navigator.doNotTrack === '1' || navigator.doNotTrack === '1' ||
@ -112,10 +137,7 @@ p.catch(err => {
new Vue({ new Vue({
router, router,
render: h => h(App), render: h => h(App)
components: {
}
}).$mount('#app') }).$mount('#app')
}) })

View File

@ -1,20 +0,0 @@
import Vue from 'vue'
import Router from 'vue-router'
import Search from '@/views/Search.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
component: Search
},
{
path: '/search',
component: Search
}
]
})

View File

@ -22,6 +22,7 @@ body {
background-color: #fff; background-color: #fff;
margin: auto; margin: auto;
padding: 50px 80px; padding: 50px 80px;
position: relative;
@media screen and (max-width: $container-width) { @media screen and (max-width: $container-width) {
width: 100%; width: 100%;

View File

@ -2,3 +2,5 @@ declare module '*.vue' {
import Vue from 'vue' import Vue from 'vue'
export default Vue export default Vue
} }
declare var $language

View File

@ -3,8 +3,8 @@
<my-header v-bind:indexName="indexName"></my-header> <my-header v-bind:indexName="indexName"></my-header>
<main> <main>
<h3 v-bind:class="{ 'none-opacity': !instancesCount }" v-translate="{ instancesCount: instancesCount, indexName: indexName }"> <h3 v-bind:class="{ 'none-opacity': !instancesCount }" v-translate="{ instancesCount: instancesCount, indexName: indexName, indexedInstancesUrl: indexedInstancesUrl }">
Search for your favorite videos and channels on %{instancesCount} PeerTube websites listed on <strong>%{indexName}</strong>! Search for your favorite videos and channels on <a href="%{indexedInstancesUrl}" target="_blank">%{instancesCount} PeerTube websites</a> listed on <strong>%{indexName}</strong>!
</h3> </h3>
<div id="search-anchor" class="search-container"> <div id="search-anchor" class="search-container">
@ -155,7 +155,7 @@
</div> </div>
<div class="button-block"> <div class="button-block">
<input class="peertube-button" type="button" v-bind:value="'Apply filters' | translate" v-on:click="scrollToResults(); doNewSearch()" /> <input class="peertube-button" type="button" v-bind:value="applyFiltersLabel" v-on:click="scrollToResults(); doNewSearch()" />
</div> </div>
</form> </form>
@ -194,6 +194,14 @@
font-weight: normal; font-weight: normal;
font-size: 16px; font-size: 16px;
a {
color: $orange-darken;
&:hover {
color: $orange;
}
}
@media screen and (max-width: $small-screen) { @media screen and (max-width: $small-screen) {
font-size: 14px; font-size: 14px;
} }
@ -395,6 +403,7 @@
indexName: '', indexName: '',
instancesCount: 0, instancesCount: 0,
indexedInstancesUrl: '',
results: [] as (EnhancedVideo | EnhancedVideoChannel)[], results: [] as (EnhancedVideo | EnhancedVideoChannel)[],
resultsCount: null as number, resultsCount: null as number,
@ -432,6 +441,7 @@
const config = await getConfig() const config = await getConfig()
this.instancesCount = config.indexedHostsCount this.instancesCount = config.indexedHostsCount
this.indexedInstancesUrl = config.indexedInstancesUrl
this.indexName = config.searchInstanceName this.indexName = config.searchInstanceName
this.loadUrl() this.loadUrl()
@ -451,6 +461,10 @@
}, },
computed: { computed: {
applyFiltersLabel () {
return this.$gettext('Apply filters')
},
inputPlaceholder () { inputPlaceholder () {
return this.$gettext('Keyword, channel, video, etc.') return this.$gettext('Keyword, channel, video, etc.')
}, },

View File

@ -25,6 +25,10 @@ instances-index:
# Must answer the following format: https://framagit.org/framasoft/peertube/instances-peertube#peertube-auto-follow-global-search # Must answer the following format: https://framagit.org/framasoft/peertube/instances-peertube#peertube-auto-follow-global-search
url: 'https://instances.joinpeertube.org/api/v1/instances/hosts' url: 'https://instances.joinpeertube.org/api/v1/instances/hosts'
# A public URL that will be displayed in the search page
# So your users can see what instances your search instance indexed
public_url: 'https://instances.joinpeertube.org'
# Useful to do tests # Useful to do tests
whitelist: whitelist:
enabled: false enabled: false

View File

@ -18,6 +18,7 @@ export { configRouter }
async function getConfig (req: express.Request, res: express.Response) { async function getConfig (req: express.Request, res: express.Response) {
return res.json({ return res.json({
searchInstanceName: CONFIG.SEARCH_INSTANCE.NAME, searchInstanceName: CONFIG.SEARCH_INSTANCE.NAME,
indexedHostsCount: VideosIndexer.Instance.getIndexedHosts().length indexedHostsCount: VideosIndexer.Instance.getIndexedHosts().length,
indexedInstancesUrl: CONFIG.INSTANCES_INDEX.PUBLIC_URL
} as ServerConfig) } as ServerConfig)
} }

View File

@ -28,6 +28,7 @@ const CONFIG = {
}, },
INSTANCES_INDEX: { INSTANCES_INDEX: {
URL: config.get<string>('instances-index.url'), URL: config.get<string>('instances-index.url'),
PUBLIC_URL: config.get<string>('instances-index.public_url'),
WHITELIST: { WHITELIST: {
ENABLED: config.get<boolean>('instances-index.whitelist.enabled'), ENABLED: config.get<boolean>('instances-index.whitelist.enabled'),
HOSTS: config.get<string[]>('instances-index.whitelist.hosts') HOSTS: config.get<string[]>('instances-index.whitelist.hosts')

View File

@ -2,4 +2,6 @@ export interface ServerConfig {
searchInstanceName: string searchInstanceName: string
indexedHostsCount: number indexedHostsCount: number
indexedInstancesUrl: string
} }