diff --git a/bower.json b/bower.json index 00f405129..452cf7824 100644 --- a/bower.json +++ b/bower.json @@ -26,6 +26,9 @@ "nz-tour": "^1.2.1", "jquery": "^3.1.0", "leaflet-plugins": "^3.0.0", - "angularjs-slider": "^6.2.2" + "angularjs-slider": "^6.2.2", + "angular-translate": "^2.18.1", + "angular-translate-loader-static-files": "^2.18.1", + "angular-translate-interpolation-messageformat": "^2.18.1" } } diff --git a/www/i18n/en.json b/www/i18n/en.json new file mode 100644 index 000000000..2e356b6d3 --- /dev/null +++ b/www/i18n/en.json @@ -0,0 +1,247 @@ +{ + "loading" : "Loading...", + "map-refresh": "Refresh", + "map-fixmap": "Fix Map", + + "weekdays-all": "All", + "weekdays-select": "Select day of the week", + + "post-trip-prompt":{ + "notification-option-mute": "Mute", + "notification-option-snooze": "Snooze", + "notification-option-choose": "Choose", + "notification-title": "How and why did you come here?", + "choose-mode": "Choose Mode", + "skip": "Skip", + "snoozed-reminder": "Snoozed reminder", + "snoozed-reapper-message": "Will reappear in 30 mins", + "platform-specific-message-ios": "Swipe left or tap to add information about this trip.", + "platform-specific-message-android": "See options or tap to add information about this trip.", + "platform-specific-message-other": "Tap to add information about this trip.", + "notifications-muted": "Notifications for TRIP_END incident report muted", + "notifications-reenabled": "Can be re-enabled from the Profile -> Developer Zone screen. Select to re-enable now, clear to ignore", + "muted": "Muted", + "unmute": "Unmute", + "keep-muted": "Keep muted" + }, + + "post-trip-map-display-tour-incident": "Zoom in as much as possible to the location where the incident occurred and click on the blue line of the trip to mark a or incident", + + "tour-next": "Next", + "tour-previous": "Previous", + "tour-finish": "Finish", + + "trip-confirm": { + "recenttrip": "Recent trip from: {{startTime}} → to: {{endTime}}", + "continue": "Continue", + "done": "Done", + "services-please-fill-in": "Please fill in the {{text}} not listed.", + "services-cancel": "Cancel", + "services-save": "Save" + }, + + "place-common-place": "Common place", + "place-successor-trips": "Successor trips", + "place-trips-to": "{{trips}} trips to", + "place-usually-starts": "Usually starts at: {{hour}}:00", + "place-usually-takes": "Usually takes: {{duration}}", + + "trip-start-hours": "Start hours", + "trip-start-duration": "Duration", + + "control":{ + "profile": "Profile", + "tracking": "Tracking", + "medium-accuracy": "Medium accuracy", + "dark-theme": "Dark theme", + "force-sync": "Force sync", + "share": "Share", + "check-ui-updates": "Check for UI updates", + "download-json-dump": "Download json dump", + "email-log": "Email log", + "user-data": "User data", + "erase-data": "Erase data", + "dev-zone": "Developer zone", + "refresh": "Refresh", + "end-trip-sync": "End trip + sync", + "check-consent": "Check consent", + "invalidate-cached-docs": "Invalidate cached docs", + "nuke-all": "Nuke all buffers and cache", + "set-ui-channel": "Set UI channel", + "check-log": "Check log", + "check-sensed-data": "Check sensed data", + "check-map": "Check map", + "collection": "Collection", + "sync": "Sync", + "transition-notify": "Transition Notify" + }, + + "general-settings":{ + "choose-date" : "Choose date to download data", + "nuke-ui-state-only" : "UI state only", + "nuke-native-cache-only" : "Native cache only", + "nuke-everything" : "Everything", + "clear-data": "Clear data", + "cancel": "Cancel", + "user-data-erased": "User data erased.", + "consent-not-found": "Consent for data collection not found, consent now?", + "no-consent-message": "OK! Note that you won't get any personalized stats until you do!", + "consent-found": "Consent found!", + "consented-to": "Consented to protocol {{protocol_id}}, {{approval_date}}", + "consented-ok": "OK", + "share-message": "Join me in making transportation greener and healthier \nDownload the emission app:", + "share-subject": "Emission - UC Berkeley Research Project", + "share-url": "https://bic2cal.eecs.berkeley.edu/#download" + }, + + "metrics":{ + "cancel": "Cancel", + "confirm": "Confirm", + "get": "Get", + "range": "Range", + "filter": "Filter", + "from": "From:", + "to": "To:", + "last-week": "Last week", + "frequency": "Frequency:", + "pandafreqoptions-daily": "DAILY", + "pandafreqoptions-weekly": "WEEKLY", + "pandafreqoptions-biweekly": "BIWEEKLY", + "pandafreqoptions-monthly": "MONTHLY", + "pandafreqoptions-yearly": "YEARLY", + "freqoptions-daily": "DAILY", + "freqoptions-monthly": "MONTHLY", + "freqoptions-yearly": "YEARLY", + "select-pandafrequency": "Select summary freqency", + "select-frequency": "Select summary freqency", + "chart-xaxis-date": "Date", + "chart-no-data": "No Data Available", + "trips-yaxis-number": "Number", + "calorie-data-change": " change", + "carbon-data-change": " change", + "carbon-data-calculating": "Calculating...", + "carbon-data-unknown": "Unknown", + "calorie-data-unknown": "Unknown...", + "calorie-data-change-increase": " increase over a week", + "calorie-data-change-decrease": " decrease over a week", + "carbon-data-change-increase": " increase over a week", + "carbon-data-change-decrease": " decrease over a week", + "pick-a-date": "Pick a date" + }, + + "diary": { + "current-trip": "Current Trip", + "current-yesterday": "Yesterday", + "current-weekago": "Week ago", + "history": "History", + "began": "Began {{startTime}}", + "report-incident": "Report Incident", + "draft": "DRAFT", + "distance-in-time": "{{distance}} km in {{time}}", + "distance": "Distance", + "time": "Time", + "mode": "Mode", + "purpose": "Purpose", + "choose-mode": "Choose Mode", + "choose-purpose": "Choose Purpose", + "how-did-you-get-here": "How did you get here?", + "why-did-you-come-here": "Why did you come here?", + "list-pick-a-date": "Pick a date" + }, + + "user-gender": "Gender", + "gender-male": "Male", + "gender-female": "Female", + "user-height": "Height", + "user-weight": "Weight", + "user-age": "Age", + + "main-metrics":{ + "dashboard": "Dashboard", + "summary": "Summary", + "chart": "Chart", + "change-data": "Change data:", + "distance": "Distance", + "trips": "Trips", + "duration": "Duration", + "speed": "Speed", + "footprint": "Footprint", + "optimal": "Optimal:", + "average": "Average:", + "worst": "Worst:", + "lastweek": "Last Week:", + "calories": "Calories", + "calibrate": "Calibrate", + "no-summary-data": "No summary data", + "median-speed": "Median Speed", + "equals-cookies": "Equals {cookies, plural, =0{0 homemade chocolate chip cookies} one {1 homemade chocolate chip cookie} other {# homemade chocolate chip cookies}}", + "equals-icecream": "Equals {icecream, plural, =0{0 half cups vanilla ice cream} one {1 half cup vanilla ice cream} other {# half cups vanilla ice cream}}", + "equals-bananas": "Equals {bananas, plural, =0{0 bananas} one {1 banana} other {# bananas}}" + }, + + "main-diary" : "Diary", + + "main-heatmap":{ + "title": "Heatmap", + "counts" : "Counts", + "stress" : "Stress", + "from" : "From:", + "to" : "To:", + "get" : "Get!", + "all": "ALL", + "none": "NONE", + "bicycling": "BICYCLING", + "walking": "WALKING", + "in-vehicle": "IN_VEHICLE", + "select-travel-mode" : "Select travel mode", + "cancel": "Cancel", + "tour-datepicker": "This heatmap shows the aggregate data for all E-mission users. Select the dates you want to see, and filter by hours of the day (24h format) and days of the week. For example, if you enter 16 and 19 in the last field, and select Monday and Friday, you'll see the Heatmap filtered to show the traffic on weekdays between 4pm and 7pm.", + "tour-mode": "Click here to filter your results by mode of transportation. The default is to show all modes.", + "tour-get": "Click here to generate the heatmap." + }, + + "details":{ + "speed": "Speed", + "time": "Temps", + "tour-detail-content": "To report an incident, zoom in as much as possible to the location where the incident occurred and click on the trip to mark a ☻ or ☹ incident", + "tour-sectionList-content": "Trip sections, along with times and modes", + "tour-sectionPct-content": "% of time spent in each mode for this trip" + }, + + "list-explainDraft-alert": "This trip has not yet been analysed. If it stays in this state, please ask your sysadmin to check what is wrong.", + "list-datepicker-today": "Today", + "list-datepicker-close": "Close", + "list-datepicker-set": "Set", + "list-tour-datepicker-button" : "Use this to select the day you want to see.", + "list-tour-diary-entry" : "Click on the map to see more details about each trip.", + "list-tour-map-fix-button" : "Use this to fix the map tiles if they have not loaded properly.", + + "service":{ + "reading-server": "Reading from server...", + "reading-cache": "Reading from cache...", + "reading-unprocessed-data": "Reading unprocessed data..." + }, + + + "post-trip-manual-incident-time" : "Choose incident time", + + "recent":{ + "email-account-not-configured": "Email account is not configured, cannot send email", + "email-account-mail-app": "You must have the mail app on your phone configured with an email address. Otherwise, this won't work", + "going-to-email": "Going to email database from {{parentDir}}/userCacheDB" + }, + + "consent":{ + "button-accept": "I accept", + "button-decline": "I refuse" + }, + + "updatecheck":{ + "downloading-update": "Downloading UI-only update", + "extracting-update": "Extracting UI-only update", + "done": "Update done, reloading...", + "download-new-ui": "Download new UI-only update?.", + "download-not-now": "Not now", + "download-apply": "Apply" + } +} diff --git a/www/index.html b/www/index.html index d4f39f03f..dbd1648da 100644 --- a/www/index.html +++ b/www/index.html @@ -3,7 +3,7 @@ - + @@ -30,7 +30,13 @@ + + + + + + diff --git a/www/js/app.js b/www/js/app.js index 0914f9960..2711e562e 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -11,7 +11,8 @@ angular.module('emission', ['ionic', 'emission.controllers','emission.services', 'emission.plugin.logger', 'emission.splash.customURLScheme', 'emission.splash.referral', 'emission.splash.updatecheck', - 'emission.intro', 'emission.main']) + 'emission.intro', 'emission.main', + 'pascalprecht.translate']) .run(function($ionicPlatform, $rootScope, $http, Logger, CustomURLScheme, ReferralHandler, UpdateCheck) { @@ -70,7 +71,7 @@ angular.module('emission', ['ionic', console.log("Ending run"); }) -.config(function($stateProvider, $urlRouterProvider) { +.config(function($stateProvider, $urlRouterProvider, $translateProvider) { console.log("Starting config"); // alert("config"); @@ -102,5 +103,23 @@ angular.module('emission', ['ionic', // alert("about to fall back to otherwise"); // if none of the above states are matched, use this as the fallback $urlRouterProvider.otherwise('/splash'); + + // Allow the use of MessageForm interpolation for Gender and Plural. + $translateProvider.addInterpolation('$translateMessageFormatInterpolation'); + + // Define where we can find the .json and the fallback language + $translateProvider + .fallbackLanguage('en') + .registerAvailableLanguageKeys(['en', 'fr'], { + 'en_*': 'en', + 'fr_*': 'fr', + '*': 'en' + }) + .determinePreferredLanguage() + .useStaticFilesLoader({ + prefix: 'i18n/', + suffix: '.json' + }); + console.log("Ending config"); }); diff --git a/www/js/control/general-settings.js b/www/js/control/general-settings.js index 2f616fad7..c74b9ec08 100644 --- a/www/js/control/general-settings.js +++ b/www/js/control/general-settings.js @@ -21,13 +21,16 @@ angular.module('emission.main.control',['emission.services', ControlCollectionHelper, ControlSyncHelper, ControlTransitionNotifyHelper, UpdateCheck, - CalorieCal, ClientStats, CommHelper, Logger) { + CalorieCal, ClientStats, CommHelper, Logger, + $translate) { var datepickerObject = { - todayLabel: 'Today', //Optional - closeLabel: 'Close', //Optional - setLabel: 'Set', //Optional - titleLabel: 'Choose date to download data', + todayLabel: $translate.instant('list-datepicker-today'), //Optional + closeLabel: $translate.instant('list-datepicker-close'), //Optional + setLabel: $translate.instant('list-datepicker-set'), //Optional + monthsList: moment.monthsShort(), + weeksList: moment.weekdaysMin(), + titleLabel: $translate.instant('general-settings.choose-date'), setButtonType : 'button-positive', //Optional todayButtonType : 'button-stable', //Optional closeButtonType : 'button-stable', //Optional @@ -61,7 +64,7 @@ angular.module('emission.main.control',['emission.services', age: userDataFromStorage.age, height: height + (userDataFromStorage.heightUnit == 1? ' cm' : ' ft'), weight: weight + (userDataFromStorage.weightUnit == 1? ' kg' : ' lb'), - gender: userDataFromStorage.gender == 1? 'Male' : 'Female' + gender: userDataFromStorage.gender == 1? $translate.instant('gender-male') : $translate.instant('gender-female') } for (var i in temp) { $scope.userData.push({key: i, value: temp[i]}); @@ -183,16 +186,16 @@ angular.module('emission.main.control',['emission.services', } $scope.nukeUserCache = function() { - var nukeChoiceActions = [{text: "UI state only", + var nukeChoiceActions = [{text: $translate.instant('general-settings.nuke-ui-state-only'), action: KVStore.clearOnlyLocal}, - {text: 'Native cache only', + {text: $translate.instant('general-settings.nuke-native-cache-only'), action: KVStore.clearOnlyNative}, - {text: 'Everything', + {text: $translate.instant('general-settings.nuke-everything'), action: KVStore.clearAll}]; $ionicActionSheet.show({ - titleText: "Clear data", - cancelText: "Cancel", + titleText: $translate.instant('general-settings.clear-data'), + cancelText: $translate.instant('general-settings.cancel'), buttons: nukeChoiceActions, buttonClicked: function(index, button) { button.action(); @@ -414,7 +417,7 @@ angular.module('emission.main.control',['emission.services', } $scope.eraseUserData = function() { CalorieCal.delete().then(function() { - $ionicPopup.alert({template: 'User data erased.'}); + $ionicPopup.alert({template: $translate.instant('general-settings.user-data-erased')}); }); } $scope.parseState = function(state) { @@ -456,13 +459,13 @@ angular.module('emission.main.control',['emission.services', } var handleNoConsent = function(resultDoc) { - $ionicPopup.confirm({template: "Consent for data collection not found, consent now?"}) + $ionicPopup.confirm({template: $translate.instant('general-settings.consent-not-found')}) .then(function(res){ if (res) { $state.go("root.reconsent"); } else { $ionicPopup.alert({ - template: "OK! Note that you won't get any personalized stats until you do!"}); + template: $translate.instant('general-settings.no-consent-message')}); } }); } @@ -470,13 +473,13 @@ angular.module('emission.main.control',['emission.services', var handleConsent = function(resultDoc) { $scope.consentDoc = resultDoc; $ionicPopup.confirm({ - template: 'Consented to protocol {{consentDoc.protocol_id}}, {{consentDoc.approval_date}}', + template: $translate.instant('general-settings.consented-to',{protocol_id: $scope.consentDoc.protocol_id,approval_date: $scope.consentDoc.approval_date}), scope: $scope, - title: "Consent found!", + title: $translate.instant('general-settings.consent-found'), buttons: [ // {text: "View", // type: 'button-calm'}, - {text: "OK", + {text: ""+ $translate.instant('general-settings.consented-ok') +"", type: 'button-positive'} ] }).finally(function(res) { $scope.consentDoc = null; @@ -496,9 +499,9 @@ angular.module('emission.main.control',['emission.services', } var prepopulateMessage = { - message: 'Join me in making transportation greener and healthier \nDownload the emission app:', // not supported on some apps (Facebook, Instagram) - subject: 'Emission - UC Berkeley Research Project', // fi. for email - url: 'https://bic2cal.eecs.berkeley.edu/#download' + message: $translate.instant('general-settings.share-message'), // not supported on some apps (Facebook, Instagram) + subject: $translate.instant('general-settings.share-subject'), // fi. for email + url: $translate.instant('general-settings.share-url') } $scope.share = function() { diff --git a/www/js/diary/current.js b/www/js/diary/current.js index 3d87c4a50..996f3a453 100644 --- a/www/js/diary/current.js +++ b/www/js/diary/current.js @@ -8,7 +8,7 @@ 'emission.plugin.logger']) .controller('CurrMapCtrl', function($scope, Config, $state, $timeout, $ionicActionSheet,leafletData, - Logger, $window, PostTripManualMarker, CommHelper, $http, KVStore, $ionicPlatform) { + Logger, $window, PostTripManualMarker, CommHelper, $http, KVStore, $ionicPlatform, $translate) { console.log("controller CurrMapCtrl called from current.js"); var _map; @@ -46,9 +46,9 @@ hideLimitLabels: true, translate: function(value) { if (value === 1) { - return "Yesterday"; + return $translate.instant('diary.current-yesterday'); } else if (value === 7) { - return "Week ago"; + return $translate.instant('diary.current-weekagos'); } return ""; } @@ -84,15 +84,9 @@ sel_region: null }; - var startTimeFn = function(ts) { - var date = new Date(ts*1000); - var hours = date.getHours(); - var minutes = date.getMinutes(); - var amOrPm = hours < 12 ? 'AM' : 'PM'; - hours = hours % 12; - hours = hours ? hours : 12; - minutes = minutes < 10 ? '0'+ minutes : minutes; - return hours + ':' + minutes + ' ' + amOrPm; + var startTimeFn = function (ts) { + var date = new Date(ts * 1000); + return moment(date).format("LT"); }; var getSpeed = function(curr_lglat, last_lglat, curr_ts, last_ts) { diff --git a/www/js/diary/detail.js b/www/js/diary/detail.js index afa67bc64..b887f9e7a 100644 --- a/www/js/diary/detail.js +++ b/www/js/diary/detail.js @@ -7,7 +7,7 @@ angular.module('emission.main.diary.detail',['ui-leaflet', 'ng-walkthrough', .controller("DiaryDetailCtrl", function($scope, $rootScope, $window, $stateParams, $ionicActionSheet, leafletData, leafletMapEvents, nzTour, KVStore, Logger, Timeline, DiaryHelper, Config, - CommHelper, PostTripManualMarker) { + CommHelper, PostTripManualMarker, $translate) { console.log("controller DiaryDetailCtrl called with params = "+ JSON.stringify($stateParams)); @@ -85,7 +85,7 @@ angular.module('emission.main.diary.detail',['ui-leaflet', 'ng-walkthrough', } var dataset = { values: data, - key: 'Speed', + key: $translate.instant('details.speed'), color: '#7777ff', } var chart = nv.models.lineChart() @@ -97,10 +97,10 @@ angular.module('emission.main.diary.detail',['ui-leaflet', 'ng-walkthrough', .showXAxis(true); //Show the x-axis chart.xAxis .tickFormat(d3.format(".1f")) - .axisLabel('Time (mins)'); + .axisLabel($translate.instant('details.time') + ' (mins)'); chart.yAxis //Chart y-axis settings - .axisLabel('Speed (m/s)') + .axisLabel($translate.instant('details.speed') + ' (m/s)') .tickFormat(d3.format('.1f')); d3.select('#chart svg') //Select the element you want to render the chart in. @@ -119,17 +119,20 @@ angular.module('emission.main.diary.detail',['ui-leaflet', 'ng-walkthrough', mask: { visibleOnNoTarget: true, clickExit: true - } + }, + previousText: $translate.instant('tour-previous'), + nextText: $translate.instant('tour-next'), + finishText: $translate.instant('tour-finish') }, steps: [{ target: '#detail', - content: 'To report an incident, zoom in as much as possible to the location where the incident occurred and click on the trip to mark a ☻ or ☹ incident' + content: $translate.instant('details.tour-detail-content') }, { target: '#sectionList', - content: 'Trip sections, along with times and modes' + content: $translate.instant('details.tour-sectionList-content') }, { target: '#sectionPct', - content: '% of time spent in each mode for this trip' + content: $translate.instant('details.tour-sectionPct-content') }] }; diff --git a/www/js/diary/list.js b/www/js/diary/list.js index 63f344415..1e8ba4f07 100644 --- a/www/js/diary/list.js +++ b/www/js/diary/list.js @@ -24,7 +24,7 @@ angular.module('emission.main.diary.list',['ui-leaflet', $ionicActionSheet, ionicDatePicker, leafletData, Timeline, CommonGraph, DiaryHelper, - Config, PostTripManualMarker, ConfirmHelper, nzTour, KVStore, Logger, UnifiedDataLoader, $ionicPopover) { + Config, PostTripManualMarker, ConfirmHelper, nzTour, KVStore, Logger, UnifiedDataLoader, $ionicPopover, $translate) { console.log("controller DiaryListCtrl called"); var MODE_CONFIRM_KEY = "manual/mode_confirm"; var PURPOSE_CONFIRM_KEY = "manual/purpose_confirm"; @@ -42,6 +42,8 @@ angular.module('emission.main.diary.list',['ui-leaflet', // TODO: Convert the usercache calls into promises so that we don't have to // do this juggling Timeline.updateForDay(day); + // This will be used to show the date of datePicker in the user language. + $scope.currDay = moment(day).format('LL'); // CommonGraph.updateCurrent(); }; @@ -59,23 +61,23 @@ angular.module('emission.main.diary.list',['ui-leaflet', angular.extend($scope.defaults, Config.getMapTiles()) - moment.locale('en', { - relativeTime : { - future: "in %s", - past: "%s ago", - s: "secs", - m: "a min", - mm: "%d m", - h: "an hr", - hh: "%d h", - d: "a day", - dd: "%d days", - M: "a month", - MM: "%d months", - y: "a year", - yy: "%d years" - } -}); +// moment.locale('en', { +// relativeTime : { +// future: "in %s", +// past: "%s ago", +// s: "secs", +// m: "a min", +// mm: "%d m", +// h: "an hr", +// hh: "%d h", +// d: "a day", +// dd: "%d days", +// M: "a month", +// MM: "%d months", +// y: "a year", +// yy: "%d years" +// } +// }); /* * While working with dates, note that the datepicker needs a javascript date because it uses @@ -123,9 +125,12 @@ angular.module('emission.main.diary.list',['ui-leaflet', $scope.datepickerObject = { - todayLabel: 'Today', //Optional - closeLabel: 'Close', //Optional - setLabel: 'Set', //Optional + todayLabel: $translate.instant('list-datepicker-today'), //Optional + closeLabel: $translate.instant('list-datepicker-close'), //Optional + setLabel: $translate.instant('list-datepicker-set'), //Optional + monthsList: moment.monthsShort(), + weeksList: moment.weekdaysMin(), + titleLabel: $translate.instant('diary.list-pick-a-date'), setButtonType : 'button-positive', //Optional todayButtonType : 'button-stable', //Optional closeButtonType : 'button-stable', //Optional @@ -225,7 +230,7 @@ angular.module('emission.main.diary.list',['ui-leaflet', $scope.explainDraft = function($event) { $event.stopPropagation(); $ionicPopup.alert({ - template: "This trip has not yet been analysed. If it stays in this state, please ask your sysadmin to check what is wrong." + template: $translate.instant('list-explainDraft-alert') }); // don't want to go to the detail screen } @@ -402,20 +407,23 @@ angular.module('emission.main.diary.list',['ui-leaflet', config: { mask: { visibleOnNoTarget: true, - clickExit: true - } + clickExit: true, + }, + previousText: $translate.instant('tour-previous'), + nextText: $translate.instant('tour-next'), + finishText: $translate.instant('tour-finish') }, steps: [{ target: '#date-picker-button', - content: 'Use this to select the day you want to see.' + content: $translate.instant('list-tour-datepicker-button') }, { target: '.diary-entry', - content: 'Click on the map to see more details about each trip.' + content: $translate.instant('list-tour-diary-entry') }, { target: '#map-fix-button', - content: 'Use this to fix the map tiles if they have not loaded properly.' + content: $translate.instant('list-tour-diary-entry') } ] }; diff --git a/www/js/diary/services.js b/www/js/diary/services.js index 249461f00..751aa4b9c 100644 --- a/www/js/diary/services.js +++ b/www/js/diary/services.js @@ -3,7 +3,7 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger', 'emission.services', 'emission.main.common.services', 'emission.incident.posttrip.manual']) -.factory('DiaryHelper', function(CommonGraph, PostTripManualMarker){ +.factory('DiaryHelper', function(CommonGraph, PostTripManualMarker, $translate){ var dh = {}; // dh.expandEarlierOrLater = function(id) { // document.querySelector('#hidden-' + id.toString()).setAttribute('style', 'display: block;'); @@ -166,12 +166,9 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger', return retVal; }; - dh.getLocalTimeString = function(dt) { - var hr = ((dt.hour > 12))? dt.hour - 12 : dt.hour; - var post = ((dt.hour >= 12))? " pm" : " am"; - var min = (dt.minute.toString().length == 1)? "0" + dt.minute.toString() : dt.minute.toString(); - return hr + ":" + min + post; - } + dh.getLocalTimeString = function (dt) { + return moment(dt).format("LT"); + }; dh.getFormattedTime = function(ts_in_secs) { if (angular.isDefined(ts_in_secs)) { @@ -439,7 +436,7 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger', return dh; }) .factory('Timeline', function(CommHelper, $http, $ionicLoading, $window, - $rootScope, CommonGraph, UnifiedDataLoader, Logger) { + $rootScope, CommonGraph, UnifiedDataLoader, Logger, $translate) { var timeline = {}; // corresponds to the old $scope.data. Contains all state for the current // day, including the indication of the current day @@ -456,7 +453,7 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger', timeline.updateFromDatabase = function(day) { console.log("About to show 'Reading from cache'"); $ionicLoading.show({ - template: 'Reading from cache...' + template: $translate.instant('service.reading-cache') }); return window.cordova.plugins.BEMUserCache.getDocument(getKeyForDate(day), false) .then(function (timelineDoc) { @@ -477,7 +474,7 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger', timeline.updateFromServer = function(day) { console.log("About to show 'Reading from server'"); $ionicLoading.show({ - template: 'Reading from server...' + template: $translate.instant('service.reading-server') }); return CommHelper.getTimelineForDay(day).then(function(response) { var tripList = response.timeline; @@ -830,7 +827,7 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger', * https://github.com/e-mission/e-mission-phone/issues/214#issuecomment-284312004 */ $ionicLoading.show({ - template: 'Reading unprocessed data...' + template: $translate.instant('service.reading-unprocessed-data') }); if (tripListForDay.length == 0) { var last_processed_ts = moment(day).startOf("day").unix(); diff --git a/www/js/heatmap.js b/www/js/heatmap.js index aaa76e62c..66d52e7c4 100644 --- a/www/js/heatmap.js +++ b/www/js/heatmap.js @@ -6,7 +6,7 @@ angular.module('emission.main.heatmap',['ui-leaflet', 'emission.services', .controller('HeatmapCtrl', function($scope, $ionicLoading, $ionicActionSheet, $http, leafletData, Logger, Config, PostTripManualMarker, - $window, nzTour, KVStore) { + $window, nzTour, KVStore, $translate) { $scope.mapCtrl = {}; angular.extend($scope.mapCtrl, { @@ -84,18 +84,18 @@ angular.module('emission.main.heatmap',['ui-leaflet', 'emission.services', */ $scope.modeOptions = [ - {text: "ALL", value:null}, - {text: "NONE", value:[]}, - {text: "BICYCLING", value:["BICYCLING"]}, - {text: "WALKING", value:["WALKING", "ON_FOOT"]}, - {text: "IN_VEHICLE", value:["IN_VEHICLE"]} + {text: $translate.instant('main-heatmap.all'), value:null}, + {text: $translate.instant('main-heatmap.none'), value:[]}, + {text: $translate.instant('main-heatmap.bicycling'), value:["BICYCLING"]}, + {text: $translate.instant('main-heatmap.walking'), value:["WALKING", "ON_FOOT"]}, + {text: $translate.instant('main-heatmap.in-vehicle'), value:["IN_VEHICLE"]} ]; $scope.changeMode = function() { $ionicActionSheet.show({ buttons: $scope.modeOptions, - titleText: "Select travel mode", - cancelText: "Cancel", + titleText: $translate.instant('main-heatmap.select-travel-mode'), + cancelText: $translate.instant('main-heatmap.cancel'), buttonClicked: function(index, button) { $scope.selectCtrl.modeString = button.text; $scope.selectCtrl.modes = button.value; @@ -120,19 +120,19 @@ angular.module('emission.main.heatmap',['ui-leaflet', 'emission.services', $scope.changeWeekday = function(stringSetFunction, localDateObj) { var weekdayOptions = [ - {text: "All", value: null}, - {text: "Monday", value: 0}, - {text: "Tuesday", value: 1}, - {text: "Wednesday", value: 2}, - {text: "Thursday", value: 3}, - {text: "Friday", value: 4}, - {text: "Saturday", value: 5}, - {text: "Sunday", value: 6} + {text: $translate.instant('weekdays-all'), value: null}, + {text: moment.weekdays(1), value: 0}, + {text: moment.weekdays(2), value: 1}, + {text: moment.weekdays(3), value: 2}, + {text: moment.weekdays(4), value: 3}, + {text: moment.weekdays(5), value: 4}, + {text: moment.weekdays(6), value: 5}, + {text: moment.weekdays(0), value: 6} ]; $ionicActionSheet.show({ buttons: weekdayOptions, - titleText: "Select day of the week", - cancelText: "Cancel", + titleText: $translate.instant('weekdays-select'), + cancelText: $translate.instant('main-heatmap.cancel'), buttonClicked: function(index, button) { stringSetFunction(button.text); localDateObj.weekday = button.value; @@ -173,11 +173,11 @@ angular.module('emission.main.heatmap',['ui-leaflet', 'emission.services', $scope.selectCtrl.showStress = false; $scope.selectCtrl.showCount = true; $scope.selectCtrl.modes = null; - $scope.selectCtrl.modeString = "ALL"; - $scope.selectCtrl.fromDate = moment2Localdate(dayago) + $scope.selectCtrl.modeString = $translate.instant('main-heatmap.all'); + $scope.selectCtrl.fromDate = moment2Localdate(dayago); $scope.selectCtrl.toDate = moment2Localdate(now); - $scope.selectCtrl.fromDateWeekdayString = "All" - $scope.selectCtrl.toDateWeekdayString = "All" + $scope.selectCtrl.fromDateWeekdayString = $translate.instant('weekdays-all'); + $scope.selectCtrl.toDateWeekdayString = $translate.instant('weekdays-all'); $scope.selectCtrl.region = null; }; @@ -235,7 +235,7 @@ angular.module('emission.main.heatmap',['ui-leaflet', 'emission.services', var setSelData = function(map, selData) { if (selData.isLoading == true) { $ionicLoading.show({ - template: 'Loading...' + template: $translate.instant('loading') }); // Don't set any layer - it will be filled in when the load completes } else { @@ -339,19 +339,23 @@ angular.module('emission.main.heatmap',['ui-leaflet', 'emission.services', mask: { visibleOnNoTarget: true, clickExit: true - } + }, + previousText: $translate.instant('tour-previous'), + nextText: $translate.instant('tour-next'), + finishText: $translate.instant('tour-finish') }, steps: [{ target: '.datepicker', - content: 'This heatmap shows the aggregate data for all E-mission users. Select the dates you want to see, and filter by hours of the day (24h format) and days of the week. For example, if you enter 16 and 19 in the last field, and select Monday and Friday, you\'ll see the Heatmap filtered to show the traffic on weekdays between 4pm and 7pm.' + content: $translate.instant('main-heatmap.tour-datepicker') }, - { + { target: '.heatmap-mode-button', - content: 'Click here to filter your results by mode of transportation. The default is to show all modes.' + content: $translate.instant('main-heatmap.tour-mode') + }, - { + { target: '.heatmap-get-button', - content: 'Click here to generate the heatmap.' + content: $translate.instant('main-heatmap.tour-get') }] }; diff --git a/www/js/incident/post-trip-manual.js b/www/js/incident/post-trip-manual.js index d2e52fc1c..ae0424575 100644 --- a/www/js/incident/post-trip-manual.js +++ b/www/js/incident/post-trip-manual.js @@ -3,7 +3,7 @@ angular.module('emission.incident.posttrip.manual', ['emission.plugin.logger', 'emission.main.diary.services']) .factory('PostTripManualMarker', function($window, $state, $ionicActionSheet, $ionicPlatform, - Logger, Timeline) { + Logger, Timeline, $translate) { var ptmm = {}; var MULTI_PASS_THRESHOLD = 90; @@ -409,7 +409,7 @@ angular.module('emission.incident.posttrip.manual', ['emission.plugin.logger', return {text: getFormattedTime(ts), selValue: ts}; }); - $ionicActionSheet.show({titleText: "Choose incident time", + $ionicActionSheet.show({titleText: $translate.instant('post-trip-manual-incident-time'), buttons: timeSelActions, buttonClicked: function(index, button) { var ts = button.selValue; diff --git a/www/js/incident/post-trip-map-display.js b/www/js/incident/post-trip-map-display.js index 23cbd0f4f..3381ae0ec 100644 --- a/www/js/incident/post-trip-map-display.js +++ b/www/js/incident/post-trip-map-display.js @@ -9,7 +9,7 @@ angular.module('emission.incident.posttrip.map',['ui-leaflet', 'ng-walkthrough', $stateParams, $ionicLoading, leafletData, leafletMapEvents, nzTour, KVStore, Logger, Timeline, DiaryHelper, Config, - UnifiedDataLoader, PostTripManualMarker) { + UnifiedDataLoader, PostTripManualMarker, $translate) { Logger.log("controller PostTripMapDisplay called with params = "+ JSON.stringify($stateParams)); $scope.mapCtrl = {}; @@ -54,7 +54,7 @@ angular.module('emission.incident.posttrip.map',['ui-leaflet', 'ng-walkthrough', }; Logger.log("About to query buffer for "+JSON.stringify(tq)); $ionicLoading.show({ - template: 'Loading...' + template: $translate.instant('loading') }); UnifiedDataLoader.getUnifiedSensorDataForInterval(LOC_KEY, tq) // .then(PostTripManualMarker.addLatLng) @@ -159,11 +159,14 @@ angular.module('emission.incident.posttrip.map',['ui-leaflet', 'ng-walkthrough', mask: { visibleOnNoTarget: true, clickExit: true - } + }, + previousText: $translate.instant('tour-previous'), + nextText: $translate.instant('tour-next'), + finishText: $translate.instant('tour-finish') }, steps: [{ target: '#incident', - content: 'Zoom in as much as possible to the location where the incident occurred and click on the blue line of the trip to mark a or incident' + content: $translate.instant('post-trip-map-display-tour-incident') }] }; diff --git a/www/js/intro.js b/www/js/intro.js index bfe778dff..43da7f16d 100644 --- a/www/js/intro.js +++ b/www/js/intro.js @@ -19,7 +19,27 @@ angular.module('emission.intro', ['emission.splash.startprefs', }) .controller('IntroCtrl', function($scope, $state, $ionicSlideBoxDelegate, - $ionicPopup, $ionicHistory, ionicToast, $timeout, CommHelper, StartPrefs) { + $ionicPopup, $ionicHistory, ionicToast, $timeout, CommHelper, StartPrefs, $translate, $cordovaFile) { + + $scope.getConsentFile = function () { + var lang = $translate.use(); + $scope.consentFile = "templates/intro/consent.html"; + if (lang != 'en') { + var url = "www/i18n/intro/consent-" + lang + ".html"; + $cordovaFile.checkFile(cordova.file.applicationDirectory, url).then( function(result){ + window.Logger.log(window.Logger.LEVEL_DEBUG, + "Successfully found the consent file, result is " + JSON.stringify(result)); + $scope.consentFile = url.replace("www/", ""); + }, function (err) { + window.Logger.log(window.Logger.LEVEL_DEBUG, + "Consent file not found, loading english version, error is " + JSON.stringify(err)); + $scope.consentFile = "templates/intro/consent.html"; + }); + } + } + + $scope.getConsentFile(); + $scope.getIntroBox = function() { return $ionicSlideBoxDelegate.$getByHandle('intro-box'); }; diff --git a/www/js/main.js b/www/js/main.js index a542bfbe7..d0f95ffb9 100644 --- a/www/js/main.js +++ b/www/js/main.js @@ -146,10 +146,13 @@ angular.module('emission.main', ['emission.main.recent', } }) -.controller('MainCtrl', function($scope, $state, $rootScope) { +.controller('MainCtrl', function($scope, $state, $rootScope, $translate) { // Currently this is blank since it is basically a placeholder for the // three screens. But we can totally add hooks here if we want. It is the // controller for all the screens because none of them do anything for now. + + moment.locale($translate.use()); + $scope.tabsCustomClass = function() { return "tabs-icon-top tabs-custom"; } diff --git a/www/js/metrics-factory.js b/www/js/metrics-factory.js index 8d9116eff..554f41e6b 100644 --- a/www/js/metrics-factory.js +++ b/www/js/metrics-factory.js @@ -5,37 +5,68 @@ angular.module('emission.main.metrics.factory', ['emission.plugin.kvstore']) .factory('FootprintHelper', function() { var fh = {}; var footprint = { - train: 92/1609, - car: 287/1609, - ON_FOOT: 0, - BICYCLING: 0 - } - var readable = function(v) { - return v > 9999? Math.round(v / 1000) + 'k kg CO₂' : Math.round(v) + ' kg CO₂'; + WALKING: 0, + BICYCLING: 0, + CAR: 267/1609, + BUS: 278/1609, + TRAIN: 92/1609, + AIR_OR_HSR: 217/1609 } var mtokm = function(v) { return v / 1000; } - fh.getFootprintRaw = function(distance, mode) { - if (mode === "IN_VEHICLE") { - return [footprint.train * mtokm(distance), footprint.car * mtokm(distance)]; - } else { - return footprint[mode] * mtokm(distance); + + fh.readableFormat = function(v) { + return v > 999? Math.round(v / 1000) + 'k kg CO₂' : Math.round(v) + ' kg CO₂'; + } + fh.getFootprintForMetrics = function(userMetrics) { + var result = 0; + for (var i in userMetrics) { + var mode = userMetrics[i].key; + if (mode == 'ON_FOOT') { + mode = 'WALKING'; + } + if (mode in footprint) { + result += footprint[mode] * mtokm(userMetrics[i].values); + } + else if (mode == 'IN_VEHICLE') { + result += ((footprint['CAR'] + footprint['BUS'] + footprint['TRAIN']) / 3) * mtokm(userMetrics[i].values); + } + else { + console.warn('WARNING FootprintHelper.getFootprintFromMetrics() was requested for an unknown mode: ' + mode + " metrics JSON: " + JSON.stringify(userMetrics)); + } + } + return result; + } + fh.getLowestFootprintForDistance = function(distance) { + // Find the mode with the lowest carbon footprint (excluding non-motorized modes). + // Another option would be to pre-calculate and store this value only once. + var lowestFootprint = Number.MAX_VALUE; + for (var mode in footprint) { + if (mode == 'WALKING' || mode == 'BICYCLING') { + // these modes aren't considered when determining the lowest carbon footprint + } + else { + lowestFootprint = Math.min(lowestFootprint, footprint[mode]); + } } + return lowestFootprint * mtokm(distance); } - fh.getFootprint = function(distance, mode) { - if (mode === "IN_VEHICLE") { - return readable(footprint.train * mtokm(distance)) + ' ~ ' + readable(footprint.car * mtokm(distance)); - } else { - return readable(footprint[mode] * mtokm(distance)); + fh.getHighestFootprintForDistance = function(distance) { + // Find the mode with the highest carbon footprint. + // Another option would be to pre-calculate and store this value only once. + var highestFootprint = 0; + for (var mode in footprint) { + highestFootprint = Math.max(highestFootprint, footprint[mode]); } + return highestFootprint * mtokm(distance); } return fh; }) .factory('CalorieCal', function(KVStore){ - var cc = {}; + var cc = {}; var USER_DATA_KEY = "user-data"; cc.set = function(info) { @@ -51,8 +82,12 @@ angular.module('emission.main.metrics.factory', ['emission.plugin.kvstore']) return this >= min && this <= max; }; cc.getMet = function(mode, speed) { + if (mode == 'ON_FOOT') { + console.log("CalorieCal.getMet() converted 'ON_FOOT' to 'WALKING'"); + mode = 'WALKING'; + } if (!standardMETs[mode]) { - console.log("Illegal mode"); + console.log("CalorieCal.getMet() Illegal mode: " + mode); return 0; //So the calorie sum does not break with wrong return type } for (var i in standardMETs[mode]) { @@ -88,7 +123,7 @@ angular.module('emission.main.metrics.factory', ['emission.plugin.kvstore']) return weightInKg * durationInMin * met; } var standardMETs = { - "ON_FOOT": { + "WALKING": { "VERY_SLOW": { range: [0, 2.0], mets: 2.0 @@ -130,12 +165,6 @@ angular.module('emission.main.metrics.factory', ['emission.plugin.kvstore']) mets: 9.8 } }, - "IN_VEHICLE": { - "ALL": { - range: [0, Number.MAX_VALUE], - mets: 0 - } - }, "BICYCLING": { "VERY_VERY_SLOW": { range: [0, 5.5], @@ -165,6 +194,36 @@ angular.module('emission.main.metrics.factory', ['emission.plugin.kvstore']) range: [20, Number.MAX_VALUE], mets: 15.8 } + }, + "IN_VEHICLE": { + "ALL": { + range: [0, Number.MAX_VALUE], + mets: 0 + } + }, + "CAR": { + "ALL": { + range: [0, Number.MAX_VALUE], + mets: 0 + } + }, + "BUS": { + "ALL": { + range: [0, Number.MAX_VALUE], + mets: 0 + } + }, + "TRAIN": { + "ALL": { + range: [0, Number.MAX_VALUE], + mets: 0 + } + }, + "AIR_OR_HSR": { + "ALL": { + range: [0, Number.MAX_VALUE], + mets: 0 + } } } return cc; diff --git a/www/js/metrics.js b/www/js/metrics.js index 2431e02ec..01d9c2742 100644 --- a/www/js/metrics.js +++ b/www/js/metrics.js @@ -6,14 +6,15 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date CommHelper, $window, $ionicPopup, ionicDatePicker, $ionicPlatform, FootprintHelper, CalorieCal, $ionicModal, $timeout, KVStore, - $rootScope, $location, $state, ReferHelper, $http, Logger) { + $rootScope, $location, $state, ReferHelper, $http, Logger, + $translate) { var lastTwoWeeksQuery = true; var first = true; var lastWeekCalories = 0; var lastWeekCarbon = "0 kg CO₂"; var twoWeeksAgoCarbon = ""; - var lastWeekCarbonInt = []; - var twoWeeksAgoCarbonInt = []; + var lastWeekCarbonInt = 0; + var twoWeeksAgoCarbonInt = 0; var twoWeeksAgoCalories = 0; var DURATION = "duration"; @@ -24,7 +25,7 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.onCurrentTrip = function() { window.cordova.plugins.BEMDataCollection.getState().then(function(result) { Logger.log("Current trip state" + JSON.stringify(result)); - if(JSON.stringify(result) == "\"STATE_ONGOING_TRIP\""|| + if(JSON.stringify(result) == "\"STATE_ONGOING_TRIP\""|| JSON.stringify(result) == "\"local.state.ongoing_trip\"") { $state.go("root.main.current"); } @@ -266,6 +267,7 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date bottom: 40, left: 55 }, + noData: $translate.instant('metrics.chart-no-data'), showControls: false, showValues: true, stacked: false, @@ -287,7 +289,7 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date xAxis: { axisLabelDistance: 3, - axisLabel: 'Date', + axisLabel: $translate.instant('metrics.chart-xaxis-date'), tickFormat: function(d) { var day = new Date(d * 1000) day.setDate(day.getDate()+1) // Had to add a day to match date with data @@ -297,7 +299,7 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date staggerLabels: true }, yAxis: { - axisLabel: "Number", + axisLabel: $translate.instant('metrics.trips-yaxis-number'), axisLabelDistance: -10 }, callback: function(chart) { @@ -426,7 +428,7 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date var getMetrics = function() { $ionicLoading.show({ - template: 'Loading...' + template: $translate.instant('loading') }); if(!first){ $scope.uictrl.current = "Custom"; @@ -444,15 +446,15 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.caloriesData.aggrCalories = 0; $scope.caloriesData.lastWeekUserCalories = 0; $scope.caloriesData.changeInPercentage = "0%" - $scope.caloriesData.change = " change"; + $scope.caloriesData.change = $translate.instant('metrics.calorie-data-change'); $scope.carbonData.userCarbon = "0 kg CO₂"; - $scope.carbonData.aggrCarbon = "Calculating..."; + $scope.carbonData.aggrCarbon = $translate.instant('metrics.carbon-data-calculating');; $scope.carbonData.optimalCarbon = "0 kg CO₂"; $scope.carbonData.worstCarbon = "0 kg CO₂"; $scope.carbonData.lastWeekUserCarbon = "0 kg CO₂"; $scope.carbonData.changeInPercentage = "0%"; - $scope.carbonData.change = " change"; + $scope.carbonData.change = $translate.instant('metrics.carbon-data-change'); $scope.summaryData.userSummary = []; $scope.chartDataUser = {}; @@ -512,8 +514,8 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date }) .catch(function(error) { $ionicLoading.hide(); - $scope.carbonData.aggrCarbon = "Unknown"; - $scope.caloriesData.aggrCalories = "Unknown..."; + $scope.carbonData.aggrCarbon = $translate.instant('metrics.carbon-data-unknown'); + $scope.caloriesData.aggrCalories = $translate.instant('metrics.calorie-data-unknown'); Logger.displayError("Error loading aggregate data, averages not available", error); }); @@ -644,11 +646,11 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date if (isValidNumber(calorieCalculation)) { $scope.caloriesData.changeInPercentage = calorieCalculation + "%"; if(lastWeekCalories > twoWeeksAgoCalories){ - $scope.caloriesData.change = " increase over a week"; + $scope.caloriesData.change = $translate.instant('metrics.calorie-data-change-increase'); $scope.caloriesUp = true; $scope.caloriesDown = false; } else { - $scope.caloriesData.change = " decrease over a week" + $scope.caloriesData.change = $translate.instant('metrics.calorie-data-change-decrease'); $scope.caloriesUp = false; $scope.caloriesDown = true; } @@ -692,77 +694,75 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.fillFootprintCardUserVals = function(userDistance, twoWeeksAgoDistance) { if (userDistance) { var userCarbonData = getSummaryDataRaw(userDistance, 'distance'); + var optimalDistance = getOptimalFootprintDistance(userDistance); - var worstDistance = getWorstFootprintDistance(userDistance); + var worstDistance = getWorstFootprintDistance(userDistance); var date1 = $scope.selectCtrl.fromDateTimestamp; var date2 = $scope.selectCtrl.toDateTimestamp; var duration = moment.duration(date2.diff(date1)); var days = duration.asDays(); - //$scope.ca2020 = 43.771628 / 5 * days; // kg/day + $scope.carbonData.ca2035 = Math.round(40.142892 / 5 * days) + ' kg CO₂'; // kg/day $scope.carbonData.ca2050 = Math.round(8.28565 / 5 * days) + ' kg CO₂'; - //$scope.carbonData.userCarbon = []; - for (var i in userCarbonData) { - //$scope.carbonData.userCarbon.push({key: userCarbonData[i].key, values: FootprintHelper.getFootprint(userCarbonData[i].values, userCarbonData[i].key)}); - if (userCarbonData[i].key === "IN_VEHICLE") { - $scope.carbonData.userCarbon = FootprintHelper.getFootprint(userCarbonData[i].values, userCarbonData[i].key); - $scope.carbonData.optimalCarbon = FootprintHelper.getFootprint(optimalDistance, userCarbonData[i].key); - $scope.carbonData.worstCarbon = FootprintHelper.getFootprint(worstDistance, userCarbonData[i].key); - lastWeekCarbonInt = FootprintHelper.getFootprintRaw(userCarbonData[i].values, userCarbonData[i].key); - } - } + + $scope.carbonData.userCarbon = FootprintHelper.readableFormat(FootprintHelper.getFootprintForMetrics(userCarbonData)); + $scope.carbonData.optimalCarbon = FootprintHelper.readableFormat(FootprintHelper.getLowestFootprintForDistance(optimalDistance)); + $scope.carbonData.worstCarbon = FootprintHelper.readableFormat(FootprintHelper.getHighestFootprintForDistance(worstDistance)); + lastWeekCarbonInt = FootprintHelper.getFootprintForMetrics(userCarbonData); } if (first) { if (twoWeeksAgoDistance) { - var userCarbonData = getSummaryDataRaw(twoWeeksAgoDistance, 'distance'); - for (var i in userCarbonData) { - if (userCarbonData[i].key === "IN_VEHICLE") { - twoWeeksAgoCarbon = FootprintHelper.getFootprint(userCarbonData[i].values, userCarbonData[i].key); - twoWeeksAgoCarbonInt = FootprintHelper.getFootprintRaw(userCarbonData[i].values, userCarbonData[i].key); - if(first){ - lastWeekCarbon = twoWeeksAgoCarbon; - } - $scope.carbonData.lastWeekUserCarbon = lastWeekCarbon; - } - } + var userCarbonDataTwoWeeks = getSummaryDataRaw(twoWeeksAgoDistance, 'distance'); + twoWeeksAgoCarbon = 0; + twoWeeksAgoCarbonInt = 0; + + twoWeeksAgoCarbonInt = FootprintHelper.getFootprintForMetrics(userCarbonDataTwoWeeks); + + twoWeeksAgoCarbon = FootprintHelper.readableFormat(twoWeeksAgoCarbonInt); + lastWeekCarbon = twoWeeksAgoCarbon; } } + $scope.carbonData.lastWeekUserCarbon = lastWeekCarbon; var change = ""; - console.log("Running calculation with " - + (lastWeekCarbonInt[0] + lastWeekCarbonInt[1]) - + " and " - + (twoWeeksAgoCarbonInt[0] + twoWeeksAgoCarbonInt[1])) - var calculation = (((lastWeekCarbonInt[0] + lastWeekCarbonInt[1]) / 2) - / ((twoWeeksAgoCarbonInt[0] + twoWeeksAgoCarbonInt[1]) / 2)) - * 100 - 100; + console.log("Running calculation with " + lastWeekCarbonInt + " and " + twoWeeksAgoCarbonInt); + var calculation = (lastWeekCarbonInt/twoWeeksAgoCarbonInt) * 100 - 100; // TODO: Refactor this so that we can filter out bad values ahead of time // instead of having to work around it here if (isValidNumber(calculation)) { - if(lastWeekCarbonInt[0] > twoWeeksAgoCarbonInt[0]){ - $scope.carbonData.change = " increase over a week"; - $scope.carbonUp = true; - $scope.carbonDown = false; - } else { - $scope.carbonData.change = " decrease over a week" - $scope.carbonUp = false; - $scope.carbonDown = true; - } - $scope.carbonData.changeInPercentage = Math.abs(Math.round(calculation)) + "%" + if(lastWeekCarbonInt > twoWeeksAgoCarbonInt){ + $scope.carbonData.change = $translate.instant('metrics.carbon-data-change-increase'); + $scope.carbonUp = true; + $scope.carbonDown = false; + } else { + $scope.carbonData.change = $translate.instant('metrics.carbon-data-change-decrease'); + $scope.carbonUp = false; + $scope.carbonDown = true; + } + $scope.carbonData.changeInPercentage = Math.abs(Math.round(calculation)) + "%" + } + else { + $scope.carbonData.change = ""; + $scope.carbonData.changeInPercentage = "0%"; } }; $scope.fillFootprintAggVals = function(aggDistance) { if (aggDistance) { var aggrCarbonData = getAvgSummaryDataRaw(aggDistance, 'distance'); + + // Issue 422: + // https://github.com/e-mission/e-mission-docs/issues/422 for (var i in aggrCarbonData) { - if (aggrCarbonData[i].key === "IN_VEHICLE") { - $scope.carbonData.aggrVehicleRange = FootprintHelper.getFootprintRaw(aggrCarbonData[i].values, aggrCarbonData[i].key); - $scope.carbonData.aggrCarbon = FootprintHelper.getFootprint(aggrCarbonData[i].values, aggrCarbonData[i].key); + if (isNaN(aggrCarbonData[i].values)) { + console.warn("WARNING fillFootprintAggVals(): value is NaN for mode " + aggrCarbonData[i].key + ", changing to 0"); + aggrCarbonData[i].values = 0; } } + + $scope.carbonData.aggrCarbon = FootprintHelper.readableFormat(FootprintHelper.getFootprintForMetrics(aggrCarbonData)); } }; @@ -772,7 +772,7 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.data.duration = getDataFromMetrics(agg_metrics.duration); $scope.data.speed = getDataFromMetrics(agg_metrics.speed); $scope.countOptions = angular.copy($scope.options) - $scope.countOptions.chart.yAxis.axisLabel = 'Number'; + $scope.countOptions.chart.yAxis.axisLabel = $translate.instant('metrics.trips-yaxis-number'); $scope.distanceOptions = angular.copy($scope.options) $scope.distanceOptions.chart.yAxis.axisLabel = 'm'; $scope.durationOptions = angular.copy($scope.options) @@ -781,16 +781,16 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.speedOptions.chart.yAxis.axisLabel = 'm/sec' }; $scope.pandaFreqOptions = [ - {text: "DAILY", value: 'D'}, - {text: "WEEKLY", value: 'W'}, - {text: "BIWEEKLY", value: '2W'}, - {text: "MONTHLY", value: 'M'}, - {text: "YEARLY", value: 'A'} + {text: $translate.instant('metrics.pandafreqoptions-daily'), value: 'D'}, + {text: $translate.instant('metrics.pandafreqoptions-weekly'), value: 'W'}, + {text: $translate.instant('metrics.pandafreqoptions-biweekly'), value: '2W'}, + {text: $translate.instant('metrics.pandafreqoptions-monthly'), value: 'M'}, + {text: $translate.instant('metrics.pandafreqoptions-yearly'), value: 'A'} ]; $scope.freqOptions = [ - {text: "DAILY", value:'DAILY'}, - {text: "MONTHLY", value: 'MONTHLY'}, - {text: "YEARLY", value: 'YEARLY'} + {text: $translate.instant('metrics.freqoptions-daily'), value:'DAILY'}, + {text: $translate.instant('metrics.freqoptions-monthly'), value: 'MONTHLY'}, + {text: $translate.instant('metrics.freqoptions-yearly'), value: 'YEARLY'} ]; var getAvgDataFromMetrics = function(metrics) { var mode_bins = {}; @@ -847,6 +847,7 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date } return rtn; } + var getSummaryDataRaw = function(metrics, metric) { var data = getDataFromMetrics(metrics); for (var i = 0; i < data.length; i++) { @@ -863,15 +864,18 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date } return data; } + /*var sortNumber = function(a,b) { return a - b; }*/ + var getOptimalFootprintDistance = function(metrics){ var data = getDataFromMetrics(metrics); var distance = 0; var longTrip = 5000; + // total distance for long trips using motorized vehicles for(var i = 0; i < data.length; i++) { - if(data[i].key == "IN_VEHICLE") { + if(data[i].key == "CAR" || data[i].key == "BUS" || data[i].key == "TRAIN" || data[i].key == "AIR_OR_HSR") { for(var j = 0; j < data[i].values.length; j++){ if(data[i].values[j][1] >= longTrip){ distance += data[i].values[j][1]; @@ -961,19 +965,19 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.changeWeekday = function(stringSetFunction, target) { var weekdayOptions = [ - {text: "All", value: null}, - {text: "Monday", value: 0}, - {text: "Tuesday", value: 1}, - {text: "Wednesday", value: 2}, - {text: "Thursday", value: 3}, - {text: "Friday", value: 4}, - {text: "Saturday", value: 5}, - {text: "Sunday", value: 6} + {text: $translate.instant('weekdays-all'), value: null}, + {text: moment.weekdays(1), value: 0}, + {text: moment.weekdays(2), value: 1}, + {text: moment.weekdays(3), value: 2}, + {text: moment.weekdays(4), value: 3}, + {text: moment.weekdays(5), value: 4}, + {text: moment.weekdays(6), value: 5}, + {text: moment.weekdays(0), value: 6} ]; $ionicActionSheet.show({ buttons: weekdayOptions, - titleText: "Select day of the week", - cancelText: "Cancel", + titleText: $translate.instant('weekdays-select'), + cancelText: $translate.instant('metrics.cancel'), buttonClicked: function(index, button) { stringSetFunction(button.text); if (target === 'from') { @@ -990,8 +994,8 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.changeFreq = function() { $ionicActionSheet.show({ buttons: $scope.freqOptions, - titleText: "Select summary freqency", - cancelText: "Cancel", + titleText: $translate.instant('metrics.select-frequency'), + cancelText: $translate.instant('metrics.cancel'), buttonClicked: function(index, button) { $scope.selectCtrl.freqString = button.text; $scope.selectCtrl.freq = button.value; @@ -1003,8 +1007,8 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.changePandaFreq = function() { $ionicActionSheet.show({ buttons: $scope.pandaFreqOptions, - titleText: "Select summary freqency", - cancelText: "Cancel", + titleText: $translate.instant('metrics.select-pandafrequency'), + cancelText: $translate.instant('metrics.cancel'), buttonClicked: function(index, button) { $scope.selectCtrl.pandaFreqString = button.text; $scope.selectCtrl.pandaFreq = button.value; @@ -1027,9 +1031,9 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date var now = moment().utc(); var weekAgoFromNow = moment().utc().subtract(7, 'd'); $scope.selectCtrl.freq = 'DAILY'; - $scope.selectCtrl.freqString = "DAILY"; + $scope.selectCtrl.freqString = $translate.instant('metrics.freqoptions-daily'); $scope.selectCtrl.pandaFreq = 'D'; - $scope.selectCtrl.pandaFreqString = "DAILY"; + $scope.selectCtrl.pandaFreqString = $translate.instant('metrics.pandafreqoptions-daily'); // local_date saved as localdate $scope.selectCtrl.fromDateLocalDate = moment2Localdate(weekAgoFromNow); $scope.selectCtrl.toDateLocalDate = moment2Localdate(now); @@ -1037,8 +1041,8 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.selectCtrl.fromDateTimestamp= weekAgoFromNow; $scope.selectCtrl.toDateTimestamp = now; - $scope.selectCtrl.fromDateWeekdayString = "All" - $scope.selectCtrl.toDateWeekdayString = "All" + $scope.selectCtrl.fromDateWeekdayString = $translate.instant('weekdays-all'); + $scope.selectCtrl.toDateWeekdayString = $translate.instant('weekdays-all'); $scope.selectCtrl.fromDateWeekdayValue = null; $scope.selectCtrl.toDateWeekdayValue = null; @@ -1062,6 +1066,7 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.modeIcon = function(key) { var icons = {"BICYCLING":"ion-android-bicycle", "ON_FOOT":" ion-android-walk", + "WALKING":" ion-android-walk", "IN_VEHICLE":"ion-speedometer", "CAR":"ion-android-car", "BUS":"ion-android-bus", @@ -1104,9 +1109,9 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date title: '', scope: $scope, buttons: [ - { text: 'Cancel' }, + { text: $translate.instant('metrics.cancel') }, { - text: 'Confirm', + text: ''+ $translate.instant('metrics.confirm') +'', type: 'button-positive', onTap: function(e) { if (!($scope.userData.gender != -1 && $scope.userData.age && $scope.userData.weight && $scope.userData.height)) { @@ -1123,34 +1128,36 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date $scope.datepickerObjFrom = { callback: $scope.setCurDayFrom, inputDate: $scope.selectCtrl.fromDateTimestamp.toDate(), - setLabel: 'Set', - todayLabel: 'Today', - closeLabel: 'Close', + todayLabel: $translate.instant('list-datepicker-today'), //Optional + closeLabel: $translate.instant('list-datepicker-close'), //Optional + setLabel: $translate.instant('list-datepicker-set'), //Optional + titleLabel: $translate.instant('metrics.pick-a-date'), mondayFirst: false, - weeksList: ["S", "M", "T", "W", "T", "F", "S"], - monthsList: ["Jan", "Feb", "March", "April", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"], + weeksList: moment.weekdaysMin(), + monthsList: moment.monthsShort(), templateType: 'popup', from: new Date(2015, 1, 1), to: new Date(), showTodayButton: true, - dateFormat: 'MMM dd yyyy', + dateFormat: 'dd/MM/yyyy', closeOnSelect: false, - disableWeekdays: [6] + // add this instruction if you want to exclude a particular weekday, e.g. Saturday disableWeekdays: [6] }; $scope.datepickerObjTo = { callback: $scope.setCurDayTo, inputDate: $scope.selectCtrl.toDateTimestamp.toDate(), - setLabel: 'Set', - todayLabel: 'Today', - closeLabel: 'Close', + todayLabel: $translate.instant('list-datepicker-today'), //Optional + closeLabel: $translate.instant('list-datepicker-close'), //Optional + setLabel: $translate.instant('list-datepicker-set'), //Optional + titleLabel: $translate.instant('metrics.pick-a-date'), mondayFirst: false, - weeksList: ["S", "M", "T", "W", "T", "F", "S"], - monthsList: ["Jan", "Feb", "March", "April", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"], + weeksList: moment.weekdaysMin(), + monthsList: moment.monthsShort(), templateType: 'popup', from: new Date(2015, 1, 1), to: new Date(), showTodayButton: true, - dateFormat: 'MMM dd yyyy', + dateFormat: 'dd/MM/yyyy', closeOnSelect: false, // add this instruction if you want to exclude a particular weekday, e.g. Saturday disableWeekdays: [6] }; @@ -1192,5 +1199,5 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date return ($scope.expandedc)? "expanded-calorie-card" : "small-calorie-card"; } - + }); diff --git a/www/js/recent.js b/www/js/recent.js index db2bc3bc0..a3af09464 100644 --- a/www/js/recent.js +++ b/www/js/recent.js @@ -81,7 +81,7 @@ angular.module('emission.main.recent', ['ngCordova', 'emission.services']) $scope.refreshEntries(); }) -.controller('sensedDataCtrl', function($scope, $cordovaEmailComposer, $ionicActionSheet) { +.controller('sensedDataCtrl', function($scope, $cordovaEmailComposer, $ionicActionSheet, $translate) { var currentStart = 0; /* Let's keep a reference to the database for convenience */ @@ -109,7 +109,7 @@ angular.module('emission.main.recent', ['ngCordova', 'emission.services']) $cordovaEmailComposer.isAvailable().then(function() { // is available }, function () { - alert("Email account is not configured, cannot send email"); + alert($translate.instant('recent.email-account-not-configured')); return; }); @@ -117,7 +117,7 @@ angular.module('emission.main.recent', ['ngCordova', 'emission.services']) parentDir = "app://databases"; } if (ionic.Platform.isIOS()) { - alert("You must have the mail app on your phone configured with an email address. Otherwise, this won't work"); + alert($translate.instant('recent.email-account-mail-app')); parentDir = cordova.file.dataDirectory+"../LocalDatabase"; } @@ -125,7 +125,7 @@ angular.module('emission.main.recent', ['ngCordova', 'emission.services']) window.Logger.log(window.Logger.LEVEL_INFO, "Going to export logs to "+parentDir); */ - alert("Going to email database from "+parentDir+"/userCacheDB"); + alert($translate.instant('recent.going-to-email', {parentDir: parentDirs})); var email = { to: ['shankari@eecs.berkeley.edu'], diff --git a/www/js/splash/updatecheck.js b/www/js/splash/updatecheck.js index d7604ffa8..f788f1499 100644 --- a/www/js/splash/updatecheck.js +++ b/www/js/splash/updatecheck.js @@ -3,7 +3,7 @@ angular.module('emission.splash.updatecheck', ['emission.plugin.logger', 'emission.plugin.kvstore']) -.factory('UpdateCheck', function($ionicPopup, $ionicPlatform, $rootScope, $window, Logger, KVStore) { +.factory('UpdateCheck', function($ionicPopup, $ionicPlatform, $rootScope, $window, Logger, KVStore, $translate) { var uc = {}; var CHANNEL_KEY = 'deploy_channel'; @@ -122,7 +122,7 @@ angular.module('emission.splash.updatecheck', ['emission.plugin.logger', } $rootScope.progress = 0; var downloadPop = $ionicPopup.show({ - title: "Downloading UI-only update", + title: $translate.instant('updatecheck.downloading-update'), template: '', scope: $rootScope, buttons: [] @@ -132,7 +132,7 @@ angular.module('emission.splash.updatecheck', ['emission.plugin.logger', downloadPop.close(); // alert("download -> extract"); var extractPop = $ionicPopup.show({ - title: "Extracting UI-only update", + title: $translate.instant('updatecheck.extracting-update'), template: '', scope: $rootScope, buttons: [] @@ -142,7 +142,7 @@ angular.module('emission.splash.updatecheck', ['emission.plugin.logger', // alert("extract -> reload"); Logger.log('Ionic Deploy: Update Success! ' + res); var reloadAlert = $ionicPopup.alert({ - title: "Update done, reloading..." + title: $translate.instant('updatecheck.done') }); reloadAlert.then(function(res) { uc.redirectPromise(); @@ -172,14 +172,14 @@ angular.module('emission.splash.updatecheck', ['emission.plugin.logger', Logger.log('Ionic Deploy: found update, asking user: '); $ionicPopup.show({ - title: "Download new UI-only update?", + title: $translate.instant('updatecheck.download-new-ui'), templateUrl: 'templates/splash/release-notes.html', scope: $rootScope, buttons: [{ // Array[Object] (optional). Buttons to place in the popup footer. - text: 'Not now', + text: $translate.instant('updatecheck.download-not-now'), type: 'button-default', }, { - text: 'Apply', + text: $translate.instant('updatecheck.download-apply'), type: 'button-positive', onTap: function(e) { return true; diff --git a/www/js/tripconfirm/post-trip-map-display.js b/www/js/tripconfirm/post-trip-map-display.js index e363617e8..f0bebb486 100644 --- a/www/js/tripconfirm/post-trip-map-display.js +++ b/www/js/tripconfirm/post-trip-map-display.js @@ -10,7 +10,8 @@ angular.module('emission.tripconfirm.posttrip.map',['ui-leaflet', 'ng-walkthroug $stateParams, $ionicLoading, leafletData, leafletMapEvents, nzTour, KVStore, Logger, DiaryHelper, ConfirmHelper, Config, - UnifiedDataLoader, $ionicSlideBoxDelegate, $ionicPopup) { + UnifiedDataLoader, $ionicSlideBoxDelegate, $ionicPopup, + $translate) { Logger.log("controller PostTripMapDisplay called with params = "+ JSON.stringify($stateParams)); var MODE_CONFIRM_KEY = "manual/mode_confirm"; @@ -74,7 +75,7 @@ angular.module('emission.tripconfirm.posttrip.map',['ui-leaflet', 'ng-walkthroug }; Logger.log("About to query buffer for "+JSON.stringify(tq)); $ionicLoading.show({ - template: 'Loading...' + template: $translate.instant('loading') }); UnifiedDataLoader.getUnifiedSensorDataForInterval(LOC_KEY, tq) .then(function(resultList) { diff --git a/www/js/tripconfirm/post-trip-prompt.js b/www/js/tripconfirm/post-trip-prompt.js index 1df0c8429..4131e4819 100644 --- a/www/js/tripconfirm/post-trip-prompt.js +++ b/www/js/tripconfirm/post-trip-prompt.js @@ -2,7 +2,7 @@ angular.module('emission.tripconfirm.posttrip.prompt', ['emission.plugin.logger']) .factory("PostTripAutoPrompt", function($window, $ionicPlatform, $rootScope, $state, - $ionicPopup, Logger) { + $ionicPopup, Logger, $translate) { var ptap = {}; var REPORT = 737678; // REPORT on the phone keypad var TRIP_CONFIRM_TEXT = 'TRIP_CONFIRM'; @@ -10,12 +10,12 @@ angular.module('emission.tripconfirm.posttrip.prompt', ['emission.plugin.logger' var reportMessage = function(platform) { var platformSpecificMessage = { - "ios": "Swipe left or tap to add information about this trip.", - "android": "See options or tap to add information about this trip." + "ios": $translate.instant('post-trip-prompt.platform-specific-message-ios'), + "android": $translate.instant('post-trip-prompt.platform-specific-message-android') }; var selMessage = platformSpecificMessage[platform]; if (!angular.isDefined(selMessage)) { - selMessage = "Tap to add information about this trip."; + selMessage = $translate.instant('post-trip-prompt.platform-specific-message-other'); } return selMessage; }; @@ -23,21 +23,21 @@ angular.module('emission.tripconfirm.posttrip.prompt', ['emission.plugin.logger' var getTripEndReportNotification = function() { var actions = [{ identifier: 'MUTE', - title: 'Mute', + title: $translate.instant('post-trip-prompt.notification-option-mute'), icon: 'res://ic_moreoptions', activationMode: 'background', destructive: false, authenticationRequired: false }, { identifier: 'SNOOZE', - title: 'Snooze', + title: $translate.instant('post-trip-prompt.notification-option-snooze'), icon: 'res://ic_moreoptions', activationMode: 'background', destructive: false, authenticationRequired: false }, { identifier: 'CHOOSE', - title: 'Choose', + title: $translate.instant('post-trip-prompt.notification-option-choose'), icon: 'res://ic_signin', activationMode: 'foreground', destructive: false, @@ -46,7 +46,7 @@ angular.module('emission.tripconfirm.posttrip.prompt', ['emission.plugin.logger' var reportNotifyConfig = { id: REPORT, - title: "How and why did you come here?", + title: $translate.instant('post-trip-prompt.notification-title'), text: reportMessage(ionic.Platform.platform()), icon: 'file://img/icon.png', smallIcon: 'res://ic_mood_question.png', @@ -93,14 +93,14 @@ angular.module('emission.tripconfirm.posttrip.prompt', ['emission.plugin.logger' scope: newScope, template: "{{getFormattedTime(start_ts)}} -> {{getFormattedTime(end_ts)}}", buttons: [{ - text: 'Choose Mode', + text: $translate.instant('post-trip-prompt.choose-mode'), type: 'button-positive', onTap: function(e) { // e.preventDefault() will stop the popup from closing when tapped. return true; } }, { - text: 'Skip', + text: $translate.instant('post-trip-prompt.skip'), type: 'button-positive', onTap: function(e) { return false; @@ -149,8 +149,8 @@ angular.module('emission.tripconfirm.posttrip.prompt', ['emission.plugin.logger' $window.cordova.plugins.notification.local.schedule([after_30_mins_prompt]); if ($ionicPlatform.is('android')) { $ionicPopup.alert({ - title: "Snoozed reminder", - template: "Will reappear in 30 mins" + title: $translate.instant('post-trip-prompt.snoozed-reminder'), + template: $translate.instant('post-trip-prompt.snoozed-reapper-message') }); } } else if (data.identifier === 'MUTE') { @@ -161,23 +161,23 @@ angular.module('emission.tripconfirm.posttrip.prompt', ['emission.plugin.logger' if ($ionicPlatform.is('ios')) { $window.cordova.plugins.notification.local.schedule([{ id: REPORT, - title: "Notifications for TRIP_END incident report muted", - text: "Can be re-enabled from the Profile -> Developer Zone screen. Select to re-enable now, clear to ignore", + title: $translate.instant('post-trip-prompt.notifications-muted'), + text: $translate.instant('post-trip-prompt.notifications-reenabled'), at: _1_min_from_now, data: {redirectTo: "root.main.control"} }]); } else if ($ionicPlatform.is('android')) { $ionicPopup.show({ - title: "Muted", - template: "Notifications for TRIP_END incident report muted", + title: $translate.instant('post-trip-prompt.muted'), + template: $translate.instant('post-trip-prompt.notifications-muted'), buttons: [{ - text: 'Unmute', + text: $translate.instant('post-trip-prompt.unmute'), type: 'button-positive', onTap: function(e) { return true; } }, { - text: 'Keep muted', + text: $translate.instant('post-trip-prompt.keep-muted'), type: 'button-positive', onTap: function(e) { return false; diff --git a/www/js/tripconfirm/trip-confirm-services.js b/www/js/tripconfirm/trip-confirm-services.js index d155a495b..b82e69055 100644 --- a/www/js/tripconfirm/trip-confirm-services.js +++ b/www/js/tripconfirm/trip-confirm-services.js @@ -1,5 +1,5 @@ angular.module('emission.tripconfirm.services', ['ionic', "emission.plugin.logger"]) -.factory("ConfirmHelper", function($http, $ionicPopup, Logger) { +.factory("ConfirmHelper", function($http, $ionicPopup, $translate, Logger) { var ch = {}; ch.otherModes = []; ch.otherPurposes = []; @@ -12,19 +12,28 @@ angular.module('emission.tripconfirm.services', ['ionic', "emission.plugin.logge ch.purposeOptions = confirmConfig.data.purposeOptions; } - var loadAndPopulateOptions = function(filename) { - return $http.get(filename) + var loadAndPopulateOptions = function (lang) { + if (lang != "en") { + return $http.get("i18n/trip_confirm_options-" + lang + ".json") + .then(fillInOptions) + .catch(function (err) { + console.log("error "+JSON.stringify(err)+" while reading confirm options in your language, reverting to english options"); + return loadAndPopulateOptions("en"); + }); + } + + return $http.get("json/trip_confirm_options.json") + .then(fillInOptions) + .catch(function(err) { + // no prompt here since we have a fallback + console.log("error "+JSON.stringify(err)+" while reading confirm options, reverting to defaults"); + return $http.get("json/trip_confirm_options.json.sample") .then(fillInOptions) .catch(function(err) { - // no prompt here since we have a fallback - console.log("error "+JSON.stringify(err)+" while reading confirm options, reverting to defaults"); - return $http.get(filename+".sample") - .then(fillInOptions) - .catch(function(err) { - // prompt here since we don't have a fallback - Logger.displayError("Error while reading default confirm options", err); - }); + // prompt here since we don't have a fallback + Logger.displayError("Error while reading default confirm options", err); }); + }); } /* @@ -34,8 +43,9 @@ angular.module('emission.tripconfirm.services', ['ionic', "emission.plugin.logge */ ch.getModeOptions = function() { if (!angular.isDefined(ch.modeOptions)) { - return loadAndPopulateOptions("json/trip_confirm_options.json") - .then(function() { return ch.modeOptions; }); + var lang = $translate.use(); + return loadAndPopulateOptions(lang) + .then(function () { return ch.modeOptions; }); } else { return Promise.resolve(ch.modeOptions); } @@ -43,8 +53,9 @@ angular.module('emission.tripconfirm.services', ['ionic', "emission.plugin.logge ch.getPurposeOptions = function() { if (!angular.isDefined(ch.purposeOptions)) { - return loadAndPopulateOptions("json/trip_confirm_options.json") - .then(function() { return ch.purposeOptions; }); + var lang = $translate.use(); + return loadAndPopulateOptions(lang) + .then(function () { return ch.purposeOptions; }); } else { return Promise.resolve(ch.purposeOptions); } @@ -53,17 +64,17 @@ angular.module('emission.tripconfirm.services', ['ionic', "emission.plugin.logge ch.checkOtherOption = function(choice, onTapFn, $scope) { if(choice.value == 'other_mode' || choice.value == 'other_purpose') { var text = choice.value == 'other_mode' ? "mode" : "purpose"; - $ionicPopup.show({title: "Please fill in the " + text + " not listed.", + $ionicPopup.show({title: $translate.instant("trip-confirm.services-please-fill-in",{text: text}), scope: $scope, template: '', buttons: [ - { text: 'Cancel', + { text: $translate.instant('trip-confirm.services-cancel'), onTap: function(e) { $scope.selected.mode = ''; $scope.selected.purpose = ''; } }, { - text: 'Save', + text: '' + $translate.instant('trip-confirm.services-save') + '', type: 'button-positive', onTap: onTapFn($scope, choice) } diff --git a/www/templates/caloriePopup.html b/www/templates/caloriePopup.html index f22d740a4..c35e839fa 100644 --- a/www/templates/caloriePopup.html +++ b/www/templates/caloriePopup.html @@ -1,10 +1,10 @@ -
Gender
+
{{'user-gender'}}
-
Male
-
Female
+
{{'gender-male'}}
+
{{'gender-female'}}
-
Height
+
{{'user-height'}}
@@ -12,7 +12,7 @@
ft
-
Weight
+
{{'user-weight'}}
@@ -20,5 +20,5 @@
lb
-
Age
+
{{'user-age'}}
diff --git a/www/templates/common/map.html b/www/templates/common/map.html index 4a14f2bb9..df10b2fdb 100644 --- a/www/templates/common/map.html +++ b/www/templates/common/map.html @@ -8,9 +8,9 @@ -->
Refresh
+ ng-click="refreshMap()" translate>{{'map-refresh'}}
Fix Map
+ ng-click="refreshTiles()" translate>{{'map-fixmap'}}
+
{{settings.auth.email}}
-
Tracking
+
{{'.tracking'}}