This commit is contained in:
krzysztofmadejski 2016-11-06 17:13:12 +00:00
parent 5976edd377
commit 4da2430334
13 changed files with 168 additions and 90 deletions

View File

@ -5,7 +5,12 @@ 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)'
'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'
}
//

View File

@ -3,40 +3,66 @@ 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',
'Go': 'Szukaj',
'Add': 'Dodaj',
'Add_row': 'Dodaj wiersz',
'Delete_row': 'Usuń wiersz',
'Update': 'Zaktualizuj',
'Cancel': 'Anuluj',
'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ń',
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',
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ń',
'num_records': '<span class="doc-count">{recordCount}</span> rekordów'
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'
}
//

View File

@ -8,8 +8,10 @@
Backbone.I18nView = Backbone.View.extend({
defaultLocale: 'en',
locale: 'en',
initializeI18n: function(locale) {
this.locale = locale;
initializeI18n: function(locale, appHardcodedLocale) {
this.defaultLocale = appHardcodedLocale || 'en';
this.locale = locale || this.defaultLocale;
// TODO implement cache
//memoizeFormatConstructor(Intl.NumberFormat).getNumberFormat();
},
@ -30,7 +32,14 @@ Backbone.I18nView = Backbone.View.extend({
}
msg = defaultMessage;
}
if (msg == null) { msg = key; } // TODO w domyślnym locale automatycznie usuwaj podkreślenia
if (msg == null) {
msg = key;
if (this.locale === this.defaultLocale) {
// no need to define lang entry for short sentences, just use underscores as spaces
msg = msg.replace(/_/g, ' ');
}
}
// TODO i18n documentation
@ -73,4 +82,4 @@ Backbone.I18nView = Backbone.View.extend({
't': formatter,
};
},
});
});

View File

