L
L
Laziness of the Romanovs2019-05-20 09:58:58
JSON
Laziness of the Romanovs, 2019-05-20 09:58:58

How to smartly specify a static resource inside a template engine?

I'm building a static website using Gulp .
The site is still static and in the future it is planned to switch to dynamics. How to refer to images, audio, etc. inside the template engine. from one file, so that when you change it, you don’t get into the template code, but fix it there.
Looking towards JSON to specify everything

like this
JSON файлик:
"static" : {
    "images" : {
        "wallpaper": {
             src : "https://example.com/images/img.png",
             alt : "Image PNG"
              },
        "graphicClip" : {
             src : "https://example.com/images/png.png"
             }
          },
      },
   "Text" : {
       English :  "English string",
       Russian : "Russian string"
       }
  }

HTML (или NUNJUCKS)
<img src={{static.images.wallpaper.src}} alt={{static.images.wallpaper.alt}}
<p>{{static.text.English}}</p>
<p>{{static.text.Russian}}</p>
offtopic
Посоветуйте, пожалуйста, рускоязычное сообщество (группы, чаты, форумы) NUNJUCKS, где можно задавать вопросы и поделиться опытом. Если знаете таковых.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
N
ned4ded, 2019-05-20
@1bragimov

Good afternoon! In my projects, I just use such a "handicraft" approach, dividing it into two stages:
1) loading global constants into the nunjucks engine before compiling pages (stored in json);
2) adding a filter for issuing locales (also stored in json, but in a separate folder).
Roughly speaking, these are two ways to implement,
the first is unloading the global constant in nunjucks into the context ( gulp-nunjucks-render , render api );
the second is the use of an entity separate from nunjucks (in my case, an i18next instance without a backend ), in which there is a synchronous process of issuing data on demand through a custom nunjucks filter.
Now, having spent about 8-9 months with such an assembly, I can say that there are a lot of conventions, bugs and performance problems during assembly. And in general, it does not fit into the framework of at least some global concept for organizing code (which will simply complicate support in the future). Probably a better solution is to set up some ready-made static site generator than to write your own build. But if you're wondering where to start, here's the structure and code:

├── datasets
│   └── meta.json
├── locales
│   ├── en
│   │   └── translation.json
│   └── ru
├── tasks
...
└── gulpfile.js

# Это упрощенная структура, 
# но если вам будет что-то непонятно, я напишу подробнее.

html code task for assembling pages
import gulp from 'gulp';
import fs from 'fs';
import path from 'path';
import i18next from 'i18next';
import config from '../gulpfile.config';
import engine from 'gulp-nunjucks-render';
import minify from 'gulp-htmlmin';
import Backend from 'i18next-sync-fs-backend';
import rename from 'gulp-rename';

i18next.use(Backend).init({
  debug: true,
  fallbackLng: ['en'],
  initImmediate: false,
  backend: {
    loadPath: config.paths.locales + '/{{lng}}/{{ns}}.json' // локали собираются по названиям из папки locales
  },
  ns: ['translation'],
  defaultNS: 'translation'
});

export function html(done) {
  const envHooks = [
    env => env.addFilter('__', function(key, ns) { // фильтр на поиск и выдачу информации по ключу в i18next
      if(!i18next.exists(key)) return 0;

      return i18next.t(key);
    }),
  ]

  const data = fs.readdirSync( config.paths.datasets ).reduce( (acc, filename) => {
    return { ...acc, [ path.basename( filename, '.json') ] : require('../' + config.paths.datasets + '/' + filename) };
  }, {});

  data.get = function(name) { 
    return this[name];
  }

  const [ def ] = i18next.options.fallbackLng;

  const rec = (arr) => {

    const [lng, ...rest] = arr;

    return i18next.changeLanguage(lng, (err) => {
      if(err) throw new Error(err);

      return gulp.src(config.paths.pages)
        .pipe(engine({
          data: {
            datasets: data,
          },
          path: ['src/pages/templates'],
          manageEnv: function(env) {
            return envHooks.forEach(fn => fn(env));
          },
        }))
        .pipe(minify({ collapseWhitespace: true }))
        .pipe(rename(function(path) {

          path.basename = lng === def ? path.basename : lng + '.' + path.basename;
        }))
        .pipe(gulp.dest(config.server.dest))
        .on('end', () => {
          return rest.length ? rec(rest) : done();
        });
    });
  }

  rec(['ru', 'en']); // если язык дефолтный, то страницы компилируются обычным образом, если нет - у них будет префикс с названием языка

  return;
};

The code demonstrates 2 approaches at once. In templates it is called in different ways:
1)
<!--
datasets/features.json
[
  {
    "name" : "independent",
    "_descr" : "features.indep.descr"
  },
  {
    "name" : "secure",
    "_descr" : "features.secure.descr",
  },
]
-->

{% set features = datasets.get('features') %}

<ul class="page-home__features">
  {% for f in features %}
   <li>
    {{ feature.make(f) }}
   </li>
  {% endfor %}
</ul>

2)
<!--
 locales/en/translation.json

  "pages" : {
    "home" : {
      "headings" : {
        "qualities": "a perfect solution for projects"
        }
      }
    }
-->

<code lang="html">
<h2 class="text-center font-weight-bold mb-4 mb-lg-6">
  {{ 'pages.home.headings.qualities' | __ }}
  <!-- фильтр зарегистрирован под названием __ -->
</h2>
</code>

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question