From 5afae758b98de2da47b30d6aa4b40a5d5a604fbc Mon Sep 17 00:00:00 2001
From: sebres <sebres@users.sourceforge.net>
Date: Tue, 10 Jan 2017 22:28:25 +0000
Subject: improve LocalizeFormat, internal caching of localized formats inside
 msgcat for locale and format objects smart reference introduced in dict
 (smart pointer with 0 object reference but increase dict-reference, provide
 changeable locale dict)

---
 generic/tclClock.c        |  64 ++++++++++++++++++++++--
 generic/tclClockFmt.c     |  75 ++++++++++++++++++++--------
 generic/tclDate.h         |  10 ++--
 generic/tclDictObj.c      | 122 ++++++++++++++++++++++++++++++++++------------
 generic/tclInt.h          |   1 +
 library/clock.tcl         |  83 ++++++++++++++++++++-----------
 library/init.tcl          |   2 +-
 library/msgcat/msgcat.tcl |   3 +-
 8 files changed, 269 insertions(+), 91 deletions(-)

diff --git a/generic/tclClock.c b/generic/tclClock.c
index 166a4b3..e066812 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -60,6 +60,7 @@ typedef enum ClockLiteral {
     LIT_GETSYSTEMTIMEZONE,
     LIT_SETUPTIMEZONE,
     LIT_MCGET,		LIT_TCL_CLOCK,
+    LIT_LOCALIZE_FORMAT,
 #if 0
     LIT_FREESCAN,
 #endif
@@ -82,7 +83,8 @@ static const char *const Literals[] = {
     "::tcl::clock::TZData",
     "::tcl::clock::GetSystemTimeZone",
     "::tcl::clock::SetupTimeZone",
-    "::msgcat::mcget", "::tcl::clock"
+    "::msgcat::mcget", "::tcl::clock",
+    "::tcl::clock::LocalizeFormat"
 #if 0
     "::tcl::clock::FreeScan"
 #endif
@@ -487,7 +489,6 @@ MODULE_SCOPE Tcl_Obj *
 ClockMCDict(ClockFmtScnCmdArgs *opts)
 {
     ClockClientData *dataPtr = opts->clientData;
-    Tcl_Obj *callargs[3];
 
     /* if dict not yet retrieved */
     if (opts->mcDictObj == NULL) {
@@ -518,6 +519,7 @@ ClockMCDict(ClockFmtScnCmdArgs *opts)
 	}
 
 	if (opts->mcDictObj == NULL) {
+	    Tcl_Obj *callargs[3];
 	    /* get msgcat dictionary - ::msgcat::mcget ::tcl::clock locale */
 	    callargs[0] = dataPtr->literals[LIT_MCGET];
 	    callargs[1] = dataPtr->literals[LIT_TCL_CLOCK];
@@ -528,6 +530,11 @@ ClockMCDict(ClockFmtScnCmdArgs *opts)
 	    }
 
 	    opts->mcDictObj = Tcl_GetObjResult(opts->interp);
+	    /* be sure that object reference not increases (dict changeable) */
+	    if (opts->mcDictObj->refCount > 0) {
+		/* smart reference (shared dict as object with no ref-counter) */
+		opts->mcDictObj = Tcl_DictObjSmartRef(opts->interp, opts->mcDictObj);
+	    }
 	    if ( opts->localeObj == dataPtr->CurrentLocale ) {
 		Tcl_SetObjRef(dataPtr->CurrentLocaleDict, opts->mcDictObj);
 	    } else {
@@ -535,6 +542,7 @@ ClockMCDict(ClockFmtScnCmdArgs *opts)
 		Tcl_UnsetObjRef(dataPtr->LastUnnormUsedLocale);
 		Tcl_SetObjRef(dataPtr->LastUsedLocaleDict, opts->mcDictObj);
 	    }
+	    Tcl_ResetResult(opts->interp);
 	}
     }
 
@@ -626,6 +634,56 @@ ClockMCGetListIdxDict(
     return valObj;
 }
 
+MODULE_SCOPE Tcl_Obj *
+ClockLocalizeFormat(
+    ClockFmtScnCmdArgs *opts)
+{
+    ClockClientData *dataPtr = opts->clientData;
+    Tcl_Obj *valObj, *keyObj;
+
+    keyObj = ClockFrmObjGetLocFmtKey(opts->interp, opts->formatObj);
+
+    if (opts->mcDictObj == NULL) {
+	ClockMCDict(opts);
+	if (opts->mcDictObj == NULL)
+	    return NULL;
+    }
+
+    /* try to find in cache within mc-catalog */
+    if (Tcl_DictObjGet(NULL, opts->mcDictObj, 
+	    keyObj, &valObj) != TCL_OK) {
+	return NULL;
+    }
+    if (valObj == NULL) {
+	Tcl_Obj *callargs[4];
+	/* call LocalizeFormat locale format fmtkey */
+	callargs[0] = dataPtr->literals[LIT_LOCALIZE_FORMAT];
+	callargs[1] = opts->localeObj;
+	callargs[2] = opts->formatObj;
+	callargs[3] = keyObj;
+	Tcl_IncrRefCount(keyObj);
+	if (Tcl_EvalObjv(opts->interp, 4, callargs, 0) != TCL_OK
+	) {
+	    Tcl_DecrRefCount(keyObj);
+	    return NULL;
+	}
+
+	valObj = Tcl_GetObjResult(opts->interp);
+
+	if (Tcl_DictObjPut(opts->interp, opts->mcDictObj,
+		keyObj, valObj) != TCL_OK
+	) {
+	    Tcl_DecrRefCount(keyObj);
+	    return NULL;
+	}
+
+	Tcl_DecrRefCount(keyObj);
+	Tcl_ResetResult(opts->interp);
+    }
+
+    return (opts->formatObj = valObj);
+}
+
 /*
  *----------------------------------------------------------------------
  */
@@ -2936,7 +2994,7 @@ ClockScanObjCmd(
 #endif
     else {
 	/* Use compiled version of Scan - */
-	
+
 	ret = ClockScan(clientData, interp, info, objv[1], &opts);
     }
 
diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c
index a3c7f20..184b52a 100644
--- a/generic/tclClockFmt.c
+++ b/generic/tclClockFmt.c
@@ -308,14 +308,14 @@ Tcl_ObjType ClockFmtObjType = {
 #define ObjClockFmtScn(objPtr) \
     (ClockFmtScnStorage *)objPtr->internalRep.twoPtrValue.ptr1;
 
-#define SetObjLitStorage(objPtr, lit) \
-    objPtr->internalRep.twoPtrValue.ptr2 = lit
-#define ObjLitStorage(objPtr) \
-    (ClockLitStorage *)objPtr->internalRep.twoPtrValue.ptr2;
-
-#define ClockFmtObj_SetObjIntRep(objPtr, fss, lit) \
-    objPtr->internalRep.twoPtrValue.ptr1 = fss, \
-    objPtr->internalRep.twoPtrValue.ptr2 = lit, \
+#define SetObjLocFmtKey(objPtr, key) \
+    Tcl_InitObjRef((Tcl_Obj *)objPtr->internalRep.twoPtrValue.ptr2, key)
+#define ObjLocFmtKey(objPtr) \
+    ((Tcl_Obj *)objPtr->internalRep.twoPtrValue.ptr2)
+
+#define ClockFmtObj_SetObjIntRep(objPtr, fss, key) \
+    objPtr->internalRep.twoPtrValue.ptr1 = fss; \
+    Tcl_InitObjRef((Tcl_Obj *)objPtr->internalRep.twoPtrValue.ptr2, key); \
     objPtr->typePtr = &ClockFmtObjType;
 
 /*
@@ -327,7 +327,6 @@ ClockFmtObj_DupInternalRep(srcPtr, copyPtr)
     Tcl_Obj *copyPtr;
 {
     ClockFmtScnStorage *fss = ObjClockFmtScn(srcPtr);
-    // ClockLitStorage	  *lit = ObjLitStorage(srcPtr);
 
     if (fss != NULL) {
 	Tcl_MutexLock(&ClockFmtMutex);
@@ -335,7 +334,7 @@ ClockFmtObj_DupInternalRep(srcPtr, copyPtr)
 	Tcl_MutexUnlock(&ClockFmtMutex);
     }
 
-    ClockFmtObj_SetObjIntRep(copyPtr, fss, NULL);
+    ClockFmtObj_SetObjIntRep(copyPtr, fss, ObjLocFmtKey(srcPtr));
 
     /* if no format representation, dup string representation */
     if (fss == NULL) {
@@ -352,7 +351,6 @@ ClockFmtObj_FreeInternalRep(objPtr)
     Tcl_Obj *objPtr;
 {
     ClockFmtScnStorage *fss = ObjClockFmtScn(objPtr);
-    // ClockLitStorage	  *lit = ObjLitStorage(objPtr);
     if (fss != NULL) {
 	Tcl_MutexLock(&ClockFmtMutex);
 	/* decrement object reference count of format/scan storage */
@@ -368,7 +366,7 @@ ClockFmtObj_FreeInternalRep(objPtr)
 	Tcl_MutexUnlock(&ClockFmtMutex);
     }
     SetObjClockFmtScn(objPtr, NULL);
-    SetObjLitStorage(objPtr, NULL);
+    Tcl_UnsetObjRef(ObjLocFmtKey(objPtr));
     objPtr->typePtr = NULL;
 };
 /*
@@ -379,16 +377,18 @@ ClockFmtObj_SetFromAny(interp, objPtr)
     Tcl_Interp *interp;
     Tcl_Obj    *objPtr;
 {
-    ClockFmtScnStorage *fss;
-    const char *strFmt = TclGetString(objPtr);
-    
-    if (!strFmt || (fss = FindOrCreateFmtScnStorage(interp, strFmt)) == NULL) {
-	return TCL_ERROR;
-    }
-    
+    /* validate string representation before free old internal represenation */
+    (void)TclGetString(objPtr);
+
+    /* free old internal represenation */
     if (objPtr->typePtr && objPtr->typePtr->freeIntRepProc)
 	objPtr->typePtr->freeIntRepProc(objPtr);
-    ClockFmtObj_SetObjIntRep(objPtr, fss, NULL);
+
+    /* initial state of format object */
+    objPtr->internalRep.twoPtrValue.ptr1 = NULL;
+    objPtr->internalRep.twoPtrValue.ptr2 = NULL;
+    objPtr->typePtr = &ClockFmtObjType;
+
     return TCL_OK;
 };
 /*
@@ -415,6 +415,33 @@ ClockFmtObj_UpdateString(objPtr)
 
 /*
  *----------------------------------------------------------------------
+ */
+MODULE_SCOPE Tcl_Obj*
+ClockFrmObjGetLocFmtKey(
+    Tcl_Interp *interp,
+    Tcl_Obj    *objPtr)
+{
+    Tcl_Obj *keyObj;
+
+    if (objPtr->typePtr != &ClockFmtObjType) {
+	if (ClockFmtObj_SetFromAny(interp, objPtr) != TCL_OK) {
+	    return NULL;
+	}
+    }
+   
+    keyObj = ObjLocFmtKey(objPtr);
+    if (keyObj) {
+	return keyObj;
+    }
+
+    keyObj = Tcl_ObjPrintf("FMT_%s", TclGetString(objPtr));
+    SetObjLocFmtKey(objPtr, keyObj);
+
+    return keyObj;
+}
+
+/*
+ *----------------------------------------------------------------------
  *
  * Tcl_GetClockFrmScnFromObj --
  *
@@ -731,7 +758,7 @@ static ClockScanTokenMap ScnWordTokenMap = {
 ClockScanToken *
 ClockGetOrParseScanFormat(
     Tcl_Interp *interp,		/* Tcl interpreter */
-    Tcl_Obj *formatObj)		/* Format container */
+    Tcl_Obj    *formatObj)	/* Format container */
 {
     ClockFmtScnStorage *fss;
     ClockScanToken     *tok;
@@ -889,6 +916,12 @@ ClockScan(
     unsigned short int	 flags = 0;
     int ret = TCL_ERROR;
 
+    /* get localized format */
+
+    if (ClockLocalizeFormat(opts) == NULL) {
+	return TCL_ERROR;
+    }
+
     if ((tok = ClockGetOrParseScanFormat(interp, opts->formatObj)) == NULL) {
 	return TCL_ERROR;
     }
diff --git a/generic/tclDate.h b/generic/tclDate.h
index 1c51a02..62fa693 100644
--- a/generic/tclDate.h
+++ b/generic/tclDate.h
@@ -345,10 +345,6 @@ typedef struct ClockFmtScnStorage {
 					 * stored by offset +sizeof(self) */
 } ClockFmtScnStorage;
 
-typedef struct ClockLitStorage {
-    int		      dummy;
-} ClockLitStorage;
-
 /*
  * Prototypes of module functions.
  */
@@ -366,9 +362,15 @@ MODULE_SCOPE Tcl_Obj *
 		    ClockMCGet(ClockFmtScnCmdArgs *opts, int mcKey);
 MODULE_SCOPE Tcl_Obj *
 		    ClockMCGetListIdxDict(ClockFmtScnCmdArgs *opts, int mcKey);
+MODULE_SCOPE Tcl_Obj *
+		    ClockLocalizeFormat(ClockFmtScnCmdArgs *opts);
 
 /* tclClockFmt.c module declarations */
 
+MODULE_SCOPE Tcl_Obj* 
+		    ClockFrmObjGetLocFmtKey(Tcl_Interp *interp,
+			Tcl_Obj *objPtr);
+
 MODULE_SCOPE ClockFmtScnStorage * 
 		    Tcl_GetClockFrmScnFromObj(Tcl_Interp *interp,
 			Tcl_Obj *objPtr);
diff --git a/generic/tclDictObj.c b/generic/tclDictObj.c
index 1115999..3944173 100644
--- a/generic/tclDictObj.c
+++ b/generic/tclDictObj.c
@@ -51,6 +51,8 @@ static int		DictSetCmd(ClientData dummy, Tcl_Interp *interp,
 			    int objc, Tcl_Obj *const *objv);
 static int		DictSizeCmd(ClientData dummy, Tcl_Interp *interp,
 			    int objc, Tcl_Obj *const *objv);
+static int		DictSmartRefCmd(ClientData dummy, Tcl_Interp *interp,
+			    int objc, Tcl_Obj *const *objv);
 static int		DictUnsetCmd(ClientData dummy, Tcl_Interp *interp,
 			    int objc, Tcl_Obj *const *objv);
 static int		DictUpdateCmd(ClientData dummy, Tcl_Interp *interp,
@@ -98,6 +100,7 @@ static const EnsembleImplMap implementationMap[] = {
     {"replace",	DictReplaceCmd, NULL, NULL, NULL, 0 },
     {"set",	DictSetCmd,	TclCompileDictSetCmd, NULL, NULL, 0 },
     {"size",	DictSizeCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 0 },
+    {"smartref",DictSmartRefCmd,NULL, NULL, NULL, 0 },
     {"unset",	DictUnsetCmd,	TclCompileDictUnsetCmd, NULL, NULL, 0 },
     {"update",	DictUpdateCmd,	TclCompileDictUpdateCmd, NULL, NULL, 0 },
     {"values",	DictValuesCmd,	TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 },
@@ -142,7 +145,7 @@ typedef struct Dict {
 				 * the entries in the order that they are
 				 * created. */
     int epoch;			/* Epoch counter */
-    size_t refCount;		/* Reference counter (see above) */
+    int refcount;		/* Reference counter (see above) */
     Tcl_Obj *chain;		/* Linked list used for invalidating the
 				 * string representations of updated nested
 				 * dictionaries. */
@@ -392,7 +395,7 @@ DupDictInternalRep(
 
     newDict->epoch = 0;
     newDict->chain = NULL;
-    newDict->refCount = 1;
+    newDict->refcount = 1;
 
     /*
      * Store in the object.
@@ -427,7 +430,8 @@ FreeDictInternalRep(
 {
     Dict *dict = DICT(dictPtr);
 
-    if (dict->refCount-- <= 1) {
+    dict->refcount--;
+    if (dict->refcount <= 0) {
 	DeleteDict(dict);
     }
     dictPtr->typePtr = NULL;
@@ -712,7 +716,7 @@ SetDictFromAny(
     TclFreeIntRep(objPtr);
     dict->epoch = 0;
     dict->chain = NULL;
-    dict->refCount = 1;
+    dict->refcount = 1;
     DICT(objPtr) = dict;
     objPtr->internalRep.twoPtrValue.ptr2 = NULL;
     objPtr->typePtr = &tclDictType;
@@ -1116,7 +1120,7 @@ Tcl_DictObjFirst(
 	searchPtr->dictionaryPtr = (Tcl_Dict) dict;
 	searchPtr->epoch = dict->epoch;
 	searchPtr->next = cPtr->nextPtr;
-	dict->refCount++;
+	dict->refcount++;
 	if (keyPtrPtr != NULL) {
 	    *keyPtrPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry);
 	}
@@ -1230,7 +1234,8 @@ Tcl_DictObjDone(
     if (searchPtr->epoch != -1) {
 	searchPtr->epoch = -1;
 	dict = (Dict *) searchPtr->dictionaryPtr;
-	if (dict->refCount-- <= 1) {
+	dict->refcount--;
+	if (dict->refcount <= 0) {
 	    DeleteDict(dict);
 	}
     }
@@ -1382,7 +1387,7 @@ Tcl_NewDictObj(void)
     InitChainTable(dict);
     dict->epoch = 0;
     dict->chain = NULL;
-    dict->refCount = 1;
+    dict->refcount = 1;
     DICT(dictPtr) = dict;
     dictPtr->internalRep.twoPtrValue.ptr2 = NULL;
     dictPtr->typePtr = &tclDictType;
@@ -1432,7 +1437,7 @@ Tcl_DbNewDictObj(
     InitChainTable(dict);
     dict->epoch = 0;
     dict->chain = NULL;
-    dict->refCount = 1;
+    dict->refcount = 1;
     DICT(dictPtr) = dict;
     dictPtr->internalRep.twoPtrValue.ptr2 = NULL;
     dictPtr->typePtr = &tclDictType;
@@ -1957,6 +1962,77 @@ DictSizeCmd(
 
 /*
  *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+Tcl_DictObjSmartRef(
+    Tcl_Interp *interp,
+    Tcl_Obj    *dictPtr)
+{
+    Tcl_Obj *result;
+    Dict    *dict;
+
+    if (dictPtr->typePtr != &tclDictType
+	    && SetDictFromAny(interp, dictPtr) != TCL_OK) {
+	return NULL;
+    }
+
+    dict = DICT(dictPtr);
+
+    result = Tcl_NewObj();
+    DICT(result) = dict;
+    dict->refcount++;
+    result->internalRep.twoPtrValue.ptr2 = NULL;
+    result->typePtr = &tclDictType;
+
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DictSmartRefCmd --
+ *
+ *	This function implements the "dict smartref" Tcl command. See the user
+ *	documentation for details on what it does, and TIP#111 for the formal
+ *	specification.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DictSmartRefCmd(
+    ClientData dummy,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *const *objv)
+{
+    Tcl_Obj *result;
+    Dict    *dict;
+
+    if (objc != 2) {
+	Tcl_WrongNumArgs(interp, 1, objv, "dictionary");
+	return TCL_ERROR;
+    }
+
+    result = Tcl_DictObjSmartRef(interp, objv[1]);
+    if (result == NULL) {
+    	return TCL_ERROR;
+    }
+
+    Tcl_SetObjResult(interp, result);
+
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
  *
  * DictExistsCmd --
  *
@@ -2280,7 +2356,7 @@ DictAppendCmd(
     Tcl_Obj *const *objv)
 {
     Tcl_Obj *dictPtr, *valuePtr, *resultPtr;
-    int allocatedDict = 0;
+    int i, allocatedDict = 0;
 
     if (objc < 3) {
 	Tcl_WrongNumArgs(interp, 1, objv, "dictVarName key ?value ...?");
@@ -2305,33 +2381,15 @@ DictAppendCmd(
 
     if ((objc > 3) || (valuePtr == NULL)) {
 	/* Only go through append activites when something will change. */
-	Tcl_Obj *appendObjPtr = NULL;
-
-	if (objc > 3) {
-	    /* Something to append */
-
-	    if (objc == 4) {
-		appendObjPtr = objv[3];
-	    } else if (TCL_OK != TclStringCatObjv(interp, /* inPlace */ 1,
-		    objc-3, objv+3, &appendObjPtr)) {
-		return TCL_ERROR;
-	    }
-	}
 
-	if (appendObjPtr == NULL) {
-	    /* => (objc == 3) => (valuePtr == NULL) */
+	if (valuePtr == NULL) {
 	    TclNewObj(valuePtr);
-	} else if (valuePtr == NULL) {
-	    valuePtr = appendObjPtr;
-	    appendObjPtr = NULL;
+	} else if (Tcl_IsShared(valuePtr)) {
+	    valuePtr = Tcl_DuplicateObj(valuePtr);
 	}
 
-	if (appendObjPtr) {
-	    if (Tcl_IsShared(valuePtr)) {
-		valuePtr = Tcl_DuplicateObj(valuePtr);
-	    }
-
-	    Tcl_AppendObjToObj(valuePtr, appendObjPtr);
+	for (i=3 ; i<objc ; i++) {
+	    Tcl_AppendObjToObj(valuePtr, objv[i]);
 	}
 
 	Tcl_DictObjPut(NULL, dictPtr, objv[2], valuePtr);
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 1b37d84..a569788 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -2905,6 +2905,7 @@ MODULE_SCOPE int	TclFindDictElement(Tcl_Interp *interp,
 			    const char *dict, int dictLength,
 			    const char **elementPtr, const char **nextPtr,
 			    int *sizePtr, int *literalPtr);
+MODULE_SCOPE Tcl_Obj *	Tcl_DictObjSmartRef(Tcl_Interp *interp, Tcl_Obj *);
 /* TIP #280 - Modified token based evulation, with line information. */
 MODULE_SCOPE int	TclEvalEx(Tcl_Interp *interp, const char *script,
 			    int numBytes, int flags, int line,
diff --git a/library/clock.tcl b/library/clock.tcl
index b4632b1..4173174 100755
--- a/library/clock.tcl
+++ b/library/clock.tcl
@@ -629,6 +629,8 @@ proc ::tcl::clock::Initialize {} {
 
     # Caches
 
+    variable LocaleFormats      {};	# Dictionary with localized formats
+
     variable LocaleNumeralCache {};	# Dictionary whose keys are locale
 					# names and whose values are pairs
 					# comprising regexes matching numerals
@@ -2323,38 +2325,59 @@ proc ::tcl::clock::LoadWindowsDateTimeFormats { locale } {
 #
 #----------------------------------------------------------------------
 
-proc ::tcl::clock::LocalizeFormat { locale format } {
+proc ::tcl::clock::LocalizeFormat { locale format {fmtkey {}} } {
+    variable LocaleFormats
+    
+    if { $fmtkey eq {} } { set fmtkey FMT_$format }
+    if { [catch {
+    	set locfmt [dict get $LocaleFormats $locale $fmtkey]
+    }] } {
 
-    # message catalog key to cache this format
-    set key FORMAT_$format
+    	# get map list cached or build it:
+	if { [catch {
+	    set mlst [dict get $LocaleFormats $locale MLST]
+	}] } {
+	
+	    # message catalog dictionary:
+	    set mcd [::msgcat::mcget ::tcl::clock $locale]
+		
+	    # Handle locale-dependent format groups by mapping them out of the format
+	    # string.  Note that the order of the [string map] operations is
+	    # significant because later formats can refer to later ones; for example
+	    # %c can refer to %X, which in turn can refer to %T.
+
+	    set mlst {
+		%% %%
+		%D %m/%d/%Y
+		%+ {%a %b %e %H:%M:%S %Z %Y}
+	    }
+	    lappend mlst %EY [string map $mlst [dict get $mcd LOCALE_YEAR_FORMAT]]
+	    lappend mlst %T  [string map $mlst [dict get $mcd TIME_FORMAT_24_SECS]]
+	    lappend mlst %R  [string map $mlst [dict get $mcd TIME_FORMAT_24]]
+	    lappend mlst %r  [string map $mlst [dict get $mcd TIME_FORMAT_12]]
+	    lappend mlst %X  [string map $mlst [dict get $mcd TIME_FORMAT]]
+	    lappend mlst %EX [string map $mlst [dict get $mcd LOCALE_TIME_FORMAT]]
+	    lappend mlst %x  [string map $mlst [dict get $mcd DATE_FORMAT]]
+	    lappend mlst %Ex [string map $mlst [dict get $mcd LOCALE_DATE_FORMAT]]
+	    lappend mlst %c  [string map $mlst [dict get $mcd DATE_TIME_FORMAT]]
+	    lappend mlst %Ec [string map $mlst [dict get $mcd LOCALE_DATE_TIME_FORMAT]]
+
+	    dict set LocaleFormats $locale MLST $mlst
+	}
 
-    if { [::msgcat::mcexists -exactlocale -exactnamespace $key] } {
-	return [mc $key]
-    }
-    # Handle locale-dependent format groups by mapping them out of the format
-    # string.  Note that the order of the [string map] operations is
-    # significant because later formats can refer to later ones; for example
-    # %c can refer to %X, which in turn can refer to %T.
-
-    set list {
-	%% %%
-	%D %m/%d/%Y
-	%+ {%a %b %e %H:%M:%S %Z %Y}
+	# translate:
+	set locfmt [string map $mlst $format]
+
+	# Save original format as long as possible, because of internal representation (performance)
+	if {$locfmt eq $format} {
+	    set locfmt $format
+	}
+
+	# cache it:
+	dict set LocaleFormats $locale $fmtkey $locfmt
     }
-    lappend list %EY [string map $list [mc LOCALE_YEAR_FORMAT]]
-    lappend list %T  [string map $list [mc TIME_FORMAT_24_SECS]]
-    lappend list %R  [string map $list [mc TIME_FORMAT_24]]
-    lappend list %r  [string map $list [mc TIME_FORMAT_12]]
-    lappend list %X  [string map $list [mc TIME_FORMAT]]
-    lappend list %EX [string map $list [mc LOCALE_TIME_FORMAT]]
-    lappend list %x  [string map $list [mc DATE_FORMAT]]
-    lappend list %Ex [string map $list [mc LOCALE_DATE_FORMAT]]
-    lappend list %c  [string map $list [mc DATE_TIME_FORMAT]]
-    lappend list %Ec [string map $list [mc LOCALE_DATE_TIME_FORMAT]]
-    set format [string map $list $format]
-
-    ::msgcat::mcset $locale $key $format
-    return $format
+
+    return $locfmt
 }
 
 #----------------------------------------------------------------------
@@ -4437,6 +4460,7 @@ proc ::tcl::clock::ChangeCurrentLocale {args} {
 
 proc ::tcl::clock::ClearCaches {} {
     variable FormatProc
+    variable LocaleFormats
     variable LocaleNumeralCache
     variable TimeZoneBad
 
@@ -4451,6 +4475,7 @@ proc ::tcl::clock::ClearCaches {} {
     }
 
     unset -nocomplain FormatProc
+    set LocaleFormats {}
     set LocaleNumeralCache {}
     set TimeZoneBad {}
     InitTZData
diff --git a/library/init.tcl b/library/init.tcl
index 06a9459..ef0a84a 100644
--- a/library/init.tcl
+++ b/library/init.tcl
@@ -178,7 +178,7 @@ if {[interp issafe]} {
 
 	# Auto-loading stubs for 'clock.tcl'
 
-	foreach cmd {add format SetupTimeZone GetSystemTimeZone __org_scan} {
+	foreach cmd {add format LocalizeFormat SetupTimeZone GetSystemTimeZone __org_scan} {
 	    proc ::tcl::clock::$cmd args {
 		variable TclLibDir
 		source -encoding utf-8 [file join $TclLibDir clock.tcl]
diff --git a/library/msgcat/msgcat.tcl b/library/msgcat/msgcat.tcl
index 3fd2cdb..a25f6c8 100644
--- a/library/msgcat/msgcat.tcl
+++ b/library/msgcat/msgcat.tcl
@@ -947,7 +947,8 @@ proc msgcat::Merge {ns locales} {
 	}
     }
     dict set Merged $ns $loc $mrgcat
-    return $mrgcat
+    # return smart reference (shared dict as object with exact one ref-counter)
+    return [dict smartref $mrgcat]
 }
 
 # msgcat::Invoke --
-- 
cgit v0.12