Commit 7b91ca6f authored by Eliot Berriot's avatar Eliot Berriot 💬

Backported locale / routing improvements from next.funkwhale.audio

parent 0874ca01
Pipeline #3796 failed with stages
in 1 minute and 37 seconds
......@@ -152,9 +152,13 @@ typings/
### VisualStudioCode template
.vscode/*
!.vscode/settings.json
!.vscode/settings
!.vscode/tasks.json
!.vscode/tasks
!.vscode/launch.json
!.vscode/launch
!.vscode/extensions.json
!.vscode/extensions
......@@ -229,6 +233,7 @@ Temporary Items
# SFTP configuration file
sftp-config.json
sftp-config
# Package control specific files
Package Control.last-run
......@@ -273,3 +278,4 @@ contributions/media/
.envs/*
src/translations.json
src/translations
......@@ -4,10 +4,11 @@ stages:
test:
stage: test
image: funkwhale/front-base
image: buildkite/puppeteer
variables:
GIT_STRATEGY: clone
before_script:
- apt-get update && apt-get install -y jq
- yarn install
script:
- yarn i18n-compile
......@@ -22,10 +23,11 @@ test:
pages:
stage: build
image: funkwhale/front-base
image: buildkite/puppeteer
variables:
GIT_STRATEGY: clone
before_script:
- apt-get update && apt-get install -y jq
- yarn install
script:
- yarn build
......@@ -34,6 +36,8 @@ pages:
# ugly hack from https://gitlab.com/gitlab-org/gitlab-pages/issues/23#note_57499396
# until Gitlab Pages has better support for SPA
- cp public/index.html public/404.html
# Gzip compression, cf https://webd97.de/post/gitlab-pages-compression/
- gzip -k -6 $(find public -type f)
cache:
key: front_node_modules
paths:
......
......@@ -17,6 +17,7 @@
"moment": "^2.22.2",
"vue": "^2.5.17",
"vue-gettext": "^2.1.1",
"vue-head": "^2.1.1",
"vue-router": "^3.0.1"
},
"devDependencies": {
......@@ -30,6 +31,7 @@
"css-loader": "^1.0.0",
"easygettext": "^2.7.0",
"node-sass": "^4.10.0",
"prerender-spa-plugin": "^3.4.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"vue-template-compiler": "^2.5.17"
......
#!/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-compile --output src/translations.json
locales=$(tail -n +2 src/locales.js | sed -e 's/module.exports = //' | jq '.locales[].code' | xargs echo)
mkdir -p src/translations
for locale in $locales; do
$(yarn bin)/gettext-compile locales/$locale/LC_MESSAGES/app.po --output src/translations/$locale.json
done
#!/bin/bash -eux
locales=$(tail -n +2 src/locales.js | sed -e 's/export default //' | jq '.locales[].code' | xargs echo)
locales=$(tail -n +2 src/locales.js | sed -e 's/module.exports = //' | 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')
......
......@@ -20,7 +20,14 @@
Language
</translate>
</label>
<select id="language" name="language" v-model="$language.current">
<select
id="language"
name="language"
:value="$language.current"
@change="
$router.push({ params: { locale: $event.target.value } })
"
>
<option v-for="(language, key) in $language.available" :key="key" :value="key">{{ language }}</option>
</select>
</div>
......@@ -44,7 +51,7 @@ export default {
</script>
<style lang="scss">
.container.text {
.container.text {
max-width: 40em;
}
......@@ -53,7 +60,6 @@ export default {
}
.navbar-brand {
.navbar-item {
img {
max-height: 2.5em;
......
<script>
import Vue from "vue";
import locales from "./locales";
let autodetected = false
export default {
props: ["locale"],
created() {
if (!autodetected) {
this.autodetectLanguage();
}
},
head: {
title() {
return {
inner: this.getInnerTitle()
};
},
meta: function() {
return [
{
name: "description",
content: this.$pgettext(
"Content/Home/Header",
"Funkwhale contribution guides"
)
},
{
"http-equiv": "content-language",
content: this.$language.current.replace("_", "-")
}
];
},
link: function() {
let self = this;
let links = locales.locales.map(l => {
let params = {
...self.$route.params,
locale: l.code
};
let resolved = self.$router.resolve({
name: self.$route.name,
params
});
return {
rel: "alternate",
hreflang: l.code.replace("_", "-"),
href: `${resolved.href}`
};
});
return links;
}
},
methods: {
getInnerTitle() {
return this.$pgettext(
"HTML/Meta/Title",
"Contribute to Funkwhale development"
);
},
autodetectLanguage() {
autodetected = true
let userLanguage =
this.locale || navigator.language || navigator.userLanguage;
let available = locales.locales.map(e => {
return e.code;
});
let candidate;
let matching = available.filter(a => {
return userLanguage.replace("-", "_") === a;
});
let almostMatching = available.filter(a => {
return userLanguage.replace("-", "_").split("_")[0] === a.split("_")[0];
});
if (matching.length > 0) {
candidate = matching[0];
} else if (almostMatching.length > 0) {
candidate = almostMatching[0];
} else {
return;
}
this.switchLanguage(candidate);
},
switchLanguage(newValue) {
let self = this;
import(`./translations/${newValue}.json`)
.then(response => {
Vue.$translations[newValue] = response.default[newValue];
})
.finally(() => {
// set current language twice, otherwise we seem to have a cache somewhere
// and rendering does not happen
self.$language.current = "noop";
self.$language.current = newValue;
if (
self.$route.params.locale &&
self.$route.params.locale !== newValue
) {
self.$router.push({ params: { locale: newValue } });
}
self.$emit("updateHead");
});
}
},
watch: {
"$route.params.locale"(newValue) {
if (newValue) {
this.switchLanguage(newValue);
}
}
}
};
</script>
......@@ -47,7 +47,7 @@
</small>
<router-link
:to="{name: 'guide', params: {slug: task.slug}}"
:to="{name: 'guide', params: {slug: task.slug, locale: $language.current}}"
v-if="task.slug && task.steps && task.steps.length > 0"
class="right floated button is-link">
<translate translate-context="Call to action to start contributing">
......
......@@ -119,7 +119,7 @@ export function getTasks (v) {
{
icon: 'comment',
text: v.$pgettext("Text for link in guide step", 'Share your feedback on Discourse (forum)'),
to: {name: 'guide', params: {slug: 'loomio-account'}},
to: {name: 'guide', params: {slug: 'loomio-account', locale: v.$language.current}},
},
{
icon: 'mastodon',
......@@ -165,7 +165,7 @@ export function getTasks (v) {
{
icon: 'comment',
text: v.$pgettext("Text for link in guide step", 'Create your Loomio account'),
to: {name: 'guide', params: {slug: 'loomio-account'}},
to: {name: 'guide', params: {slug: 'loomio-account', locale: v.$language.current}},
}
]
},
......@@ -215,7 +215,7 @@ export function getTasks (v) {
{
icon: 'gitlab',
text: v.$pgettext("Text for link in guide step", 'Create your GitLab account'),
to: {name: 'guide', params: {slug: 'gitlab-account'}},
to: {name: 'guide', params: {slug: 'gitlab-account', locale: v.$language.current}},
}
]
},
......
/* eslint-disable */
export default {
module.exports = {
"locales": [
{
"code": "ar",
......
......@@ -4,9 +4,9 @@ import Home from './views/Home'
import Guide from './views/Guide'
const routes = [
{path: '/', component: Home},
{path: '/:locale?', component: Home, props: true,},
{
path: '/guides/:slug',
path: '/:locale?/guides/:slug',
name: 'guide',
props: true,
component: Guide,
......
......@@ -2,11 +2,15 @@ import Vue from "vue";
import moment from "moment";
import VueRouter from "vue-router";
import "./components/globals"
import VueHead from "vue-head";
Vue.use(VueHead, {
separator: "-",
complement: "Funkwhale"
});
import GetTextPlugin from 'vue-gettext'
import locales from '@/locales'
import translations from './translations.json'
let availableLanguages = (function () {
let l = {}
......@@ -29,7 +33,7 @@ Vue.use(GetTextPlugin, {
}
}
},
translations: translations,
translations: {},
silent: true
})
......
......@@ -10,10 +10,11 @@
</template>
<script>
import TaskMixin from '../mixins/TaskMixin'
import PageMixin from "../PageMixin";
export default {
props: ['slug'],
mixins: [TaskMixin],
mixins: [TaskMixin, PageMixin],
data () {
return {
hash: null
......@@ -47,7 +48,6 @@ export default {
}
</script>
<style lang="scss" scoped>
@media screen and (min-width: 769px) {
.menu-wrapper {
position: fixed;
......
......@@ -99,10 +99,11 @@ import Issue from "@/components/Issue";
import Donation from "@/components/Donation";
import Modal from "@/components/Modal";
import TaskMixin from '../mixins/TaskMixin'
import PageMixin from "../PageMixin";
export default {
mixins: [TaskMixin],
mixins: [TaskMixin, PageMixin],
components: {
ContributionItem,
Donation,
......
const locales = require("./src/locales");
const path = require("path");
const PrerenderSPAPlugin = require("prerender-spa-plugin");
module.exports = {
chainWebpack: (config) => {
config.plugins.delete('prefetch');
},
configureWebpack: () => {
if (process.env.NODE_ENV !== "production") return;
let baseRoutes = ["/", "/code-of-conduct", "/community", "/contact"];
let finalRoutes = [];
baseRoutes.forEach(p => {
finalRoutes.push(p);
locales.locales.forEach(l => {
finalRoutes.push("/" + l.code + p);
});
});
return {
plugins: [
new PrerenderSPAPlugin(
// Absolute path to compiled SPA
path.resolve(__dirname, "dist"),
// List of routes to prerender
finalRoutes,
{
// options
}
)
]
};
}
};
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment