diff --git a/js/forum/src/components/FooterSecondary.js b/js/forum/src/components/FooterSecondary.js index 1936b5d57..7c9f0966c 100644 --- a/js/forum/src/components/FooterSecondary.js +++ b/js/forum/src/components/FooterSecondary.js @@ -1,4 +1,6 @@ import Component from 'flarum/Component'; +import SelectDropdown from 'flarum/components/SelectDropdown'; +import Button from 'flarum/components/Button'; import ItemList from 'flarum/utils/ItemList'; import listItems from 'flarum/helpers/listItems'; @@ -24,6 +26,31 @@ export default class FooterSecondary extends Component { items() { const items = new ItemList(); + if (Object.keys(app.locales).length > 1) { + const locales = []; + + for (const locale in app.locales) { + locales.push(Button.component({ + active: app.locale === locale, + children: app.locales[locale], + icon: app.locale === locale ? 'check' : true, + onclick: () => { + if (app.session.user) { + app.session.user.savePreferences({locale}).then(() => window.location.reload()); + } else { + document.cookie = `locale=${locale}; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT`; + window.location.reload(); + } + } + })); + } + + items.add('locale', SelectDropdown.component({ + children: locales, + buttonClassName: 'Button Button--text' + })); + } + items.add('poweredBy', ( {app.trans('core.powered_by_flarum')} diff --git a/js/forum/src/components/SettingsPage.js b/js/forum/src/components/SettingsPage.js index 8c1b7c2bf..31180d015 100644 --- a/js/forum/src/components/SettingsPage.js +++ b/js/forum/src/components/SettingsPage.js @@ -98,13 +98,10 @@ export default class SettingsPage extends UserPage { */ preferenceSaver(key) { return (value, component) => { - const preferences = this.user.preferences(); - preferences[key] = value; - if (component) component.loading = true; m.redraw(); - this.user.save({preferences}).then(() => { + this.user.savePreferences({[key]: value}).then(() => { if (component) component.loading = false; m.redraw(); }); diff --git a/js/lib/components/SelectDropdown.js b/js/lib/components/SelectDropdown.js index 5cc94de31..aea7dfa41 100644 --- a/js/lib/components/SelectDropdown.js +++ b/js/lib/components/SelectDropdown.js @@ -27,7 +27,7 @@ export default class SelectDropdown extends Dropdown { if (label instanceof Array) label = label[0]; return [ - {label}, ' ', + {label}, icon(this.props.caretIcon, {className: 'Button-caret'}) ]; } diff --git a/js/lib/models/User.js b/js/lib/models/User.js index 0897f9d59..331e381c1 100644 --- a/js/lib/models/User.js +++ b/js/lib/models/User.js @@ -100,4 +100,18 @@ export default class User extends mixin(Model, { }; image.src = this.avatarUrl(); } + + /** + * Update the user's preferences. + * + * @param {Object} newPreferences + * @return {Promise} + */ + savePreferences(newPreferences) { + const preferences = this.preferences(); + + Object.assign(preferences, newPreferences); + + return this.save({preferences}); + } } diff --git a/less/lib/App.less b/less/lib/App.less index 4c2280b62..369385587 100755 --- a/less/lib/App.less +++ b/less/lib/App.less @@ -354,14 +354,14 @@ .Footer-primary { display: inline-block; - > li { + .Footer-controls > li { margin-right: 15px; } } .Footer-secondary { float: right; - > li { + .Footer-controls > li { margin-left: 15px; } } diff --git a/less/lib/Button.less b/less/lib/Button.less index ad646c503..86e03027b 100755 --- a/less/lib/Button.less +++ b/less/lib/Button.less @@ -227,6 +227,9 @@ .Button-caret { font-size: 14px; } +.Button-caret { + margin-left: 3px; +} .Button-badge { font-size: 12px; font-weight: bold; diff --git a/migrations/2015_02_24_000000_create_users_table.php b/migrations/2015_02_24_000000_create_users_table.php index 2d3544d29..1666c4e86 100644 --- a/migrations/2015_02_24_000000_create_users_table.php +++ b/migrations/2015_02_24_000000_create_users_table.php @@ -20,7 +20,6 @@ class CreateUsersTable extends Migration $table->string('email', 150)->unique(); $table->boolean('is_activated')->default(0); $table->string('password', 100); - $table->string('locale', 10)->default('en'); $table->text('bio')->nullable(); $table->string('avatar_path', 100)->nullable(); $table->binary('preferences')->nullable(); diff --git a/src/Admin/Actions/ClientAction.php b/src/Admin/Actions/ClientAction.php index 6df9c13a4..40cb99ace 100644 --- a/src/Admin/Actions/ClientAction.php +++ b/src/Admin/Actions/ClientAction.php @@ -31,7 +31,6 @@ class ClientAction extends BaseClientAction $view = parent::render($request, $routeParams); $view->setVariable('config', $this->settings->all()); - $view->setVariable('locales', app('flarum.localeManager')->getLocales()); $view->setVariable('permissions', Permission::map()); $view->setVariable('extensions', app('flarum.extensions')->getInfo()); diff --git a/src/Core/Users/UsersServiceProvider.php b/src/Core/Users/UsersServiceProvider.php index 7e5bc6e2a..5060e5bcc 100644 --- a/src/Core/Users/UsersServiceProvider.php +++ b/src/Core/Users/UsersServiceProvider.php @@ -24,6 +24,7 @@ class UsersServiceProvider extends ServiceProvider $events->listen(RegisterUserPreferences::class, function (RegisterUserPreferences $event) { $event->register('discloseOnline', 'boolval', true); $event->register('indexProfile', 'boolval', true); + $event->register('locale'); }); $events->listen(ModelAllow::class, function (ModelAllow $event) { diff --git a/src/Locale/LocaleManager.php b/src/Locale/LocaleManager.php index c7debf173..7fe1051b9 100644 --- a/src/Locale/LocaleManager.php +++ b/src/Locale/LocaleManager.php @@ -20,6 +20,11 @@ class LocaleManager return $this->locales; } + public function hasLocale($locale) + { + return isset($this->locales[$locale]); + } + public function addTranslations($locale, $translations) { if (! isset($this->translations[$locale])) { diff --git a/src/Support/ClientAction.php b/src/Support/ClientAction.php index b02748d56..88e684066 100644 --- a/src/Support/ClientAction.php +++ b/src/Support/ClientAction.php @@ -89,31 +89,35 @@ abstract class ClientAction extends HtmlAction { $actor = app('flarum.actor'); $assets = $this->getAssets(); - $locale = $this->getLocaleCompiler($actor); + $locale = $this->getLocale($actor, $request); + $localeCompiler = $this->getLocaleCompiler($locale); $view = new ClientView( $this->apiClient, $request, $actor, $assets, - $locale, + $localeCompiler, $this->layout ); + $view->setVariable('locales', $this->locales->getLocales()); + $view->setVariable('locale', $locale); + // Now that we've set up the ClientView instance, we can fire an event // to give extensions the opportunity to add their own assets and // translations. We will pass an array to the event which specifies // which translations should be included in the locale file. Afterwards, // we will filter all of the translations for the actor's locale and // compile only the ones we need. - $translations = $this->locales->getTranslations($actor->locale); + $translations = $this->locales->getTranslations($locale); $keys = $this->translationKeys; event(new BuildClientView($this, $view, $keys)); $translations = $this->filterTranslations($translations, $keys); - $locale->setTranslations($translations); + $localeCompiler->setTranslations($translations); return $view; } @@ -202,15 +206,13 @@ abstract class ClientAction extends HtmlAction } /** - * Set up the locale compiler for the given user's locale. + * Set up the locale compiler for the given locale. * - * @param User $actor + * @param string $locale * @return LocaleJsCompiler */ - protected function getLocaleCompiler(User $actor) + protected function getLocaleCompiler($locale) { - $locale = $actor->locale; - $compiler = new LocaleJsCompiler($this->getAssetDirectory(), "$this->clientName-$locale.js"); foreach ($this->locales->getJsFiles($locale) as $file) { @@ -220,6 +222,28 @@ abstract class ClientAction extends HtmlAction return $compiler; } + /** + * Get the name of the locale to use. + * + * @param User $actor + * @param Request $request + * @return string + */ + protected function getLocale(User $actor, Request $request) + { + if ($actor->exists) { + $locale = $actor->getPreference('locale'); + } else { + $locale = array_get($request->getCookieParams(), 'locale'); + } + + if (! $locale || ! $this->locales->hasLocale($locale)) { + return $this->settings->get('default_locale', 'en'); + } + + return $locale; + } + /** * Get the path to the directory where assets should be written. *