Search code examples
javascriptcssknockout.jsmedia-queriesi18next

How to use knockout and i18next for a specfic screen size?


I have an HTML:

<div class="inter">
    <!-- ko if:emprel_id -->
    <!-- ko i18n:'dependents.editDependent' --><!-- /ko -->
    <!-- /ko -->


    <!-- ko if:emprel_id -->
    <!-- ko i18n:'dependents.DependentInformation' --><!-- /ko -->
    <!-- /ko -->
</div>

The snippets of the JS file "en-CA" is:

"dependents.editDependent": "Edit",
"dependents.DependentInformation": "Dependent Information",

At this moment, at the front-end I am able to see the following in Desktop view: enter image description here

My task is:

  1. After 767 pixels screen size, I want only "Edit" to show up in place of "Edit Dependent Information"

  2. Also, in between 320 pixels and 767 pixels(which is the mobile view), I want only "Dependent Information"

I can achieve this thorugh HTML and CSS but I am wondering how I can achieve it through JS(knockout).


Solution

  • You could use matchMedia to keep an observable state up to date. Then, you can use this state in a computed property that either translates the text, or hides an element from the DOM.

    In this example I'm doing two things: (To test, press Full page and resize)

    • Keep track of a "width state" via observables isSmall, isMedium and isLarge
    • Create a set of computed properties in a viewmodel that take this state into account and update accordingly

    In the code, you can see two approaches:

    1. Translate in viewmodel, based on the width state
    2. Translate in view, hide based on the width state

    The viewmodel holds, in both cases, the logic that transforms from specific state to specific string combination.

    // Keep track of state
    var small = window.matchMedia("(max-width: 320px)");
    var medium = window.matchMedia("(min-width: 321px) and (max-width: 768px)");
    var large = window.matchMedia("(min-width: 769px)");
    
    var isSmall = ko.observable(small.matches);
    var isMedium = ko.observable(medium.matches);
    var isLarge = ko.observable(large.matches);
    
    var onChange = function() {
      isSmall(small.matches);
      isMedium(medium.matches);
      isLarge(large.matches);
    }
    
    small.addListener(onChange);
    medium.addListener(onChange);
    large.addListener(onChange);
    
    // VM
    function ViewModel() {
      // For option 1
      this.translatedLabel = ko.pureComputed(function() {
        if (isSmall()) return i18nextko.t("dependents.editDependent");
        if (isMedium()) return i18nextko.t("dependents.DependentInformation");
        if (isLarge()) return i18nextko.t("dependents.editDependent") +
          " " + i18nextko.t("dependents.DependentInformation");
      }, this);
      
      // For option 2
      this.showLeft = ko.pureComputed(function() {
        return !isMedium();
      }, this);
      this.showRight = ko.pureComputed(function() {
        return !isSmall();
      }, this);
    }
    
    
    
    // Mock
    var i18nextko = {
      t: function(key) {
        switch (key) {
            case "dependents.editDependent":
              return "Edit";
            case "dependents.DependentInformation":
              return "Dependent Information";
        }
      }
    };
    
    ko.bindingHandlers.i18n = {
       init: function(e, valueAccessor) {
         e.innerText = i18nextko.t(valueAccessor());
       },
    }
    
    ko.applyBindings(new ViewModel());
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    
    <h2>Via computed</h2>
    <div data-bind="text: translatedLabel"></div>
    
    <h2>Via visible binding</h2>
    <span data-bind="i18n: 'dependents.editDependent', visible: showLeft"></span>
    <span data-bind="i18n: 'dependents.DependentInformation', visible: showRight"></span>

    I'm curious to know why you'd rather do it in javascript than in HTML + CSS though...