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 @@ +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 = '
Loading
' @@ -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 @@
@@ -43,8 +43,8 @@ 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 @@ @@ -22,16 +22,16 @@ +