Page MenuHomePhorge

D2395.1775774195.diff
No OneTemporary

Authored By
Unknown
Size
10 KB
Referenced Files
None
Subscribers
None

D2395.1775774195.diff

diff --git a/src/.env.example b/src/.env.example
--- a/src/.env.example
+++ b/src/.env.example
@@ -6,6 +6,7 @@
APP_PUBLIC_URL=
APP_DOMAIN=kolabnow.com
APP_THEME=default
+APP_LOCALE=en
ASSET_URL=http://127.0.0.1:8000
diff --git a/src/app/Http/Kernel.php b/src/app/Http/Kernel.php
--- a/src/app/Http/Kernel.php
+++ b/src/app/Http/Kernel.php
@@ -21,6 +21,7 @@
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\DevelConfig::class,
+ \App\Http\Middleware\Locale::class,
// FIXME: CORS handling added here, I didn't find a nice way
// to add this only to the API routes
// \App\Http\Middleware\Cors::class,
diff --git a/src/app/Http/Middleware/Locale.php b/src/app/Http/Middleware/Locale.php
new file mode 100644
--- /dev/null
+++ b/src/app/Http/Middleware/Locale.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+
+class Locale
+{
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ *
+ * @return mixed
+ */
+ public function handle($request, Closure $next)
+ {
+ $langDir = resource_path('lang');
+ $preferences = array_map(
+ function ($lang) {
+ return preg_replace('/[^a-z].*$/', '', strtolower($lang));
+ },
+ $request->getLanguages()
+ );
+
+ $default = config('app.locale');
+ $lang = null;
+
+ foreach ($preferences as $pref) {
+ if (!empty($pref) && ($pref == $default || file_exists("$langDir/$pref"))) {
+ $lang = $pref;
+ break;
+ }
+ }
+
+ if ($lang != $default) {
+ app()->setLocale($lang);
+ }
+
+ return $next($request);
+ }
+}
diff --git a/src/config/app.php b/src/config/app.php
--- a/src/config/app.php
+++ b/src/config/app.php
@@ -98,7 +98,7 @@
|
*/
- 'locale' => 'en',
+ 'locale' => env('APP_LOCALE', 'en'),
/*
|--------------------------------------------------------------------------
diff --git a/src/package.json b/src/package.json
--- a/src/package.json
+++ b/src/package.json
@@ -33,6 +33,7 @@
"stylelint": "^13.8.0",
"stylelint-config-standard": "^20.0.0",
"vue": "^2.6.12",
+ "vue-i18n": "^8.24.1",
"vue-router": "^3.4.9",
"vue-template-compiler": "^2.6.12",
"vuex": "^3.6.0"
diff --git a/src/resources/js/app.js b/src/resources/js/app.js
--- a/src/resources/js/app.js
+++ b/src/resources/js/app.js
@@ -10,6 +10,7 @@
import MenuComponent from '../vue/Widgets/Menu'
import SupportForm from '../vue/Widgets/SupportForm'
import store from './store'
+import { loadLanguageAsync, i18n } from './locale'
const loader = '<div class="app-loader"><div class="spinner-border" role="status"><span class="sr-only">Loading</span></div></div>'
@@ -53,7 +54,7 @@
loadingRoute = to.name
}
- next()
+ loadLanguageAsync().then(() => next())
})
window.router.afterEach((to, from) => {
@@ -74,6 +75,7 @@
AppComponent,
MenuComponent,
},
+ i18n,
store,
router: window.router,
data() {
diff --git a/src/resources/js/locale.js b/src/resources/js/locale.js
new file mode 100644
--- /dev/null
+++ b/src/resources/js/locale.js
@@ -0,0 +1,56 @@
+import Vue from 'vue'
+import VueI18n from 'vue-i18n'
+import messages from '../lang-js/en'
+
+Vue.use(VueI18n)
+
+export const i18n = new VueI18n({
+ locale: 'en',
+ fallbackLocale: 'en',
+ messages: { en: messages },
+ silentFallbackWarn: true
+})
+
+let currentLanguage
+
+const loadedLanguages = [ 'en' ] // our default language that is preloaded
+
+const setI18nLanguage = (lang) => {
+ i18n.locale = lang
+ window.axios.defaults.headers.common['Accept-Language'] = lang
+ document.querySelector('html').setAttribute('lang', lang)
+
+ return lang
+}
+
+export const getLang = () => {
+ // TODO: On init get the current user language from the browser?
+ if (!currentLanguage) {
+ currentLanguage = document.querySelector('html').getAttribute('lang') || 'en'
+ }
+
+ return currentLanguage
+}
+
+export const setLang = lang => {
+ // TODO: Save the selected language in the browser?
+ currentLanguage = lang
+ loadLanguageAsync()
+}
+
+export function loadLanguageAsync() {
+ const lang = getLang()
+
+ // If the language was already loaded
+ if (loadedLanguages.includes(lang)) {
+ return Promise.resolve(setI18nLanguage(lang))
+ }
+
+ // If the language hasn't been loaded yet
+ return import(/* webpackChunkName: "locale/[request]" */ `../lang-js/${lang}`)
+ .then(messages => {
+ i18n.setLocaleMessage(lang, messages.default)
+ loadedLanguages.push(lang)
+ return setI18nLanguage(lang)
+ })
+}
diff --git a/src/resources/lang-js/de.js b/src/resources/lang-js/de.js
new file mode 100644
--- /dev/null
+++ b/src/resources/lang-js/de.js
@@ -0,0 +1,18 @@
+export default {
+ buttons: {
+ cancel: "Stornieren",
+ save: "Speichern"
+ },
+ menu: {
+ cockpit: "Cockpit",
+ login: "Einloggen",
+ logout: "Ausloggen",
+ signup: "Signup",
+ toggle: "Navigation umschalten"
+ },
+ login: {
+ forgot_password: "Passwort vergessen?",
+ sign_in: "Anmelden",
+ webmail: "Webmail"
+ }
+}
diff --git a/src/resources/lang-js/en.js b/src/resources/lang-js/en.js
new file mode 100644
--- /dev/null
+++ b/src/resources/lang-js/en.js
@@ -0,0 +1,18 @@
+export default {
+ buttons: {
+ cancel: "Cancel",
+ save: "Save"
+ },
+ menu: {
+ cockpit: "Cockpit",
+ login: "Login",
+ logout: "Logout",
+ signup: "Signup",
+ toggle: "Toggle navigation"
+ },
+ login: {
+ forgot_password: "Forgot password?",
+ sign_in: "Sign in",
+ webmail: "Webmail"
+ }
+}
diff --git a/src/resources/themes/menu.scss b/src/resources/themes/menu.scss
--- a/src/resources/themes/menu.scss
+++ b/src/resources/themes/menu.scss
@@ -56,6 +56,17 @@
}
}
+#language-selector {
+ width: auto;
+ position: absolute;
+ right: 0;
+ top: 0;
+ border: 0;
+ background-color: transparent;
+ padding-right: 1.4rem;
+ margin: 0.25em;
+}
+
@include media-breakpoint-up(lg) {
#header-menu {
a.menulogin {
diff --git a/src/resources/vue/Login.vue b/src/resources/vue/Login.vue
--- a/src/resources/vue/Login.vue
+++ b/src/resources/vue/Login.vue
@@ -35,7 +35,7 @@
</div>
<div class="text-center">
<button class="btn btn-primary" type="submit">
- <svg-icon icon="sign-in-alt"></svg-icon> Sign in
+ <svg-icon icon="sign-in-alt"></svg-icon> {{ $t('login.sign_in') }}
</button>
</div>
</form>
@@ -43,8 +43,8 @@
</div>
</div>
<div id="logon-form-footer" class="mt-1">
- <router-link v-if="!$root.isAdmin && $root.hasRoute('password-reset')" :to="{ name: 'password-reset' }" id="forgot-password">Forgot password?</router-link>
- <a v-if="webmailURL && !$root.isAdmin" :href="webmailURL" id="webmail">Webmail</a>
+ <router-link v-if="!$root.isAdmin && $root.hasRoute('password-reset')" :to="{ name: 'password-reset' }" id="forgot-password">{{ $t('login.forgot_password') }}</router-link>
+ <a v-if="webmailURL && !$root.isAdmin" :href="webmailURL" id="webmail">{{ $t('login.webmail') }}</a>
</div>
</div>
</template>
diff --git a/src/resources/vue/Widgets/Menu.vue b/src/resources/vue/Widgets/Menu.vue
--- a/src/resources/vue/Widgets/Menu.vue
+++ b/src/resources/vue/Widgets/Menu.vue
@@ -4,7 +4,7 @@
<router-link class="navbar-brand" to="/" v-html="$root.logo(mode)"></router-link>
<button v-if="mode == 'header'" class="navbar-toggler" type="button"
data-toggle="collapse" :data-target="'#' + mode + '-menu-navbar'"
- aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"
+ aria-controls="navbar" aria-expanded="false" :aria-label="$t('menu.toggle')"
>
<span class="navbar-toggler-icon"></span>
</button>
@@ -22,16 +22,16 @@
</router-link>
</li>
<li class="nav-item" v-if="!loggedIn && !$root.isAdmin">
- <router-link class="nav-link link-signup" active-class="active" :to="{name: 'signup'}">Signup</router-link>
+ <router-link class="nav-link link-signup" active-class="active" :to="{name: 'signup'}">{{ $t('menu.signup') }}</router-link>
</li>
<li class="nav-item" v-if="loggedIn">
- <router-link class="nav-link link-dashboard" active-class="active" :to="{name: 'dashboard'}">Cockpit</router-link>
+ <router-link class="nav-link link-dashboard" active-class="active" :to="{name: 'dashboard'}">{{ $t('menu.cockpit') }}</router-link>
</li>
<li class="nav-item" v-if="loggedIn">
- <router-link class="nav-link menulogin link-logout" active-class="active" :to="{name: 'logout'}">Logout</router-link>
+ <router-link class="nav-link menulogin link-logout" active-class="active" :to="{name: 'logout'}">{{ $t('menu.logout') }}</router-link>
</li>
<li class="nav-item" v-if="!loggedIn">
- <router-link class="nav-link menulogin link-login" :to="{name: 'login'}">Login</router-link>
+ <router-link class="nav-link menulogin link-login" :to="{name: 'login'}">{{ $t('menu.login') }}</router-link>
</li>
</ul>
<div v-if="mode == 'footer'" class="footer">
@@ -40,10 +40,16 @@
</div>
</div>
</div>
+ <select v-if="mode == 'header'" id="language-selector" class="custom-select custom-select-sm" @change="setLang()">
+ <option value="en" :selected="getLang() == 'en'">English</option>
+ <option value="de" :selected="getLang() == 'de'">German</option>
+ </select>
</nav>
</template>
<script>
+ import { setLang, getLang } from '../../js/locale'
+
export default {
props: {
mode: { type: String, default: 'header' },
@@ -93,6 +99,12 @@
})
return menu
+ },
+ getLang() {
+ return getLang()
+ },
+ setLang() {
+ setLang($('#language-selector').val())
}
}
}

File Metadata

Mime Type
text/plain
Expires
Thu, Apr 9, 10:36 PM (14 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18853303
Default Alt Text
D2395.1775774195.diff (10 KB)

Event Timeline