Skip to content

Commit 4305bc7

Browse files
committed
possibility to load multiple language files #112
1 parent 2c4bdbe commit 4305bc7

File tree

7 files changed

+165
-102
lines changed

7 files changed

+165
-102
lines changed

Gruntfile.js

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
var deepmerge = require('deepmerge');
22

3+
function removeJshint(src) {
4+
return src
5+
.replace(/\/\*jshint [a-z:]+ \*\/\r?\n\r?\n?/g, '')
6+
.replace(/\/\*jshint -[EWI]{1}[0-9]{3} \*\/\r?\n\r?\n?/g, '');
7+
}
8+
39
module.exports = function(grunt) {
410
grunt.util.linefeed = '\n';
511

612
var all_modules = {},
713
all_langs = {},
814
loaded_modules = [],
9-
loaded_lang = '',
15+
loaded_langs = [],
1016
js_core_files = [
1117
'src/main.js',
1218
'src/defaults.js',
@@ -59,27 +65,27 @@ module.exports = function(grunt) {
5965
}
6066
}
6167

68+
// default language
69+
js_files_to_load.push('dist/i18n/en.js');
70+
loaded_langs.push('en');
71+
6272
// parse 'lang' parameter
63-
var arg_lang = grunt.option('lang');
64-
if (typeof arg_lang === 'string') {
65-
if (all_langs[arg_lang]) {
66-
if (arg_lang != 'en') {
67-
js_files_to_load.push(all_langs[arg_lang].replace(/^src/, 'dist'));
68-
loaded_lang = arg_lang;
73+
var arg_langs = grunt.option('languages');
74+
if (typeof arg_langs === 'string') {
75+
arg_langs.replace(/ /g, '').split(',').forEach(function(l) {
76+
if (all_langs[l]) {
77+
if (l !== 'en') {
78+
js_files_to_load.push(all_langs[l].replace(/^src/, 'dist').replace(/json$/, 'js'));
79+
loaded_langs.push(l);
80+
}
6981
}
70-
}
71-
else {
72-
grunt.fail.warn('Lang '+ arg_lang +' unknown');
73-
}
82+
else {
83+
grunt.fail.warn('Language '+ l +' unknown');
84+
}
85+
});
7486
}
7587
}());
7688

77-
function removeJshint(src) {
78-
return src
79-
.replace(/\/\*jshint [a-z:]+ \*\/\r?\n\r?\n?/g, '')
80-
.replace(/\/\*jshint -[EWI]{1}[0-9]{3} \*\/\r?\n\r?\n?/g, '');
81-
}
82-
8389

8490
grunt.initConfig({
8591
pkg: grunt.file.readJSON('package.json'),
@@ -181,12 +187,11 @@ module.exports = function(grunt) {
181187
options: {
182188
stripBanners: false,
183189
process: function(src, file) {
184-
var lang = file.split(/[\/\.]/)[2],
185-
content = JSON.parse(src),
186-
header;
190+
var lang = file.split(/[\/\.]/)[2];
191+
var content = JSON.parse(src);
187192

188-
grunt.config.set('lang_copyright', content.__copyright || (l + ' translation'));
189-
header = grunt.template.process('<%= langBanner %>\n\n');
193+
grunt.config.set('lang_copyright', content.__copyright || (lang + ' translation'));
194+
var header = grunt.template.process('<%= langBanner %>');
190195
delete content.__copyright;
191196

192197
loaded_modules.forEach(function(m) {
@@ -196,8 +201,13 @@ module.exports = function(grunt) {
196201
content = deepmerge(content, grunt.file.readJSON(plugin_file));
197202
}
198203
});
199-
200-
return header + 'jQuery.fn.queryBuilder.defaults({ lang: ' + JSON.stringify(content, null, 2) + '});';
204+
205+
return header
206+
+ '\n\n'
207+
+ 'jQuery.fn.queryBuilder.regional[\'' + lang + '\'] = '
208+
+ JSON.stringify(content, null, 2)
209+
+ ';\n\n'
210+
+ 'jQuery.fn.queryBuilder.defaults({ lang_code: \'' + lang + '\' });'
201211
}
202212
}
203213
},
@@ -223,14 +233,13 @@ module.exports = function(grunt) {
223233
options: {
224234
separator: '',
225235
wrapper: function() {
226-
var wrapper = grunt.file.read('src/.wrapper.js').replace(/\r\n/g, '\n')
227-
wrapper = wrapper.split(/@@js\n/);
236+
var wrapper = grunt.file.read('src/.wrapper.js').replace(/\r\n/g, '\n').split(/@@js\n/);
228237

229238
if (loaded_modules.length) {
230239
wrapper[0] = '// Modules: ' + loaded_modules.join(', ') + '\n' + wrapper[0];
231240
}
232-
if (loaded_lang.length) {
233-
wrapper[0] = '// Language: ' + loaded_lang + '\n' + wrapper[0];
241+
if (loaded_langs.length) {
242+
wrapper[0] = '// Languages: ' + loaded_langs.join(', ') + '\n' + wrapper[0];
234243
}
235244
wrapper[0] = grunt.template.process('<%= banner %>\n\n') + wrapper[0];
236245

@@ -311,9 +320,10 @@ module.exports = function(grunt) {
311320
// jshint tests
312321
jshint: {
313322
lib: {
314-
files: {
315-
src: js_files_to_load
316-
}
323+
options: {
324+
'-W069': true // accesses to "regional" in language files
325+
},
326+
src: js_files_to_load
317327
}
318328
},
319329

examples/index.html

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
<link rel="stylesheet" href="bower_components/selectize/dist/css/selectize.bootstrap3.css">
1212

1313
<link rel="stylesheet" href="../dist/css/query-builder.default.css" id="qb-theme">
14+
15+
<link rel="stylesheet" href="http://mistic100.github.io/jQuery-QueryBuilder/assets/flags/flags.css">
1416
</head>
1517

1618
<body>
@@ -26,10 +28,32 @@ <h1>jQuery QueryBuilder <small>Example</small></h1>
2628
You must execute <code>bower install</code> in the example directory to run this demo.
2729
</div>
2830

29-
<div class="btn-group">
30-
<button class="btn btn-default btn-sm" disabled>Theme:</button>
31-
<button class="btn btn-primary btn-sm change-theme" data-qb="../dist/css/query-builder.default.min.css" data-bt="bower_components/bootstrap/dist/css/bootstrap.min.css">Default</button>
32-
<button class="btn btn-primary btn-sm change-theme" data-qb="../dist/css/query-builder.dark.min.css" data-bt="bower_components/bootswatch-dist/css/bootstrap.min.css">Dark</button>
31+
<div class="well well-sm">
32+
<label>Theme:</label>
33+
<div class="btn-group">
34+
<button class="btn btn-primary btn-sm change-theme" data-qb="../dist/css/query-builder.default.min.css" data-bt="bower_components/bootstrap/dist/css/bootstrap.min.css">Default</button>
35+
<button class="btn btn-primary btn-sm change-theme" data-qb="../dist/css/query-builder.dark.min.css" data-bt="bower_components/bootswatch-dist/css/bootstrap.min.css">Dark</button>
36+
</div>
37+
38+
<label>Language:</label>
39+
<select name="language" class="selectpicker show-tick show-menu-arrow" data-width="auto">
40+
<optgroup label="Complete">
41+
<option value="de" data-icon="flag flag-de">German</option>
42+
<option value="en" data-icon="flag flag-gb" selected>English</option>
43+
<option value="es" data-icon="flag flag-es">Spanish</option>
44+
<option value="fr" data-icon="flag flag-fr">French</option>
45+
<option value="nl" data-icon="flag flag-nl">Dutch</option>
46+
<option value="pl" data-icon="flag flag-pl">Polish</option>
47+
<option value="pt-BR" data-icon="flag flag-pt">Portuguese</option>
48+
<option value="ru" data-icon="flag flag-ru">Russian</option>
49+
</optgroup>
50+
<optgroup label="Partial">
51+
<option value="da" data-icon="flag flag-dk">Danish</option>
52+
<option value="it" data-icon="flag flag-it">Italian</option>
53+
<option value="no" data-icon="flag flag-no">Norwegian</option>
54+
<option value="ro" data-icon="flag flag-ro">Romanian</option>
55+
</optgroup>
56+
</select>
3357
</div>
3458

3559
<div id="builder"></div>
@@ -67,8 +91,10 @@ <h3>Output</h3>
6791
<script src="../dist/js/query-builder.js"></script>
6892

6993
<script>
94+
var $b = $('#builder');
95+
7096
// define filters
71-
$('#builder').queryBuilder({
97+
var options = {
7298
allow_empty: true,
7399

74100
plugins: {
@@ -292,7 +318,10 @@ <h3>Output</h3>
292318
}
293319
}
294320
}]
295-
});
321+
};
322+
323+
// init
324+
$('#builder').queryBuilder(options);
296325

297326
$('#builder').on('afterCreateRuleInput.queryBuilder', function(e, rule) {
298327
if (rule.filter.plugin == 'selectize') {
@@ -301,6 +330,28 @@ <h3>Output</h3>
301330
}
302331
});
303332

333+
// change language
334+
$('[name=language]').selectpicker().on('change', function() {
335+
var lang = $(this).val();
336+
337+
var done = function() {
338+
var rules = $b.queryBuilder('getRules');
339+
if (!$.isEmptyObject(rules)) {
340+
options.rules = rules;
341+
}
342+
options.lang_code = lang;
343+
$b.queryBuilder('destroy');
344+
$('#builder').queryBuilder(options);
345+
};
346+
347+
if ($.fn.queryBuilder.regional[lang] === undefined) {
348+
$.getScript('../dist/i18n/' + lang + '.js', done);
349+
}
350+
else {
351+
done();
352+
}
353+
});
354+
304355
// set rules
305356
$('.set').on('click', function() {
306357
$('#builder').queryBuilder('setRules', {

src/core.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,16 @@ QueryBuilder.prototype.init = function($el, options) {
2727

2828
// SETTINGS SHORTCUTS
2929
this.filters = this.settings.filters;
30-
this.lang = this.settings.lang;
3130
this.icons = this.settings.icons;
3231
this.operators = this.settings.operators;
3332
this.template = this.settings.template;
3433
this.plugins = this.settings.plugins;
34+
35+
// translations : english << 'lang_code' << custom
36+
if (QueryBuilder.regional['en'] === undefined) {
37+
error('"i18n/en.js" not loaded.');
38+
}
39+
this.lang = $.extendext(true, 'replace', {}, QueryBuilder.regional['en'], QueryBuilder.regional[this.settings.lang_code], this.settings.lang);
3540

3641
if (this.template.group === null) {
3742
this.template.group = this.getGroupTemplate;

src/defaults.js

Lines changed: 19 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/**
2+
* Allowed types and their internal representation
3+
*/
14
QueryBuilder.types = {
25
'string': 'string',
36
'integer': 'number',
@@ -8,6 +11,9 @@ QueryBuilder.types = {
811
'boolean': 'boolean'
912
};
1013

14+
/**
15+
* Allowed inputs
16+
*/
1117
QueryBuilder.inputs = [
1218
'text',
1319
'textarea',
@@ -16,12 +22,23 @@ QueryBuilder.inputs = [
1622
'select'
1723
];
1824

25+
/**
26+
* Runtime modifiable options with `setOptions` method
27+
*/
1928
QueryBuilder.modifiable_options = [
2029
'display_errors',
2130
'allow_groups',
2231
'allow_empty'
2332
];
2433

34+
/**
35+
* Localized strings (populated by `i18n` files)
36+
*/
37+
QueryBuilder.regional = {};
38+
39+
/**
40+
* Default configuration
41+
*/
2542
QueryBuilder.DEFAULTS = {
2643
filters: [],
2744
plugins: [],
@@ -46,63 +63,8 @@ QueryBuilder.DEFAULTS = {
4663
rule: null
4764
},
4865

49-
lang: {
50-
"add_rule": 'Add rule',
51-
"add_group": 'Add group',
52-
"delete_rule": 'Delete',
53-
"delete_group": 'Delete',
54-
55-
"conditions": {
56-
"AND": "AND",
57-
"OR": "OR"
58-
},
59-
60-
"operators": {
61-
"equal": "equal",
62-
"not_equal": "not equal",
63-
"in": "in",
64-
"not_in": "not in",
65-
"less": "less",
66-
"less_or_equal": "less or equal",
67-
"greater": "greater",
68-
"greater_or_equal": "greater or equal",
69-
"between": "between",
70-
"begins_with": "begins with",
71-
"not_begins_with": "doesn't begin with",
72-
"contains": "contains",
73-
"not_contains": "doesn't contain",
74-
"ends_with": "ends with",
75-
"not_ends_with": "doesn't end with",
76-
"is_empty": "is empty",
77-
"is_not_empty": "is not empty",
78-
"is_null": "is null",
79-
"is_not_null": "is not null"
80-
},
81-
82-
"errors": {
83-
"no_filter": "No filter selected",
84-
"empty_group": "The group is empty",
85-
"radio_empty": "No value selected",
86-
"checkbox_empty": "No value selected",
87-
"select_empty": "No value selected",
88-
"string_empty": "Empty value",
89-
"string_exceed_min_length": "Must contain at least {0} characters",
90-
"string_exceed_max_length": "Must not contain more than {0} characters",
91-
"string_invalid_format": "Invalid format ({0})",
92-
"number_nan": "Not a number",
93-
"number_not_integer": "Not an integer",
94-
"number_not_double": "Not a real number",
95-
"number_exceed_min": "Must be greater than {0}",
96-
"number_exceed_max": "Must be lower than {0}",
97-
"number_wrong_step": "Must be a multiple of {0}",
98-
"datetime_empty": "Empty value",
99-
"datetime_invalid": "Invalid date format ({0})",
100-
"datetime_exceed_min": "Must be after {0}",
101-
"datetime_exceed_max": "Must be before {0}",
102-
"boolean_not_valid": "Not a boolean",
103-
"operator_not_multiple": "Operator {0} cannot accept multiple values"
104-
}
105-
},
66+
lang_code: 'en',
67+
lang: {},
10668

10769
operators: [
10870
{type: 'equal', nb_inputs: 1, multiple: false, apply_to: ['string', 'number', 'datetime', 'boolean']},

src/jquery.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ $.fn.queryBuilder = function(option) {
2222
$.fn.queryBuilder.constructor = QueryBuilder;
2323
$.fn.queryBuilder.defaults = QueryBuilder.defaults;
2424
$.fn.queryBuilder.extend = QueryBuilder.extend;
25-
$.fn.queryBuilder.define = QueryBuilder.define;
25+
$.fn.queryBuilder.define = QueryBuilder.define;
26+
$.fn.queryBuilder.regional = QueryBuilder.regional;

tests/common.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
/**
2+
* Sync load of language file once QUnit and Blanket are ready
3+
* Otherwise the language file is loaded before instrumented files
4+
*/
5+
QUnit.begin(function() {
6+
$.ajax({
7+
async: false,
8+
url: '../dist/i18n/en.js',
9+
dataType: 'script'
10+
});
11+
});
12+
113
/**
214
* Add GitHub link in header
315
*/
@@ -23,6 +35,7 @@ QUnit.done(function(){
2335
});
2436
});
2537

38+
2639
/**
2740
* Custom assert to compare rules objects
2841
*/
@@ -130,6 +143,7 @@ QUnit.assert.match = function(actual, regex, message) {
130143
this.push(regex.test(actual), actual, regex, message);
131144
};
132145

146+
133147
/**
134148
* Drag & Drop simulation
135149
* https://gist.github.com/mistic100/37c95fab77b5626c5623

0 commit comments

Comments
 (0)