mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-06-28 14:55:48 +00:00
Use Intl.DateTimeFormat instead of momentjs where we can
Need to solve the Intl.RelativeTimeFormat to drop momentjs
This commit is contained in:
@@ -12,13 +12,14 @@ export function format(timeStampInUTC, formatStr) {
|
||||
case 'SHORT': {
|
||||
if (4 >= (now - time) / 3600000)
|
||||
return m.fromNow();
|
||||
const ymd = m.format('Ymd'), date = new Date;
|
||||
if (date.format('Ymd') === ymd)
|
||||
const mt = m.getTime(), date = new Date,
|
||||
dt = date.setHours(0,0,0,0);
|
||||
if (mt > dt)
|
||||
return i18n('MESSAGE_LIST/TODAY_AT', {TIME: m.format('LT')});
|
||||
if (new Date(now - 86400000).format('Ymd') === ymd)
|
||||
if (mt > dt - 86400000)
|
||||
return i18n('MESSAGE_LIST/YESTERDAY_AT', {TIME: m.format('LT')});
|
||||
if (date.getFullYear() === m.getFullYear())
|
||||
return m.format('d M.');
|
||||
return m.format('d M');
|
||||
return m.format('LL');
|
||||
}
|
||||
case 'FULL':
|
||||
|
||||
@@ -132,26 +132,11 @@ export function i18nToNodes(element) {
|
||||
, 1);
|
||||
}
|
||||
|
||||
const reloadData = () => {
|
||||
if (window.rainloopI18N) {
|
||||
I18N_DATA = window.rainloopI18N || {};
|
||||
|
||||
i18nToNodes(doc);
|
||||
|
||||
dispatchEvent(new CustomEvent('reload-time'));
|
||||
trigger(!trigger());
|
||||
}
|
||||
|
||||
window.rainloopI18N = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
export function initNotificationLanguage() {
|
||||
I18N_NOTIFICATION_MAP.forEach((item) => {
|
||||
I18N_NOTIFICATION_DATA[item[0]] = i18n(item[1]);
|
||||
});
|
||||
I18N_NOTIFICATION_MAP.forEach(item => I18N_NOTIFICATION_DATA[item[0]] = i18n(item[1]));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,22 +144,9 @@ export function initNotificationLanguage() {
|
||||
* @param {Function=} langCallback = null
|
||||
*/
|
||||
export function initOnStartOrLangChange(startCallback, langCallback = null) {
|
||||
if (startCallback) {
|
||||
startCallback();
|
||||
}
|
||||
|
||||
if (langCallback) {
|
||||
trigger.subscribe(() => {
|
||||
if (startCallback) {
|
||||
startCallback();
|
||||
}
|
||||
if (langCallback) {
|
||||
langCallback();
|
||||
}
|
||||
});
|
||||
} else if (startCallback) {
|
||||
trigger.subscribe(startCallback);
|
||||
}
|
||||
startCallback && startCallback();
|
||||
startCallback && trigger.subscribe(startCallback);
|
||||
langCallback && trigger.subscribe(langCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,11 +223,8 @@ export function reload(admin, language) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return fetch(langLink(language, admin), {cache: 'reload'})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
const type = response.headers.get('Content-Type');
|
||||
if (type.includes('application/javascript')) {
|
||||
return response.text();
|
||||
}
|
||||
if (response.ok && response.headers.get('Content-Type').includes('application/javascript')) {
|
||||
return response.text();
|
||||
}
|
||||
reject(new Error('Invalid response'))
|
||||
}, error => {
|
||||
@@ -267,15 +236,14 @@ export function reload(admin, language) {
|
||||
doc.head.appendChild(script).remove();
|
||||
setTimeout(
|
||||
() => {
|
||||
reloadData();
|
||||
|
||||
const isRtl = ['ar', 'ar_sa', 'he', 'he_he', 'ur', 'ur_ir'].includes((language || '').toLowerCase()),
|
||||
htmlCL = doc.documentElement.classList;
|
||||
|
||||
htmlCL.remove('rl-rtl', 'rl-ltr');
|
||||
htmlCL.add(isRtl ? 'rl-rtl' : 'rl-ltr');
|
||||
// doc.documentElement.dir = isRtl ? 'rtl' : 'ltr'
|
||||
|
||||
// reload the data
|
||||
if (window.rainloopI18N) {
|
||||
I18N_DATA = window.rainloopI18N || {};
|
||||
i18nToNodes(doc);
|
||||
dispatchEvent(new CustomEvent('reload-time'));
|
||||
trigger(!trigger());
|
||||
}
|
||||
window.rainloopI18N = null;
|
||||
resolve();
|
||||
},
|
||||
500 < Date.now() - start ? 1 : 500
|
||||
|
||||
@@ -31,12 +31,8 @@ class SettingsUserScreen extends AbstractSettingsScreen {
|
||||
super([SystemDropDownSettingsUserView, MenuSettingsUserView, PaneSettingsUserView]);
|
||||
|
||||
initOnStartOrLangChange(
|
||||
() => {
|
||||
this.sSettingsTitle = i18n('TITLES/SETTINGS');
|
||||
},
|
||||
() => {
|
||||
this.setSettingsTitle();
|
||||
}
|
||||
() => this.sSettingsTitle = i18n('TITLES/SETTINGS'),
|
||||
() => this.setSettingsTitle()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
200
dev/prototype.js
vendored
200
dev/prototype.js
vendored
@@ -8,28 +8,62 @@
|
||||
|
||||
// Import momentjs locales function
|
||||
w.moment = {
|
||||
defineLocale: (name, config)=>{
|
||||
locale = config;
|
||||
const m = config.monthsShort;
|
||||
if (Array.isArray(m)) {
|
||||
Date.shortMonths = m;
|
||||
} else for (let i = 0; i < 12; ++i) {
|
||||
Date.shortMonths[i] = config.monthsShort({month:()=>i}, '-MMM-');
|
||||
}
|
||||
Date.longMonths = config.months,
|
||||
Date.longDays = config.weekdays;
|
||||
Date.shortDays = config.weekdaysMin;
|
||||
}
|
||||
defineLocale: (name, config) => locale = config
|
||||
};
|
||||
|
||||
let locale = {
|
||||
longDateFormat: {
|
||||
LT : 'h:mm A', // 'g:i A',
|
||||
L : 'MM/DD/YYYY', // 'Y-m-d',
|
||||
LL : 'MMMM D, YYYY', // 'F j, Y'
|
||||
LLL : 'MMMM D, YYYY h:mm A', // 'F j, Y g:i A'
|
||||
LLLL : 'dddd, MMMM D, YYYY h:mm A' // 'l, F j, Y g:i A'
|
||||
},
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat
|
||||
let formats = {
|
||||
LT : {hour: 'numeric', minute: 'numeric'},
|
||||
L : {},
|
||||
LL : {year: 'numeric', month: 'short', day: 'numeric'},
|
||||
LLL : {year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric'},
|
||||
LLLL : {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric'},
|
||||
'd M': {day: '2-digit', month: 'short'}
|
||||
},
|
||||
phpFormats = {
|
||||
// Day
|
||||
d: {day: '2-digit'},
|
||||
D: {weekday: 'short'},
|
||||
j: {day: 'numeric'},
|
||||
l: {weekday: 'long'},
|
||||
// N: {},
|
||||
// w: {},
|
||||
// z: {},
|
||||
// Week
|
||||
// W: {},
|
||||
// Month
|
||||
F: {month: 'long'},
|
||||
m: {month: '2-digit'},
|
||||
M: {month: 'short'},
|
||||
n: {month: 'numeric'},
|
||||
// t: {},
|
||||
// Year
|
||||
// L: {},
|
||||
// o: {},
|
||||
Y: {year: 'numeric'},
|
||||
y: {year: '2-digit'},
|
||||
// Time
|
||||
a: {hour12: true},
|
||||
A: {hour12: true},
|
||||
g: {hour: 'numeric', hourCycle: 'h12'},
|
||||
G: {hour: 'numeric'},
|
||||
h: {hour: '2-digit', hourCycle: 'h12'},
|
||||
H: {hour: '2-digit'},
|
||||
i: {minute: '2-digit'},
|
||||
s: {second: '2-digit'},
|
||||
u: {fractionalSecondDigits: 3},
|
||||
// Timezone
|
||||
// O: return UTC ? 'Z' : (d.Z > 0 ? '+' : '-') + pad2(Math.abs(d.Z / 60)) + '00';
|
||||
// P: return UTC ? 'Z' : (d.Z > 0 ? '+' : '-') + pad2(Math.abs(d.Z / 60)) + :' + pad2(Math.abs(d.Z % 60));
|
||||
// T: return UTC ? 'UTC' : new Date(d.Y, 0, 1).toTimeString().replace(/^.+ \(?([^)]+)\)?$/, '$1');
|
||||
Z: {timeZone: 'UTC'}
|
||||
// Full Date/Time
|
||||
// c: {},
|
||||
// r: {},
|
||||
// U: {},
|
||||
// options.timeZoneName = 'short';
|
||||
},
|
||||
locale = {
|
||||
relativeTime : {
|
||||
future : 'in %s',
|
||||
past : '%s ago',
|
||||
@@ -47,117 +81,29 @@
|
||||
yy : '%d years'
|
||||
}
|
||||
},
|
||||
pad2 = v => 10 > v ? '0' + v : v,
|
||||
getISODay = x => x.getDay() || 7,
|
||||
getDayOfYear = x => Math.floor((Date.UTC(x.getFullYear(),x.getMonth(),x.getDate())
|
||||
- Date.UTC(x.getFullYear(),0,1)) / 86400000),
|
||||
getWeek = x => {
|
||||
let d = new Date(x.getFullYear(),0,1),
|
||||
wd = getISODay(d),
|
||||
w = Math.ceil((getDayOfYear(x)+wd) / 7);
|
||||
/* ISO 8601 states that week 1 is the week with january 4th in it */
|
||||
if (4 < wd) --w;
|
||||
return (1 > w
|
||||
? getWeek(new Date(x.getFullYear()-1,11,31)) /* previous year, last week */
|
||||
: (52 < w && 4 > getISODay(x) ? 1 /* next year, first week */ : w) );
|
||||
};
|
||||
|
||||
// Defining locale
|
||||
Date.shortMonths = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
Date.longMonths = ['January', 'February', 'March', 'April', 'May', 'June',
|
||||
'July', 'August', 'September', 'October', 'November', 'December'];
|
||||
Date.shortDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
Date.longDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||
pad2 = v => 10 > v ? '0' + v : v;
|
||||
|
||||
// Simulate PHP's date function
|
||||
Date.prototype.format = function (str, UTC) {
|
||||
if (locale.longDateFormat[str]) {
|
||||
str = locale.longDateFormat[str]
|
||||
.replace('YYYY', 'Y')
|
||||
.replace(/(^|[^M])M([^M]|$)/, '$1n$2')
|
||||
.replace('MMMM', 'F')
|
||||
.replace('MMM', 'M')
|
||||
.replace('MM', 'm')
|
||||
.replace(/(^|[^D])D([^D]|$)/, '$1j$2')
|
||||
.replace('DD', 'd')
|
||||
.replace('dddd', 'l')
|
||||
.replace('ddd', 'D')
|
||||
.replace('dd', 'D')
|
||||
.replace(/(^|[^H])H([^H]|$)/, '$1G$2')
|
||||
.replace('HH', 'H')
|
||||
.replace('h', 'g')
|
||||
.replace('hh', 'h')
|
||||
.replace('mm', 'i');
|
||||
if ('Y-m-d\\TH:i:s' == str) {
|
||||
return this.getFullYear() + '-' + pad2(1 + this.getMonth()) + '-' + pad2(this.getDate())
|
||||
+ 'T' + pad2(this.getHours()) + ':' + pad2(this.getMinutes()) + ':' + pad2(this.getSeconds());
|
||||
}
|
||||
UTC = UTC || str.match(/\\Z$/);
|
||||
let x = this,
|
||||
d = UTC ? {
|
||||
D: x.getUTCDay(),
|
||||
Y: x.getUTCFullYear(),
|
||||
m: x.getUTCMonth(),
|
||||
d: x.getUTCDate(),
|
||||
H: x.getUTCHours(),
|
||||
Z: 0
|
||||
} : {
|
||||
D: x.getDay(),
|
||||
Y: x.getFullYear(),
|
||||
m: x.getMonth(),
|
||||
d: x.getDate(),
|
||||
H: x.getHours(),
|
||||
Z: -x.getTimezoneOffset()
|
||||
};
|
||||
return str
|
||||
? str.replace(/\\?[a-zA-Z]/g, m => {
|
||||
if (m[0] === '\\') { return m[1]; }
|
||||
switch (m) {
|
||||
// Day
|
||||
case 'd': return pad2(d.d);
|
||||
case 'D': return Date.shortDays[d.D];
|
||||
case 'j': return d.d;
|
||||
case 'l': return Date.longDays[d.D];
|
||||
case 'N': return getISODay(x);
|
||||
case 'w': return d.D;
|
||||
case 'z': return getDayOfYear(x);
|
||||
// Week
|
||||
case 'W': return pad2(getWeek(x));
|
||||
// Month
|
||||
case 'F': return Date.longMonths[d.m];
|
||||
case 'm': return pad2(d.m + 1);
|
||||
case 'M': return Date.shortMonths[d.m];
|
||||
case 'n': return d.m + 1;
|
||||
case 't': return 32 - new Date(x.getFullYear(), x.getMonth(), 32).getDate();
|
||||
// Year
|
||||
case 'L': return (((d.Y%4===0)&&(d.Y%100 !== 0)) || (d.Y%400===0)) ? '1' : '0';
|
||||
case 'o': return new Date(
|
||||
x.getFullYear(),
|
||||
x.getMonth(),
|
||||
x.getDate() - ((x.getDay() + 6) % 7) + 3
|
||||
).getFullYear();
|
||||
case 'Y': return d.Y;
|
||||
case 'y': return ('' + d.Y).substr(2);
|
||||
// Time
|
||||
case 'a': return d.H < 12 ? "am" : "pm";
|
||||
case 'A': return d.H < 12 ? "AM" : "PM";
|
||||
case 'g': return d.H % 12 || 12;
|
||||
case 'G': return d.H;
|
||||
case 'h': return pad2(d.H % 12 || 12);
|
||||
case 'H': return pad2(d.H);
|
||||
case 'i': return pad2(UTC?x.getUTCMinutes():x.getMinutes());
|
||||
case 's': return pad2(UTC?x.getUTCSeconds():x.getSeconds());
|
||||
case 'u': return (UTC?x.getUTCMilliseconds():x.getMilliseconds()).toString().padStart(3,'0');
|
||||
// Timezone
|
||||
case 'O': return UTC ? 'Z' : (d.Z > 0 ? '+' : '-') + pad2(Math.abs(d.Z / 60)) + '00';
|
||||
case 'P': return UTC ? 'Z' : (d.Z > 0 ? '+' : '-') + pad2(Math.abs(d.Z / 60)) + ':' + pad2(Math.abs(d.Z % 60));
|
||||
case 'T': return UTC ? 'UTC' : new Date(d.Y, 0, 1).toTimeString().replace(/^.+ \(?([^)]+)\)?$/, '$1');
|
||||
case 'Z': return d.Z * 60;
|
||||
// Full Date/Time
|
||||
case 'c': return x.format("Y-m-d\\TH:i:sO");
|
||||
case 'r': return x.format("D, d M Y H:i:s O");
|
||||
case 'U': return x.getTime() / 1000;
|
||||
}
|
||||
return m;
|
||||
})
|
||||
: x.toString();
|
||||
let options = {};
|
||||
if (formats[str]) {
|
||||
options = formats[str];
|
||||
} else {
|
||||
console.log('Date.format('+str+', '+(UTC?'true':'false')+')');
|
||||
if (UTC) {
|
||||
str += 'Z';
|
||||
}
|
||||
let i = str.length;
|
||||
while (i--) {
|
||||
phpFormats[str[i]] && Object.entries(phpFormats[str[i]]).forEach(([k,v])=>options[k]=v);
|
||||
}
|
||||
formats[str] = options;
|
||||
}
|
||||
return new Intl.DateTimeFormat(document.documentElement.lang, options).format(this);
|
||||
};
|
||||
|
||||
// Simulate momentjs fromNow function
|
||||
|
||||
@@ -470,8 +470,8 @@ class ServiceActions
|
||||
|
||||
if (!empty($this->aPaths[3]))
|
||||
{
|
||||
$bAdmim = 'Admin' === (isset($this->aPaths[2]) ? (string) $this->aPaths[2] : 'App');
|
||||
$sLanguage = $this->oActions->ValidateLanguage($this->aPaths[3], '', $bAdmim);
|
||||
$bAdmin = 'Admin' === (isset($this->aPaths[2]) ? (string) $this->aPaths[2] : 'App');
|
||||
$sLanguage = $this->oActions->ValidateLanguage($this->aPaths[3], '', $bAdmin);
|
||||
|
||||
$bCacheEnabled = $this->Config()->Get('labs', 'cache_system_data', true);
|
||||
if (!empty($sLanguage) && $bCacheEnabled)
|
||||
@@ -483,14 +483,14 @@ class ServiceActions
|
||||
if ($bCacheEnabled)
|
||||
{
|
||||
$sCacheFileName = KeyPathHelper::LangCache(
|
||||
$sLanguage, $bAdmim, $this->oActions->Plugins()->Hash());
|
||||
$sLanguage, $bAdmin, $this->oActions->Plugins()->Hash());
|
||||
|
||||
$sResult = $this->Cacher()->Get($sCacheFileName);
|
||||
}
|
||||
|
||||
if (0 === \strlen($sResult))
|
||||
{
|
||||
$sResult = $this->compileLanguage($sLanguage, $bAdmim, false);
|
||||
$sResult = $this->compileLanguage($sLanguage, $bAdmin);
|
||||
if ($bCacheEnabled && 0 < \strlen($sCacheFileName))
|
||||
{
|
||||
$this->Cacher()->Set($sCacheFileName, $sResult);
|
||||
@@ -1041,28 +1041,10 @@ class ServiceActions
|
||||
return $bJsOutput ? 'rl.TEMPLATES='.\MailSo\Base\Utils::Php2js($sHtml, $this->Logger()).';' : $sHtml;
|
||||
}
|
||||
|
||||
private function convertLanguageNameToMomentLanguageName(string $sLanguage) : string
|
||||
{
|
||||
$aHelper = array('en_gb' => 'en-gb', 'fr_ca' => 'fr-ca', 'pt_br' => 'pt-br',
|
||||
'uk_ua' => 'ua', 'zh_cn' => 'zh-cn', 'zh_tw' => 'zh-tw', 'fa_ir' => 'fa');
|
||||
|
||||
return isset($aHelper[$sLanguage]) ? $aHelper[$sLanguage] : \substr($sLanguage, 0, 2);
|
||||
}
|
||||
|
||||
private function compileLanguage(string $sLanguage, bool $bAdmin = false, bool $bWrapByScriptTag = true) : string
|
||||
private function compileLanguage(string $sLanguage, bool $bAdmin = false) : string
|
||||
{
|
||||
$aResultLang = array();
|
||||
|
||||
$sMoment = 'window.moment && window.moment.locale && window.moment.locale(\'en\');';
|
||||
$sMomentFileName = APP_VERSION_ROOT_PATH.'app/localization/moment/'.
|
||||
$this->convertLanguageNameToMomentLanguageName($sLanguage).'.js';
|
||||
|
||||
if (\is_file($sMomentFileName))
|
||||
{
|
||||
$sMoment = \file_get_contents($sMomentFileName);
|
||||
$sMoment = \preg_replace('/\/\/[^\n]+\n/', '', $sMoment);
|
||||
}
|
||||
|
||||
Utils::ReadAndAddLang(APP_VERSION_ROOT_PATH.'app/localization/langs.yml', $aResultLang);
|
||||
Utils::ReadAndAddLang(APP_VERSION_ROOT_PATH.'app/localization/'.
|
||||
($bAdmin ? 'admin' : 'webmail').'/_source.en.yml', $aResultLang);
|
||||
@@ -1071,7 +1053,7 @@ class ServiceActions
|
||||
|
||||
$this->Plugins()->ReadLang($sLanguage, $aResultLang);
|
||||
|
||||
$sLangJs = '';
|
||||
$sResult = '';
|
||||
$aLangKeys = \array_keys($aResultLang);
|
||||
foreach ($aLangKeys as $sKey)
|
||||
{
|
||||
@@ -1081,17 +1063,25 @@ class ServiceActions
|
||||
$sString = \implode("\n", $sString);
|
||||
}
|
||||
|
||||
$sLangJs .= '"'.\str_replace('"', '\\"', \str_replace('\\', '\\\\', $sKey)).'":'
|
||||
$sResult .= '"'.\str_replace('"', '\\"', \str_replace('\\', '\\\\', $sKey)).'":'
|
||||
.'"'.\str_replace(array("\r", "\n", "\t"), array('\r', '\n', '\t'),
|
||||
\str_replace('"', '\\"', \str_replace('\\', '\\\\', $sString))).'",';
|
||||
}
|
||||
$sResult = $sResult ? '{'.\substr($sResult, 0, -1).'}' : 'null';
|
||||
|
||||
$sResult = empty($sLangJs) ? 'null' : '{'.\substr($sLangJs, 0, -1).'}';
|
||||
$sLanguage = strtr($sLanguage, '_', '-');
|
||||
|
||||
return
|
||||
($bWrapByScriptTag ? '<script data-cfasync="false">' : '').
|
||||
'window.rainloopI18N='.$sResult.';'.$sMoment.
|
||||
($bWrapByScriptTag ? '</script>' : '')
|
||||
;
|
||||
$sMoment = 'window.moment && window.moment.locale && window.moment.locale(\'en\');';
|
||||
$options = [$sLanguage, \substr($sLanguage, 0, 2)];
|
||||
foreach ($options as $lang) {
|
||||
$sMomentFileName = APP_VERSION_ROOT_PATH.'app/localization/moment/'.$lang.'.js';
|
||||
if (\is_file($sMomentFileName)) {
|
||||
$sMoment = \file_get_contents($sMomentFileName);
|
||||
$sMoment = \preg_replace('/\/\/[^\n]+\n/', '', $sMoment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 'document.documentElement.lang = "'.$sLanguage.'";window.rainloopI18N='.$sResult.';'.$sMoment;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user