i18next + strftime + dateformats

i18next + strftime + dateformats

by Csaba Hellinger

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/i18next/22.0.6/i18next.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.12/dayjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/liquidjs/10.16.0/liquid.browser.min.js"></script>

CSS

body {
  background: #222;
  color: #ccc;
  font-family: monospace;
  line-height: 1.8;
}

JavaScript

// use Liquid as an strftime formatter
const engine = new liquidjs.Liquid()
const strftime = (value, format) => engine.parseAndRenderSync(`{{value | date: '${format}'}}`, { value });

// i18next interpolation formatter for both date formats
const myFormatter = (value, format) => {
  // strftime date format
  const strftimeFormat = format.match(/^datetime\((.*)\)$/i)?.[1];
  if (strftimeFormat) return strftime(value, strftimeFormat)
  // relative date format 
  if (format === 'relativeDate()') {
    // just calculating dates for the demo
  	const days = dayjs().diff(value, 'days');
    const relFormat = new Intl.RelativeTimeFormat('en', { style: 'narrow' });
    return relFormat.format(-days, 'day')
  }  
  // duration format 
  if (format === 'duration()') {
    return [
      Math.floor(value / 3600) || null, 
      String(Math.floor((value % 3600) / 60)).padStart(2,'0'),
      String(value % 60).padStart(2,'0'),
    ].filter(x => x !== null).join(':'); 
  }  
  // fall back to the old format
  return dayjs(value).format(format);
}

// configure i18next, just as it is on web today
i18next.init({
  initImmediate: true,
  lng: 'en',
  nsSeparator: '',
  returnNull: false,
  compatibilityJSON: 'v3',
  interpolation: {
    prefix: '__',
    suffix: '__',
    format: myFormatter
  },
  resources: {
    'en': {
      translation: {
        'date.old': "__-today, YYYY/MM/DD__",        
        'date.new': "__-today, datetime(%Y-%m-%d %H:%M)__",
				'relative': "__-releaseDate, relativeDate()__",
        'duration': "__-videoDuration, duration()__",
      },
    },
  },
  returnedObjectHandler: (key) => key,
});

// tests

const today = new Date();
const translate = (translation, context) => {
  document.body.innerText += `${translation} = ${JSON.stringify(i18next.t(translation, context))}\n`;
}

translate('date.old', { today });
translate('date.new', { today });
translate('relative', { releaseDate: new Date('2024-04-14') });
translate('duration', { videoDuration: 754...