diff --git a/etemplate/inc/class.etemplate.inc.php b/etemplate/inc/class.etemplate.inc.php index 03bb158e6c..b1da790f59 100644 --- a/etemplate/inc/class.etemplate.inc.php +++ b/etemplate/inc/class.etemplate.inc.php @@ -135,14 +135,14 @@ class etemplate_new extends etemplate_widget_template { if (!in_array($l_app, array($currentapp, 'custom'))) { - $langRequire[$l_app] = array('app' => $l_app, 'lang' => $lang); + $langRequire[$l_app] = array('app' => $l_app, 'lang' => $lang, 'etag' => translation::etag($l_app, $lang)); } } foreach(array($currentapp, 'custom') as $l_app) { if (isset(translation::$loaded_apps[$l_app])) { - $langRequire[$l_app] = array('app' => $l_app, 'lang' => translation::$loaded_apps[$l_app]); + $langRequire[$l_app] = array('app' => $l_app, 'lang' => translation::$loaded_apps[$l_app], 'etag' => translation::etag($l_app, translation::$loaded_apps[$l_app])); } } diff --git a/phpgwapi/inc/class.config.inc.php b/phpgwapi/inc/class.config.inc.php index e5a5b80e56..66f85ab83c 100755 --- a/phpgwapi/inc/class.config.inc.php +++ b/phpgwapi/inc/class.config.inc.php @@ -378,6 +378,9 @@ class config } } } + // some things need on client-side which are not direct configs + $client_config['phpgwapi']['lang_ctimes_md5'] = md5(json_encode(self::$configs['phpgwapi']['lang_ctimes'])); + return $client_config; } diff --git a/phpgwapi/inc/class.translation.inc.php b/phpgwapi/inc/class.translation.inc.php index 55d09b5338..22ee496380 100644 --- a/phpgwapi/inc/class.translation.inc.php +++ b/phpgwapi/inc/class.translation.inc.php @@ -428,6 +428,29 @@ class translation } } + /** + * Get a state / etag for a given app's translations + * + * We currently only use a single state for all none-instance-specific apps depending on lang_ctimes. + * + * @param string $_app + * @param string $_lang + * @return string + */ + static function etag($_app, $_lang) + { + if (!in_array($_app, translation::$instance_specific_translations)) + { + $etag = md5(json_encode($GLOBALS['egw_info']['server']['lang_ctimes'])); + } + else + { + $etag = md5(json_encode(egw_cache::getCache(egw_cache::INSTANCE, __CLASS__, $_app.':'.$_lang))); + } + error_log(__METHOD__."('$_app', '$_lang') returning '$etag'"); + return $etag; + } + /** * Loads translations for an application direct from the lang-file(s) * diff --git a/phpgwapi/js/jsapi/egw_config.js b/phpgwapi/js/jsapi/egw_config.js index 8034c84916..875a2576ed 100644 --- a/phpgwapi/js/jsapi/egw_config.js +++ b/phpgwapi/js/jsapi/egw_config.js @@ -20,7 +20,7 @@ egw.extend('config', egw.MODULE_GLOBAL, function() { /** * Clientside config - * + * * @access: private, use egw.config(_name, _app="phpgwapi") */ var configs = {}; @@ -28,7 +28,7 @@ egw.extend('config', egw.MODULE_GLOBAL, function() { return { /** * Query clientside config - * + * * @param string _name name of config variable * @param string _app default "phpgwapi" * @return mixed @@ -37,14 +37,14 @@ egw.extend('config', egw.MODULE_GLOBAL, function() { { if (typeof _app == 'undefined') _app = 'phpgwapi'; - if (typeof configs[_app] == 'undefined') return null; - - return configs[_app][_name]; + if (typeof this.configs[_app] == 'undefined') return null; + + return this.configs[_app][_name]; }, - + /** * Set clientside configuration for all apps - * + * * @param array/object */ set_configs: function(_configs) diff --git a/phpgwapi/js/jsapi/egw_lang.js b/phpgwapi/js/jsapi/egw_lang.js index eb18f55b17..4b92ae3376 100644 --- a/phpgwapi/js/jsapi/egw_lang.js +++ b/phpgwapi/js/jsapi/egw_lang.js @@ -25,7 +25,7 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() { /** * Translations - * + * * @access: private, use egw.lang() or egw.set_lang_arr() */ var lang_arr = {}; @@ -34,7 +34,7 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() { return { /** * Set translation for a given application - * + * * @param string _app * @param object _message message => translation pairs * @memberOf egw @@ -46,10 +46,10 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() { lang_arr[_app] = _messages; } }, - + /** * Translate a given phrase replacing optional placeholders - * + * * @param string _msg message to translate * @param string _arg1 ... _argN */ @@ -62,7 +62,7 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() { } var translation = _msg; _msg = _msg.toLowerCase(); - + // search apps in given order for a replacement var apps = this.lang_order || ['custom', this.getAppName(), 'etemplate', 'common']; for(var i = 0; i < apps.length; ++i) @@ -75,9 +75,9 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() { } } if (arguments.length == 1) return translation; - + if (arguments.length == 2) return translation.replace('%1', arguments[1]); - + // to cope with arguments containing '%2' (eg. an urlencoded path like a referer), // we first replace all placeholders '%N' with '|%N|' and then we replace all '|%N|' with arguments[N] translation = translation.replace(/%([0-9]+)/g, '|%$1|'); @@ -116,9 +116,10 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() { { if (typeof lang_arr[_apps[i].app] === "undefined") { - jss.push(this.webserverUrl - + '/phpgwapi/lang.php?app=' - + _apps[i].app + '&lang=' + _apps[i].lang); + jss.push(this.webserverUrl + + '/phpgwapi/lang.php?app=' + _apps[i].app + + '&lang=' + _apps[i].lang + + '&etag=' + (_apps[i].etag || this.config('lang_ctimes_md5'))); } apps.push(_apps[i].app); } @@ -138,7 +139,7 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() { { // Require a "ready postpone token" var token = ready.readyWaitFor(); - + // Call "readyDone" once all js files have been included. files.includeJS(jss, function () { ready.readyDone(token); diff --git a/phpgwapi/js/jsapi/egw_links.js b/phpgwapi/js/jsapi/egw_links.js index 986dc611c0..da27200b20 100644 --- a/phpgwapi/js/jsapi/egw_links.js +++ b/phpgwapi/js/jsapi/egw_links.js @@ -457,7 +457,7 @@ egw.extend('links', egw.MODULE_GLOBAL, function() { this.value = ''; }); // need to load common translations for app-names - this.includeJS([this.webserverUrl+'/phpgwapi/lang.php?app=common&lang='+this.preference('lang')], function(){ + this.langRequire(window, [{app: 'common', lang: this.preference('lang')}], function(){ select.append(jQuery(document.createElement('option')).attr('value', '').text(self.lang('Add')+' ...')); var apps = self.link_app_list('add'); for(var app in apps) diff --git a/phpgwapi/lang.php b/phpgwapi/lang.php index 844115102a..1f9ee60137 100644 --- a/phpgwapi/lang.php +++ b/phpgwapi/lang.php @@ -30,23 +30,15 @@ $GLOBALS['egw_info'] = array( include '../header.inc.php'; // use an etag with app, lang and a hash over the creation-times of all lang-files -if (!in_array($_GET['app'], translation::$instance_specific_translations)) -{ - $etag = '"'.$_GET['app'].'-'.$_GET['lang'].'-'.md5(serialize($GLOBALS['egw_info']['server']['lang_ctimes'])).'"'; -} -else -{ - translation::add_app($_GET['app'], $_GET['lang']); - if (!count(translation::$lang_arr)) - { - translation::add_app($_GET['app'], 'en'); - } - $etag = '"'.$_GET['app'].'-'.$_GET['lang'].'-'.md5(serialize(translation::$lang_arr)); -} +$etag = '"'.$_GET['app'].'-'.$_GET['lang'].'-'.translation::etag($_GET['app'], $_GET['lang']).'"'; + +// tell browser/caches to cache for one day, we change url on real modifications +$expires = 864000; // 10days // headers to allow caching of one month Header('Content-Type: text/javascript; charset=utf-8'); -Header('Cache-Control: public, no-transform'); +Header('Cache-Control: public, no-transform, max-age='.$expires); +header('Expires: ' . gmdate('D, d M Y H:i:s', time()+$expires) . ' GMT'); Header('Pragma: cache'); Header('ETag: '.$etag); @@ -57,13 +49,10 @@ if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $ common::egw_exit(); } -if (!in_array($_GET['app'], translation::$instance_specific_translations)) +translation::add_app($_GET['app'], $_GET['lang']); +if (!count(translation::$lang_arr)) { - translation::add_app($_GET['app'], $_GET['lang']); - if (!count(translation::$lang_arr)) - { - translation::add_app($_GET['app'], 'en'); - } + translation::add_app($_GET['app'], 'en'); } $content = 'egw.set_lang_arr("'.$_GET['app'].'", '.json_encode(translation::$lang_arr).');';