I believe I had a brief discussion of this somewhere but cannot for the life of me locate the original discussion...

I'm working on a user registration page for a site that supports multiple languages. The project is built using CodeIgniter 3 and I'm working hard to honor Model-View-Controller pattern. According to the OWASP Recommendations for authentication, it's considered 'best practice' to provide users with real time feedback as to whether the password they are choosing is valid. This gets complicated for a few reasons:

1) I need to provide language strings to my Javascript
I need to feed these language strings into my view which will in turn make them available to my Javascript. I would prefer to have these strings defined somewhere in the <head></head> region of my HTML output where I would also like to define my JS that attaches onChange() events to my form inputs when I'm sure they have been defined.

2) I really want to avoid defining JS or HTML in my controllers.
CI's view implementation doesn't really lend itself all that well to nesting of complex interface elements into some other element. One typically loads views in sequence:

// a controller method
public function foo() {
  // collect $data here somehow
  $this->load->view("header"); // contains opening <body> tag, probably <head>, also HTML to define site-wide menu
  $this->load->view("some_page", $data); // data for the current page. E.g., product info or a user profile or an article or something
  $this->load->view("footer"); // maybe some google analytics stuff or something, closing </body> tag, etc.
}

If I want to get JS defined between the <head>...<head> tags in the header view, I have to either break up my header template, drastically complicating my view organization, or I must define my JS in a $variable in my controller -- this is bad form as I understand things. Shouldn't be writing PHP to generate HTML or JS strings. The sequential loading of views in this way also doesn't really seem to jibe all that well with the nested/hierarchical structure of HTML. I.e., we are often loading one view to open an element (e.g., <html>, <body>, possibly a large <div> or <form> and another, separate, view to close the HTML element with its </html> counterpart.

3) I'd prefer not to define a bunch of redundant Javascript files that are intermingled with language-specific data
While I can certainly imagine creating a bunch of JS files that define only prompts for each language so that I can separate language out from the regex logic to check one's password, it seems like poor organization to me to have these prompts intermingled with HTML and/or JS. This sort of intermingling typically makes translation work difficult because your translators need JS and/or HTML chops and awareness to avoid breaking things.

Bottom line is that it seems optimal to get my language strings from my database or from some data store and NOT have it intermingled with a bunch of HTML or JS. It also seems like I'll need to inject complex JS/HTML into my views or risk drastically complicating my view organization (and controllers) if I have to redesign these views to break them up further. The most maddening part about this will be situations where I have some opening <HTML_ELEMENT> tag in one view with its corresponding </HTML_ELEMENT> tag in another.

Can anyone offer a shrewd approach to managing views that eases this issue? I've looked at the Output class but can't really tell from the docs how I might use it to load a view and capture its contents after they are merged with data. I can also imagine an approach using DOM but that seems like it might be complicated too.

