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
|
$ 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 |
|
@ -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">
|
<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> >> Check all guides on joinpeertube.org << </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>
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 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')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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;
|
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%;
|
||||||
|
|
|
@ -2,3 +2,5 @@ declare module '*.vue' {
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
export default Vue
|
export default Vue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare var $language
|
||||||
|
|
|
@ -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.')
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -2,4 +2,6 @@ export interface ServerConfig {
|
||||||
searchInstanceName: string
|
searchInstanceName: string
|
||||||
|
|
||||||
indexedHostsCount: number
|
indexedHostsCount: number
|
||||||
|
|
||||||
|
indexedInstancesUrl: string
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue