Merge pull request #519 from datopian/DanePubliczneGovPl-feature-459-i18n
Fixes #459 by introducing i18n (internationalization) support into master.
This commit is contained in:
@@ -3,3 +3,4 @@ markdown: kramdown
|
|||||||
|
|
||||||
title: Recline Data Explorer and Library
|
title: Recline Data Explorer and Library
|
||||||
|
|
||||||
|
exclude: []
|
||||||
|
|||||||
@@ -46,6 +46,10 @@
|
|||||||
|
|
||||||
<script type="text/javascript" src="{{page.root}}vendor/moment/2.0.0/moment.js"></script>
|
<script type="text/javascript" src="{{page.root}}vendor/moment/2.0.0/moment.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}vendor/timeline/js/timeline.js"></script>
|
<script type="text/javascript" src="{{page.root}}vendor/timeline/js/timeline.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="{{page.root}}node_modules/intl-messageformat/dist/intl-messageformat-with-locales.min.js"></script>
|
||||||
|
<script type="text/javascript" src="{{page.root}}vendor/common-intl-wmustache.js"></script>
|
||||||
|
|
||||||
<!--[if lte IE 7]>
|
<!--[if lte IE 7]>
|
||||||
<script language="javascript" type="text/javascript" src="{{page.root}}vendor/json/json2.js"></script>
|
<script language="javascript" type="text/javascript" src="{{page.root}}vendor/json/json2.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
@@ -66,6 +70,8 @@
|
|||||||
<script type="text/javascript" src="http://okfnlabs.org/csv.js/csv.js"></script>
|
<script type="text/javascript" src="http://okfnlabs.org/csv.js/csv.js"></script>
|
||||||
|
|
||||||
<!-- views -->
|
<!-- views -->
|
||||||
|
<script type="text/javascript" src="{{page.root}}src/i18n/en.js"></script>
|
||||||
|
<script type="text/javascript" src="{{page.root}}src/i18n/pl.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}src/view.grid.js"></script>
|
<script type="text/javascript" src="{{page.root}}src/view.grid.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}src/view.slickgrid.js"></script>
|
<script type="text/javascript" src="{{page.root}}src/view.slickgrid.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}src/view.flot.js"></script>
|
<script type="text/javascript" src="{{page.root}}src/view.flot.js"></script>
|
||||||
|
|||||||
26
css/grid.css
26
css/grid.css
@@ -193,3 +193,29 @@ div.data-table-cell-content-numeric > a.data-table-cell-edit {
|
|||||||
.recline-read-only a.row-header-menu {
|
.recline-read-only a.row-header-menu {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*************************
|
||||||
|
* WCAG 2.0
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
.wcag_hide {
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
left:-10000px;
|
||||||
|
width:1px;
|
||||||
|
height:1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wcag_hide2 {
|
||||||
|
clip: rect(1px, 1px, 1px, 1px);
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wcag_show_on_focus {
|
||||||
|
font-size: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wcag_show_on_focus:focus {
|
||||||
|
font-size: 1em !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -64,10 +64,11 @@ var createMultiView = function(dataset, state) {
|
|||||||
$el.appendTo(window.explorerDiv);
|
$el.appendTo(window.explorerDiv);
|
||||||
|
|
||||||
// customize the subviews for the MultiView
|
// customize the subviews for the MultiView
|
||||||
|
var fmt = I18nMessages('recline', recline.View.translations);
|
||||||
var views = [
|
var views = [
|
||||||
{
|
{
|
||||||
id: 'grid',
|
id: 'grid',
|
||||||
label: 'Grid',
|
label: fmt.t('Grid'),
|
||||||
view: new recline.View.SlickGrid({
|
view: new recline.View.SlickGrid({
|
||||||
model: dataset,
|
model: dataset,
|
||||||
state: {
|
state: {
|
||||||
@@ -91,15 +92,14 @@ var createMultiView = function(dataset, state) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'graph',
|
id: 'graph',
|
||||||
label: 'Graph',
|
label: fmt.t('Graph'),
|
||||||
view: new recline.View.Graph({
|
view: new recline.View.Graph({
|
||||||
model: dataset
|
model: dataset
|
||||||
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'map',
|
id: 'map',
|
||||||
label: 'Map',
|
label: fmt.t('Map'),
|
||||||
view: new recline.View.Map({
|
view: new recline.View.Map({
|
||||||
model: dataset
|
model: dataset
|
||||||
})
|
})
|
||||||
|
|||||||
26
dist/recline.css
vendored
26
dist/recline.css
vendored
@@ -219,6 +219,32 @@ div.data-table-cell-content-numeric > a.data-table-cell-edit {
|
|||||||
.recline-read-only a.row-header-menu {
|
.recline-read-only a.row-header-menu {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*************************
|
||||||
|
* WCAG 2.0
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
.wcag_hide {
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
left:-10000px;
|
||||||
|
width:1px;
|
||||||
|
height:1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wcag_hide2 {
|
||||||
|
clip: rect(1px, 1px, 1px, 1px);
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wcag_show_on_focus {
|
||||||
|
font-size: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wcag_show_on_focus:focus {
|
||||||
|
font-size: 1em !important;
|
||||||
|
}
|
||||||
.recline-map .map {
|
.recline-map .map {
|
||||||
height: 500px;
|
height: 500px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,3 +105,94 @@ of such behaviour see the DataExplorer view.
|
|||||||
|
|
||||||
See the existing Views.
|
See the existing Views.
|
||||||
|
|
||||||
|
## Internationalization
|
||||||
|
|
||||||
|
### Adding translations to templates
|
||||||
|
|
||||||
|
Internationalization is implemented with help of [intl-messageformat](https://www.npmjs.com/package/intl-messageformat) library and supports [ICU Message syntax](http://userguide.icu-project.org/formatparse/messages).
|
||||||
|
|
||||||
|
Translation keys are using Mustache tags prefixed by `t.`. You can specify translated strings in two ways:
|
||||||
|
|
||||||
|
1. Simple text with no variables is rendered using Mustache variable-tag
|
||||||
|
```javascript
|
||||||
|
$template = '{{ "{{t.Add_row"}}}}'; // will show "Add row" in defaultLocale (English)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Text with variables or special characters is rendered using Mustache section-tag
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$template = '{{ "{{#t.desc"}}}}Add first_row field{{ "{{/t.desc"}}}}';
|
||||||
|
// using special chars; will show "Add first_row field" in defaultLocale
|
||||||
|
|
||||||
|
$template = '{{ "{{#t.num_records"}}}}{recordCount, plural, =0 {no records} =1{# record} other {# records}}{{ "{{/t.num_records"}}}}';
|
||||||
|
// will show "no records", "1 record" or "x records" in defaultLocale (English)
|
||||||
|
```
|
||||||
|
|
||||||
|
When using section-tags in existing templates be sure to remove a bracket from variables inside sections:
|
||||||
|
```javascript
|
||||||
|
#template___notranslation = '{{ "{{recordCount"}}}} records';
|
||||||
|
#template_withtranslation = '{{ "{{#t.num_records"}}}} {recordCount} records {{ "{{/t.num_records"}}}}';
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Then setup Mustache to use translation by injecting tranlation tags in `render` function:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ============== BEFORE ===================
|
||||||
|
|
||||||
|
my.MultiView = Backbone.View.extend({
|
||||||
|
render: function() {
|
||||||
|
var tmplData = this.model.toTemplateJSON();
|
||||||
|
var output = Mustache.render(this.template, tmplData);
|
||||||
|
...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ============== AFTER ====================
|
||||||
|
|
||||||
|
my.MultiView = Backbone.View.extend({
|
||||||
|
render: function() {
|
||||||
|
var tmplData = this.model.toTemplateJSON();
|
||||||
|
tmplData = I18nMessages('recline', recline.View.translations).injectMustache(tmplData); // inject Moustache formatter
|
||||||
|
var output = Mustache.render(this.template, tmplData);
|
||||||
|
...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Language resolution
|
||||||
|
|
||||||
|
By default the language is detected from the root `lang` attributes - <html lang="xx">` and `<html xml:lang="xx">`.
|
||||||
|
|
||||||
|
If you want to override this functionality then override `I18nMessages.languageResolver` with your implementation.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="text/javascript" src="common-intl-wmustache.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
I18nMessages.languageResolver = function() {
|
||||||
|
// implement here your language resolution
|
||||||
|
return 'fr';
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
Libraries can also ask for specific language: `I18nMessages('recline', recline.View.translations, 'pl')`. Language resolver is not used in that case.
|
||||||
|
|
||||||
|
If you're creating templates using default language other than English (why?) set appropriately appHardcodedLocale: `I18nMessages('recline', recline.View.translations, undefined, 'pl')`. Then missing strings won't be reported in console and underscores in simple translations will be converted to spaces.
|
||||||
|
|
||||||
|
### Adding new language
|
||||||
|
|
||||||
|
Create a copy of `src/i18n/pl.js` and translate all the keys. Long English messages can be found in `en.js`.
|
||||||
|
|
||||||
|
### Overriding defined messages
|
||||||
|
|
||||||
|
If you want to override translations from provided locale do it in a script tag after including recline:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="text/javascript" src="recline.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
this.recline.View.translations['en'] = {
|
||||||
|
Add_row: 'Add new row'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
2
make
2
make
@@ -7,6 +7,8 @@ def cat():
|
|||||||
print("** Combining js files")
|
print("** Combining js files")
|
||||||
cmd = 'ls src/*.js | grep -v couchdb | xargs cat > dist/recline.js'
|
cmd = 'ls src/*.js | grep -v couchdb | xargs cat > dist/recline.js'
|
||||||
os.system(cmd)
|
os.system(cmd)
|
||||||
|
cmd = 'ls src/**/*.js | grep -v couchdb | xargs cat >> dist/recline.js'
|
||||||
|
os.system(cmd)
|
||||||
|
|
||||||
cmd = 'cat src/model.js src/backend.memory.js > dist/recline.dataset.js'
|
cmd = 'cat src/model.js src/backend.memory.js > dist/recline.dataset.js'
|
||||||
os.system(cmd)
|
os.system(cmd)
|
||||||
|
|||||||
@@ -14,13 +14,18 @@
|
|||||||
{
|
{
|
||||||
"name": "Max Ogden",
|
"name": "Max Ogden",
|
||||||
"email": "max@maxogden.com"
|
"email": "max@maxogden.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Krzysztof Madejski",
|
||||||
|
"email": "krzysztof.madejski@epf.org.pl"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"backbone": ">=0.5",
|
"backbone": ">=0.5",
|
||||||
"jquery": ">=1.6",
|
"jquery": ">=1.6",
|
||||||
"mustache": ">=0.5.2",
|
"mustache": ">=0.5.2",
|
||||||
"underscore": ">=1.0"
|
"underscore": ">=1.0",
|
||||||
|
"intl-messageformat": "1.3.x"
|
||||||
},
|
},
|
||||||
"homepage": "http://reclinejs.com/",
|
"homepage": "http://reclinejs.com/",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
14
src/i18n/en.js
Normal file
14
src/i18n/en.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
this.recline = this.recline || {};
|
||||||
|
this.recline.View = this.recline.View || {};
|
||||||
|
this.recline.View.translations = this.recline.View.translations || {};
|
||||||
|
|
||||||
|
this.recline.View.translations['en'] = {
|
||||||
|
'date_required': "A date is required, check field field-date-format",
|
||||||
|
'backend_error': 'There was an error querying the backend',
|
||||||
|
'Distance_km': 'Distance (km)',
|
||||||
|
flot_Group_Column: 'Group Column (Axis 1)',
|
||||||
|
|
||||||
|
map_mapping: 'Coordinates source',
|
||||||
|
map_mapping_lat_lon: 'Latitude / Longitude fields',
|
||||||
|
map_mapping_geojson: 'GeoJSON field'
|
||||||
|
};
|
||||||
69
src/i18n/pl.js
Normal file
69
src/i18n/pl.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
this.recline = this.recline || {};
|
||||||
|
this.recline.View = this.recline.View || {};
|
||||||
|
this.recline.View.translations = this.recline.View.translations || {};
|
||||||
|
|
||||||
|
this.recline.View.translations['pl'] = {
|
||||||
|
Grid: 'Tabela',
|
||||||
|
Graph: 'Wykres',
|
||||||
|
Map: 'Mapa',
|
||||||
|
Timeline: 'Oś czasu',
|
||||||
|
Search_data: 'Wyszukaj w danych',
|
||||||
|
Search: 'Szukaj',
|
||||||
|
Add: 'Dodaj',
|
||||||
|
Add_row: 'Dodaj wiersz',
|
||||||
|
Delete_row: 'Usuń wiersz',
|
||||||
|
Reorder_row: 'Przesuń wiersz',
|
||||||
|
Update: 'Zaktualizuj',
|
||||||
|
Cancel: 'Anuluj',
|
||||||
|
Updating_row: 'Aktualizuję wiersz',
|
||||||
|
Row_updated_successfully: 'Wiersz został zaktualizowany',
|
||||||
|
Error_saving_row: 'Wystąpił błąd aktualizacji wiersza',
|
||||||
|
Filters: 'Filtry',
|
||||||
|
Add_filter: 'Dodaj filtr',
|
||||||
|
Remove_this_filter: 'Usuń filtr',
|
||||||
|
Fields: 'Kolumny',
|
||||||
|
Field: 'Kolumna',
|
||||||
|
Filter_type: 'Typ filtra',
|
||||||
|
Value: 'Wartość',
|
||||||
|
Range: 'Zakres',
|
||||||
|
Geo_distance: 'Odległość',
|
||||||
|
From: 'Od',
|
||||||
|
To: 'Do',
|
||||||
|
Longitude: 'Długość geograficzna',
|
||||||
|
Latitude: 'Szerokość geograficzna',
|
||||||
|
Distance_km: 'Odległość (km)',
|
||||||
|
backend_error: 'Wystąpił błąd połączenia z serwerem',
|
||||||
|
Unknown: '???',
|
||||||
|
Edit_this_cell: 'Edytuj komórkę',
|
||||||
|
date_required: "Data jest wymagana: sprawdź kolumnę field-date-format",
|
||||||
|
Show_field: 'Pokaż kolumnę',
|
||||||
|
Force_fit_columns: 'Dopasuj kolumny do zawartości',
|
||||||
|
Expand_and_collapse: 'Rozwiń i zwiń',
|
||||||
|
|
||||||
|
flot_info: '<h3 class="alert-heading">Witamy!</h3> \
|
||||||
|
<p>Jakie kolumny powinny zostać narysowane na wykresie?</p> \
|
||||||
|
<p>Wybierz je <strong>używając menu po prawej</strong>, a wykres pojawi się automatycznie.</p>',
|
||||||
|
Graph_Type: 'Typ wykresu',
|
||||||
|
Lines_and_Points: 'Linie z punktami',
|
||||||
|
Lines: 'Linie',
|
||||||
|
Points: 'Punkty',
|
||||||
|
Bars: 'Słupki poziome',
|
||||||
|
Columns: 'Słupki',
|
||||||
|
flot_Group_Column: 'Kolumna (Oś X)',
|
||||||
|
Please_choose: 'Proszę wybrać',
|
||||||
|
Remove: 'Usuń',
|
||||||
|
Series: 'Seria',
|
||||||
|
Axis_2: 'Oś Y',
|
||||||
|
Add_Series: 'Dodaj serię danych',
|
||||||
|
Save: 'Zapisz',
|
||||||
|
|
||||||
|
map_mapping: 'Źródło koordynatów',
|
||||||
|
map_mapping_lat_lon: 'Szerokość i długość geo.',
|
||||||
|
map_mapping_geojson: 'Jedna kolumna typu GeoJSON',
|
||||||
|
Latitude_field: 'Kolumna szerokości geo. (WGS84)',
|
||||||
|
Longitude_field: 'Kolumna długości geo. (WGS84)',
|
||||||
|
Auto_zoom_to_features: 'Kadruj, aby pokazać wszytkie punkty',
|
||||||
|
Cluster_markers: 'Łącz pobliskie punkty w grupy',
|
||||||
|
|
||||||
|
num_records: '<span class="doc-count">{recordCount}</span> rekordów'
|
||||||
|
};
|
||||||
@@ -27,9 +27,9 @@ my.Flot = Backbone.View.extend({
|
|||||||
<div class="recline-flot"> \
|
<div class="recline-flot"> \
|
||||||
<div class="panel graph" style="display: block;"> \
|
<div class="panel graph" style="display: block;"> \
|
||||||
<div class="js-temp-notice alert alert-warning alert-block"> \
|
<div class="js-temp-notice alert alert-warning alert-block"> \
|
||||||
<h3 class="alert-heading">Hey there!</h3> \
|
{{#t.flot_info}}<h3 class="alert-heading">Hey there!</h3> \
|
||||||
<p>There\'s no graph here yet because we don\'t know what fields you\'d like to see plotted.</p> \
|
<p>There\'s no graph here yet because we don\'t know what fields you\'d like to see plotted.</p> \
|
||||||
<p>Please tell us by <strong>using the menu on the right</strong> and a graph will automatically appear.</p> \
|
<p>Please tell us by <strong>using the menu on the right</strong> and a graph will automatically appear.</p>{{/t.flot_info}} \
|
||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
@@ -67,7 +67,7 @@ my.Flot = Backbone.View.extend({
|
|||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var tmplData = this.model.toTemplateJSON();
|
var tmplData = I18nMessages('recline', recline.View.translations).injectMustache(this.model.toTemplateJSON());
|
||||||
var htmls = Mustache.render(this.template, tmplData);
|
var htmls = Mustache.render(this.template, tmplData);
|
||||||
this.$el.html(htmls);
|
this.$el.html(htmls);
|
||||||
this.$graph = this.$el.find('.panel.graph');
|
this.$graph = this.$el.find('.panel.graph');
|
||||||
@@ -369,22 +369,22 @@ my.FlotControls = Backbone.View.extend({
|
|||||||
<form class="form-stacked"> \
|
<form class="form-stacked"> \
|
||||||
<div class="clearfix"> \
|
<div class="clearfix"> \
|
||||||
<div class="form-group"> \
|
<div class="form-group"> \
|
||||||
<label for ="form-field-type">Graph Type</label> \
|
<label>{{t.Graph_Type}}</label> \
|
||||||
<div class="input editor-type"> \
|
<div class="input editor-type"> \
|
||||||
<select id="form-field-type" class="form-control"> \
|
<select class="form-control"> \
|
||||||
<option value="lines-and-points">Lines and Points</option> \
|
<option value="lines-and-points">{{t.Lines_and_Points}}</option> \
|
||||||
<option value="lines">Lines</option> \
|
<option value="lines">{{t.Lines}}</option> \
|
||||||
<option value="points">Points</option> \
|
<option value="points">{{t.Points}}</option> \
|
||||||
<option value="bars">Bars</option> \
|
<option value="bars">{{t.Bars}}</option> \
|
||||||
<option value="columns">Columns</option> \
|
<option value="columns">{{t.Columns}}</option> \
|
||||||
</select> \
|
</select> \
|
||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="form-group"> \
|
<div class="form-group"> \
|
||||||
<label for="field-form-group">Group Column (Axis 1)</label> \
|
<label>{{t.flot_Group_Column}}</label> \
|
||||||
<div class="input editor-group"> \
|
<div class="input editor-group"> \
|
||||||
<select id="field-form-group" class="form-control"> \
|
<select class="form-control"> \
|
||||||
<option value="">Please choose ...</option> \
|
<option value="">{{t.Please_choose}} ...</option> \
|
||||||
{{#fields}} \
|
{{#fields}} \
|
||||||
<option value="{{id}}">{{label}}</option> \
|
<option value="{{id}}">{{label}}</option> \
|
||||||
{{/fields}} \
|
{{/fields}} \
|
||||||
@@ -395,10 +395,10 @@ my.FlotControls = Backbone.View.extend({
|
|||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="editor-buttons"> \
|
<div class="editor-buttons"> \
|
||||||
<button class="btn btn-default editor-add">Add Series</button> \
|
<button class="btn btn-default editor-add">{{t.Add_Series}}</button> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="editor-buttons editor-submit" comment="hidden temporarily" style="display: none;"> \
|
<div class="editor-buttons editor-submit" comment="hidden temporarily" style="display: none;"> \
|
||||||
<button class="editor-save">Save</button> \
|
<button class="editor-save">{{t.Save}}</button> \
|
||||||
<input type="hidden" class="editor-id" value="chart-1" /> \
|
<input type="hidden" class="editor-id" value="chart-1" /> \
|
||||||
</div> \
|
</div> \
|
||||||
</form> \
|
</form> \
|
||||||
@@ -407,15 +407,10 @@ my.FlotControls = Backbone.View.extend({
|
|||||||
templateSeriesEditor: ' \
|
templateSeriesEditor: ' \
|
||||||
<div class="editor-series js-series-{{seriesIndex}}"> \
|
<div class="editor-series js-series-{{seriesIndex}}"> \
|
||||||
<div class="form-group"> \
|
<div class="form-group"> \
|
||||||
<label for="form-field-{{seriesName}}">Series <span>{{seriesName}} (Axis 2)</span> \
|
<label>{{t.Series}} <span>{{seriesName}} ({{t.Axis_2}})</span> \
|
||||||
[<a href="#remove" class="action-remove-series">Remove</a>] \
|
[<a href="#remove" class="action-remove-series">{{t.Remove}}</a>] \
|
||||||
</label> \
|
|
||||||
<div class="input"> \
|
|
||||||
<select id="form-field-{{seriesName}}" class="form-control"> \
|
|
||||||
{{#fields}} \
|
|
||||||
<option value="{{id}}">{{label}}</option> \
|
<option value="{{id}}">{{label}}</option> \
|
||||||
{{/fields}} \
|
{{/fields}} \
|
||||||
</select> \
|
|
||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
@@ -437,6 +432,7 @@ my.FlotControls = Backbone.View.extend({
|
|||||||
render: function() {
|
render: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var tmplData = this.model.toTemplateJSON();
|
var tmplData = this.model.toTemplateJSON();
|
||||||
|
tmplData = I18nMessages('recline', recline.View.translations).injectMustache(tmplData);
|
||||||
var htmls = Mustache.render(this.template, tmplData);
|
var htmls = Mustache.render(this.template, tmplData);
|
||||||
this.$el.html(htmls);
|
this.$el.html(htmls);
|
||||||
|
|
||||||
@@ -499,6 +495,7 @@ my.FlotControls = Backbone.View.extend({
|
|||||||
seriesName: String.fromCharCode(idx + 64 + 1)
|
seriesName: String.fromCharCode(idx + 64 + 1)
|
||||||
}, this.model.toTemplateJSON());
|
}, this.model.toTemplateJSON());
|
||||||
|
|
||||||
|
data = I18nMessages('recline', recline.View.translations).injectMustache(data);
|
||||||
var htmls = Mustache.render(this.templateSeriesEditor, data);
|
var htmls = Mustache.render(this.templateSeriesEditor, data);
|
||||||
this.$el.find('.editor-series-group').append(htmls);
|
this.$el.find('.editor-series-group').append(htmls);
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ my.Grid = Backbone.View.extend({
|
|||||||
var state = _.extend({
|
var state = _.extend({
|
||||||
hiddenFields: []
|
hiddenFields: []
|
||||||
}, modelEtc.state
|
}, modelEtc.state
|
||||||
);
|
);
|
||||||
this.state = new recline.Model.ObjectState(state);
|
this.state = new recline.Model.ObjectState(state);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -113,7 +113,10 @@ my.Grid = Backbone.View.extend({
|
|||||||
field.set({width: width});
|
field.set({width: width});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var htmls = Mustache.render(this.template, this.toTemplateJSON());
|
var tmplData = this.toTemplateJSON();
|
||||||
|
tmplData = I18nMessages('recline', recline.View.translations).injectMustache(tmplData);
|
||||||
|
|
||||||
|
var htmls = Mustache.render(this.template, tmplData);
|
||||||
this.$el.html(htmls);
|
this.$el.html(htmls);
|
||||||
this.model.records.forEach(function(doc) {
|
this.model.records.forEach(function(doc) {
|
||||||
var tr = $('<tr />');
|
var tr = $('<tr />');
|
||||||
@@ -174,7 +177,7 @@ my.GridRow = Backbone.View.extend({
|
|||||||
{{#cells}} \
|
{{#cells}} \
|
||||||
<td data-field="{{field}}" style="width: {{width}}px; max-width: {{width}}px; min-width: {{width}}px;"> \
|
<td data-field="{{field}}" style="width: {{width}}px; max-width: {{width}}px; min-width: {{width}}px;"> \
|
||||||
<div class="data-table-cell-content"> \
|
<div class="data-table-cell-content"> \
|
||||||
<a href="javascript:{}" class="data-table-cell-edit" title="Edit this cell"> </a> \
|
<a href="javascript:{}" class="data-table-cell-edit" title="{{t.Edit_this_cell}}"> </a> \
|
||||||
<div class="data-table-cell-value">{{{value}}}</div> \
|
<div class="data-table-cell-value">{{{value}}}</div> \
|
||||||
</div> \
|
</div> \
|
||||||
</td> \
|
</td> \
|
||||||
@@ -201,7 +204,9 @@ my.GridRow = Backbone.View.extend({
|
|||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
this.$el.attr('data-id', this.model.id);
|
this.$el.attr('data-id', this.model.id);
|
||||||
var html = Mustache.render(this.template, this.toTemplateJSON());
|
var tmplData = this.toTemplateJSON();
|
||||||
|
tmplData = I18nMessages('recline', recline.View.translations).injectMustache(tmplData);
|
||||||
|
var html = Mustache.render(this.template, tmplData);
|
||||||
this.$el.html(html);
|
this.$el.html(html);
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
@@ -214,8 +219,8 @@ my.GridRow = Backbone.View.extend({
|
|||||||
<textarea class="data-table-cell-editor-editor" bind="textarea">{{value}}</textarea> \
|
<textarea class="data-table-cell-editor-editor" bind="textarea">{{value}}</textarea> \
|
||||||
<div id="data-table-cell-editor-actions"> \
|
<div id="data-table-cell-editor-actions"> \
|
||||||
<div class="data-table-cell-editor-action"> \
|
<div class="data-table-cell-editor-action"> \
|
||||||
<button class="okButton btn primary">Update</button> \
|
<button class="okButton btn primary">{{t.Update}}</button> \
|
||||||
<button class="cancelButton btn danger">Cancel</button> \
|
<button class="cancelButton btn danger">{{t.Cancel}}</button> \
|
||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
@@ -229,8 +234,10 @@ my.GridRow = Backbone.View.extend({
|
|||||||
$(e.target).addClass("hidden");
|
$(e.target).addClass("hidden");
|
||||||
var cell = $(e.target).siblings('.data-table-cell-value');
|
var cell = $(e.target).siblings('.data-table-cell-value');
|
||||||
cell.data("previousContents", cell.text());
|
cell.data("previousContents", cell.text());
|
||||||
var templated = Mustache.render(this.cellEditorTemplate, {value: cell.text()});
|
|
||||||
cell.html(templated);
|
var tmplData = I18nMessages('recline', recline.View.translations).injectMustache({value: cell.text()});
|
||||||
|
var output = Mustache.render(this.cellEditorTemplate, tmplData);
|
||||||
|
cell.html(output);
|
||||||
},
|
},
|
||||||
|
|
||||||
onEditorOK: function(e) {
|
onEditorOK: function(e) {
|
||||||
@@ -242,13 +249,15 @@ my.GridRow = Backbone.View.extend({
|
|||||||
var newData = {};
|
var newData = {};
|
||||||
newData[field] = newValue;
|
newData[field] = newValue;
|
||||||
this.model.set(newData);
|
this.model.set(newData);
|
||||||
this.trigger('recline:flash', {message: "Updating row...", loader: true});
|
|
||||||
|
var fmt = I18nMessages('recline', recline.View.translations);
|
||||||
|
this.trigger('recline:flash', {message: fmt.t("Updating_row") + "...", loader: true});
|
||||||
this.model.save().then(function(response) {
|
this.model.save().then(function(response) {
|
||||||
this.trigger('recline:flash', {message: "Row updated successfully", category: 'success'});
|
this.trigger('recline:flash', {message: fmt.t("Row_updated_successfully"), category: 'success'});
|
||||||
})
|
})
|
||||||
.fail(function() {
|
.fail(function() {
|
||||||
this.trigger('recline:flash', {
|
this.trigger('recline:flash', {
|
||||||
message: 'Error saving row',
|
message: fmt.t('Error_saving_row'),
|
||||||
category: 'error',
|
category: 'error',
|
||||||
persist: true
|
persist: true
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -174,7 +174,8 @@ my.Map = Backbone.View.extend({
|
|||||||
// Also sets up the editor fields and the map if necessary.
|
// Also sets up the editor fields and the map if necessary.
|
||||||
render: function() {
|
render: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var htmls = Mustache.render(this.template, this.model.toTemplateJSON());
|
var tmplData = I18nMessages('recline', recline.View.translations).injectMustache(this.model.toTemplateJSON());
|
||||||
|
var htmls = Mustache.render(this.template, tmplData);
|
||||||
this.$el.html(htmls);
|
this.$el.html(htmls);
|
||||||
this.$map = this.$el.find('.panel.map');
|
this.$map = this.$el.find('.panel.map');
|
||||||
this.redraw();
|
this.redraw();
|
||||||
@@ -513,15 +514,16 @@ my.MapMenu = Backbone.View.extend({
|
|||||||
<form class="form-stacked"> \
|
<form class="form-stacked"> \
|
||||||
<div class="clearfix"> \
|
<div class="clearfix"> \
|
||||||
<div class="editor-field-type"> \
|
<div class="editor-field-type"> \
|
||||||
|
<span>{{t.map_mapping}}:</span> \
|
||||||
<label class="radio"> \
|
<label class="radio"> \
|
||||||
<input type="radio" id="editor-field-type-latlon" name="editor-field-type" value="latlon" checked="checked"/> \
|
<input type="radio" id="editor-field-type-latlon" name="editor-field-type" value="latlon" checked="checked"/> \
|
||||||
Latitude / Longitude fields</label> \
|
{{t.map_mapping_lat_lon}}</label> \
|
||||||
<label class="radio"> \
|
<label class="radio"> \
|
||||||
<input type="radio" id="editor-field-type-geom" name="editor-field-type" value="geom" /> \
|
<input type="radio" id="editor-field-type-geom" name="editor-field-type" value="geom" /> \
|
||||||
GeoJSON field</label> \
|
{{t.map_mapping_geojson}}</label> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="editor-field-type-latlon"> \
|
<div class="editor-field-type-latlon"> \
|
||||||
<label for="form-field-lat-field">Latitude field</label> \
|
<label>{{t.Latitude_field}}</label> \
|
||||||
<div class="input editor-lat-field"> \
|
<div class="input editor-lat-field"> \
|
||||||
<select id="form-field-lat-field" class="form-control"> \
|
<select id="form-field-lat-field" class="form-control"> \
|
||||||
<option value=""></option> \
|
<option value=""></option> \
|
||||||
@@ -530,7 +532,7 @@ my.MapMenu = Backbone.View.extend({
|
|||||||
{{/fields}} \
|
{{/fields}} \
|
||||||
</select> \
|
</select> \
|
||||||
</div> \
|
</div> \
|
||||||
<label for="form-field-lon-field">Longitude field</label> \
|
<label>{{t.Longitude_field}}</label> \
|
||||||
<div class="input editor-lon-field"> \
|
<div class="input editor-lon-field"> \
|
||||||
<select id="form-field-lon-field" class="form-control"> \
|
<select id="form-field-lon-field" class="form-control"> \
|
||||||
<option value=""></option> \
|
<option value=""></option> \
|
||||||
@@ -541,7 +543,7 @@ my.MapMenu = Backbone.View.extend({
|
|||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="editor-field-type-geom" style="display:none"> \
|
<div class="editor-field-type-geom" style="display:none"> \
|
||||||
<label for="form-field-type-geom">Geometry field (GeoJSON)</label> \
|
<label>{{t.map_mapping_geojson}}</label> \
|
||||||
<div class="input editor-geom-field"> \
|
<div class="input editor-geom-field"> \
|
||||||
<select id="form-field-type-geom" class="form-control"> \
|
<select id="form-field-type-geom" class="form-control"> \
|
||||||
<option value=""></option> \
|
<option value=""></option> \
|
||||||
@@ -553,15 +555,15 @@ my.MapMenu = Backbone.View.extend({
|
|||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="editor-buttons"> \
|
<div class="editor-buttons"> \
|
||||||
<button class="btn btn-default editor-update-map">Update</button> \
|
<button class="btn btn-default editor-update-map">{{t.Update}}</button> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="editor-options" > \
|
<div class="editor-options" > \
|
||||||
<label class="checkbox"> \
|
<label class="checkbox"> \
|
||||||
<input type="checkbox" id="editor-auto-zoom" value="autozoom" checked="checked" /> \
|
<input type="checkbox" id="editor-auto-zoom" value="autozoom" checked="checked" /> \
|
||||||
Auto zoom to features</label> \
|
{{t.Auto_zoom_to_features}}</label> \
|
||||||
<label class="checkbox"> \
|
<label class="checkbox"> \
|
||||||
<input type="checkbox" id="editor-cluster" value="cluster"/> \
|
<input type="checkbox" id="editor-cluster" value="cluster"/> \
|
||||||
Cluster markers</label> \
|
{{t.Cluster_markers}}</label> \
|
||||||
</div> \
|
</div> \
|
||||||
<input type="hidden" class="editor-id" value="map-1" /> \
|
<input type="hidden" class="editor-id" value="map-1" /> \
|
||||||
</form> \
|
</form> \
|
||||||
@@ -589,7 +591,8 @@ my.MapMenu = Backbone.View.extend({
|
|||||||
// Also sets up the editor fields and the map if necessary.
|
// Also sets up the editor fields and the map if necessary.
|
||||||
render: function() {
|
render: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var htmls = Mustache.render(this.template, this.model.toTemplateJSON());
|
var tmplData = I18nMessages('recline', recline.View.translations).injectMustache(this.model.toTemplateJSON());
|
||||||
|
var htmls = Mustache.render(this.template, tmplData);
|
||||||
this.$el.html(htmls);
|
this.$el.html(htmls);
|
||||||
|
|
||||||
if (this._geomReady() && this.model.fields.length){
|
if (this._geomReady() && this.model.fields.length){
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ my.MultiView = Backbone.View.extend({
|
|||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="recline-results-info"> \
|
<div class="recline-results-info"> \
|
||||||
<span class="doc-count">{{recordCount}}</span> records\
|
{{#t.num_records}}<span class="doc-count">{recordCount}</span> {recordCount, plural, =1{record} other{records}}{{/t.num_records}}\
|
||||||
</div> \
|
</div> \
|
||||||
<div class="menu-right"> \
|
<div class="menu-right"> \
|
||||||
<div class="btn-group" data-toggle="buttons-checkbox"> \
|
<div class="btn-group" data-toggle="buttons-checkbox"> \
|
||||||
@@ -131,6 +131,7 @@ my.MultiView = Backbone.View.extend({
|
|||||||
initialize: function(options) {
|
initialize: function(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this._setupState(options.state);
|
this._setupState(options.state);
|
||||||
|
var fmt = I18nMessages('recline', recline.View.translations);
|
||||||
|
|
||||||
// Hash of 'page' views (i.e. those for whole page) keyed by page name
|
// Hash of 'page' views (i.e. those for whole page) keyed by page name
|
||||||
if (options.views) {
|
if (options.views) {
|
||||||
@@ -138,28 +139,28 @@ my.MultiView = Backbone.View.extend({
|
|||||||
} else {
|
} else {
|
||||||
this.pageViews = [{
|
this.pageViews = [{
|
||||||
id: 'grid',
|
id: 'grid',
|
||||||
label: 'Grid',
|
label: fmt.t('Grid'),
|
||||||
view: new my.SlickGrid({
|
view: new my.SlickGrid({
|
||||||
model: this.model,
|
model: this.model,
|
||||||
state: this.state.get('view-grid')
|
state: this.state.get('view-grid')
|
||||||
})
|
})
|
||||||
}, {
|
}, {
|
||||||
id: 'graph',
|
id: 'graph',
|
||||||
label: 'Graph',
|
label: fmt.t('Graph'),
|
||||||
view: new my.Graph({
|
view: new my.Graph({
|
||||||
model: this.model,
|
model: this.model,
|
||||||
state: this.state.get('view-graph')
|
state: this.state.get('view-graph')
|
||||||
})
|
})
|
||||||
}, {
|
}, {
|
||||||
id: 'map',
|
id: 'map',
|
||||||
label: 'Map',
|
label: fmt.t('Map'),
|
||||||
view: new my.Map({
|
view: new my.Map({
|
||||||
model: this.model,
|
model: this.model,
|
||||||
state: this.state.get('view-map')
|
state: this.state.get('view-map')
|
||||||
})
|
})
|
||||||
}, {
|
}, {
|
||||||
id: 'timeline',
|
id: 'timeline',
|
||||||
label: 'Timeline',
|
label: fmt.t('Timeline'),
|
||||||
view: new my.Timeline({
|
view: new my.Timeline({
|
||||||
model: this.model,
|
model: this.model,
|
||||||
state: this.state.get('view-timeline')
|
state: this.state.get('view-timeline')
|
||||||
@@ -172,13 +173,13 @@ my.MultiView = Backbone.View.extend({
|
|||||||
} else {
|
} else {
|
||||||
this.sidebarViews = [{
|
this.sidebarViews = [{
|
||||||
id: 'filterEditor',
|
id: 'filterEditor',
|
||||||
label: 'Filters',
|
label: fmt.t('Filters'),
|
||||||
view: new my.FilterEditor({
|
view: new my.FilterEditor({
|
||||||
model: this.model
|
model: this.model
|
||||||
})
|
})
|
||||||
}, {
|
}, {
|
||||||
id: 'fieldsView',
|
id: 'fieldsView',
|
||||||
label: 'Fields',
|
label: fmt.t('Fields'),
|
||||||
view: new my.Fields({
|
view: new my.Fields({
|
||||||
model: this.model
|
model: this.model
|
||||||
})
|
})
|
||||||
@@ -204,7 +205,7 @@ my.MultiView = Backbone.View.extend({
|
|||||||
});
|
});
|
||||||
this.listenTo(this.model, 'query:done', function() {
|
this.listenTo(this.model, 'query:done', function() {
|
||||||
self.clearNotifications();
|
self.clearNotifications();
|
||||||
self.$el.find('.doc-count').text(self.model.recordCount || 'Unknown');
|
self.$el.find('.doc-count').text(self.model.recordCount || fmt.t('Unknown'));
|
||||||
});
|
});
|
||||||
this.listenTo(this.model, 'query:fail', function(error) {
|
this.listenTo(this.model, 'query:fail', function(error) {
|
||||||
self.clearNotifications();
|
self.clearNotifications();
|
||||||
@@ -219,7 +220,7 @@ my.MultiView = Backbone.View.extend({
|
|||||||
msg += error.message;
|
msg += error.message;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
msg = 'There was an error querying the backend';
|
msg = fmt.t('backend_error', {}, 'There was an error querying the backend');
|
||||||
}
|
}
|
||||||
self.notify({message: msg, category: 'error', persist: true});
|
self.notify({message: msg, category: 'error', persist: true});
|
||||||
});
|
});
|
||||||
@@ -238,6 +239,7 @@ my.MultiView = Backbone.View.extend({
|
|||||||
var tmplData = this.model.toTemplateJSON();
|
var tmplData = this.model.toTemplateJSON();
|
||||||
tmplData.views = this.pageViews;
|
tmplData.views = this.pageViews;
|
||||||
tmplData.sidebarViews = this.sidebarViews;
|
tmplData.sidebarViews = this.sidebarViews;
|
||||||
|
tmplData = I18nMessages('recline', recline.View.translations).injectMustache(tmplData);
|
||||||
var template = Mustache.render(this.template, tmplData);
|
var template = Mustache.render(this.template, tmplData);
|
||||||
this.$el.html(template);
|
this.$el.html(template);
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ my.SlickGrid = Backbone.View.extend({
|
|||||||
|
|
||||||
// Template for row delete menu , change it if you don't love
|
// Template for row delete menu , change it if you don't love
|
||||||
this.templates = {
|
this.templates = {
|
||||||
"deleterow" : '<button href="#" class="recline-row-delete btn btn-default" title="Delete row">X</button>'
|
deleterow : '<button href="#" class="recline-row-delete btn btn-default" title="{{t.Delete_row}}"><span class="wcag_hide">{{t.Delete_row}}</span><span aria-hidden="true">X</span></button>',
|
||||||
|
reorderrows: '<div title="{{t.Reorder_row}}" style="height: 100%;"><span class="wcag_hide">{{t.Reorder_row}}</span></div>'
|
||||||
};
|
};
|
||||||
|
|
||||||
_.bindAll(this, 'render', 'onRecordChanged');
|
_.bindAll(this, 'render', 'onRecordChanged');
|
||||||
@@ -102,15 +103,22 @@ my.SlickGrid = Backbone.View.extend({
|
|||||||
}, self.state.get('gridOptions'));
|
}, self.state.get('gridOptions'));
|
||||||
|
|
||||||
// We need all columns, even the hidden ones, to show on the column picker
|
// We need all columns, even the hidden ones, to show on the column picker
|
||||||
var columns = [];
|
var columns = [];
|
||||||
|
var fmt = I18nMessages('recline', recline.View.translations);
|
||||||
|
|
||||||
// custom formatter as default one escapes html
|
// custom formatter as default one escapes html
|
||||||
// plus this way we distinguish between rendering/formatting and computed value (so e.g. sort still works ...)
|
// plus this way we distinguish between rendering/formatting and computed value (so e.g. sort still works ...)
|
||||||
// row = row index, cell = cell index, value = value, columnDef = column definition, dataContext = full row values
|
// row = row index, cell = cell index, value = value, columnDef = column definition, dataContext = full row values
|
||||||
var formatter = function(row, cell, value, columnDef, dataContext) {
|
var formatter = function(row, cell, value, columnDef, dataContext) {
|
||||||
if(columnDef.id == "del"){
|
if(columnDef.id == "del"){
|
||||||
return self.templates.deleterow
|
var formatted = Mustache.render(self.templates.deleterow, fmt.injectMustache({}));
|
||||||
|
return formatted;
|
||||||
}
|
}
|
||||||
|
if(columnDef.id == "#"){
|
||||||
|
var formatted = Mustache.render(self.templates.reorderrows, fmt.injectMustache({}));
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
|
||||||
var field = self.model.fields.get(columnDef.id);
|
var field = self.model.fields.get(columnDef.id);
|
||||||
if (field.renderer) {
|
if (field.renderer) {
|
||||||
return field.renderer(value, field, dataContext);
|
return field.renderer(value, field, dataContext);
|
||||||
@@ -127,7 +135,7 @@ my.SlickGrid = Backbone.View.extend({
|
|||||||
if (field.type == "date" && isNaN(Date.parse(value))){
|
if (field.type == "date" && isNaN(Date.parse(value))){
|
||||||
return {
|
return {
|
||||||
valid: false,
|
valid: false,
|
||||||
msg: "A date is required, check field field-date-format"
|
msg: fmt.t('date_required', {}, "A date is required, check field field-date-format")
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {valid: true, msg :null }
|
return {valid: true, msg :null }
|
||||||
@@ -144,7 +152,8 @@ my.SlickGrid = Backbone.View.extend({
|
|||||||
behavior: "selectAndMove",
|
behavior: "selectAndMove",
|
||||||
selectable: false,
|
selectable: false,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
cssClass: "recline-cell-reorder"
|
cssClass: "recline-cell-reorder",
|
||||||
|
formatter: formatter
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Add column for row delete support
|
// Add column for row delete support
|
||||||
@@ -436,7 +445,7 @@ my.SlickGrid = Backbone.View.extend({
|
|||||||
my.GridControl= Backbone.View.extend({
|
my.GridControl= Backbone.View.extend({
|
||||||
className: "recline-row-add",
|
className: "recline-row-add",
|
||||||
// Template for row edit menu , change it if you don't love
|
// Template for row edit menu , change it if you don't love
|
||||||
template: '<h1><button href="#" class="recline-row-add btn btn-default">Add row</button></h1>',
|
template: '<h1><button href="#" class="recline-row-add btn btn-default">{{t.Add_row}}</button></h1>',
|
||||||
|
|
||||||
initialize: function(options){
|
initialize: function(options){
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -447,7 +456,10 @@ my.GridControl= Backbone.View.extend({
|
|||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.$el.html(this.template)
|
|
||||||
|
var tmplData = I18nMessages('recline', recline.View.translations).injectMustache({});
|
||||||
|
var formatted = Mustache.render(this.template, tmplData);
|
||||||
|
this.$el.html(formatted);
|
||||||
},
|
},
|
||||||
|
|
||||||
events : {
|
events : {
|
||||||
@@ -514,7 +526,7 @@ my.GridControl= Backbone.View.extend({
|
|||||||
$input = $('<input type="checkbox" />').data('option', 'autoresize').attr('id','slick-option-autoresize');
|
$input = $('<input type="checkbox" />').data('option', 'autoresize').attr('id','slick-option-autoresize');
|
||||||
$input.appendTo($li);
|
$input.appendTo($li);
|
||||||
$('<label />')
|
$('<label />')
|
||||||
.text('Force fit columns')
|
.text(this.t('Force_fit_columns'))
|
||||||
.attr('for','slick-option-autoresize')
|
.attr('for','slick-option-autoresize')
|
||||||
.appendTo($li);
|
.appendTo($li);
|
||||||
if (grid.getOptions().forceFitColumns) {
|
if (grid.getOptions().forceFitColumns) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ my.Fields = Backbone.View.extend({
|
|||||||
className: 'recline-fields-view',
|
className: 'recline-fields-view',
|
||||||
template: ' \
|
template: ' \
|
||||||
<div class="panel-group fields-list well"> \
|
<div class="panel-group fields-list well"> \
|
||||||
<h3>Fields <a href="#" class="js-show-hide">+</a></h3> \
|
<h3>{{t.Fields}} <a href="#" class="js-show-hide" title="{{t.Show_field}}"><span class="wcag_hide">{{t.Show_field}}</span><span aria-hidden="true">+</span></a></h3> \
|
||||||
{{#fields}} \
|
{{#fields}} \
|
||||||
<div class="panel panel-default field"> \
|
<div class="panel panel-default field"> \
|
||||||
<div class="panel-heading"> \
|
<div class="panel-heading"> \
|
||||||
@@ -35,7 +35,7 @@ my.Fields = Backbone.View.extend({
|
|||||||
{{label}} \
|
{{label}} \
|
||||||
<small> \
|
<small> \
|
||||||
{{type}} \
|
{{type}} \
|
||||||
<a class="accordion-toggle" data-toggle="collapse" href="#collapse{{id}}"> » </a> \
|
<a class="accordion-toggle" data-toggle="collapse" href="#collapse{{id}}" title="{{t.Expand_and_collapse}}"> <span class="wcag_hide">{{t.Expand_and_collapse}}</span><span aria-hidden="true">»</span> </a> \
|
||||||
</small> \
|
</small> \
|
||||||
</h4> \
|
</h4> \
|
||||||
</div> \
|
</div> \
|
||||||
@@ -87,6 +87,7 @@ my.Fields = Backbone.View.extend({
|
|||||||
out.facets = field.facets.toJSON();
|
out.facets = field.facets.toJSON();
|
||||||
tmplData.fields.push(out);
|
tmplData.fields.push(out);
|
||||||
});
|
});
|
||||||
|
var tmplData = I18nMessages('recline', recline.View.translations).injectMustache(tmplData);
|
||||||
var templated = Mustache.render(this.template, tmplData);
|
var templated = Mustache.render(this.template, tmplData);
|
||||||
this.$el.html(templated);
|
this.$el.html(templated);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,33 +10,33 @@ my.FilterEditor = Backbone.View.extend({
|
|||||||
className: 'recline-filter-editor well',
|
className: 'recline-filter-editor well',
|
||||||
template: ' \
|
template: ' \
|
||||||
<div class="filters"> \
|
<div class="filters"> \
|
||||||
<h3>Filters</h3> \
|
<h3>{{t.Filters}}</h3> \
|
||||||
<a href="#" class="js-add-filter">Add filter</a> \
|
<a href="#" class="js-add-filter">{{t.Add_filter}}</a> \
|
||||||
<form class="form-stacked js-add" style="display: none;"> \
|
<form class="form-stacked js-add" style="display: none;"> \
|
||||||
<div class="form-group"> \
|
<div class="form-group"> \
|
||||||
<label for="form-field-add-field">Field</label> \
|
<label>{{t.Field}}</label> \
|
||||||
<select id="form-field-add-field" class="fields form-control"> \
|
<select class="fields form-control"> \
|
||||||
{{#fields}} \
|
{{#fields}} \
|
||||||
<option value="{{id}}">{{label}}</option> \
|
<option value="{{id}}">{{label}}</option> \
|
||||||
{{/fields}} \
|
{{/fields}} \
|
||||||
</select> \
|
</select> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="form-group"> \
|
<div class="form-group"> \
|
||||||
<label for="form-field-filter-type">Filter type</label> \
|
<label>{{t.Filter_type}}</label> \
|
||||||
<select id="form-field-filter-type" class="filterType form-control"> \
|
<select class="filterType form-control"> \
|
||||||
<option value="term">Value</option> \
|
<option value="term">{{t.Value}}</option> \
|
||||||
<option value="range">Range</option> \
|
<option value="range">{{t.Range}}</option> \
|
||||||
<option value="geo_distance">Geo distance</option> \
|
<option value="geo_distance">{{t.Geo_distance}}</option> \
|
||||||
</select> \
|
</select> \
|
||||||
</div> \
|
</div> \
|
||||||
<button type="submit" class="btn btn-default">Add</button> \
|
<button type="submit" class="btn btn-default">{{t.Add}}</button> \
|
||||||
</form> \
|
</form> \
|
||||||
<form class="form-stacked js-edit"> \
|
<form class="form-stacked js-edit"> \
|
||||||
{{#filters}} \
|
{{#filters}} \
|
||||||
{{{filterRender}}} \
|
{{{filterRender}}} \
|
||||||
{{/filters}} \
|
{{/filters}} \
|
||||||
{{#filters.length}} \
|
{{#filters.length}} \
|
||||||
<button type="submit" class="btn btn-default">Update</button> \
|
<button type="submit" class="btn btn-default">{{t.Update}}</button> \
|
||||||
{{/filters.length}} \
|
{{/filters.length}} \
|
||||||
</form> \
|
</form> \
|
||||||
</div> \
|
</div> \
|
||||||
@@ -47,7 +47,7 @@ my.FilterEditor = Backbone.View.extend({
|
|||||||
<fieldset> \
|
<fieldset> \
|
||||||
<legend> \
|
<legend> \
|
||||||
{{field}} <small>{{type}}</small> \
|
{{field}} <small>{{type}}</small> \
|
||||||
<a class="js-remove-filter" href="#" title="Remove this filter" data-filter-id="{{id}}">×</a> \
|
<a class="js-remove-filter" href="#" title="{{t.Remove_this_filter}}" data-filter-id="{{id}}"><span class="wcag_hide">{{t.Remove_this_filter}}</span><span aria-hidden="true">×</span></a> \
|
||||||
</legend> \
|
</legend> \
|
||||||
<input class="input-sm" type="text" value="{{term}}" name="term" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
<input class="input-sm" type="text" value="{{term}}" name="term" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
</fieldset> \
|
</fieldset> \
|
||||||
@@ -58,14 +58,14 @@ my.FilterEditor = Backbone.View.extend({
|
|||||||
<fieldset> \
|
<fieldset> \
|
||||||
<legend> \
|
<legend> \
|
||||||
{{field}} <small>{{type}}</small> \
|
{{field}} <small>{{type}}</small> \
|
||||||
<a class="js-remove-filter" href="#" title="Remove this filter" data-filter-id="{{id}}">×</a> \
|
<a class="js-remove-filter" href="#" title="{{t.Remove_this_filter}}" data-filter-id="{{id}}"><span class="wcag_hide">{{t.Remove_this_filter}}</span><span aria-hidden="true">×</span></a> \
|
||||||
</legend> \
|
</legend> \
|
||||||
<div class="form-group"> \
|
<div class="form-group"> \
|
||||||
<label class="control-label" for="">From</label> \
|
<label class="control-label" for="">{{t.From}}</label> \
|
||||||
<input class="input-sm" type="text" value="{{from}}" name="from" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
<input class="input-sm" type="text" value="{{from}}" name="from" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="form-group"> \
|
<div class="form-group"> \
|
||||||
<label class="control-label" for="">To</label> \
|
<label class="control-label" for="">{{t.To}}</label> \
|
||||||
<input class="input-sm" type="text" value="{{to}}" name="to" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
<input class="input-sm" type="text" value="{{to}}" name="to" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
</div> \
|
</div> \
|
||||||
</fieldset> \
|
</fieldset> \
|
||||||
@@ -76,18 +76,18 @@ my.FilterEditor = Backbone.View.extend({
|
|||||||
<fieldset> \
|
<fieldset> \
|
||||||
<legend> \
|
<legend> \
|
||||||
{{field}} <small>{{type}}</small> \
|
{{field}} <small>{{type}}</small> \
|
||||||
<a class="js-remove-filter" href="#" title="Remove this filter" data-filter-id="{{id}}">×</a> \
|
<a class="js-remove-filter" href="#" title="{{t.Remove_this_filter}}" data-filter-id="{{id}}"><span class="wcag_hide">{{t.Remove_this_filter}}</span><span aria-hidden="true">×</span></a> \
|
||||||
</legend> \
|
</legend> \
|
||||||
<div class="form-group"> \
|
<div class="form-group"> \
|
||||||
<label class="control-label" for="">Longitude</label> \
|
<label class="control-label" for="">{{t.Longitude}}</label> \
|
||||||
<input class="input-sm" type="text" value="{{point.lon}}" name="lon" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
<input class="input-sm" type="text" value="{{point.lon}}" name="lon" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="form-group"> \
|
<div class="form-group"> \
|
||||||
<label class="control-label" for="">Latitude</label> \
|
<label class="control-label" for="">{{t.Latitude}}</label> \
|
||||||
<input class="input-sm" type="text" value="{{point.lat}}" name="lat" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
<input class="input-sm" type="text" value="{{point.lat}}" name="lat" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="form-group"> \
|
<div class="form-group"> \
|
||||||
<label class="control-label" for="">Distance (km)</label> \
|
<label class="control-label" for="">{{t.Distance_km}}</label> \
|
||||||
<input class="input-sm" type="text" value="{{distance}}" name="distance" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
<input class="input-sm" type="text" value="{{distance}}" name="distance" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
</div> \
|
</div> \
|
||||||
</fieldset> \
|
</fieldset> \
|
||||||
@@ -116,8 +116,10 @@ my.FilterEditor = Backbone.View.extend({
|
|||||||
});
|
});
|
||||||
tmplData.fields = this.model.fields.toJSON();
|
tmplData.fields = this.model.fields.toJSON();
|
||||||
tmplData.filterRender = function() {
|
tmplData.filterRender = function() {
|
||||||
return Mustache.render(self.filterTemplates[this.type], this);
|
var filterData = I18nMessages('recline', recline.View.translations).injectMustache(this);
|
||||||
|
return Mustache.render(self.filterTemplates[this.type], filterData);
|
||||||
};
|
};
|
||||||
|
tmplData = I18nMessages('recline', recline.View.translations).injectMustache(tmplData);
|
||||||
var out = Mustache.render(this.template, tmplData);
|
var out = Mustache.render(this.template, tmplData);
|
||||||
this.$el.html(out);
|
this.$el.html(out);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ my.QueryEditor = Backbone.View.extend({
|
|||||||
<div class="input-group-addon"> \
|
<div class="input-group-addon"> \
|
||||||
<i class="glyphicon glyphicon-search"></i> \
|
<i class="glyphicon glyphicon-search"></i> \
|
||||||
</div> \
|
</div> \
|
||||||
<label for="q">Search</label> \
|
<label for="q">{{t.Search}}</label> \
|
||||||
<input class="form-control search-query" type="text" id="q" name="q" value="{{q}}" placeholder="Search data ..."> \
|
<input class="form-control search-query" type="text" id="q" name="q" value="{{q}}" placeholder="{{t.Search_data}} ..."> \
|
||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
<button type="submit" class="btn btn-default">Go »</button> \
|
<button type="submit" class="btn btn-default">{{t.Search}} »</button> \
|
||||||
</form> \
|
</form> \
|
||||||
',
|
',
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ my.QueryEditor = Backbone.View.extend({
|
|||||||
this.model.set({q: query});
|
this.model.set({q: query});
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
var tmplData = this.model.toJSON();
|
var tmplData = I18nMessages('recline', recline.View.translations).injectMustache(this.model.toJSON());
|
||||||
var templated = Mustache.render(this.template, tmplData);
|
var templated = Mustache.render(this.template, tmplData);
|
||||||
this.$el.html(templated);
|
this.$el.html(templated);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,17 +10,17 @@ my.ValueFilter = Backbone.View.extend({
|
|||||||
className: 'recline-filter-editor well',
|
className: 'recline-filter-editor well',
|
||||||
template: ' \
|
template: ' \
|
||||||
<div class="filters"> \
|
<div class="filters"> \
|
||||||
<h3>Filters</h3> \
|
<h3>{{t.Filters}}</h3> \
|
||||||
<button class="btn js-add-filter add-filter">Add filter</button> \
|
<button class="btn js-add-filter add-filter">{{t.Add_filter}}</button> \
|
||||||
<form class="form-stacked js-add" style="display: none;"> \
|
<form class="form-stacked js-add" style="display: none;"> \
|
||||||
<fieldset> \
|
<fieldset> \
|
||||||
<label>Field</label> \
|
<label>{{t.Field}}</label> \
|
||||||
<select class="fields form-control"> \
|
<select class="fields form-control"> \
|
||||||
{{#fields}} \
|
{{#fields}} \
|
||||||
<option value="{{id}}">{{label}}</option> \
|
<option value="{{id}}">{{label}}</option> \
|
||||||
{{/fields}} \
|
{{/fields}} \
|
||||||
</select> \
|
</select> \
|
||||||
<button type="submit" class="btn">Add</button> \
|
<button type="submit" class="btn">{{t.Add}}</button> \
|
||||||
</fieldset> \
|
</fieldset> \
|
||||||
</form> \
|
</form> \
|
||||||
<form class="form-stacked js-edit"> \
|
<form class="form-stacked js-edit"> \
|
||||||
@@ -28,7 +28,7 @@ my.ValueFilter = Backbone.View.extend({
|
|||||||
{{{filterRender}}} \
|
{{{filterRender}}} \
|
||||||
{{/filters}} \
|
{{/filters}} \
|
||||||
{{#filters.length}} \
|
{{#filters.length}} \
|
||||||
<button type="submit" class="btn update-filter">Update</button> \
|
<button type="submit" class="btn update-filter">{{t.Update}}</button> \
|
||||||
{{/filters.length}} \
|
{{/filters.length}} \
|
||||||
</form> \
|
</form> \
|
||||||
</div> \
|
</div> \
|
||||||
@@ -38,7 +38,7 @@ my.ValueFilter = Backbone.View.extend({
|
|||||||
<div class="filter-{{type}} filter"> \
|
<div class="filter-{{type}} filter"> \
|
||||||
<fieldset> \
|
<fieldset> \
|
||||||
{{field}} \
|
{{field}} \
|
||||||
<a class="js-remove-filter" href="#" title="Remove this filter" data-filter-id="{{id}}">×</a> \
|
<a class="js-remove-filter" href="#" title="{{t.Remove_this_filter}}" data-filter-id="{{id}}"><span class="wcag_hide">{{t.Remove_this_filter}}</span><span aria-hidden="true">×</span></a> \
|
||||||
<input type="text" value="{{term}}" name="term" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
<input type="text" value="{{term}}" name="term" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
</fieldset> \
|
</fieldset> \
|
||||||
</div> \
|
</div> \
|
||||||
@@ -65,10 +65,11 @@ my.ValueFilter = Backbone.View.extend({
|
|||||||
return filter;
|
return filter;
|
||||||
});
|
});
|
||||||
tmplData.fields = this.model.fields.toJSON();
|
tmplData.fields = this.model.fields.toJSON();
|
||||||
|
var fmt = I18nMessages('recline', recline.View.translations);
|
||||||
tmplData.filterRender = function() {
|
tmplData.filterRender = function() {
|
||||||
return Mustache.render(self.filterTemplates.term, this);
|
return Mustache.render(self.filterTemplates.term, fmt.injectMustache(this));
|
||||||
};
|
};
|
||||||
var out = Mustache.render(this.template, tmplData);
|
var out = Mustache.render(this.template, fmt.injectMustache(tmplData));
|
||||||
this.$el.html(out);
|
this.$el.html(out);
|
||||||
},
|
},
|
||||||
updateFilter: function(input) {
|
updateFilter: function(input) {
|
||||||
|
|||||||
@@ -22,6 +22,14 @@ var Fixture = {
|
|||||||
];
|
];
|
||||||
var dataset = new recline.Model.Dataset({records: documents, fields: fields});
|
var dataset = new recline.Model.Dataset({records: documents, fields: fields});
|
||||||
return dataset;
|
return dataset;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTranslations: function() {
|
||||||
|
return {
|
||||||
|
pl: {
|
||||||
|
Grid: 'Tabela'
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,10 @@
|
|||||||
<script type="text/javascript" src="../vendor/slickgrid/2.2/plugins/slick.rowmovemanager.js"></script>
|
<script type="text/javascript" src="../vendor/slickgrid/2.2/plugins/slick.rowmovemanager.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript" src="../vendor/timeline/js/timeline.js"></script>
|
<script type="text/javascript" src="../vendor/timeline/js/timeline.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="../node_modules/intl-messageformat/dist/intl-messageformat-with-locales.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../vendor/common-intl-wmustache.js"></script>
|
||||||
|
|
||||||
<!--[if lte IE 7]>
|
<!--[if lte IE 7]>
|
||||||
<script language="javascript" type="text/javascript" src="../vendor/json/json2.js"></script>
|
<script language="javascript" type="text/javascript" src="../vendor/json/json2.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
@@ -55,6 +59,8 @@
|
|||||||
<script type="text/javascript" src="backend.dataproxy.test.js"></script>
|
<script type="text/javascript" src="backend.dataproxy.test.js"></script>
|
||||||
|
|
||||||
<!-- views and view tests -->
|
<!-- views and view tests -->
|
||||||
|
<script type="text/javascript" src="../src/i18n/en.js"></script>
|
||||||
|
<script type="text/javascript" src="../src/i18n/pl.js"></script>
|
||||||
<script type="text/javascript" src="../src/view.grid.js"></script>
|
<script type="text/javascript" src="../src/view.grid.js"></script>
|
||||||
<script type="text/javascript" src="../src/view.slickgrid.js"></script>
|
<script type="text/javascript" src="../src/view.slickgrid.js"></script>
|
||||||
<script type="text/javascript" src="../src/view.flot.js"></script>
|
<script type="text/javascript" src="../src/view.flot.js"></script>
|
||||||
@@ -73,10 +79,12 @@
|
|||||||
<script type="text/javascript" src="view.map.test.js"></script>
|
<script type="text/javascript" src="view.map.test.js"></script>
|
||||||
<script type="text/javascript" src="view.timeline.test.js"></script>
|
<script type="text/javascript" src="view.timeline.test.js"></script>
|
||||||
<script type="text/javascript" src="view.multiview.test.js"></script>
|
<script type="text/javascript" src="view.multiview.test.js"></script>
|
||||||
|
<script type="text/javascript" src="view.i18n.test.js"></script>
|
||||||
<script type="text/javascript" src="util.test.js"></script>
|
<script type="text/javascript" src="util.test.js"></script>
|
||||||
<script type="text/javascript" src="widget.filtereditor.test.js"></script>
|
<script type="text/javascript" src="widget.filtereditor.test.js"></script>
|
||||||
<script type="text/javascript" src="widget.valuefilter.test.js"></script>
|
<script type="text/javascript" src="widget.valuefilter.test.js"></script>
|
||||||
<script type="text/javascript" src="widget.pager.test.js"></script>
|
<script type="text/javascript" src="widget.pager.test.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 id="qunit-header">Qunit Tests</h1>
|
<h1 id="qunit-header">Qunit Tests</h1>
|
||||||
|
|||||||
134
test/view.i18n.test.js
Normal file
134
test/view.i18n.test.js
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
(function ($) {
|
||||||
|
|
||||||
|
module("I18nMessages");
|
||||||
|
|
||||||
|
test('translate simple key custom locale', function () {
|
||||||
|
var fmt = I18nMessages('somelib', Fixture.getTranslations(), 'pl');
|
||||||
|
|
||||||
|
equal(fmt.t('Grid'), 'Tabela');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('translate simple key default locale', function () {
|
||||||
|
var fmt = I18nMessages('somelib', Fixture.getTranslations());
|
||||||
|
|
||||||
|
equal(fmt.t('Add_row'), 'Add row');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fallback to key if translation not present', function () {
|
||||||
|
var fmt = I18nMessages('somelib', Fixture.getTranslations());
|
||||||
|
|
||||||
|
equal(fmt.t('thiskeydoesnotexist'), 'thiskeydoesnotexist');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fallback to default message', function () {
|
||||||
|
var fmt = I18nMessages('somelib', Fixture.getTranslations());
|
||||||
|
|
||||||
|
equal(fmt.t('thiskeydoesnotexist', {}, 'Fallback to default message'), 'Fallback to default message');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('mustache formatter - simple key', function () {
|
||||||
|
var fmt = I18nMessages('somelib', Fixture.getTranslations(), 'pl');
|
||||||
|
|
||||||
|
var template = '{{t.Grid}}';
|
||||||
|
var tmplData = {};
|
||||||
|
|
||||||
|
tmplData = fmt.injectMustache(tmplData);
|
||||||
|
|
||||||
|
var out = Mustache.render(template, tmplData);
|
||||||
|
equal(out, 'Tabela');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('mustache formatter - complex key', function () {
|
||||||
|
var fmt = I18nMessages('somelib', Fixture.getTranslations(), 'pl');
|
||||||
|
|
||||||
|
var template = '{{#t.num_records}}{recordCount} records{{/t.num_records}}';
|
||||||
|
var tmplData = {recordCount: 5};
|
||||||
|
|
||||||
|
// injecting i18n support [do it in view before passing data to render functions]
|
||||||
|
tmplData = fmt.injectMustache(tmplData);
|
||||||
|
|
||||||
|
var out = Mustache.render(template, tmplData);
|
||||||
|
equal(out, '5 records');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('translate complex key default locale', function () {
|
||||||
|
var fmt = I18nMessages('somelib', Fixture.getTranslations(), 'pl');
|
||||||
|
|
||||||
|
equal(fmt.t('codeforall', {records: 3}, '<span>{records} records</span>'), '<span>3 records</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('mustache formatter - translate complex key custom locale', function () {
|
||||||
|
var translations = {
|
||||||
|
pl: {
|
||||||
|
codeforall: '<span>{records} rekordy</span>'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var fmt = I18nMessages('somelib2', translations, 'pl');
|
||||||
|
|
||||||
|
equal(fmt.t('codeforall', {records: 3}, '<span>{records} records</span>'), '<span>3 rekordy</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('mustache formatter - translate complex key custom locale custom count', function () {
|
||||||
|
var translations = {
|
||||||
|
pl: {
|
||||||
|
codeforall: '{records, plural, ' +
|
||||||
|
'=0 {brak zdjęć}' +
|
||||||
|
'=1 {{records} zdjęcie}' +
|
||||||
|
'few {{records} zdjęcia}' +
|
||||||
|
'other {{records} zdjęć}}'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var fmt = I18nMessages('somelib3', translations, 'pl');
|
||||||
|
|
||||||
|
equal(fmt.t('codeforall', {records: 0}), 'brak zdjęć');
|
||||||
|
equal(fmt.t('codeforall', {records: 1}), '1 zdjęcie');
|
||||||
|
equal(fmt.t('codeforall', {records: 3}), '3 zdjęcia');
|
||||||
|
equal(fmt.t('codeforall', {records: 5}), '5 zdjęć');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('I18nMessages specified locale', function () {
|
||||||
|
var fmt = I18nMessages('somelib', {}, 'pl');
|
||||||
|
|
||||||
|
equal(fmt.getLocale(), 'pl');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('I18nMessages default locale', function () {
|
||||||
|
var fmt = I18nMessages('somelib', {});
|
||||||
|
|
||||||
|
// no language set in HTML tag
|
||||||
|
equal($('html').attr('lang'), undefined);
|
||||||
|
|
||||||
|
equal(fmt.getLocale(), 'en');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('I18nMessages custom language resolver', function () {
|
||||||
|
var localeResolver = function() { return 'fr'; };
|
||||||
|
var fmt = I18nMessages('somelib', {}, localeResolver);
|
||||||
|
|
||||||
|
equal(fmt.getLocale(), 'fr');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('I18nMessages override language resolver', function () {
|
||||||
|
var oldResolver = I18nMessages.languageResolver;
|
||||||
|
I18nMessages.languageResolver = function() {
|
||||||
|
return 'fr';
|
||||||
|
};
|
||||||
|
var fmt = I18nMessages('somelib', {});
|
||||||
|
|
||||||
|
equal(fmt.getLocale(), 'fr');
|
||||||
|
I18nMessages.languageResolver = oldResolver;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('I18nMessages singletons', function () {
|
||||||
|
var lib1_pl = I18nMessages('lib1', {}, 'pl');
|
||||||
|
var lib2_pl = I18nMessages('lib2', {}, 'pl');
|
||||||
|
var lib1_en = I18nMessages('lib1', {}, 'en');
|
||||||
|
|
||||||
|
strictEqual(I18nMessages('lib1', {}, 'pl'), lib1_pl);
|
||||||
|
notStrictEqual(lib1_pl, lib1_en);
|
||||||
|
notStrictEqual(lib1_pl, lib2_pl);
|
||||||
|
});
|
||||||
|
|
||||||
|
})(this.jQuery);
|
||||||
|
|
||||||
126
vendor/common-intl-wmustache.js
vendored
Normal file
126
vendor/common-intl-wmustache.js
vendored
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2016 Krzysztof Madejski
|
||||||
|
*
|
||||||
|
* This code is licensed under GPL v3 http://choosealicense.com/licenses/gpl-3.0/
|
||||||
|
*/
|
||||||
|
(function (global) {
|
||||||
|
"use strict";
|
||||||
|
var I18nMessages = function(libraryID, translations, languageResolverOrLocale, appHardcodedLocale) {
|
||||||
|
if (typeof this === 'undefined') {
|
||||||
|
return new I18nMessages(libraryID, translations, languageResolverOrLocale, appHardcodedLocale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// which locale should we use?
|
||||||
|
languageResolverOrLocale = (typeof languageResolverOrLocale !== 'undefined') ? languageResolverOrLocale : I18nMessages.languageResolver;
|
||||||
|
appHardcodedLocale = appHardcodedLocale || 'en';
|
||||||
|
|
||||||
|
if (typeof(languageResolverOrLocale) === 'function') {
|
||||||
|
languageResolverOrLocale = languageResolverOrLocale();
|
||||||
|
}
|
||||||
|
if (languageResolverOrLocale == undefined) {
|
||||||
|
languageResolverOrLocale = appHardcodedLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((I18nMessages.prototype._formatters || {})[libraryID] || {})[languageResolverOrLocale]) {
|
||||||
|
return I18nMessages.prototype._formatters[libraryID][languageResolverOrLocale];
|
||||||
|
}
|
||||||
|
I18nMessages.prototype._formatters = I18nMessages.prototype._formatters || {};
|
||||||
|
I18nMessages.prototype._formatters[libraryID] = I18nMessages.prototype._formatters[libraryID] || {};
|
||||||
|
I18nMessages.prototype._formatters[libraryID][languageResolverOrLocale] = this;
|
||||||
|
|
||||||
|
// ========== VARIABLES & FUNCTIONS ==========
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.uniqueID = this.uniqueID;
|
||||||
|
this.locale = languageResolverOrLocale;
|
||||||
|
this.translations = translations;
|
||||||
|
this.appHardcodedLocale = appHardcodedLocale;
|
||||||
|
this.cache= {};
|
||||||
|
|
||||||
|
this.getLocale = function() {
|
||||||
|
return this.locale;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============= FormatJS.io backend =========
|
||||||
|
|
||||||
|
this.t = function(key, values, defaultMessage) {
|
||||||
|
values = (typeof values !== 'undefined') ? values : {};
|
||||||
|
|
||||||
|
// get the message from current locale
|
||||||
|
var msg = (this.translations[this.locale] || {})[key];
|
||||||
|
|
||||||
|
// fallback to key or default message if no translation is defined
|
||||||
|
if (msg == null) {
|
||||||
|
if (this.locale != this.appHardcodedLocale) {
|
||||||
|
console.warn("Missing locale for " + this.locale + "." + key);
|
||||||
|
}
|
||||||
|
msg = defaultMessage;
|
||||||
|
}
|
||||||
|
if (msg == null) {
|
||||||
|
msg = key;
|
||||||
|
if (this.locale === this.appHardcodedLocale) {
|
||||||
|
// no need to define lang entry for short sentences, just use underscores as spaces
|
||||||
|
msg = msg.replace(/_/g, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var formatter = this.cache[msg];
|
||||||
|
if (formatter === undefined) {
|
||||||
|
this.cache[msg] = formatter = new IntlMessageFormat(msg, this.locale);
|
||||||
|
}
|
||||||
|
var formatted = formatter.format(values);
|
||||||
|
|
||||||
|
return formatted;
|
||||||
|
} catch (e) {
|
||||||
|
var err = "Got error while formatting \"" + msg + "\": " + e.message;
|
||||||
|
if (e.name == 'SyntaxError' && e.found == '{') {
|
||||||
|
err += '. Probably you should change double brackets around variables (Mustache style) to single brackets (Intl style).';
|
||||||
|
}
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// ============ Mustache integration ========
|
||||||
|
|
||||||
|
this.mustacheI18Tags = function() {
|
||||||
|
var tagsProxy = new Proxy(this, {
|
||||||
|
get: function(messages, name) {
|
||||||
|
return function() {
|
||||||
|
var f = function (text, render) {
|
||||||
|
var trans = messages.t(name, this, text);
|
||||||
|
return render(trans);
|
||||||
|
}
|
||||||
|
f.toString = function() {
|
||||||
|
return messages.t(name);
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
has: function(target, prop) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
't': tagsProxy,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
this.injectMustache = function(tmplData) {
|
||||||
|
return _.extend(tmplData, self.mustacheI18Tags());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
I18nMessages.languageResolver = function() {
|
||||||
|
var h = $('html');
|
||||||
|
return h.attr('lang') || h.attr('xml:lang');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
global.I18nMessages = I18nMessages;
|
||||||
|
|
||||||
|
}(window));
|
||||||
Reference in New Issue
Block a user