Any help or suggestions would be greatly appreciated!

    If you only have one message per input (i.e. you're being generic and saying the password doesn't meet the requirements) instead of being specific about the error with the field (e.g. "Passwords must be between 8 and 24 characters and contain at least one number, one uppercase letter, and one symbol"), then you can just output the translated text into a hidden element and only show it when the error is triggered.

    If you want to have specific errors, you'll have to hold all error messages in a javascript object and insert the correct one per the error triggered. The problem with this approach is that it almost requires you to either create a new view to be inserted into the <head>. A sample javascript object of what would be done is:

    <script type="text/javascript">var LANG = {
        EMPTY_FIELD = <?php echo json_encode(lang('error_empty_field')); ?>,
        PASSWORD_MIN_REQs = <?php echo json_encode(lang('error_password_minimum_requirements')); ?>,
        DUPLICATE_EMAIL = <?php echo json_encode(lang('error_duplicate_email')); ?>,
        GENERAL_ERROR = <?php echo json_encode(lang('error_general')); ?>
    };</script>

    Then in your error checking javascript, instead of just inserting your message, you just reference the variable that has been translated by CI into the language of the user.

    <script type="text/javascript">
    // ... Sample only, error checking logic simplified
    if (/* field_x error is empty */) {
        document.getElementById('field_x_error_msg').innerHTML = LANG.EMPTY_FIELD;
    } else if (/* field_x error is not meeting minimum requirements */) {
        document.getElementById('field_x_error_msg').innerHTML = LANG.PASSWORD_MIN_REQS;
    } else {
        document.getElementById('field_x_error_msg').innerHTML = LANG.GENERAL_ERROR;
    }

      Thanks for that! This looks like a pretty tidy approach.

      The trick, of course, is getting the JS inserted into another view or I run the risk of enormously complicated view management problem. I also want to avoid a bunch of PHP in my controller that generates HTML or JS strings by concatening strings, etc.

        Alternatively, you could load objects containing localized strings by loading js files according to what language is used simply by mapping urls properly. E.g. regardless of what format you use to determine output language

        LANG.example.com
        example.com/LANG/
        

        you can map any src url, for a script element regarding localization data, to its corresponding js file. Given

        <script src="/js/errors/signup.js"></script>
        

        You could map this to

        /docroot/js/errors/LANG/signup.js
        

        You may still wish to keep your localized messages in a db or other centralized storage and then pre-generate these javascript files depending on your needs, such as providing multiple data entry points: the view in question, public rest api, soap... Otherwise it might suffice to have the strings accessible from javascript. Once data hit the model, it will probably suffice to either throw "invalid data" or something like that, or use more detailed messages but only in english. Anyone using SOAP etc should be able to deal with this.

        js elements and where to put them
        I don't know when or where the "must go in the html head element" comes from. Perhaps because it was once easier to see what js files where involved when they all appeared in the head element? Today that information is readily available through the browsers' dev tools. Perhaps for other reasons? But you might just as well let each view include whatever javascript sources they require. I do not know CI, but if you have a view which contains three other views, then the "containing view" should not have to deal with including js sources for nested views. And having to query each view for requirements and stick those elsewhere also seems bad. Also, when loading script sources you may use "defer" attribute to defer the fetch until the rest of the page is done, or "async" to continue constructing the DOM and loading resources in parallel. Should a browser not understand either of those attributes, the script source will simply load in place.

        In case multiple views need access to the same js sources, you'd need to construct a js source loader which lets views register requirements so that the same source is not loaded twice.

          johanafm;11044779 wrote:

          Alternatively, you could load objects containing localized strings by loading js files according to what language is used simply by mapping urls properly. E.g. regardless of what format you use to determine output language

          LANG.example.com
          example.com/LANG/
          

          We are in fact already doing this for our CodeIgniter controllers. We very much want to avoid an enormously complex set of mod_rewrite rules for controllers versus JS versus CSS versus images, etc. Additionally, we want to avoid having largely redundant JS files. Moreover, we would like to have a SINGLE location for our language assets rather than having some in the DB, some in JS files, some in views, etc.

          johanafm;11044779 wrote:

          You may still wish to keep your localized messages in a db or other centralized storage and then pre-generate these javascript files depending on your needs, such as providing multiple data entry points: the view in question, public rest api, soap... Otherwise it might suffice to have the strings accessible from javascript. Once data hit the model, it will probably suffice to either throw "invalid data" or something like that, or use more detailed messages but only in english. Anyone using SOAP etc should be able to deal with this.

          This is a helpful comment and I think I understand what you are saying here but probably not to the full extent you do. I do understand there's not much point in throwing error exceptions in different languages if it's for a SOAP or REST interface -- we can hopefully rely on developers to understand English.

          We do very much want to centralize all localized messages as much as possible. I think the key here is to balance this desire for centralization of language to one place against the complexities that may arrive in trying to do so. Having only one place for all of your language assets has the trade-off that you have to implement channels to get language from that one place (probably a DB or PHP file or data file in our case) to all of the places where it needs to be utilized (HTML, JS, emails, etc).

          johanafm;11044779 wrote:

          js elements and where to put them
          I don't know when or where the "must go in the html head element" comes from. Perhaps because it was once easier to see what js files where involved when they all appeared in the head element? Today that information is readily available through the browsers' dev tools. Perhaps for other reasons? But you might just as well let each view include whatever javascript sources they require.

          This is a pretty interesting point that you make. Off-hand, I was thinking that one must typically wait until form elements are rendered before attaching onChange events to them, but I suppose you can still create an onDomReady anonymous function anywhere in the document? I guess I'm a bit worried that the onDomReady event might be triggered before my onDomReady function gets defined but the more I think about it, the more I think that would be impossible because the onDomReady event cannot possibly fire before the final </body></html> has been parsed....right? Do I have that correctly?

          johanafm;11044779 wrote:

          I do not know CI, but if you have a view which contains three other views, then the "containing view" should not have to deal with including js sources for nested views.

          CI does not support nested views AFAIK. I could be wrong about this. Seems odd to me to define a view that, within its code, calls another view.

          johanafm;11044779 wrote:

          And having to query each view for requirements and stick those elsewhere also seems bad. Also, when loading script sources you may use "defer" attribute to defer the fetch until the rest of the page is done, or "async" to continue constructing the DOM and loading resources in parallel. Should a browser not understand either of those attributes, the script source will simply load in place.

          Ooooooh...defer? async? These are new to me. It is the loading in place that concerns me. More specifically, I'm worried about out-of-sequence code execution or an attempt by a script to try and process page elements which do not yet exist.

          johanafm;11044779 wrote:

          In case multiple views need access to the same js sources, you'd need to construct a js source loader which lets views register requirements so that the same source is not loaded twice.

          Thanks for pointing this out. I don't expect it'll be a problem unless I have views loading other views or get careless somehow.

            7 days later
            sneakyimp;11044785 wrote:

            This is a pretty interesting point that you make. Off-hand, I was thinking that one must typically wait until form elements are rendered before attaching onChange events to them, but I suppose you can still create an onDomReady anonymous function anywhere in the document? I guess I'm a bit worried that the onDomReady event might be triggered before my onDomReady function gets defined but the more I think about it, the more I think that would be impossible because the onDomReady event cannot possibly fire before the final </body></html> has been parsed....right? Do I have that correctly?

            You are correct. What happens is that when the browser starts parsing the page, the first (or one of the first) thing that happens is that a document object and a document node are created. document ready is a special case of readystatechange event for the document (similar to XHR).

            This little example shows what is executed when (see fiddle). Open dev tools console to see console.log output during page processing and after document ready. The fiddle also shows how to attach event handler to a containing node in order to deal with events that originate on elements that are added later on. Do use Chrome or the pure js event handler for clicks on the containing body element may not work properly.

            sneakyimp;11044785 wrote:

            CI does not support nested views AFAIK. I could be wrong about this. Seems odd to me to define a view that, within its code, calls another view.

            I do not know what CI supports. But I find that it makes perfect sense. Let's say you have a Picture view, which shows an image with a title overlay at bottom left. Then you have a Picture Carousel view that shows several such picture views in a carousel selector. Which in turn could be included in a forum signature, user profile information, forum posts. But such a picture carousel could also be viewed on its own to make use of all available window space. Or requested via XHR for inclusion somewhere else.

            sneakyimp;11044785 wrote:

            Ooooooh...defer? async? These are new to me. It is the loading in place that concerns me. More specifically, I'm worried about out-of-sequence code execution or an attempt by a script to try and process page elements which do not yet exist.

            If you need to manipulate the DOM, you have to know that those DOM nodes exist before you do so. But while you do not necessarily have to wait for document ready in order to do so, the only thing that has to be done during initial construction of the page are calls to document.write, because they modify the document to be parsed. Thus, if you chuck two script elements in sequence like this

            <script src="one.js"></script>
            <script src="two.js"></script>
            

            because one.js MAY use document.write, two.js cannot be executed until one.js has finished executing. But if you'd wait until after document ready and instead create two script nodes and append them,

            one = document.createElement('script');
            two = document.createElement('script');
            one.src = "one.js";
            two.src = "two.js";
            document.appendChild(one);
            document.appendChild(two);
            

            there is no longer any guarantee as to which will execute first. The same goes for script elements with defer, because they are executed (and loaded?) after document ready.

              Write a Reply...