Is there a way to translate date placeholders in Javascript? - momentjs

I'm looking for a way to translate the placeholder for a date picker in the input as soon as the user changes the language.
For example, the default (EN) format is dd/mm/yyyy, but if the user changes the language to french, this should be changed to jj/mm/aaaa. Currently I'm using the momentJS library but unfortunately, this doesn't support date translation, only the correct local format.
Is there a library/other way suitable for this?
Thank you

Using the default <input type="date">, the browser UI element is rendered. This is not something that you can directly control. You can add a helper for the browser to display a specific lang, eg <input type="date" lang="en-GB">, as this is a global attribute. That being said, the browsers will implement this differently, and I believe that as it currently stands, most browsers ignore this attribute and instead use the locale currently set by on the browser eg, Intl.DateTimeFormat().resolvedOptions().locale.
Eg:
<label for="datepicker">Select Date:</label>
<input type="date" id="datepicker" lang="fr-CA">
For me (using Chrome), this stays as my current locale (en-GB).
What can I do?
You cannot override your browser locale my javascript, it must be set through the browser config (eg, chrome://settings/languages) or through browser launch settings.
The easiest way to do this effectively would be to use a non browser UI date picker, eg, something like jQuery datepicker:
$(function() {
$("#datepicker").attr('placeholder', $.datepicker.regional["fr"].dateFormat)
$("#datepicker").datepicker($.datepicker.regional["fr"]);
$("#locale").on("change", function() {
const locale = $.datepicker.regional[$(this).val()]
$("#datepicker").attr('placeholder', locale.dateFormat)
$("#datepicker").datepicker("option", locale);
});
});
<link rel="stylesheet" href="//code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
<link rel="stylesheet" href="/resources/demos/style.css">
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
<script src="https://jqueryui.com/resources/demos/datepicker/i18n/datepicker-ar.js"></script>
<script src="https://jqueryui.com/resources/demos/datepicker/i18n/datepicker-fr.js"></script>
<script src="https://jqueryui.com/resources/demos/datepicker/i18n/datepicker-he.js"></script>
<script src="https://jqueryui.com/resources/demos/datepicker/i18n/datepicker-zh-TW.js"></script>
<p>Date: <input type="text" id="datepicker"> 
<select id="locale">
<option value="ar">Arabic (‫(العربية</option>
<option value="zh-TW">Chinese Traditional (繁體中文)</option>
<option value>English</option>
<option value="fr" selected="selected">French (Français)</option>
<option value="he">Hebrew (‫(עברית</option>
</select>
</p>
But even this is not perfect. I'm sure that are a lot better date pickers out there.

I'm having the exact same problem as you (see How do I get the localized date format for a given locale?).
I have yet to find a library that features date placeholder mappings as part of their locale data. As of now, I need to maintain a mapping for the languages I support in my application.
const datePlaceholderLocaleData = {
fr: { year: 'a', month: 'm', day: 'j' },
en: { year: 'y', month: 'm', day: 'd' }
}
I'll keep you informed if I find a better solution.

Try this Javascript snippet I left the part of language code change but I am pretty sure it will help however you set the langCode. I tried several langCode all of them worked.
const langcodes = ["es", "ca", "en" ,"nl", "fr", "de"];
Use this Array and map over if to generate langCode for testing purposes.
const myDate = new Date();
const langCode = document.documentElement.lang || navigator.language;
console.log(langCode)
const formatter = new Intl.DateTimeFormat(langCode);
console.log(formatter.format(myDate))

The toLocaleDateString() method returns a string with a language-sensitive representation of the date portion of the specified date in the user agent's timezone.

Related

Handlebars: inherited/implicit parameters

Is there an easy way how to pass a "configuration" parameter to all embedded partials in handlebars?
In my case, I would like to set language by the page layout partial and would like all the embedded partials to have access to it, something like this:
#layout-en.hbs:
SOMEHOW SETTING LANG PARAMETER TO "EN"
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{title}}</title>
</head>
<body>
<p>Top of all English pages</p>
{{{content}}}
<p>Footer of all English pages</p>
</body>
</html>
#layout-fr.hbs - analogous
some-page-en.hbs:
{{#> #layout_en title="Title in English"}}
some content in English
{{#> demo}}
some more content in English
{{/#layout_en}}
some-page-fr.hbs:
{{#> #layout_fr title="Title in French"}}
some content in French
{{#> demo}}
some more content in French
{{/#layout_fr}}
Is it possible for demo to return a button with a different text when included on a page using layout_en and different when on a page using layout_fr?
Or should this be done completely differently?
It's possible to set a variable that is accessible to all views and partials that are rendered in the response. I am doubtful that such a variable could be set from a partial and, even if it could, I would recommend against it because I think it would make the application more difficult to troubleshoot if global variables were being set in views.
Instead, I think a better approach would be to set such a variable in your controller logic.
You have not mentioned what Node web server framework you are using, but I am going to assume you are using express with the express-handlebars package.
The express API provides a locals object on the response (res), to which variables can be attached and made available to all rendered templates.
A simple example of setting a global lang variable within our express handlers looks like:
app.get("/en", (req, res) => {
res.locals.lang = "en";
res.render("some-page-en");
});
app.get("/fr", (req, res) => {
res.locals.lang = "fr";
res.render("some-page-fr");
});
This will allow us to use {{lang}} from within any rendered view/layout/partial and the corresponding value will be rendered.
The problem with this approach is that it does allow for simple conditions within our template of the sort lang === "fr" ? "French content" : "English content". This is because Handlebars does not ship with a way to perform such a conditional check. A custom helper could help, however, if your application will only support the two languages - English and French - then we could replace our lang string variable with a boolean one - ex., isFrench:
app.get("/en", (req, res) => {
res.locals.isFrench = false;
res.render("some-page-en");
});
app.get("/fr", (req, res) => {
res.locals.isFrench = true;
res.render("some-page-fr");
});
As a boolean, this variable can be used with a Handlebars #if helper. The demo partial could then look something like:
<button type="button">
{{#if isFrench}}
Cliquez ici
{{else}}
Click here
{{/if}}
</button>
Additionally, I would recommend using a similar pattern within a single layout file instead of potentially having an English and a French layout with mostly duplicated HTML.
Additional note:
{{#> demo}} on its own is not valid Handlebars syntax. The #> is for rendering Partial Blocks and those must have an accompanying closing tag: {{/demo}}. A regular (non-Block) partial would be rendered with {{> demo}}.

jQuery Datepicker displays wrong language until interection with the calendar

In a totally custom Wordpress page, written as a PHP snippet using Woody Snippets plugin, I need to use a jQuery datepicker to select a date.
I imported the necessary files correctly, and also called and set up the datepicker correctly. Yesterday, I attached the datepicker to a hidden input field, so that when I clicked on an icon, the calendar would float in place, and everything was fine, even the localization, although I hadn't taken any measures for the localization of the calendar (importing the i18n files etc)...
Today, I had to change the page's layout a bit, so I had to make the calendar show from the beginning (inline). According to the demo page, in order to put the datepicker inline, all you have to do is attach it to a div instead of an input field.
The datepicker indeed turned into an inline element, but the localization went away... The initial language is English, until I interact in any way with the calendar (go back or forth a month, or pick a date, etc). Once I do that, the localization turns into my site's language!
What's more crazy is that this situation continued even after I specifically imported and set up the localization.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js" integrity="sha512-uto9mlQzrs59VwILcLiRYeLKPPbS/bT71da/OEBYEwcdNUk8jYIy+D176RYoop1Da+f9mvkYrmj5MCLZWEtQuA==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/i18n/jquery-ui-i18n.min.js" integrity="sha512-zZ27MiE6yuwkKbHnJ/7ATQF/7l+Jwk5jSxgmLJ1SS5QJexaYswmP3OKBPDVMfM8TlSOudAKHTWH2UtS+0LDddw==" crossorigin="anonymous"></script>
<script>
$(document).ready(function() {
$('#frontpages-date-filter').datepicker($.extend({},
$.datepicker.regional['el'], {
dateFormat: 'yymmdd',
minDate: new Date({$min['y']}, {$min['m']}, {$min['d']}),
maxDate: new Date({$max['y']}, {$max['m']}, {$max['d']}),
onSelect: function(dateText) {
window.location.href = location.protocol + '//' + location.host + location.pathname + '?date=' + dateText;
}
}
));
});
</script>
Exactly the same code works fine right from the start in the following JSFiddle
From what I've searched so far, it most likely has to do with WP v4.6+ where localization is automatically added to jQuery, but no matter what I've tried, I haven't been able to make the inline datepicker load localized without having to interact with it in any way!
BTW, I had noticed the exact same behavior, and even had recorded a short video for the developers of ACF, thinking it had to do with their plugin, but they responded it wasn't due to their plugin, so as it was in the Admin side, I didn't bother losing time to debug it further. But this time, the page is in the public side, so it has to be fixed!
Any help will be very much appreciated. Thanks in advance.
This must have been a conflict in my site. TBH, I wasn't able to spot the conflict, but I was able to circumvent it by setting the necessary lang strings just before datepicker's initiation like so:
$(function() {
$.datepicker.regional['el'] = {
closeText: 'Κλείσιμο',
prevText: 'Προηγούμενος',
nextText: 'Επόμενος',
currentText: 'Σήμερα',
monthNames: ['Ιανουάριος', 'Φεβρουάριος', 'Μάρτιος', 'Απρίλιος', 'Μάιος', 'Ιούνιος', 'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος', 'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'],
monthNamesShort: ['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μαι', 'Ιουν', 'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'],
dayNames: ['Κυριακή', 'Δευτέρα', 'Τρίτη', 'Τετάρτη', 'Πέμπτη', 'Παρασκευή', 'Σάββατο'],
dayNamesShort: ['Κυρ', 'Δευ', 'Τρι', 'Τετ', 'Πεμ', 'Παρ', 'Σαβ'],
dayNamesMin: ['Κ', 'Δ', 'Τ', 'Τ', 'Π', 'Π', 'Σ'],
weekHeader: 'Εβδ',
dateFormat: 'dd/mm/yy',
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ''
};
$.datepicker.setDefaults($.datepicker.regional['el']);
$('#frontpages-date-filter').datepicker();
});

Isolating select2 version doesn't work

I'm using Select2 on my WordPress plugin and I need to isolate the version I load in order to avoid conflicts with other plugins also using Select2.
I have found this answer by #Kevin Brown where he proposes to use save the select2 function into a variable just after being loaded and before removing it to avoid issues with other loads:
<script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.js"></script>
<script>
var myOwnSelect2 = $.fn.select2;
delete $.fn.select2;
</script>
The problem I have is that it works in edit-post.php pages, but not anywhere else. I mean, "select" tags/elements are being replaced on edit-post.php pages but not at my plugin settings page. The code on each page looks like this:
edit-post.php
HTML
<div class="myplugin-metabox">
...
<select name="_myplugin_item" class="searchable select2-hidden-accessible" data-placeholder="Select an item..." tabindex="-1" aria-hidden="true">
<option>...</option>
</select>
...
</div>
Javascript
myOwnSelect2.call( $('.myplugin-metabox select'), { dropdownAutoWidth: true, minimumResultsForSearch: Infinity } );
admin.php?page=myplugin
HTML
<div id="myplugin-settings">
...
<select id="option-1" name="myplugin-option-1" class="">
<option value="all">...</option>
...
</select>
...
</div>
Javascript
myOwnSelect2.call( $('#myplugin-settings select'), { dropdownAutoWidth: true, minimumResultsForSearch: Infinity } );
If I don't use any isolation method, everything within my plugin is working just fine... but I need the isolation because many other popular plugins load their own version/copy of Select2.
Javascript initializations are made on the same file. Keeping isolation, if I remove initialization for edit-post.php selects, selects on my plugin settings page doesn't get converted to Select2 dropdowns.
Any suggestion of what I might be doing wrong?
Thanks!
Well, I found the cause of the problem myself. I'm writing it here so it might be of help to others in my situation.
The thing was that I was referencing jQuery as "$", when I should have referenced it as "jQuery". Check out the code below:
Didn't work (in my case):
<script>
var myOwnSelect2 = $.fn.select2;
delete $.fn.select2;
</script>
Does work (in my case):
<script>
var myOwnSelect2 = jQuery.fn.select2;
delete jQuery.fn.select2;
</script>

Ember.js : how to bind a model attribute on a custom component

I would like to integrate a date picker in a form. So I created a custom DateTimePickerView like this :
App.DateTimePickerView = Em.View.extend
templateName: 'datetimepicker'
didInsertElement: ->
self = this
onChangeDate = (ev) ->
self.set "value", moment.utc(ev.date).format("dd/MM/yyyy hh:mm")
#$('.datetimepicker').datetimepicker(language: 'fr', format: 'dd/mm/yyyy hh:ii').on "changeDate", onChangeDate
Here is the template :
<script type="text/x-handlebars" data-template-name="datetimepicker" >
<input type="text" class="datetimepicker" readonly>
</script>
In my form I want to bind this component to an attribute of my model (I am using the RestAdapter) :
<script type="text/x-handlebars" id="post/_edit">
<p>{{view Ember.TextField valueBinding='title'}}</p>
<p>{{view App.DateTimePickerView valueBinding='date'}}</p>
</script>
Everything works fine in apparence : the DateTimePicker is well displayed and the value is set inside the input.
But there is a problem in the effective binding : when I send the form, the post param "date" (corresponding to the attribute) is null.
When I look inside the generated html code I can see the following :
<p>
<input id="ember393" class="ember-view ember-text-field" type="text" value="Event 1">
</p>
<div id="ember403" class="ember-view">
<input type="text" class="datetimepicker" readonly="">
</div>
I am not an expert in the global ember structure, but I guess that the id element is important for the binding. In that case, for my component, the ember id is put to the container of my component and not to the input containing the value. So I guess the problem is here.
So what could be the correct way to make it work ?
I just created a working jsfiddle here ; we can see that the modifications in the title field are taken into account but not the modifications in the DateTimePickerView component.
I guess the problem lies in the fact that you where trying to listen on an event fired from the datetimepicker which is not captured, and thus the model value not set.
To make things more solid you should get the datetimepicker current date value in your doneEditing function, just before saving the model back to the store.
Let me show in code what I mean:
window.App.EventController = Ember.ObjectController.extend(
...
doneEditing: ->
// relevant code line
#get("model").set("date_begin", $('.datetimepicker').data('date'))
#set "isEditing", false
#get("store").commit()
...
)
And here your (working) jsfiddle.
Hope it helps
Edit
After reading your comment I've modified the input field inside your datetimepicker template. Please see here an updated jsfiddle that also initializes the input field of the datetimepicker on edit begin when calling edit.
...
edit: ->
#set "isEditing", true
startDate = #get("model").get("date_begin")
#$(".datetimepicker").data({date: startDate}).datetimepicker("update")
...
You are now safe to remove the onDateChange function and do init and save inside your edit and doneEditing respectively, applying format or whatever.
Edit 2
Reading your last comment, this is how you register customEvents for example in your App.DateTimePickerView:
...
customEvents: {
changedate: "changeDate"
}
...
this way Ember will be aware of your custom events. You can register whatever events you want but notice that the keyname is lowercased and the value must have the event name to listen to camelcased. For more infos on custom events see here.
Please see here for another update jsfiddle with the changeDate custom event registered.
I have finally resolved this problem making some controls when using the moment.js library.
Everything was working fine with the binding process of the custom datetimepickerview.
Here is a working jsfiddle : here
The relevant code is here :
window.App.DateTimePickerView = Ember.View.extend(
templateName: 'datetimepicker'
didInsertElement: ->
#this test is important and was the problem
if #.get 'value'
#$('input').val moment.utc(#.get 'value').format('LLL')
onChangeDate = (ev) =>
date = moment.utc(ev.date).format('LLL')
#.set "value", date
#$('.datetimepicker').datetimepicker(format: 'dd/MM/yyyy', pickTime: false).on "changeDate", onChangeDate
)

Knockout.js "options" binding not updating in my Durandal app

I'm building a Durandal app, and the view I'm currently working on has two <select> boxes. I've got both of them bound to a ko.observableArray and their value to another ko.observable as follows:
<select data-bind="options: dateOptions, optionsText: 'display', value: selectedDate></select>
<select data-bind="options: buyerOptions, optionsText: 'display', value: slectedBuyer"></select>
The second one is dependent on the value of the first one, so I'm populating them at different times. For the first, I'm querying my data source during the activate() call and then passing the data to a separate method to populate the array the data (in the form of simple JS objects) when the promise returned by the request is resolved:
var populateDateOptions = function(dates) {
$.each(dates, function() {
dateOptions.push({
display: dateToString(this.pbDateOpt),
date: this.pbDateOpt
});
});
};
That works fine - the <select> has values ready for me when the view is rendered. But, after that, I can't get either <select> to respond to changes in their respective observable arrays. The next <select> is populated in a nearly-identical fashion once a value is selected in the first <select>, and I can verify that the buyerOptions array is indeed being populated, but the <select> doesn't change. I also tried adding a value to the first <select> by pushing an object into its array via dev tools and get the same result: the array is updated, but the UI doesn't change.
I've used the "options" binding in several other apps and never had this issue before, but this is my first Durandal app so I'm wondering if maybe there's something I'm missing?
Thanks!
Update
I added some more bound elements to the view and none of them are working, so there must be something weird going on with the composer. Not sure where to start looking for an issue (the viewmodel and view are very similar in structure to another pair that is working fine). Any tips?
Just as a reference, this isn't a Durandal issue - this is a Knockout issue.
Also, a way that I have found most efficient to do this is the following the same way you have it -
<select data-bind="options: dateOptions, optionsText: 'display', value: selectedDate></select>
<select data-bind="options: buyerOptions, optionsText: 'display', value: selectedBuyer"></select>
but in your view model make the buyerOptions dependent directly on the dateOptions like so -
var buyerOptions = ko.computed(function () {
var opts = ko.observableArray();
if (!selectedDate()) { opts.push(display: '<Select a Date>'); }
else { opts(getBuyersOptions(selectedDate().id()); }
return opts;
});
This way if your selectedDate observable is empty (one hasn't been selected) then no buyerOptions appear, but if you select a date it will populate it based off some value in selectedDate, like Id. It will also automatically update whenever a new date is chosen, without you having to explicitly tell it to with JavaScript or w/e
Turning on Durandal's debug mode by setting system.debug(true) in main.js helped me discover some code errors that weren't presenting themselves via console warnings. With those resolved, everything bound/worked correctly.
Lesson learned - leave debug mode on when you're in development!

Resources