3.7.23.7.13.5.010.12.0After migrating from embr-i18n I got a weird issue.
I set up a default locale as follows in application.js route:
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
export default Route.extend(ApplicationRouteMixin, {
currentUser: service(),
intl: service(),
beforeModel() {
let locale = this.figureOutLocale();
this.intl.setLocale(locale);
return this._loadCurrentUser();
},
figureOutLocale() {
let locale = this.calculateLocale(this.intl.get('locales'));
return locale;
},
calculateLocale(locales) {
// whatever you do to pick a locale for the user:
const language = navigator.languages[0] || navigator.language || navigator.userLanguage;
return locales.includes(language.toLowerCase()) ? language : 'en';
}
});
I have just 2 translations files (en.yml and fr.yml) and it seems to work as needed in all the the views exept the one using Constants service that I defined as follows:
#services/constants.js
import Service from '@ember/service';
import { inject as service } from '@ember/service';
export default Service.extend({
intl: service('intl'),
init() {
this._super(...arguments);
console.log('IN CONSTANTS service locale: ' + this.intl.get('locale'));
this.set('states',
[
{id: 'open', name: this.intl.t('states.open')},
{id: 'closed', name: this.intl.t('states.closed')},
{id: 'divided', name: this.intl.t('states.divided')}
]
);
this.set('days',
[
{day: 'monday', name: this.intl.t('weekdays.monday')},
{day: 'tuesday', name: this.intl.t('weekdays.tuesday')},
{day: 'wednesday', name: this.intl.t('weekdays.wednesday')},
{day: 'thursday', name: this.intl.t('weekdays.thursday')},
{day: 'friday', name: this.intl.t('weekdays.friday')},
{day: 'saturday', name: this.intl.t('weekdays.saturday')},
{day: 'sunday', name: this.intl.t('weekdays.sunday')}
]
);
this.set('categories',
[
'DECATHLON',
'SEASONAL',
'ESSENTIAL',
'WORKSHOP',
'BRAND_SPECIALIST',
'CITY'
]
)
}
});
Weird but, the above console log statement displays a wrong locale:
IN CONSTANTS service locale: en-us
instead of just en or fr(this is the case if I put a debug point in application.js route).
So the translated states values are not displayed in the drop-down lists:

Here is the template code to display the drop-down list:
#templates/components/weekday-row.hbs
<div class="col-sm-2">
<select
class="form-control"
onchange={{action
"selectState"
value="target.value"
}}
>
{{#each states as |state|}}
<option value={{state.id}} selected={{eq state.id weekday.state}}>{{state.name}}</option>
{{/each}}
</select>
</div>
And here is how I initialize the states in the component JS file:
#components/weekday-row.js
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
export default Component.extend({
intl: service('intl'),
constants: service(),
weekday: null,
state: null,
states: [],
tagName: '',
init() {
this._super(...arguments);
this.set('states', this.get('constants.states'));
},
...
What am I missing here ? Thank you.
@belgoros services/constants.js is a singleton and as such are instantiated when they are created (init called when this.intl(....). In this scenario, you have a something getting the constants service earlier than beforeModel is called during the application lifecycle. So you are seeing the default en-us before you set it.
What would be recommended is to do any logic (hopefully not just setting a primitive value in which case just access it via this.intl) you need to do in your constants service in a defined method (say this.contants.setIntlConstant(...)) and call that method in any lifecycle hook in application.js (beforeModel, afterModel, etc).
@snewcomer Thanks a lot for your response and explanation. But I'm setting all the translations in init method of constants.js. I believed that having a method setIntlService, for example as you suggested in constants.js and call it from beforeModel hook in application.js route would not change anything:
#services/constants.js
export default Service.extend({
init() {
this._super(...arguments);
console.log('IN CONSTANTS service locale: ' + this.intl.get('locale'));
this.set('states',
[
{id: 'open', name: this.intl.t('states.open')},
{id: 'closed', name: this.intl.t('states.closed')},
{id: 'divided', name: this.intl.t('states.divided')}
]
);
...
// other translations setting as before
setIntlLocale(locale) {
console.log('+++ #setIntlLocale :' + locale);
this.intl.setLocale(locale);
}
});
in application.js route:
beforeModel() {
let locale = this.figureOutLocale();
this.intl.setLocale(locale);
this.constants.setIntlLocale(locale);
return this._loadCurrentUser();
},
I still have no clues with that idea 馃槩
Doing like that still fails because init method in constants.js is called before its setIntlLocale(locale):

Yep and that makes perfect sense! Essentially you are at the whim of the application lifecycle and as such, no way to access the correct locale in init of the constants service (well there is, but could be flakey). Could you maybe describe your situation that necessitates this?
@snewcomer With pleasure 馃槃. I have to display a page with working hours for a shop. So I display all the 7 weekdays (Monday to Sunday), with a state (open, closed, divided), and hours.
To do that, I have constants.js service defined as follows:
#services/constants.js
import Service from '@ember/service';
import { inject as service } from '@ember/service';
export default Service.extend({
i18n: service(),
init() {
this._super(...arguments);
this.set('states',
[
{id: 'open', name: this.get('i18n').t('states.open')},
{id: 'closed', name: this.get('i18n').t('states.closed')},
{id: 'divided', name: this.get('i18n').t('states.divided')}
]
);
this.set('days',
[
{day: 'monday', name: this.get('i18n').t('weekdays.monday')},
{day: 'tuesday', name: this.get('i18n').t('weekdays.tuesday')},
{day: 'wednesday', name: this.get('i18n').t('weekdays.wednesday')},
{day: 'thursday', name: this.get('i18n').t('weekdays.thursday')},
{day: 'friday', name: this.get('i18n').t('weekdays.friday')},
{day: 'saturday', name: this.get('i18n').t('weekdays.saturday')},
{day: 'sunday', name: this.get('i18n').t('weekdays.sunday')}
]
);
this.set('categories',
[
'DECATHLON',
'SEASONAL',
'ESSENTIAL',
'WORKSHOP',
'BRAND_SPECIALIST',
'CITY'
]
)
}
});
I used ember-i18n before and it worked, i18nhas been replaced by running the migrator provided by DockYard for that purposes.
To display every weekday row, I'm using a component - weekday-row:
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
export default Component.extend({
i18n: service('i18n'),
constants: service(),
weekday: null,
state: null,
states: [],
tagName: '',
init() {
this._super(...arguments);
this.states = this.get('constants.states');
},
translatedWeekDay: computed('weekday.day', function() {
let key = 'weekdays.' + this.get('weekday.day');
return this.get('i18n').t(key);
}),
isClosed: computed('weekday.state', function() {
return this.get('weekday').get('state') === 'closed';
}),
isDivided: computed('weekday.state', function() {
return this.get('weekday').get('state') === 'divided';
}),
actions: {
selectState(state) {
this.get('weekday').set('state', state);
this.get('weekday').set('opens', null);
this.get('weekday').set('closes', null);
}
}
});
And here is the corresponding component template, weekday-row.hbs:
<label for="state" class="col-sm-2 col-form-label">{{translatedWeekDay}}</label>
<div class="col-sm-2">
<select
class="form-control"
onchange={{action
"selectState"
value="target.value"
}}
>
{{#each states as |state|}}
<option value={{state.id}} selected={{eq state.id weekday.state}}>{{state.name}}</option>
{{/each}}
</select>
</div>
...
I omit the rest of the content, dislpay the states being the thre reason of the problem.
So, as you see, the problem is to init/translate weekdays and theirs states.
Thank you !
Thanks to the help I got from Discord (special thanks to @Lux and @Windivs), I got it working by using a computed property:
import Service from '@ember/service';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
export default Service.extend({
intl: service(),
...
states: computed('intl.locale', function(){
return [
{id: 'open', name: this.intl.t('states.open')},
{id: 'closed', name: this.intl.t('states.closed')},
{id: 'divided', name: this.intl.t('states.divided')}
];
}),
days: computed('intl.locale', function(){
return [
{day: 'monday', name: this.get('intl').t('weekdays.monday')},
{day: 'tuesday', name: this.get('intl').t('weekdays.tuesday')},
{day: 'wednesday', name: this.get('intl').t('weekdays.wednesday')},
{day: 'thursday', name: this.get('intl').t('weekdays.thursday')},
{day: 'friday', name: this.get('intl').t('weekdays.friday')},
{day: 'saturday', name: this.get('intl').t('weekdays.saturday')},
{day: 'sunday', name: this.get('intl').t('weekdays.sunday')}
];
})
});
I set up the locale for intl service in application route as described earlier:
beforeModel() {
let locale = this.figureOutLocale();
this.intl.setLocale(locale);
return this._loadCurrentUser();
},
Hope this helps. Thank you for your time !