@ -22,14 +22,14 @@ this.recline.View = this.recline.View || {};
//
// NB: should *not* provide an el argument to the view but must let the view
// generate the element itself (you can then append view.el to the DOM.
my.Flot = Backbone.View.extend({
my.Flot = Backbone.I18nView.extend({
template: ' \
<div class="recline-flot"> \
<div class="panel graph" style="display: 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>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> \
@ -38,6 +38,7 @@ my.Flot = Backbone.View.extend({
initialize: function(options) {
var self = this;
this.graphColors = ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"];
this.initializeI18n(options.locale);
_.bindAll(this, 'render', 'redraw', '_toolTip', '_xaxisLabel');
this.needToRedraw = false;
@ -56,7 +57,8 @@ my.Flot = Backbone.View.extend({
this.previousTooltipPoint = {x: null, y: null};
this.editor = new my.FlotControls({
model: this.model,
state: this.state.toJSON()
state: this.state.toJSON(),
locale: this.locale
});
this.listenTo(this.editor.state, 'change', function() {
self.state.set(self.editor.state.toJSON());
@ -67,7 +69,7 @@ my.Flot = Backbone.View.extend({
render: function() {
var self = this;
var tmplData = this.model.toTemplateJSON();
var tmplData = _.extend(this.model.toTemplateJSON(), this.MustacheFormatter());
var htmls = Mustache.render(this.template, tmplData);
this.$el.html(htmls);
this.$graph = this.$el.find('.panel.graph');
@ -362,29 +364,29 @@ my.Flot = Backbone.View.extend({
}
});
my.FlotControls = Backbone.View.extend({
my.FlotControls = Backbone.I18nView.extend({
className: "editor",
template: ' \
<div class="editor"> \
<form class="form-stacked"> \
<div class="clearfix"> \
<div class="form-group"> \
<label>Graph Type</label> \
<label>{{t.Graph_Type}}</label> \
<div class="input editor-type"> \
<select class="form-control"> \
<option value="lines-and-points">Lines and Points</option> \
<option value="lines">Lines</option> \
<option value="points">Points</option> \
<option value="bars">Bars</option> \
<option value="columns">Columns</option> \
<option value="lines-and-points">{{t.Lines_and_Points}}</option> \
<option value="lines">{{t.Lines}}</option> \
<option value="points">{{t.Points}}</option> \
<option value="bars">{{t.Bars}}</option> \
<option value="columns">{{t.Columns}}</option> \
</select> \
</div> \
</div> \
<div class="form-group"> \
<label>Group Column (Axis 1)</label> \
<label>{{t.flot_Group_Column}}</label> \
<div class="input editor-group"> \
<select class="form-control"> \
<option value="">Please choose ...</option> \
<option value="">{{t.Please_choose}} ...</option> \
{{#fields}} \
<option value="{{id}}">{{label}}</option> \
{{/fields}} \
@ -395,10 +397,10 @@ my.FlotControls = Backbone.View.extend({
</div> \
</div> \
<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 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" /> \
</div> \
</form> \
@ -407,8 +409,8 @@ my.FlotControls = Backbone.View.extend({
templateSeriesEditor: ' \
<div class="editor-series js-series-{{seriesIndex}}"> \
<div class="form-group"> \
<label>Series <span>{{seriesName}} (Axis 2)</span> \
[<a href="#remove" class="action-remove-series">Remove</a>] \
<label>{{t.Series}} <span>{{seriesName}} ({{t.Axis_2}})</span> \
[<a href="#remove" class="action-remove-series">{{t.Remove}}</a>] \
</label> \
<div class="input"> \
<select class="form-control"> \
@ -431,12 +433,14 @@ my.FlotControls = Backbone.View.extend({
_.bindAll(this, 'render');
this.listenTo(this.model.fields, 'reset add', this.render);
this.state = new recline.Model.ObjectState(options.state);
this.initializeI18n(options.locale);
this.render();
},
render: function() {
var self = this;
var tmplData = this.model.toTemplateJSON();
tmplData = _.extend(tmplData, this.MustacheFormatter());
var htmls = Mustache.render(this.template, tmplData);
this.$el.html(htmls);
@ -499,6 +503,7 @@ my.FlotControls = Backbone.View.extend({
seriesName: String.fromCharCode(idx + 64 + 1)
}, this.model.toTemplateJSON());
data = _.extend(data, this.MustacheFormatter());
var htmls = Mustache.render(this.templateSeriesEditor, data);
this.$el.find('.editor-series-group').append(htmls);
return this;

View File

@ -22,7 +22,8 @@ my.Grid = Backbone.I18nView.extend({
var state = _.extend({
hiddenFields: []
}, modelEtc.state
);
);
this.initializeI18n(modelEtc.locale);
this.state = new recline.Model.ObjectState(state);
},

View File

@ -58,7 +58,7 @@ my.Map = Backbone.I18nView.extend({
// this will be the Leaflet L.Map object (setup below)
this.map = null;
this.initializeI18n(options.locale || 'en');
this.initializeI18n(options.locale);
var stateData = _.extend({
geomField: null,
@ -97,7 +97,8 @@ my.Map = Backbone.I18nView.extend({
this.menu = new my.MapMenu({
model: this.model,
state: this.state.toJSON()
state: this.state.toJSON(),
locale: this.locale
});
this.listenTo(this.menu.state, 'change', function() {
self.state.set(self.menu.state.toJSON());
@ -514,15 +515,16 @@ my.MapMenu = Backbone.I18nView.extend({
<form class="form-stacked"> \
<div class="clearfix"> \
<div class="editor-field-type"> \
<span>{{t.map_mapping}}:</span> \
<label class="radio"> \
<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"> \
<input type="radio" id="editor-field-type-geom" name="editor-field-type" value="geom" /> \
GeoJSON field</label> \
{{t.map_mapping_geojson}}</label> \
</div> \
<div class="editor-field-type-latlon"> \
<label>Latitude field</label> \
<label>{{t.Latitude_field}}</label> \
<div class="input editor-lat-field"> \
<select class="form-control"> \
<option value=""></option> \
@ -531,7 +533,7 @@ my.MapMenu = Backbone.I18nView.extend({
{{/fields}} \
</select> \
</div> \
<label>Longitude field</label> \
<label>{{t.Longitude_field}}</label> \
<div class="input editor-lon-field"> \
<select class="form-control"> \
<option value=""></option> \
@ -542,7 +544,7 @@ my.MapMenu = Backbone.I18nView.extend({
</div> \
</div> \
<div class="editor-field-type-geom" style="display:none"> \
<label>Geometry field (GeoJSON)</label> \
<label>{{t.map_mapping_geojson}}</label> \
<div class="input editor-geom-field"> \
<select class="form-control"> \
<option value=""></option> \
@ -554,15 +556,15 @@ my.MapMenu = Backbone.I18nView.extend({
</div> \
</div> \
<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 class="editor-options" > \
<label class="checkbox"> \
<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"> \
<input type="checkbox" id="editor-cluster" value="cluster"/> \
Cluster markers</label> \
{{t.Cluster_markers}}</label> \
</div> \
<input type="hidden" class="editor-id" value="map-1" /> \
</form> \
@ -582,6 +584,7 @@ my.MapMenu = Backbone.I18nView.extend({
this.listenTo(this.model.fields, 'change', this.render);
this.state = new recline.Model.ObjectState(options.state);
this.listenTo(this.state, 'change', this.render);
this.initializeI18n(options.locale);
this.render();
},

View File

@ -132,7 +132,7 @@ my.MultiView = Backbone.I18nView.extend({
var self = this;
this._setupState(options.state);
// todo any better idea to pass locale than by initialization? singleton? or does Intl API handle that?
this.initializeI18n(options.locale || 'en');
this.initializeI18n(options.locale);
// Hash of 'page' views (i.e. those for whole page) keyed by page name
if (options.views) {
@ -176,13 +176,15 @@ my.MultiView = Backbone.I18nView.extend({
id: 'filterEditor',
label: this.t('Filters'),
view: new my.FilterEditor({
model: this.model
model: this.model,
locale: this.locale
})
}, {
id: 'fieldsView',
label: this.t('Fields'),
view: new my.Fields({
model: this.model
model: this.model,
locale: this.locale
})
}];
}
@ -271,7 +273,8 @@ my.MultiView = Backbone.I18nView.extend({
this.$el.find('.recline-results-info').after(this.pager.el);
this.queryEditor = new recline.View.QueryEditor({
model: this.model.queryState
model: this.model.queryState,
locale: this.locale
});
this.$el.find('.query-editor-here').append(this.queryEditor.el);

View File

@ -44,10 +44,12 @@ my.SlickGrid = Backbone.I18nView.extend({
initialize: function(modelEtc) {
var self = this;
this.$el.addClass('recline-slickgrid');
this.initializeI18n(modelEtc.locale);
// Template for row delete menu , change it if you don't love
this.templates = {
"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>'
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');
@ -71,7 +73,7 @@ my.SlickGrid = Backbone.I18nView.extend({
if(this.state.get("gridOptions")
&& this.state.get("gridOptions").enabledAddRow != undefined
&& this.state.get("gridOptions").enabledAddRow == true ){
this.editor = new my.GridControl()
this.editor = new my.GridControl({locale: this.locale})
this.elSidebar = this.editor.$el
this.listenTo(this.editor.state, 'change', function(){
this.model.records.add(new recline.Model.Record())
@ -109,8 +111,14 @@ my.SlickGrid = Backbone.I18nView.extend({
// row = row index, cell = cell index, value = value, columnDef = column definition, dataContext = full row values
var formatter = function(row, cell, value, columnDef, dataContext) {
if(columnDef.id == "del"){
return self.templates.deleterow
var formatted = Mustache.render(self.templates.deleterow, _.extend({}, self.MustacheFormatter()));
return formatted;
}
if(columnDef.id == "#"){
var formatted = Mustache.render(self.templates.reorderrows, _.extend({}, self.MustacheFormatter()));
return formatted;
}
var field = self.model.fields.get(columnDef.id);
if (field.renderer) {
return field.renderer(value, field, dataContext);
@ -145,7 +153,8 @@ my.SlickGrid = Backbone.I18nView.extend({
behavior: "selectAndMove",
selectable: false,
resizable: false,
cssClass: "recline-cell-reorder"
cssClass: "recline-cell-reorder",
formatter: formatter
})
}
// Add column for row delete support
@ -434,21 +443,25 @@ my.SlickGrid = Backbone.I18nView.extend({
// Add new grid Control to display a new row add menu bouton
// It display a simple side-bar menu ,for user to add new
// row to grid
my.GridControl= Backbone.View.extend({
my.GridControl= Backbone.I18nView.extend({
className: "recline-row-add",
// Template for row edit menu , change it if you don't love
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;
_.bindAll(this, 'render');
this.state = new recline.Model.ObjectState();
this.initializeI18n(options.locale);
this.render();
},
render: function() {
var self = this;
this.$el.html(this.template)
var formatted = Mustache.render(this.template, _.extend({}, this.MustacheFormatter()));
this.$el.html(formatted);
},
events : {

View File

@ -75,6 +75,8 @@ my.Fields = Backbone.I18nView.extend({
self.render();
});
this.$el.find('.collapse').collapse();
this.initializeI18n(model.locale);
this.render();
},
render: function() {

View File

@ -58,7 +58,7 @@ my.FilterEditor = Backbone.I18nView.extend({
<fieldset> \
<legend> \
{{field}} <small>{{type}}</small> \
<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">&times;</span></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">&times;</span></a> \
</legend> \
<div class="form-group"> \
<label class="control-label" for="">{{t.From}}</label> \
@ -100,10 +100,12 @@ my.FilterEditor = Backbone.I18nView.extend({
'submit form.js-edit': 'onTermFiltersUpdate',
'submit form.js-add': 'onAddFilter'
},
initialize: function() {
initialize: function(options) {
_.bindAll(this, 'render');
this.listenTo(this.model.fields, 'all', this.render);
this.listenTo(this.model.queryState, 'change change:filters:new-blank', this.render);
this.initializeI18n(options.locale);
this.render();
},
render: function() {
@ -116,10 +118,10 @@ my.FilterEditor = Backbone.I18nView.extend({
});
tmplData.fields = this.model.fields.toJSON();
tmplData.filterRender = function() {
var filterData = _.extend(this, this.MustacheFormatter());
var filterData = _.extend(this, self.MustacheFormatter());
return Mustache.render(self.filterTemplates[this.type], filterData);
};
tmplData = _.extend(tmplData, this.MustacheFormatter());
tmplData = _.extend(tmplData, self.MustacheFormatter());
var out = Mustache.render(this.template, tmplData);
this.$el.html(out);
},

View File

@ -19,7 +19,7 @@ my.QueryEditor = Backbone.I18nView.extend({
<input class="form-control search-query" type="text" id="q" name="q" value="{{q}}" placeholder="{{t.Search_data}} ..."> \
</div> \
</div> \
<button type="submit" class="btn btn-default">{{t.Go}} &raquo;</button> \
<button type="submit" class="btn btn-default">{{t.Search}} &raquo;</button> \
</form> \
',
@ -27,9 +27,11 @@ my.QueryEditor = Backbone.I18nView.extend({
'submit form': 'onFormSubmit'
},
initialize: function() {
initialize: function(options = {}) {
_.bindAll(this, 'render');
this.listenTo(this.model, 'change', this.render);
this.initializeI18n(options.locale);
this.render();
},
onFormSubmit: function(e) {

View File

@ -50,10 +50,12 @@ my.ValueFilter = Backbone.I18nView.extend({
'submit form.js-edit': 'onTermFiltersUpdate',
'submit form.js-add': 'onAddFilter'
},
initialize: function() {
initialize: function(options = {}) {
_.bindAll(this, 'render');
this.listenTo(this.model.fields, 'all', this.render);
this.listenTo(this.model.queryState, 'change change:filters:new-blank', this.render);
this.initializeI18n(options.locale);
this.render();
},
render: function() {

View File

@ -129,11 +129,16 @@ test('translate complex key custom locale custom count', function () {
locale: 'pl'
});
// todo custom count string
equal(view.t('some_msg', {records: 0}, '<span>{{records}} records</span>'), '<span>brak rekordów</span>');
equal(view.t('some_msg', {records: 1}, '<span>{{records}} records</span>'), '<span>1 rekord</span>');
equal(view.t('some_msg', {records: 3}, '<span>{{records}} records</span>'), '<span>3 rekordy</span>');
equal(view.t('some_msg', {records: 5}, '<span>{{records}} records</span>'), '<span>5 rekordów</span>');
recline.View.translations['pl']['codeforall'] = '{records, plural, ' +
'=0 {brak zdjęć}' +
'=1 {{records} zdjęcie}' +
'few {{records} zdjęcia}' +
'other {{records} zdjęć}}';
equal(view.t('codeforall', {records: 0}), 'brak zdjęć');
equal(view.t('codeforall', {records: 1}), '1 zdjęcie');
equal(view.t('codeforall', {records: 3}), '3 zdjęcia');
equal(view.t('codeforall', {records: 5}), '5 zdjęć');
});