Preferred Languages - Version 1.7.0

Version Description

This release improves Multisite support and contains a new preferred_languages_merge_translations filter that allows merging translations.

Download this release

Release Info

Developer swissspidy
Plugin Icon Preferred Languages
Version 1.7.0
Comparing to
See all releases

Code changes from version 1.6.0 to 1.7.0

CHANGELOG.md DELETED
@@ -1,48 +0,0 @@
1
- # Changelog
2
-
3
- ## 1.6.0
4
-
5
- * Enhancement: Added support for JavaScript internationalization as used in the new block editor in WordPress 5.0. Requires WordPress 5.0.3.
6
- * Fixed: Fixes an issue where the preferred languages list wasn't updating after sorting via drag & drop.
7
-
8
- ## 1.5.0
9
-
10
- * Enhancement: Improved compatibility with multilingual plugins like Polylang.
11
- * Fixed: Fixes an issue with keyboard navigation.
12
- * Fixed: Make sure the correct languages are retrieved when updating the settings.
13
-
14
- ## 1.4.0
15
-
16
- * New: Keyboard navigation improvements.
17
- * New: Tooltips now show the available keyboard shortcuts.
18
- * New: Missing translations are now downloaded even when no changes were made.
19
- * New: A warning is shown when some of the preferred languages aren't installed.
20
- * New: Settings form is now hidden when JavaScript is disabled.
21
- * Fixed: Improved setting the current locale.
22
- * Fixed: CSS is no longer enqueued on the front end.
23
-
24
- ## 1.3.0
25
-
26
- * New: Users can now choose English (United States) again as a preferred locale.
27
- * New: Users with the right capabilities can now install languages in their user profile as well.
28
-
29
- ## 1.2.0
30
-
31
- * Fixed: Other English locales can now be added again.
32
- * Fixed: Prevented some errors when adding all available locales.
33
-
34
- ## 1.1.0
35
-
36
- * New: Support for just-in-time loading of translations.
37
- * New: Keyboard shortcut for making inactive locales active.
38
- * Fixed: Responsive design improvements.
39
- * Fixed: Worked around a few edge cases with the various controls.
40
- * Fixed: Added missing text domains.
41
-
42
- ## 1.0.1
43
-
44
- * Fixed: Fixed a bug that prevented saving changes.
45
-
46
- ## 1.0.0
47
-
48
- * Initial release.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/preferred-languages-rtl.css ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .active-locales {
2
+ margin: 1em 0;
3
+ }
4
+
5
+ .active-locales-list,
6
+ .inactive-locales-list {
7
+ width: 25em;
8
+ margin: 0 0 1em 1em;
9
+ clear: both;
10
+ float: right;
11
+ }
12
+
13
+ .active-locales-controls {
14
+ clear: both;
15
+ }
16
+
17
+ .active-locales-controls ul {
18
+ margin: 0;
19
+ }
20
+
21
+ .active-locales-controls .button {
22
+ margin: 0;
23
+ }
24
+
25
+ @media screen and (min-width: 510px) {
26
+
27
+ .active-locales-controls {
28
+ clear: none;
29
+ }
30
+ }
31
+
32
+ .active-locales-list {
33
+ height: 98px;
34
+ overflow-y: scroll;
35
+ margin-top: 0;
36
+ background: #fff;
37
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07);
38
+ border: 1px solid #ddd;
39
+ color: #32373c;
40
+ box-sizing: border-box;
41
+ }
42
+
43
+ .active-locales-list.empty-list {
44
+ background: rgba(255, 255, 255, 0.5);
45
+ border-color: rgba(222, 222, 222, 0.75);
46
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.04);
47
+ display: flex;
48
+ align-items: center;
49
+ }
50
+
51
+ .active-locales-list li {
52
+ box-sizing: border-box;
53
+ width: 100%;
54
+ height: 28px;
55
+ line-height: 28px;
56
+ margin: 0;
57
+ padding: 0 8px 0 0;
58
+ cursor: pointer;
59
+ }
60
+
61
+ .active-locales-list li:hover {
62
+ background: rgba(0, 0, 0, 0.07);
63
+ }
64
+
65
+ .active-locales-list li[aria-selected="true"] {
66
+ background: rgba(0, 0, 0, 0.15);
67
+ }
68
+
69
+ #active-locales-empty-message {
70
+ display: flex;
71
+ justify-content: center;
72
+ flex-direction: column;
73
+ position: relative;
74
+ text-align: center;
75
+ width: 25em;
76
+ height: 98px;
77
+ margin-bottom: -98px;
78
+ box-sizing: border-box;
79
+ color: rgba(51, 51, 51, 0.5);
80
+ }
81
+
82
+ #active-locales-empty-message.hidden {
83
+ display: none;
84
+ }
85
+
86
+ .inactive-locales {
87
+ clear: both;
88
+ }
89
+
90
+ .inactive-locales select {
91
+ width: 100%;
92
+ }
93
+
94
+ .preferred-languages .notice {
95
+ display: inline-block;
96
+ }
97
+
98
+ /* stylelint-disable selector-id-pattern */
99
+ .js label[for="WPLANG"],
100
+ .js #WPLANG,
101
+ .js .user-language-wrap,
102
+ .no-js .preferred-languages,
103
+ .no-js #preferred-languages-label,
104
+ .no-js .user-preferred-languages-wrap {
105
+ display: none;
106
+ }
107
+ /* stylelint-enable selector-id-pattern */
108
+
109
+ /* Tooltips inspired by https://github.com/primer/primer */
110
+
111
+ @-webkit-keyframes tooltip-appear {
112
+
113
+ from {
114
+ opacity: 0;
115
+ }
116
+
117
+ to {
118
+ opacity: 1;
119
+ }
120
+ }
121
+
122
+ @keyframes tooltip-appear {
123
+
124
+ from {
125
+ opacity: 0;
126
+ }
127
+
128
+ to {
129
+ opacity: 1;
130
+ }
131
+ }
132
+
133
+ .tooltipped {
134
+ position: relative;
135
+ }
136
+
137
+ .tooltipped::before {
138
+ position: absolute;
139
+ z-index: 1000001;
140
+ display: none;
141
+ width: 0;
142
+ height: 0;
143
+ color: #191e23;
144
+ pointer-events: none;
145
+ content: "";
146
+ border: 6px solid transparent;
147
+ opacity: 0;
148
+ top: -7px;
149
+ left: 50%;
150
+ bottom: auto;
151
+ margin-left: -6px;
152
+ border-top-color: #191e23;
153
+ }
154
+
155
+ .tooltipped::after {
156
+ position: absolute;
157
+ z-index: 1000000;
158
+ display: none;
159
+ padding: 0.5em 0.75em;
160
+ font: normal 400 11px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
161
+ -webkit-font-smoothing: subpixel-antialiased;
162
+ color: #fff;
163
+ text-align: center;
164
+ text-decoration: none;
165
+ text-shadow: none;
166
+ text-transform: none;
167
+ letter-spacing: normal;
168
+ word-wrap: break-word;
169
+ white-space: pre;
170
+ pointer-events: none;
171
+ content: attr(data-tooltip);
172
+ background: #191e23;
173
+ border-radius: 3px;
174
+ opacity: 0;
175
+ left: auto;
176
+ right: 50%;
177
+ margin-right: -16px;
178
+ bottom: 100%;
179
+ margin-bottom: 6px;
180
+ }
181
+
182
+ .tooltipped:hover::before,
183
+ .tooltipped:active::before,
184
+ .tooltipped:focus::before,
185
+ .tooltipped:hover::after,
186
+ .tooltipped:active::after,
187
+ .tooltipped:focus::after {
188
+ opacity: 1;
189
+ display: inline-block;
190
+ text-decoration: none;
191
+ -webkit-animation-name: tooltip-appear;
192
+ animation-name: tooltip-appear;
193
+ -webkit-animation-duration: 0.1s;
194
+ animation-duration: 0.1s;
195
+ -webkit-animation-fill-mode: backwards;
196
+ animation-fill-mode: backwards;
197
+ -webkit-animation-timing-function: ease-in;
198
+ animation-timing-function: ease-in;
199
+ -webkit-animation-delay: 0.4s;
200
+ animation-delay: 0.4s;
201
+ }
202
+
build/preferred-languages.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-a11y', 'wp-i18n', 'wp-polyfill'), 'version' => 'db84fb8954710b116b049c69edf8a5ef');
build/preferred-languages.css ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .active-locales {
2
+ margin: 1em 0;
3
+ }
4
+
5
+ .active-locales-list,
6
+ .inactive-locales-list {
7
+ width: 25em;
8
+ margin: 0 1em 1em 0;
9
+ clear: both;
10
+ float: left;
11
+ }
12
+
13
+ .active-locales-controls {
14
+ clear: both;
15
+ }
16
+
17
+ .active-locales-controls ul {
18
+ margin: 0;
19
+ }
20
+
21
+ .active-locales-controls .button {
22
+ margin: 0;
23
+ }
24
+
25
+ @media screen and (min-width: 510px) {
26
+
27
+ .active-locales-controls {
28
+ clear: none;
29
+ }
30
+ }
31
+
32
+ .active-locales-list {
33
+ height: 98px;
34
+ overflow-y: scroll;
35
+ margin-top: 0;
36
+ background: #fff;
37
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07);
38
+ border: 1px solid #ddd;
39
+ color: #32373c;
40
+ box-sizing: border-box;
41
+ }
42
+
43
+ .active-locales-list.empty-list {
44
+ background: rgba(255, 255, 255, 0.5);
45
+ border-color: rgba(222, 222, 222, 0.75);
46
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.04);
47
+ display: flex;
48
+ align-items: center;
49
+ }
50
+
51
+ .active-locales-list li {
52
+ box-sizing: border-box;
53
+ width: 100%;
54
+ height: 28px;
55
+ line-height: 28px;
56
+ margin: 0;
57
+ padding: 0 0 0 8px;
58
+ cursor: pointer;
59
+ }
60
+
61
+ .active-locales-list li:hover {
62
+ background: rgba(0, 0, 0, 0.07);
63
+ }
64
+
65
+ .active-locales-list li[aria-selected="true"] {
66
+ background: rgba(0, 0, 0, 0.15);
67
+ }
68
+
69
+ #active-locales-empty-message {
70
+ display: flex;
71
+ justify-content: center;
72
+ flex-direction: column;
73
+ position: relative;
74
+ text-align: center;
75
+ width: 25em;
76
+ height: 98px;
77
+ margin-bottom: -98px;
78
+ box-sizing: border-box;
79
+ color: rgba(51, 51, 51, 0.5);
80
+ }
81
+
82
+ #active-locales-empty-message.hidden {
83
+ display: none;
84
+ }
85
+
86
+ .inactive-locales {
87
+ clear: both;
88
+ }
89
+
90
+ .inactive-locales select {
91
+ width: 100%;
92
+ }
93
+
94
+ .preferred-languages .notice {
95
+ display: inline-block;
96
+ }
97
+
98
+ /* stylelint-disable selector-id-pattern */
99
+ .js label[for="WPLANG"],
100
+ .js #WPLANG,
101
+ .js .user-language-wrap,
102
+ .no-js .preferred-languages,
103
+ .no-js #preferred-languages-label,
104
+ .no-js .user-preferred-languages-wrap {
105
+ display: none;
106
+ }
107
+ /* stylelint-enable selector-id-pattern */
108
+
109
+ /* Tooltips inspired by https://github.com/primer/primer */
110
+
111
+ @-webkit-keyframes tooltip-appear {
112
+
113
+ from {
114
+ opacity: 0;
115
+ }
116
+
117
+ to {
118
+ opacity: 1;
119
+ }
120
+ }
121
+
122
+ @keyframes tooltip-appear {
123
+
124
+ from {
125
+ opacity: 0;
126
+ }
127
+
128
+ to {
129
+ opacity: 1;
130
+ }
131
+ }
132
+
133
+ .tooltipped {
134
+ position: relative;
135
+ }
136
+
137
+ .tooltipped::before {
138
+ position: absolute;
139
+ z-index: 1000001;
140
+ display: none;
141
+ width: 0;
142
+ height: 0;
143
+ color: #191e23;
144
+ pointer-events: none;
145
+ content: "";
146
+ border: 6px solid transparent;
147
+ opacity: 0;
148
+ top: -7px;
149
+ right: 50%;
150
+ bottom: auto;
151
+ margin-right: -6px;
152
+ border-top-color: #191e23;
153
+ }
154
+
155
+ .tooltipped::after {
156
+ position: absolute;
157
+ z-index: 1000000;
158
+ display: none;
159
+ padding: 0.5em 0.75em;
160
+ font: normal 400 11px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
161
+ -webkit-font-smoothing: subpixel-antialiased;
162
+ color: #fff;
163
+ text-align: center;
164
+ text-decoration: none;
165
+ text-shadow: none;
166
+ text-transform: none;
167
+ letter-spacing: normal;
168
+ word-wrap: break-word;
169
+ white-space: pre;
170
+ pointer-events: none;
171
+ content: attr(data-tooltip);
172
+ background: #191e23;
173
+ border-radius: 3px;
174
+ opacity: 0;
175
+ right: auto;
176
+ left: 50%;
177
+ margin-left: -16px;
178
+ bottom: 100%;
179
+ margin-bottom: 6px;
180
+ }
181
+
182
+ .tooltipped:hover::before,
183
+ .tooltipped:active::before,
184
+ .tooltipped:focus::before,
185
+ .tooltipped:hover::after,
186
+ .tooltipped:active::after,
187
+ .tooltipped:focus::after {
188
+ opacity: 1;
189
+ display: inline-block;
190
+ text-decoration: none;
191
+ -webkit-animation-name: tooltip-appear;
192
+ animation-name: tooltip-appear;
193
+ -webkit-animation-duration: 0.1s;
194
+ animation-duration: 0.1s;
195
+ -webkit-animation-fill-mode: backwards;
196
+ animation-fill-mode: backwards;
197
+ -webkit-animation-timing-function: ease-in;
198
+ animation-timing-function: ease-in;
199
+ -webkit-animation-delay: 0.4s;
200
+ animation-delay: 0.4s;
201
+ }
202
+
build/preferred-languages.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var t={};function a(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,a),r.l=!0,r.exports}a.m=e,a.c=t,a.d=function(e,t,n){a.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,t){if(1&t&&(e=a(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(a.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)a.d(n,r,function(t){return e[t]}.bind(null,r));return n},a.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(t,"a",t),t},a.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},a.p="",a(a.s=2)}([function(e,t){e.exports=window.wp.a11y},function(e,t){e.exports=window.wp.i18n},function(e,t,a){"use strict";a.r(t);var n=a(0),r=a(1);a(3),function(e){var t=e(document),a=e(".active-locales-list"),l=e(".active-locales-controls"),i=e("#active-locales-empty-message"),o=e(".inactive-locales-list"),s=o.find("select"),c=e(".inactive-locales-controls"),d=e('input[name="preferred_languages"]'),f=a.find('li[aria-selected="true"]');function u(e){var t=a.find(".active-locale");l.find(".locales-move-up").attr("disabled",a.hasClass("empty-list")||t.first().is(e)),l.find(".locales-move-down").attr("disabled",a.hasClass("empty-list")||t.last().is(e)),l.find(".locales-remove").attr("disabled",a.hasClass("empty-list")),c.find(".locales-add").attr("disabled","disabled"===s.attr("disabled"))}function p(e){var t=e.attr("aria-selected"),n="true"!==t;"true"!==t&&(f.attr("aria-selected",!1),e.attr("aria-selected",n),!0===n&&(f=e,a.attr("aria-activedescendant",f.attr("id"))),u(e))}function v(){var t=[];a.children(".active-locale").each((function(a,n){t.push(e(n).attr("id"))})),d.val(t.join(","))}function m(){f.insertBefore(f.prev()),v(),u(f),Object(n.speak)(Object(r.__)("Locale moved up","preferred-languages"))}function g(){f.insertAfter(f.next()),v(),u(f),Object(n.speak)(Object(r.__)("Locale moved down","preferred-languages"))}function h(){a.hasClass("empty-list")?(a.removeClass("empty-list"),i.addClass("hidden")):(a.addClass("empty-list"),a.attr("aria-activedescendant",""),i.removeClass("hidden"),Object(n.speak)(i.data("a11y-message")))}function b(){var e,t=f.attr("id");0===(e=f.prevAll(":first")).length&&(e=f.nextAll(":first")),f.remove(),e.length?p(e):h(),s.find('option[value="'.concat(t,'"]')).removeClass("hidden"),s.attr("disabled",!1),v(),u(f),Object(n.speak)(Object(r.__)("Locale removed from list","preferred-languages"))}function y(t){var l,i=t.val()||"en_US";if(t.removeAttr("selected").addClass("hidden"),(l=t.prevAll(":not(.hidden):first")).length||(l=t.nextAll(":not(.hidden):first")),l.length||(l=s.find("option:not(.hidden):first")),l.length||s.attr("disabled",!0),l.attr("selected",!0),c.val(l.val()),!a.find("#".concat(i)).length){a.hasClass("empty-list")&&h();var o=e("<li/>",{text:t.text(),role:"option","aria-selected":!1,id:i,class:"active-locale"});a.append(o),p(o),a.animate({scrollTop:o.offset().top-a.offset().top+a.scrollTop()}),v(),Object(n.speak)(Object(r.__)("Locale added to list","preferred-languages"))}}if(e(".user-language-wrap").replaceWith(e(".user-preferred-languages-wrap")),e(".options-general-php #WPLANG").parent().parent().replaceWith(e(".site-preferred-languages-wrap")),e(".network-admin.settings-php #WPLANG").parent().parent().replaceWith(e(".network-preferred-languages-wrap")),o.filter('[data-show-en_US="false"]').find('[lang="en"][value=""]').remove(),u(f),d.val().length){e.each(d.val().split(","),(function(e,t){t="en_US"===t?"":t,s.find('[value="'.concat(t,'"]')).removeAttr("selected").addClass("hidden")}));var w=s.find("option:not(.hidden):first");w.attr("selected",!0),c.val(w.val())}0===s.find(":not(.hidden)").length&&(s.attr("disabled",!0),c.find(".locales-add").attr("disabled",!0)),a.sortable({axis:"y",cursor:"move",items:":not(#active-locales-list-empty-message)",update:function(){v(),u(f)}}),t.on("keydown",(function(e){if(document.querySelector(".preferred-languages").contains(document.activeElement))switch(e.which){case 38:e.altKey?m():f.prev().length?p(f.prev()):a.focus(),e.preventDefault();break;case 40:e.altKey?g():f.next().length?p(f.next()):a.focus(),e.preventDefault();break;case 65:e.altKey&&y(s.find("option:selected")),e.preventDefault();break;case 8:b(),e.preventDefault()}})),c.find(".locales-add").on("click",(function(){y(s.find("option:selected"))})),a.on("click",".active-locale",(function(t){p(e(t.currentTarget))})),l.find(".locales-move-up").on("click",m),l.find(".locales-move-down").on("click",g),l.find(".locales-remove").on("click",b)}(window.jQuery)},function(e,t,a){}]);
css/preferred-languages-rtl.css DELETED
@@ -1 +0,0 @@
1
- .active-locales{margin:1em 0}.active-locales-list,.inactive-locales-list{width:25em;margin:0 0 1em 1em;clear:both;float:right}.active-locales-controls{clear:both}.active-locales-controls .button,.active-locales-controls ul{margin:0}@media screen and (min-width:510px){.active-locales-controls{clear:none}}.active-locales-list{height:98px;overflow-y:scroll;margin-top:0;background:#fff;box-shadow:inset 0 1px 2px rgba(0,0,0,.07);border:1px solid #ddd;color:#32373c;box-sizing:border-box}.active-locales-list.empty-list{background:hsla(0,0%,100%,.5);border-color:hsla(0,0%,87.1%,.75);box-shadow:inset 0 1px 2px rgba(0,0,0,.04);display:flex;align-items:center}.active-locales-list li{box-sizing:border-box;width:100%;height:28px;line-height:28px;margin:0;padding:0 8px 0 0;cursor:pointer}.active-locales-list li:hover{background:rgba(0,0,0,.07)}.active-locales-list li[aria-selected=true]{background:rgba(0,0,0,.15)}#active-locales-empty-message{display:flex;justify-content:center;flex-direction:column;position:relative;text-align:center;width:25em;height:98px;margin-bottom:-98px;box-sizing:border-box;color:rgba(51,51,51,.5)}#active-locales-empty-message.hidden{display:none}.inactive-locales{clear:both}.inactive-locales select{width:100%}.preferred-languages .notice{display:inline-block}.js #WPLANG,.js .user-language-wrap,.js label[for=WPLANG],.no-js #preferred-languages-label,.no-js .preferred-languages,.no-js .user-preferred-languages-wrap{display:none}@keyframes tooltip-appear{0%{opacity:0}to{opacity:1}}.tooltipped{position:relative}.tooltipped:active:after,.tooltipped:active:before,.tooltipped:focus:after,.tooltipped:focus:before,.tooltipped:hover:after,.tooltipped:hover:before{opacity:1;display:inline-block;text-decoration:none;animation-name:tooltip-appear;animation-duration:.1s;animation-fill-mode:backwards;animation-timing-function:ease-in;animation-delay:.4s}.tooltipped:before{z-index:1000001;width:0;height:0;color:#191e23;content:"";top:-7px;left:50%;bottom:auto;margin-left:-6px;border:6px solid transparent;border-top-color:#191e23}.tooltipped:after,.tooltipped:before{position:absolute;display:none;pointer-events:none;opacity:0}.tooltipped:after{z-index:1000000;padding:.5em .75em;font:normal normal 11px/1.5 -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;-webkit-font-smoothing:subpixel-antialiased;color:#fff;text-align:center;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-wrap:break-word;white-space:pre;content:attr(data-tooltip);background:#191e23;border-radius:3px;left:auto;right:50%;margin-right:-16px;bottom:100%;margin-bottom:6px}
 
css/preferred-languages.css DELETED
@@ -1 +0,0 @@
1
- .active-locales{margin:1em 0}.active-locales-list,.inactive-locales-list{width:25em;margin:0 1em 1em 0;clear:both;float:left}.active-locales-controls{clear:both}.active-locales-controls .button,.active-locales-controls ul{margin:0}@media screen and (min-width:510px){.active-locales-controls{clear:none}}.active-locales-list{height:98px;overflow-y:scroll;margin-top:0;background:#fff;box-shadow:inset 0 1px 2px rgba(0,0,0,.07);border:1px solid #ddd;color:#32373c;box-sizing:border-box}.active-locales-list.empty-list{background:hsla(0,0%,100%,.5);border-color:hsla(0,0%,87.1%,.75);box-shadow:inset 0 1px 2px rgba(0,0,0,.04);display:flex;align-items:center}.active-locales-list li{box-sizing:border-box;width:100%;height:28px;line-height:28px;margin:0;padding:0 0 0 8px;cursor:pointer}.active-locales-list li:hover{background:rgba(0,0,0,.07)}.active-locales-list li[aria-selected=true]{background:rgba(0,0,0,.15)}#active-locales-empty-message{display:flex;justify-content:center;flex-direction:column;position:relative;text-align:center;width:25em;height:98px;margin-bottom:-98px;box-sizing:border-box;color:rgba(51,51,51,.5)}#active-locales-empty-message.hidden{display:none}.inactive-locales{clear:both}.inactive-locales select{width:100%}.preferred-languages .notice{display:inline-block}.js #WPLANG,.js .user-language-wrap,.js label[for=WPLANG],.no-js #preferred-languages-label,.no-js .preferred-languages,.no-js .user-preferred-languages-wrap{display:none}@keyframes tooltip-appear{0%{opacity:0}to{opacity:1}}.tooltipped{position:relative}.tooltipped:active:after,.tooltipped:active:before,.tooltipped:focus:after,.tooltipped:focus:before,.tooltipped:hover:after,.tooltipped:hover:before{opacity:1;display:inline-block;text-decoration:none;animation-name:tooltip-appear;animation-duration:.1s;animation-fill-mode:backwards;animation-timing-function:ease-in;animation-delay:.4s}.tooltipped:before{z-index:1000001;width:0;height:0;color:#191e23;content:"";top:-7px;right:50%;bottom:auto;margin-right:-6px;border:6px solid transparent;border-top-color:#191e23}.tooltipped:after,.tooltipped:before{position:absolute;display:none;pointer-events:none;opacity:0}.tooltipped:after{z-index:1000000;padding:.5em .75em;font:normal normal 11px/1.5 -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;-webkit-font-smoothing:subpixel-antialiased;color:#fff;text-align:center;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-wrap:break-word;white-space:pre;content:attr(data-tooltip);background:#191e23;border-radius:3px;right:auto;left:50%;margin-left:-16px;bottom:100%;margin-bottom:6px}
 
inc/default-filters.php CHANGED
@@ -13,19 +13,21 @@ add_action( 'init', 'preferred_languages_register_setting' );
13
  add_action( 'init', 'preferred_languages_register_meta' );
14
  add_action( 'init', 'preferred_languages_register_scripts' );
15
 
16
- add_action( 'admin_enqueue_scripts', 'preferred_languages_enqueue_scripts' );
17
-
18
  add_action( 'admin_init', 'preferred_languages_settings_field' );
 
 
19
 
20
  add_action( 'personal_options', 'preferred_languages_personal_options' );
21
  add_action( 'personal_options_update', 'preferred_languages_update_user_option' );
22
  add_action( 'edit_user_profile_update', 'preferred_languages_update_user_option' );
23
 
24
  add_filter( 'pre_update_option', 'preferred_languages_pre_update_option', 10, 3 );
 
25
  add_filter( 'update_option_preferred_languages', 'preferred_languages_update_option', 10, 2 );
 
26
  add_filter( 'update_user_metadata', 'preferred_languages_pre_update_user_meta', 10, 5 );
27
  add_filter( 'update_user_meta', 'preferred_languages_update_user_meta', 10, 4 );
28
  add_filter( 'get_user_metadata', 'preferred_languages_filter_user_locale', 10, 3 );
29
  add_filter( 'locale', 'preferred_languages_filter_locale', 5 ); // Before WP_Locale_Switcher.
30
- add_filter( 'load_textdomain_mofile', 'preferred_languages_load_textdomain_mofile' );
31
  add_filter( 'load_script_translation_file', 'preferred_languages_load_script_translation_file' );
13
  add_action( 'init', 'preferred_languages_register_meta' );
14
  add_action( 'init', 'preferred_languages_register_scripts' );
15
 
 
 
16
  add_action( 'admin_init', 'preferred_languages_settings_field' );
17
+ add_action( 'wpmu_options', 'preferred_languages_network_settings_field' );
18
+ add_action( 'update_wpmu_options', 'preferred_languages_update_network_settings' );
19
 
20
  add_action( 'personal_options', 'preferred_languages_personal_options' );
21
  add_action( 'personal_options_update', 'preferred_languages_update_user_option' );
22
  add_action( 'edit_user_profile_update', 'preferred_languages_update_user_option' );
23
 
24
  add_filter( 'pre_update_option', 'preferred_languages_pre_update_option', 10, 3 );
25
+ add_filter( 'pre_update_site_option', 'preferred_languages_pre_update_option', 10, 3 );
26
  add_filter( 'update_option_preferred_languages', 'preferred_languages_update_option', 10, 2 );
27
+ add_filter( 'update_site_option_preferred_languages', 'preferred_languages_update_site_option', 10, 2 );
28
  add_filter( 'update_user_metadata', 'preferred_languages_pre_update_user_meta', 10, 5 );
29
  add_filter( 'update_user_meta', 'preferred_languages_update_user_meta', 10, 4 );
30
  add_filter( 'get_user_metadata', 'preferred_languages_filter_user_locale', 10, 3 );
31
  add_filter( 'locale', 'preferred_languages_filter_locale', 5 ); // Before WP_Locale_Switcher.
32
+ add_filter( 'load_textdomain_mofile', 'preferred_languages_load_textdomain_mofile', 10, 2 );
33
  add_filter( 'load_script_translation_file', 'preferred_languages_load_script_translation_file' );
inc/functions.php CHANGED
@@ -95,6 +95,18 @@ function preferred_languages_get_site_list() {
95
  return array_filter( explode( ',', $preferred_languages ) );
96
  }
97
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  /**
99
  * Returns the list of preferred languages.
100
  *
@@ -117,7 +129,14 @@ function preferred_languages_get_list() {
117
  }
118
 
119
  // Fall back to site setting.
120
- return preferred_languages_get_site_list();
 
 
 
 
 
 
 
121
  }
122
 
123
  /**
@@ -224,31 +243,73 @@ function preferred_languages_update_option( $old_value, $value ) {
224
  load_default_textdomain( reset( $preferred_languages_list ) );
225
  }
226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  /**
228
  * Downloads language packs upon updating the option.
229
  *
230
  * @since 1.0.0
231
  *
232
- * @param array $locales The language packs to install.
233
- * @return array|false The installed language packs or false on failure.
234
  */
235
  function preferred_languages_download_language_packs( $locales ) {
236
- if ( ! current_user_can( 'install_languages' ) ) {
237
- return false;
238
- }
239
-
240
  // Handle translation install.
241
  require_once ABSPATH . 'wp-admin/includes/translation-install.php';
242
 
243
- $installed_languages = array();
 
 
244
 
245
  foreach ( $locales as $locale ) {
 
 
 
 
 
 
 
 
 
246
  $language = wp_download_language_pack( $locale );
 
247
  if ( $language ) {
248
  $installed_languages[] = $language;
249
  }
250
  }
251
 
 
 
 
 
 
 
 
 
 
 
252
  return $installed_languages;
253
  }
254
 
@@ -279,6 +340,10 @@ function preferred_languages_sanitize_list( $preferred_languages ) {
279
  function preferred_languages_filter_locale( $locale ) {
280
  $preferred_languages = preferred_languages_get_site_list();
281
 
 
 
 
 
282
  if ( empty( $preferred_languages ) ) {
283
  return $locale;
284
  }
@@ -318,14 +383,11 @@ function preferred_languages_filter_user_locale( $value, $object_id, $meta_key )
318
  * @since 1.0.0
319
  *
320
  * @param string $mofile Path to the MO file.
 
321
  *
322
  * @return string The modified MO file path.
323
  */
324
- function preferred_languages_load_textdomain_mofile( $mofile ) {
325
- if ( is_readable( $mofile ) ) {
326
- return $mofile;
327
- }
328
-
329
  $preferred_locales = preferred_languages_get_list();
330
 
331
  if ( empty( $preferred_locales ) ) {
@@ -339,14 +401,43 @@ function preferred_languages_load_textdomain_mofile( $mofile ) {
339
  return $mofile;
340
  }
341
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  foreach ( $preferred_locales as $locale ) {
343
  $preferred_mofile = str_replace( $current_locale, $locale, $mofile );
344
 
345
  if ( is_readable( $preferred_mofile ) ) {
346
- return $preferred_mofile;
 
 
 
 
 
 
 
 
347
  }
348
  }
349
 
 
 
 
 
 
 
350
  return $mofile;
351
  }
352
 
@@ -394,51 +485,34 @@ function preferred_languages_load_script_translation_file( $file ) {
394
  * @since 1.0.0
395
  */
396
  function preferred_languages_register_scripts() {
397
- $suffix = SCRIPT_DEBUG ? '' : '.min';
 
 
 
 
 
 
 
398
 
399
  wp_register_script(
400
  'preferred-languages',
401
- plugin_dir_url( dirname( __FILE__ ) ) . 'js/preferred-languages' . $suffix . '.js',
402
- array(
403
- 'jquery',
404
- 'jquery-ui-sortable',
405
- 'wp-a11y',
406
- ),
407
- '20190107',
408
  true
409
  );
410
 
411
- wp_localize_script(
412
- 'preferred-languages',
413
- 'preferredLanguages',
414
- array(
415
- 'l10n' => array(
416
- 'localeAdded' => __( 'Locale added to list', 'preferred-languages' ),
417
- 'localeRemoved' => __( 'Locale removed from list', 'preferred-languages' ),
418
- 'movedUp' => __( 'Locale moved up', 'preferred-languages' ),
419
- 'movedDown' => __( 'Locale moved down', 'preferred-languages' ),
420
- ),
421
- )
422
- );
423
 
424
  wp_register_style(
425
  'preferred-languages',
426
- plugin_dir_url( dirname( __FILE__ ) ) . 'css/preferred-languages.css',
427
  array(),
428
- '20190107',
429
  'screen'
430
  );
431
 
432
- wp_styles()->add_data( 'preferred-languages', 'rtl', true );
433
- }
434
-
435
- /**
436
- * Enqueues the stylesheet in the admin.
437
- *
438
- * @since 1.4.0
439
- */
440
- function preferred_languages_enqueue_scripts() {
441
- wp_enqueue_style( 'preferred-languages' );
442
  }
443
 
444
  /**
@@ -449,7 +523,7 @@ function preferred_languages_enqueue_scripts() {
449
  function preferred_languages_settings_field() {
450
  add_settings_field(
451
  'preferred_languages',
452
- '<span id="preferred-languages-label">' . __( 'Site Language', 'preferred-languages' ) . '<span/>',
453
  'preferred_languages_display_form',
454
  'general',
455
  'default',
@@ -458,6 +532,55 @@ function preferred_languages_settings_field() {
458
  'selected' => preferred_languages_get_site_list(),
459
  )
460
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
  }
462
 
463
  /**
@@ -476,14 +599,17 @@ function preferred_languages_personal_options( $user ) {
476
  ?>
477
  <tr class="user-preferred-languages-wrap">
478
  <th scope="row">
479
- <span id="preferred-languages-label"><?php _e( 'Language', 'preferred-languages' ); ?></span>
 
 
 
480
  </th>
481
  <td>
482
  <?php
483
  preferred_languages_display_form(
484
  array(
485
  'selected' => preferred_languages_get_user_list( $user ),
486
- 'show_available_translations' => current_user_can( 'install_languages' ),
487
  'show_option_site_default' => true,
488
  'show_option_en_US' => true,
489
  )
@@ -503,8 +629,9 @@ function preferred_languages_personal_options( $user ) {
503
  */
504
  function preferred_languages_display_form( $args = array() ) {
505
  wp_enqueue_script( 'preferred-languages' );
 
506
 
507
- $args = wp_parse_args(
508
  $args,
509
  array(
510
  'selected' => array(),
95
  return array_filter( explode( ',', $preferred_languages ) );
96
  }
97
 
98
+ /**
99
+ * Returns the list of preferred languages of the current site.
100
+ *
101
+ * @since 1.7.0
102
+ *
103
+ * @return array Preferred languages.
104
+ */
105
+ function preferred_languages_get_network_list() {
106
+ $preferred_languages = get_site_option( 'preferred_languages', '' );
107
+ return array_filter( explode( ',', $preferred_languages ) );
108
+ }
109
+
110
  /**
111
  * Returns the list of preferred languages.
112
  *
129
  }
130
 
131
  // Fall back to site setting.
132
+ $preferred_languages = preferred_languages_get_site_list();
133
+
134
+ if ( ! empty( $preferred_languages ) ) {
135
+ return $preferred_languages;
136
+ }
137
+
138
+ // Fallback to network setting.
139
+ return preferred_languages_get_network_list();
140
  }
141
 
142
  /**
243
  load_default_textdomain( reset( $preferred_languages_list ) );
244
  }
245
 
246
+ /**
247
+ * Downloads language packs upon updating the network option.
248
+ *
249
+ * @since 1.7.0
250
+ *
251
+ * @param string $old_value The old option value.
252
+ * @param string $value The new option value.
253
+ */
254
+ function preferred_languages_update_site_option( $old_value, $value ) {
255
+ remove_filter( 'update_site_option_preferred_languages', 'preferred_languages_update_option' );
256
+
257
+ $locales = array_filter( explode( ',', $value ) );
258
+ $installed_languages = preferred_languages_download_language_packs( $locales );
259
+
260
+ // Only store actually installed languages in option.
261
+ update_site_option( 'preferred_languages', implode( ',', $installed_languages ) );
262
+
263
+ add_filter( 'update_site_option_preferred_languages', 'preferred_languages_update_option', 10, 2 );
264
+
265
+ // Reload translations after save.
266
+ $preferred_languages_list = preferred_languages_get_network_list();
267
+ load_default_textdomain( reset( $preferred_languages_list ) );
268
+ }
269
+
270
  /**
271
  * Downloads language packs upon updating the option.
272
  *
273
  * @since 1.0.0
274
  *
275
+ * @param array $locales List of locales to install language packs for.
276
+ * @return array The installed and available languages.
277
  */
278
  function preferred_languages_download_language_packs( $locales ) {
 
 
 
 
279
  // Handle translation install.
280
  require_once ABSPATH . 'wp-admin/includes/translation-install.php';
281
 
282
+ $installed_languages = array();
283
+ $available_languages = get_available_languages();
284
+ $user_can_install_languages = current_user_can( 'install_languages' );
285
 
286
  foreach ( $locales as $locale ) {
287
+ if ( in_array( $locale, $available_languages, true ) ) {
288
+ $installed_languages[] = $locale;
289
+ continue;
290
+ }
291
+
292
+ if ( ! $user_can_install_languages ) {
293
+ continue;
294
+ }
295
+
296
  $language = wp_download_language_pack( $locale );
297
+
298
  if ( $language ) {
299
  $installed_languages[] = $language;
300
  }
301
  }
302
 
303
+ /**
304
+ * Fires when downloading language packs upon updating preferences.
305
+ *
306
+ * @since 1.7.0
307
+ *
308
+ * @param array $locales List of locales to install language packs for.
309
+ * @param array $installed_languages List of language packs that were successfully installed.
310
+ */
311
+ do_action( 'preferred_languages_download_language_packs', $locales, $installed_languages );
312
+
313
  return $installed_languages;
314
  }
315
 
340
  function preferred_languages_filter_locale( $locale ) {
341
  $preferred_languages = preferred_languages_get_site_list();
342
 
343
+ if ( empty( $preferred_languages ) && is_multisite() ) {
344
+ $preferred_languages = preferred_languages_get_network_list();
345
+ }
346
+
347
  if ( empty( $preferred_languages ) ) {
348
  return $locale;
349
  }
383
  * @since 1.0.0
384
  *
385
  * @param string $mofile Path to the MO file.
386
+ * @param string $domain Text domain. Unique identifier for retrieving translated strings.
387
  *
388
  * @return string The modified MO file path.
389
  */
390
+ function preferred_languages_load_textdomain_mofile( $mofile, $domain ) {
 
 
 
 
391
  $preferred_locales = preferred_languages_get_list();
392
 
393
  if ( empty( $preferred_locales ) ) {
401
  return $mofile;
402
  }
403
 
404
+ $first_mofile = null;
405
+
406
+ remove_filter( 'load_textdomain_mofile', 'preferred_languages_load_textdomain_mofile' );
407
+
408
+ /**
409
+ * Filters whether translations should be merged with existing ones.
410
+ *
411
+ * @since 1.7.0
412
+ *
413
+ * @param bool $merge Whether translations should be merged.
414
+ * @param string $domain The text domain
415
+ * @param string $current_locale The current locale.
416
+ */
417
+ $merge_translations = apply_filters( 'preferred_languages_merge_translations', false, $domain, $current_locale );
418
+
419
  foreach ( $preferred_locales as $locale ) {
420
  $preferred_mofile = str_replace( $current_locale, $locale, $mofile );
421
 
422
  if ( is_readable( $preferred_mofile ) ) {
423
+ if ( ! $merge_translations ) {
424
+ return $preferred_mofile;
425
+ }
426
+
427
+ load_textdomain( $domain, $preferred_mofile );
428
+
429
+ if ( null === $first_mofile ) {
430
+ $first_mofile = $preferred_mofile;
431
+ }
432
  }
433
  }
434
 
435
+ add_filter( 'load_textdomain_mofile', 'preferred_languages_load_textdomain_mofile', 10, 2 );
436
+
437
+ if ( null !== $first_mofile ) {
438
+ return $first_mofile;
439
+ }
440
+
441
  return $mofile;
442
  }
443
 
485
  * @since 1.0.0
486
  */
487
  function preferred_languages_register_scripts() {
488
+ $asset_file = dirname( __DIR__ ) . '/build/preferred-languages.asset.php';
489
+ $asset = is_readable( $asset_file ) ? require $asset_file : array();
490
+
491
+ $asset['dependencies'] = isset( $asset['dependencies'] ) ? $asset['dependencies'] : array();
492
+ $asset['version'] = isset( $asset['version'] ) ? $asset['version'] : '';
493
+
494
+ $asset['dependencies'][] = 'jquery';
495
+ $asset['dependencies'][] = 'jquery-ui-sortable';
496
 
497
  wp_register_script(
498
  'preferred-languages',
499
+ plugins_url( 'build/preferred-languages.js', __DIR__ ),
500
+ $asset['dependencies'],
501
+ $asset['version'],
 
 
 
 
502
  true
503
  );
504
 
505
+ wp_set_script_translations( 'preferred-languages', 'preferred-languages' );
 
 
 
 
 
 
 
 
 
 
 
506
 
507
  wp_register_style(
508
  'preferred-languages',
509
+ plugins_url( 'build/preferred-languages.css', __DIR__ ),
510
  array(),
511
+ $asset['version'],
512
  'screen'
513
  );
514
 
515
+ wp_style_add_data( 'preferred-languages', 'rtl', 'replace' );
 
 
 
 
 
 
 
 
 
516
  }
517
 
518
  /**
523
  function preferred_languages_settings_field() {
524
  add_settings_field(
525
  'preferred_languages',
526
+ '<span id="preferred-languages-label">' . __( 'Site Language', 'preferred-languages' ) . '<span/> <span class="dashicons dashicons-translation" aria-hidden="true"></span>',
527
  'preferred_languages_display_form',
528
  'general',
529
  'default',
532
  'selected' => preferred_languages_get_site_list(),
533
  )
534
  );
535
+
536
+ if ( is_multisite() ) {
537
+ add_settings_section(
538
+ 'preferred_languages',
539
+ '',
540
+ '__return_empty_string',
541
+ 'preferred_languages_network_settings'
542
+ );
543
+
544
+ add_settings_field(
545
+ 'preferred_languages',
546
+ '<span id="preferred-languages-label">' . __( 'Default Language', 'preferred-languages' ) . '<span/> <span class="dashicons dashicons-translation" aria-hidden="true"></span>',
547
+ 'preferred_languages_display_form',
548
+ 'preferred_languages_network_settings',
549
+ 'preferred_languages',
550
+ array(
551
+ 'class' => 'network-preferred-languages-wrap',
552
+ 'selected' => preferred_languages_get_network_list(),
553
+ )
554
+ );
555
+ }
556
+ }
557
+
558
+ /**
559
+ * Adds a settings field for the preferred languages option.
560
+ *
561
+ * @since 1.7.0
562
+ */
563
+ function preferred_languages_network_settings_field() {
564
+ wp_nonce_field( 'preferred_languages_network_settings', 'preferred_languages_network_settings_nonce' );
565
+ do_settings_sections( 'preferred_languages' );
566
+ do_settings_sections( 'preferred_languages_network_settings' );
567
+ }
568
+
569
+ /**
570
+ * Updates the preferred languages network settings.
571
+ *
572
+ * @since 1.7.0
573
+ */
574
+ function preferred_languages_update_network_settings() {
575
+ $nonce = isset( $_POST['preferred_languages_network_settings_nonce'] ) ? wp_unslash( $_POST['preferred_languages_network_settings_nonce'] ) : null;
576
+
577
+ if ( ! wp_verify_nonce( $nonce, 'preferred_languages_network_settings' ) ) {
578
+ return;
579
+ }
580
+
581
+ if ( isset( $_POST['preferred_languages'] ) ) {
582
+ update_site_option( 'preferred_languages', wp_unslash( $_POST['preferred_languages'] ) );
583
+ }
584
  }
585
 
586
  /**
599
  ?>
600
  <tr class="user-preferred-languages-wrap">
601
  <th scope="row">
602
+ <span id="preferred-languages-label">
603
+ <?php _e( 'Language', 'preferred-languages' ); ?>
604
+ </span>
605
+ <span class="dashicons dashicons-translation" aria-hidden="true"></span>
606
  </th>
607
  <td>
608
  <?php
609
  preferred_languages_display_form(
610
  array(
611
  'selected' => preferred_languages_get_user_list( $user ),
612
+ 'show_available_translations' => false,
613
  'show_option_site_default' => true,
614
  'show_option_en_US' => true,
615
  )
629
  */
630
  function preferred_languages_display_form( $args = array() ) {
631
  wp_enqueue_script( 'preferred-languages' );
632
+ wp_enqueue_style( 'preferred-languages' );
633
 
634
+ $args = (array) wp_parse_args(
635
  $args,
636
  array(
637
  'selected' => array(),
js/preferred-languages.js DELETED
@@ -1,313 +0,0 @@
1
- "use strict";
2
-
3
- (function (wp, settings, $) {
4
- var $document = $(document);
5
- var $activeLocales = $('.active-locales-list');
6
- var $activeLocalesControls = $('.active-locales-controls');
7
- var $emptyMessage = $('#active-locales-empty-message');
8
- var $inactiveLocalesWrap = $('.inactive-locales-list');
9
- var $inactiveLocales = $inactiveLocalesWrap.find('select');
10
- var $inactiveLocalesControls = $('.inactive-locales-controls');
11
- var $inputField = $('input[name="preferred_languages"]');
12
- var $selectedLocale = $activeLocales.find('li[aria-selected="true"]');
13
- var KEY_UP = 38;
14
- var KEY_DOWN = 40;
15
- var KEY_BACKSPACE = 8;
16
- var KEY_A = 65;
17
- /**
18
- * Changes the move button states.
19
- *
20
- * @since 1.0.0
21
- *
22
- * @param {jQuery} activeLocale Active locale element.
23
- */
24
-
25
- function changeButtonState(activeLocale) {
26
- var activeLocalesList = $activeLocales.find('.active-locale');
27
- $activeLocalesControls.find('.locales-move-up').attr('disabled', $activeLocales.hasClass('empty-list') || activeLocalesList.first().is(activeLocale));
28
- $activeLocalesControls.find('.locales-move-down').attr('disabled', $activeLocales.hasClass('empty-list') || activeLocalesList.last().is(activeLocale));
29
- $activeLocalesControls.find('.locales-remove').attr('disabled', $activeLocales.hasClass('empty-list'));
30
- $inactiveLocalesControls.find('.locales-add').attr('disabled', 'disabled' === $inactiveLocales.attr('disabled'));
31
- }
32
- /**
33
- * Toggles a locale.
34
- *
35
- * @since 1.0.0
36
- *
37
- * @param {jQuery} $locale Locale element.
38
- */
39
-
40
-
41
- function toggleLocale($locale) {
42
- var selected = $locale.attr('aria-selected');
43
- var newState = 'true' !== selected; // It's already the current locale, so nothing to do here.
44
-
45
- if ('true' === selected) {
46
- return;
47
- }
48
-
49
- $selectedLocale.attr('aria-selected', false);
50
- $locale.attr('aria-selected', newState);
51
-
52
- if (true === newState) {
53
- $selectedLocale = $locale;
54
- $activeLocales.attr('aria-activedescendant', $selectedLocale.attr('id'));
55
- }
56
-
57
- changeButtonState($locale);
58
- }
59
- /**
60
- * Updates the preferred languages input field after a change.
61
- *
62
- * @since 1.0.0
63
- */
64
-
65
-
66
- function updateHiddenInput() {
67
- var locales = [];
68
- $activeLocales.children('.active-locale').each(function (index, el) {
69
- locales.push($(el).attr('id'));
70
- });
71
- $inputField.val(locales.join(','));
72
- }
73
- /**
74
- * Moves a locale up in the list.
75
- *
76
- * @since 1.0.0
77
- */
78
-
79
-
80
- function moveLocaleUp() {
81
- // 1. Change position if possible.
82
- $selectedLocale.insertBefore($selectedLocale.prev()); // 2. Update hidden input field.
83
-
84
- updateHiddenInput(); // 3. Update buttons.
85
-
86
- changeButtonState($selectedLocale); // 4. Announce to assistive technologies.
87
-
88
- wp.a11y.speak(settings.l10n.movedUp);
89
- }
90
- /**
91
- * Moves a locale down in the list.
92
- *
93
- * @since 1.0.0
94
- */
95
-
96
-
97
- function moveLocaleDown() {
98
- // 1. Change position if possible.
99
- $selectedLocale.insertAfter($selectedLocale.next()); // 2. Update hidden input field.
100
-
101
- updateHiddenInput(); // 3. Update buttons.
102
-
103
- changeButtonState($selectedLocale); // 4. Announce to assistive technologies.
104
-
105
- wp.a11y.speak(settings.l10n.movedDown);
106
- }
107
- /**
108
- * Displays a message in case the list of active locales is empty.
109
- *
110
- * @since 1.1.1
111
- */
112
-
113
-
114
- function toggleEmptyListMessage() {
115
- if ($activeLocales.hasClass('empty-list')) {
116
- $activeLocales.removeClass('empty-list');
117
- $emptyMessage.addClass('hidden');
118
- } else {
119
- $activeLocales.addClass('empty-list');
120
- $activeLocales.attr('aria-activedescendant', '');
121
- $emptyMessage.removeClass('hidden');
122
- wp.a11y.speak($emptyMessage.data('a11y-message'));
123
- }
124
- }
125
- /**
126
- * Removes an active locale from the list.
127
- *
128
- * @since 1.0.0
129
- */
130
-
131
-
132
- function makeLocaleInactive() {
133
- var locale = $selectedLocale.attr('id');
134
- var $successor;
135
- $successor = $selectedLocale.prevAll(':first');
136
-
137
- if (0 === $successor.length) {
138
- $successor = $selectedLocale.nextAll(':first');
139
- } // 1. Remove selected locale.
140
-
141
-
142
- $selectedLocale.remove(); // 2. Make another locale the current one.
143
-
144
- if ($successor.length) {
145
- toggleLocale($successor);
146
- } else {
147
- toggleEmptyListMessage();
148
- } // 3. Make visible in dropdown again.
149
-
150
-
151
- $inactiveLocales.find("option[value=\"".concat(locale, "\"]")).removeClass('hidden');
152
- $inactiveLocales.attr('disabled', false); // 4. Update hidden input field.
153
-
154
- updateHiddenInput(); // 5. Update buttons.
155
-
156
- changeButtonState($selectedLocale); // 6. Announce to assistive technologies.
157
-
158
- wp.a11y.speak(settings.l10n.localeRemoved);
159
- }
160
- /**
161
- * Makes an inactive locale active.
162
- *
163
- * @since 1.0.0
164
- *
165
- * @param {jQuery} option The locale element.
166
- */
167
-
168
-
169
- function makeLocaleActive(option) {
170
- var locale = option.val() || 'en_US';
171
- var $newLocale = $('<li/>', {
172
- text: option.text(),
173
- 'role': 'option',
174
- 'aria-selected': false,
175
- 'id': locale,
176
- 'class': 'active-locale'
177
- });
178
- var $successor; // 1. Hide from dropdown.
179
-
180
- option.removeAttr('selected').addClass('hidden');
181
- $successor = option.prevAll(':not(.hidden):first');
182
-
183
- if (!$successor.length) {
184
- $successor = option.nextAll(':not(.hidden):first');
185
- } // Empty optgroup, just select the first option we can find.
186
-
187
-
188
- if (!$successor.length) {
189
- $successor = $inactiveLocales.find('option:not(.hidden):first');
190
- }
191
-
192
- if (!$successor.length) {
193
- $inactiveLocales.attr('disabled', true);
194
- } // 2. Change selected value in dropdown.
195
-
196
-
197
- $successor.attr('selected', true);
198
- $inactiveLocalesControls.val($successor.val()); // It's already in the list of active locales, stop here.
199
-
200
- if ($activeLocales.find("#".concat(locale)).length) {
201
- return;
202
- } // 3. Hide empty list message if present.
203
-
204
-
205
- if ($activeLocales.hasClass('empty-list')) {
206
- toggleEmptyListMessage();
207
- } // 4. Add to list.
208
-
209
-
210
- $activeLocales.append($newLocale);
211
- toggleLocale($newLocale); // 5. Scroll into view.
212
-
213
- $activeLocales.animate({
214
- scrollTop: $newLocale.offset().top - $activeLocales.offset().top + $activeLocales.scrollTop()
215
- }); // 5. Update hidden input field.
216
-
217
- updateHiddenInput(); // 6. Announce to assistive technologies.
218
-
219
- wp.a11y.speak(settings.l10n.localeAdded);
220
- } // Replace original language settings.
221
-
222
-
223
- $('.user-language-wrap').replaceWith($('.user-preferred-languages-wrap'));
224
- $('#WPLANG').parent().parent().replaceWith($('.site-preferred-languages-wrap')); // Remove en_US as an option from the dropdown.
225
-
226
- $inactiveLocalesWrap.filter('[data-show-en_US="false"]').find('[lang="en"][value=""]').remove(); // Change initial button state.
227
-
228
- changeButtonState($selectedLocale); // Initially hide already active locales from dropdown.
229
-
230
- if ($inputField.val().length) {
231
- $.each($inputField.val().split(','), function (index, value) {
232
- value = 'en_US' === value ? '' : value;
233
- var $option = $inactiveLocales.find("[value=\"".concat(value, "\"]")); // 2. Hide from dropdown.
234
-
235
- $option.removeAttr('selected').addClass('hidden');
236
- });
237
- var $firstInactiveLocale = $inactiveLocales.find('option:not(.hidden):first');
238
- $firstInactiveLocale.attr('selected', true);
239
- $inactiveLocalesControls.val($firstInactiveLocale.val());
240
- }
241
-
242
- function onSortableUpdate() {
243
- updateHiddenInput();
244
- changeButtonState($selectedLocale);
245
- } // Enabling sorting locales using drag and drop.
246
-
247
-
248
- $activeLocales.sortable({
249
- axis: 'y',
250
- cursor: 'move',
251
- items: ':not(#active-locales-list-empty-message)',
252
- update: onSortableUpdate
253
- }); // Active locales keyboard shortcuts.
254
-
255
- $document.on('keydown', function (e) {
256
- if (!document.querySelector('.preferred-languages').contains(document.activeElement)) {
257
- return;
258
- }
259
-
260
- switch (e.which) {
261
- case KEY_UP:
262
- if (e.altKey) {
263
- moveLocaleUp();
264
- } else if ($selectedLocale.prev().length) {
265
- toggleLocale($selectedLocale.prev());
266
- } else {
267
- // We're at the top of the list.
268
- $activeLocales.focus();
269
- }
270
-
271
- e.preventDefault();
272
- break;
273
-
274
- case KEY_DOWN:
275
- if (e.altKey) {
276
- moveLocaleDown();
277
- } else if ($selectedLocale.next().length) {
278
- toggleLocale($selectedLocale.next());
279
- } else {
280
- // We're at the bottom of the list.
281
- $activeLocales.focus();
282
- }
283
-
284
- e.preventDefault();
285
- break;
286
-
287
- case KEY_A:
288
- if (e.altKey) {
289
- makeLocaleActive($inactiveLocales.find('option:selected'));
290
- }
291
-
292
- e.preventDefault();
293
- break;
294
-
295
- case KEY_BACKSPACE:
296
- makeLocaleInactive();
297
- e.preventDefault();
298
- break;
299
- }
300
- }); // Add new locale to list.
301
-
302
- $inactiveLocalesControls.find('.locales-add').on('click', function () {
303
- makeLocaleActive($inactiveLocales.find('option:selected'));
304
- }); // Select a locale.
305
-
306
- $activeLocales.on('click', '.active-locale', function (e) {
307
- toggleLocale($(e.currentTarget));
308
- });
309
- $activeLocalesControls.find('.locales-move-up').on('click', moveLocaleUp);
310
- $activeLocalesControls.find('.locales-move-down').on('click', moveLocaleDown); // Remove locale from list.
311
-
312
- $activeLocalesControls.find('.locales-remove').on('click', makeLocaleInactive);
313
- })(wp, preferredLanguages, jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/preferred-languages.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";(function(wp,settings,$){var $document=$(document);var $activeLocales=$(".active-locales-list");var $activeLocalesControls=$(".active-locales-controls");var $emptyMessage=$("#active-locales-empty-message");var $inactiveLocalesWrap=$(".inactive-locales-list");var $inactiveLocales=$inactiveLocalesWrap.find("select");var $inactiveLocalesControls=$(".inactive-locales-controls");var $inputField=$('input[name="preferred_languages"]');var $selectedLocale=$activeLocales.find('li[aria-selected="true"]');var KEY_UP=38;var KEY_DOWN=40;var KEY_BACKSPACE=8;var KEY_A=65;function changeButtonState(activeLocale){var activeLocalesList=$activeLocales.find(".active-locale");$activeLocalesControls.find(".locales-move-up").attr("disabled",$activeLocales.hasClass("empty-list")||activeLocalesList.first().is(activeLocale));$activeLocalesControls.find(".locales-move-down").attr("disabled",$activeLocales.hasClass("empty-list")||activeLocalesList.last().is(activeLocale));$activeLocalesControls.find(".locales-remove").attr("disabled",$activeLocales.hasClass("empty-list"));$inactiveLocalesControls.find(".locales-add").attr("disabled","disabled"===$inactiveLocales.attr("disabled"))}function toggleLocale($locale){var selected=$locale.attr("aria-selected");var newState="true"!==selected;if("true"===selected){return}$selectedLocale.attr("aria-selected",false);$locale.attr("aria-selected",newState);if(true===newState){$selectedLocale=$locale;$activeLocales.attr("aria-activedescendant",$selectedLocale.attr("id"))}changeButtonState($locale)}function updateHiddenInput(){var locales=[];$activeLocales.children(".active-locale").each(function(index,el){locales.push($(el).attr("id"))});$inputField.val(locales.join(","))}function moveLocaleUp(){$selectedLocale.insertBefore($selectedLocale.prev());updateHiddenInput();changeButtonState($selectedLocale);wp.a11y.speak(settings.l10n.movedUp)}function moveLocaleDown(){$selectedLocale.insertAfter($selectedLocale.next());updateHiddenInput();changeButtonState($selectedLocale);wp.a11y.speak(settings.l10n.movedDown)}function toggleEmptyListMessage(){if($activeLocales.hasClass("empty-list")){$activeLocales.removeClass("empty-list");$emptyMessage.addClass("hidden")}else{$activeLocales.addClass("empty-list");$activeLocales.attr("aria-activedescendant","");$emptyMessage.removeClass("hidden");wp.a11y.speak($emptyMessage.data("a11y-message"))}}function makeLocaleInactive(){var locale=$selectedLocale.attr("id");var $successor;$successor=$selectedLocale.prevAll(":first");if(0===$successor.length){$successor=$selectedLocale.nextAll(":first")}$selectedLocale.remove();if($successor.length){toggleLocale($successor)}else{toggleEmptyListMessage()}$inactiveLocales.find('option[value="'.concat(locale,'"]')).removeClass("hidden");$inactiveLocales.attr("disabled",false);updateHiddenInput();changeButtonState($selectedLocale);wp.a11y.speak(settings.l10n.localeRemoved)}function makeLocaleActive(option){var locale=option.val()||"en_US";var $newLocale=$("<li/>",{text:option.text(),role:"option","aria-selected":false,id:locale,class:"active-locale"});var $successor;option.removeAttr("selected").addClass("hidden");$successor=option.prevAll(":not(.hidden):first");if(!$successor.length){$successor=option.nextAll(":not(.hidden):first")}if(!$successor.length){$successor=$inactiveLocales.find("option:not(.hidden):first")}if(!$successor.length){$inactiveLocales.attr("disabled",true)}$successor.attr("selected",true);$inactiveLocalesControls.val($successor.val());if($activeLocales.find("#".concat(locale)).length){return}if($activeLocales.hasClass("empty-list")){toggleEmptyListMessage()}$activeLocales.append($newLocale);toggleLocale($newLocale);$activeLocales.animate({scrollTop:$newLocale.offset().top-$activeLocales.offset().top+$activeLocales.scrollTop()});updateHiddenInput();wp.a11y.speak(settings.l10n.localeAdded)}$(".user-language-wrap").replaceWith($(".user-preferred-languages-wrap"));$("#WPLANG").parent().parent().replaceWith($(".site-preferred-languages-wrap"));$inactiveLocalesWrap.filter('[data-show-en_US="false"]').find('[lang="en"][value=""]').remove();changeButtonState($selectedLocale);if($inputField.val().length){$.each($inputField.val().split(","),function(index,value){value="en_US"===value?"":value;var $option=$inactiveLocales.find('[value="'.concat(value,'"]'));$option.removeAttr("selected").addClass("hidden")});var $firstInactiveLocale=$inactiveLocales.find("option:not(.hidden):first");$firstInactiveLocale.attr("selected",true);$inactiveLocalesControls.val($firstInactiveLocale.val())}function onSortableUpdate(){updateHiddenInput();changeButtonState($selectedLocale)}$activeLocales.sortable({axis:"y",cursor:"move",items:":not(#active-locales-list-empty-message)",update:onSortableUpdate});$document.on("keydown",function(e){if(!document.querySelector(".preferred-languages").contains(document.activeElement)){return}switch(e.which){case KEY_UP:if(e.altKey){moveLocaleUp()}else if($selectedLocale.prev().length){toggleLocale($selectedLocale.prev())}else{$activeLocales.focus()}e.preventDefault();break;case KEY_DOWN:if(e.altKey){moveLocaleDown()}else if($selectedLocale.next().length){toggleLocale($selectedLocale.next())}else{$activeLocales.focus()}e.preventDefault();break;case KEY_A:if(e.altKey){makeLocaleActive($inactiveLocales.find("option:selected"))}e.preventDefault();break;case KEY_BACKSPACE:makeLocaleInactive();e.preventDefault();break}});$inactiveLocalesControls.find(".locales-add").on("click",function(){makeLocaleActive($inactiveLocales.find("option:selected"))});$activeLocales.on("click",".active-locale",function(e){toggleLocale($(e.currentTarget))});$activeLocalesControls.find(".locales-move-up").on("click",moveLocaleUp);$activeLocalesControls.find(".locales-move-down").on("click",moveLocaleDown);$activeLocalesControls.find(".locales-remove").on("click",makeLocaleInactive)})(wp,preferredLanguages,jQuery);
 
preferred-languages.php CHANGED
@@ -3,13 +3,14 @@
3
  * Plugin Name: Preferred Languages
4
  * Plugin URI: https://github.com/swissspidy/preferred-languages/
5
  * Description: Choose languages for displaying WordPress in, in order of preference.
6
- * Version: 1.6.0
7
  * Author: Pascal Birchler
8
  * Author URI: https://pascalbirchler.com
9
  * License: GPL-2.0+
10
  * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
11
  * Text Domain: preferred-languages
12
- * Domain Path: /languages
 
13
  *
14
  * Copyright (c) 2017 Pascal Birchler (email: swissspidy@chat.wordpress.org)
15
  *
3
  * Plugin Name: Preferred Languages
4
  * Plugin URI: https://github.com/swissspidy/preferred-languages/
5
  * Description: Choose languages for displaying WordPress in, in order of preference.
6
+ * Version: 1.7.0
7
  * Author: Pascal Birchler
8
  * Author URI: https://pascalbirchler.com
9
  * License: GPL-2.0+
10
  * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
11
  * Text Domain: preferred-languages
12
+ * Requires at least: 5.3
13
+ * Requires PHP: 5.6
14
  *
15
  * Copyright (c) 2017 Pascal Birchler (email: swissspidy@chat.wordpress.org)
16
  *
readme.txt CHANGED
@@ -1,10 +1,8 @@
1
  === Preferred Languages ===
2
  Contributors: swissspidy
3
  Tags: internationalization, i18n, localization, l10n, language, locale, translation
4
- Requires at least: 4.9
5
- Tested up to: 5.0
6
- Requires PHP: 5.2
7
- Stable tag: 1.5.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -35,6 +33,10 @@ For the plugin's changelog, please head over to [the GitHub repository](https://
35
 
36
  == Upgrade Notice ==
37
 
 
 
 
 
38
  = 1.6.0 =
39
 
40
  This release adds full support for WordPress 5.0. Block editor support requires WordPress 5.0.3.
1
  === Preferred Languages ===
2
  Contributors: swissspidy
3
  Tags: internationalization, i18n, localization, l10n, language, locale, translation
4
+ Tested up to: 5.7
5
+ Stable tag: 1.7.0
 
 
6
  License: GPLv2 or later
7
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
8
 
33
 
34
  == Upgrade Notice ==
35
 
36
+ = 1.7.0 =
37
+
38
+ This release improves Multisite support and contains a new `preferred_languages_merge_translations` filter that allows merging translations.
39
+
40
  = 1.6.0 =
41
 
42
  This release adds full support for WordPress 5.0. Block editor support requires WordPress 5.0.3.