Add ability to change language
This commit is contained in:
parent
581f3a6fa2
commit
fa06aaea30
10
README.md
10
README.md
|
@ -7,9 +7,9 @@ $ git submodule update --init --recursive
|
|||
$ 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
|
||||
$ tsc -w
|
||||
|
@ -19,7 +19,11 @@ $ tsc -w
|
|||
$ node dist/server
|
||||
```
|
||||
|
||||
Then open http://localhost:3234.
|
||||
```
|
||||
$ cd client && npm run serve
|
||||
```
|
||||
|
||||
Then open http://localhost:8080.
|
||||
|
||||
## Production
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 11 KiB |
|
@ -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 |
|
@ -2,7 +2,7 @@
|
|||
<footer id="main-footer">
|
||||
|
||||
<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>
|
||||
|
@ -11,19 +11,23 @@
|
|||
<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 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 class="footer">
|
||||
<a href="https://joinpeertube.org" target="_blank" v-translate> >> Check all guides on joinpeertube.org << </a>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
|
@ -43,14 +47,17 @@
|
|||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36px;
|
||||
img {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
color: $grey;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.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) {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -103,6 +121,10 @@
|
|||
.subtitle {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<template>
|
||||
<div>
|
||||
<header>
|
||||
<interface-language-dropdown class="interface-language-dropdown"></interface-language-dropdown>
|
||||
|
||||
<h1>{{indexName}}</h1>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -21,7 +23,14 @@
|
|||
margin-bottom: 30px;
|
||||
font-family: monospace;
|
||||
|
||||
img {
|
||||
.interface-language-dropdown {
|
||||
width: 20px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.search-home {
|
||||
width: 300px;
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
|
@ -61,8 +70,12 @@
|
|||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue'
|
||||
import InterfaceLanguageDropdown from './InterfaceLanguageDropdown.vue'
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
'interface-language-dropdown': InterfaceLanguageDropdown
|
||||
},
|
||||
props: {
|
||||
indexName: String
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -3,7 +3,11 @@ import Vue from 'vue'
|
|||
import GetTextPlugin from 'vue-gettext'
|
||||
import VueMatomo from 'vue-matomo'
|
||||
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'
|
||||
|
||||
|
@ -63,6 +67,27 @@ p.catch(err => {
|
|||
|
||||
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
|
||||
if (!(navigator.doNotTrack === 'yes' ||
|
||||
navigator.doNotTrack === '1' ||
|
||||
|
@ -112,10 +137,7 @@ p.catch(err => {
|
|||
|
||||
new Vue({
|
||||
router,
|
||||
render: h => h(App),
|
||||
components: {
|
||||
|
||||
}
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
})
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]
|
||||
})
|
|
@ -22,6 +22,7 @@ body {
|
|||
background-color: #fff;
|
||||
margin: auto;
|
||||
padding: 50px 80px;
|
||||
position: relative;
|
||||
|
||||
@media screen and (max-width: $container-width) {
|
||||
width: 100%;
|
||||
|
|
|
@ -2,3 +2,5 @@ declare module '*.vue' {
|
|||
import Vue from 'vue'
|
||||
export default Vue
|
||||
}
|
||||
|
||||
declare var $language
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
<my-header v-bind:indexName="indexName"></my-header>
|
||||
|
||||
<main>
|
||||
<h3 v-bind:class="{ 'none-opacity': !instancesCount }" v-translate="{ instancesCount: instancesCount, indexName: indexName }">
|
||||
Search for your favorite videos and channels on %{instancesCount} PeerTube websites listed on <strong>%{indexName}</strong>!
|
||||
<h3 v-bind:class="{ 'none-opacity': !instancesCount }" v-translate="{ instancesCount: instancesCount, indexName: indexName, indexedInstancesUrl: indexedInstancesUrl }">
|
||||
Search for your favorite videos and channels on <a href="%{indexedInstancesUrl}" target="_blank">%{instancesCount} PeerTube websites</a> listed on <strong>%{indexName}</strong>!
|
||||
</h3>
|
||||
|
||||
<div id="search-anchor" class="search-container">
|
||||
|
@ -155,7 +155,7 @@
|
|||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
</form>
|
||||
|
@ -194,6 +194,14 @@
|
|||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
|
||||
a {
|
||||
color: $orange-darken;
|
||||
|
||||
&:hover {
|
||||
color: $orange;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $small-screen) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
@ -395,6 +403,7 @@
|
|||
|
||||
indexName: '',
|
||||
instancesCount: 0,
|
||||
indexedInstancesUrl: '',
|
||||
results: [] as (EnhancedVideo | EnhancedVideoChannel)[],
|
||||
|
||||
resultsCount: null as number,
|
||||
|
@ -432,6 +441,7 @@
|
|||
const config = await getConfig()
|
||||
|
||||
this.instancesCount = config.indexedHostsCount
|
||||
this.indexedInstancesUrl = config.indexedInstancesUrl
|
||||
this.indexName = config.searchInstanceName
|
||||
|
||||
this.loadUrl()
|
||||
|
@ -451,6 +461,10 @@
|
|||
},
|
||||
|
||||
computed: {
|
||||
applyFiltersLabel () {
|
||||
return this.$gettext('Apply filters')
|
||||
},
|
||||
|
||||
inputPlaceholder () {
|
||||
return this.$gettext('Keyword, channel, video, etc.')
|
||||
},
|
||||
|
|
|
@ -25,6 +25,10 @@ instances-index:
|
|||
# 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'
|
||||
|
||||
# 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
|
||||
whitelist:
|
||||
enabled: false
|
||||
|
|
|
@ -18,6 +18,7 @@ export { configRouter }
|
|||
async function getConfig (req: express.Request, res: express.Response) {
|
||||
return res.json({
|
||||
searchInstanceName: CONFIG.SEARCH_INSTANCE.NAME,
|
||||
indexedHostsCount: VideosIndexer.Instance.getIndexedHosts().length
|
||||
indexedHostsCount: VideosIndexer.Instance.getIndexedHosts().length,
|
||||
indexedInstancesUrl: CONFIG.INSTANCES_INDEX.PUBLIC_URL
|
||||
} as ServerConfig)
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ const CONFIG = {
|
|||
},
|
||||
INSTANCES_INDEX: {
|
||||
URL: config.get<string>('instances-index.url'),
|
||||
PUBLIC_URL: config.get<string>('instances-index.public_url'),
|
||||
WHITELIST: {
|
||||
ENABLED: config.get<boolean>('instances-index.whitelist.enabled'),
|
||||
HOSTS: config.get<string[]>('instances-index.whitelist.hosts')
|
||||
|
|
|
@ -2,4 +2,6 @@ export interface ServerConfig {
|
|||
searchInstanceName: string
|
||||
|
||||
indexedHostsCount: number
|
||||
|
||||
indexedInstancesUrl: string
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue