2019年9月3日 星期二

L10n pseudo code implementation

<?php
/*! \brief
 * This is pseudo tool to make WGF environment change to pseudo testing environment
* \author William Su
*/
// avoid excuted from web[http://].
if(isset($_SERVER['REQUEST_METHOD'])){
    echo "<html><body>Permission denied.</body></html>\n";
    exit();
}
require_once(dirname(__FILE__).'/../../../inc/class/init.php');

$strFolderPath = WF::getWidgetPoolFactory()->getWPHome();
$arrPathWpFiles = WF::getIOFactory()->getFile()->getArrPathByFolderPath($strFolderPath);

// Step 1 : get all en_US.xml in /path/to/wp1
$arrPathWpFilesEnUS = array();
echo ">>Valid L10n resource files list:\r\n";
$index = 1;
foreach($arrPathWpFiles as $strPathWpFiles){
    if(false !== stripos($strPathWpFiles, 'en_US.') //Support en_US.xml/en_US.js
&& false === strpos($strPathWpFiles, ' - Copy')){ //Skip - Copy folder
        $arrPathWpFilesEnUS[] = $strPathWpFiles;
        echo $index.")".$strPathWpFiles."\r\n";
        $index++;
    }
}

/**
 * This method help to mkdir recursively
 */
function toolPseudo_mkdirRecursive($strPathFolder){
    echo "\r\n START recursive mkdir::[".$strPathFolder."]";
    if(false === is_dir($strPathFolder)){
        $strPathFolder = dirname($strPathFolder);
    }
    $arrPathFolderNeedToCreate = array();
    while(!file_exists($strPathFolder)){
        $arrPathFolderNeedToCreate[] = $strPathFolder;
        $strPathFolder = dirname($strPathFolder);
    }
    if(is_array($arrPathFolderNeedToCreate) && $arrPathFolderNeedToCreate){
        $arrPathFolderNeedToCreate = array_reverse($arrPathFolderNeedToCreate);
        foreach($arrPathFolderNeedToCreate as $strPathFolderNeedToCreate){
            WF::getIOFactory()->getFile()->mkdir($strPathFolderNeedToCreate);
        }
    }
    echo "\r\n SUCCESS recursive mkdir::[".$strPathFolder."]\r\n";
}

/**
 * This method help to get the final pseudo base on en_US string length
 */
function toolPseudo_getPseudo($str, $isJSON = false){
    // handler the CDATA string
    $strCDATAPrefix = '';
    $strCDATASuffix = '';
    if(false !== strpos($str, '<![CDATA[')){
        $strCDATAPrefix = '<![CDATA[';
        $strCDATASuffix = ']]>';
        $str = substr($str, 9, -3);
    }
    $str = (true === $isJSON) ? addslashes($str) : $str;
    $intStrLength = strlen($str);
    $floatAdditionalSpaceNeededRatio = null; //default

    // Generate the add space ratio array
    /*
Below is the mapping table which provide by L10N team
Original length(char) :: Additonal Sapce needed
up to 10 :: 100%~200%
11 to 20 :: 80% to 100%
21 to 30 :: 60% to 80%
31 to 50 :: 40% to 60%
51 to 70 :: 31% to 40%
over 70 :: 30%
     */
    $arrAdditionalSpaceNeededRatio = array();
    for($i=1; $i<=10; $i++){
        $index = $i;
        $arrAdditionalSpaceNeededRatio[$index] = 2 - ($i * 0.1); //2.0, 1.9, 1.8 ~ 1.0
    }
    for($i=1; $i<=10; $i++){
        $index = $i+10;
        $arrAdditionalSpaceNeededRatio[$index] = 1 - ($i * 0.02); //1.0, 0.98, 0.96 ~ 0.8
    }
    for($i=1; $i<=10; $i++){
        $index = $i+20;
        $arrAdditionalSpaceNeededRatio[$index] = 0.8 - ($i * 0.02); //0.8, 0.78, 0.76 ~ 0.6
    }
    for($i=1; $i<=20; $i++){
        $index = $i+30;
        $arrAdditionalSpaceNeededRatio[$index] = 0.6 - ($i * 0.01); //0.6, 0.59, 0.58 ~ 0.4
    }
    for($i=1; $i<=20; $i++){
        $index = $i+50;
        $arrAdditionalSpaceNeededRatio[$index] = 0.4 - ($i * 0.01); //0.4, 0.39, 0.38 ~ 0.31
    }
    if($intStrLength > 70){
        $arrAdditionalSpaceNeededRatio[$intStrLength] = 0.3;
    }

    // $floatAdditionalSpaceNeededRatio is the actual ratio need to add
    $floatAdditionalSpaceNeededRatio = isset($arrAdditionalSpaceNeededRatio[$intStrLength]) ? $arrAdditionalSpaceNeededRatio[$intStrLength] : null;

    // $arrPseudo is the array of Frank language pseudo char
    $indexPseudo = 0;
    $arrPseudo = array('é','è','à');

    // $intStrLengthAfterPseudo is the L10N string length after add the pseudo string
    $intStrLengthAfterPseudo = $intStrLength + ($intStrLength * $floatAdditionalSpaceNeededRatio);
    $intStrLengthTmp = $intStrLength;
    $strPseudoFinal = $str;

    $strPrefixFinal = '';
    $strSuffixFinal = '';
    while($intStrLengthTmp < $intStrLengthAfterPseudo){
        // use the $charPseudo alternation
        $charPseudo = $arrPseudo[($indexPseudo%3)];

        $strPrefixFinal .= $charPseudo;
        $strSuffixFinal .= $charPseudo;

        $intStrLengthTmp += 2; //$strPrefix+$strSuffix = 2
        $indexPseudo++;
    }
    // use symbols '!' to make sure the start and the end position of string
    // use symbols ["] & ['] to solve javascript functional error. EX : some javascript variable may use double quote or single quote to declare
    $strPseudoFinal = $strCDATAPrefix.'!'.$strPrefixFinal.$strPseudoFinal.$strSuffixFinal.'!'.$strCDATASuffix;

    return $strPseudoFinal;
}

//var_dump($arrPathWpFilesEnUS);
//exit();
//$arrPathWpFilesEnUS = array('/Users/WilliamSu/Documents/htdocs/william_su_tw-williamsu_mbpr/Core/TMLS/Dev/TMLS-1.5/TMLS-Server/src/Server/Web/UI/widget/repository/widgetPool/../../repository/widgetPool/wp1/widgetBase/modTMLS/app/l10n/en_US.js');
if(!$arrPathWpFilesEnUS){
    echo("The L10N en_US.xml files is empty! Pseudo string prepare proccess abort!\r\n");
    exit();
}

echo("Pseudo string prepare proccess >>>>>>>>>>> START\r\n");
// Do the backup before execute the pseudo code replacement
// You can use this backup do the rollback later
$strDateCurrent = date('YmdHis');
$strPathBackup = WF::getDocumentRoot().'/repository/backup/pseudo_'.$strDateCurrent;
//toolPseudo_mkdirRecursive($strPathBackup);

function putAppEnUSJsToAppAppJs($strContentAppJS, $searchKeyWord, $sourceAppJS){
    // 2. Get content of app/l10n/en_US.js
$sourceEnUSJS = dirname(__FILE__).'/../../../'.WF::getWidgetPoolFactory()->getWPWebrootNoSlash().'/widgetBase/modTMLS/app/l10n/en_US.js';
$strContentEnUSJS = file_get_contents($sourceEnUSJS);
$strContentEnUSJSTrim = preg_replace('/\s\s+/', ' ', trim($strContentEnUSJS));
//echo '$sourceEnUSJS::'.$sourceEnUSJS."\r\n";
//echo '$strContentEnUSJSTrim::'.$strContentEnUSJSTrim."\r\n";
    echo "SUCCESS get pseudo content from app/l10n/en_US.js and remove space.\r\n";

$intPosStart = strpos($strContentAppJS,$searchKeyWord);
$intPosEnd = strpos($strContentAppJS, '"}', $intPosStart);
//echo '$strContentAppJS::'.$strContentAppJS."\r\n";
//echo '$intPosStart::'.$intPosStart."\r\n";
//echo '$intPosEnd::'.$intPosEnd."\r\n";

$strContentAppJSFinal = substr_replace($strContentAppJS, $strContentEnUSJSTrim, $intPosStart, ($intPosEnd-$intPosStart)+2);

if (WF::getIOFactory()->getFile()->writeDataSafe($sourceAppJS, 'w+', $strContentAppJSFinal)) {
echo("Success put app/l10n/en_US.js to app/app.js\r\n");
}else{
        echo("Failed to put app/l10n/en_US.js to app/app.js\r\n");
    }
}
/**
 * @param string $string
 * @return param string $new_str
 */
function removeWihteLineAndChangeNOrRToNR($string){
    //1. Chnage \n or \r to \r\n
    $new_str = str_replace(array("\n", "\r"), "\r\n", $string );
    //2. Chnage \n\n in js
    $new_str = str_replace(array("\\n\\n"), "[nnn]", $new_str );
    //3. Change end of string \r\n
    $new_str = str_replace(array(">\r\n"), "[rnrn]", $new_str );
    //4. Remove wihite line
    $new_str = str_replace(array("\r\n"), "", $new_str );
    //5. Recover end of string \r\n
    $new_str = str_replace(array("[rnrn]"), ">\r\n", $new_str );
    //6. Recover \n\n in Js.
    $new_str = str_replace(array("[nnn]"), "\\n\\n", $new_str );
    return $new_str;
}

/**
 * @param $strPathWpFilesEnUS
 * @param $strPathBackup
 * @param $matchesEnUs
 * @throws WFIOFileException
 */
function processXMLPseudo($strPathWpFilesEnUS, $strPathBackup)
{
   
    $strPathWpFilesEnUSDirname = dirname($strPathWpFilesEnUS);

    // Get content from en_US.xml
    $strXmlEnUs = file_get_contents($strPathWpFilesEnUS);
    $strXmlEnUs = removeWihteLineAndChangeNOrRToNR($strXmlEnUs);
    /**
     * The following is the example result of $matchesEnUs
     * matched: <string id="title"><![CDATA[Sample_Test]]></string>
     * part 1: <string id="title">
     * part 2: string
     * part 3: <![CDATA[Sample_Test]]>
     * part 4: </string>
     *
     * matched: <string id="title">Sample_Test2</string>
     * part 1: <string id="title">
     * part 2: string
     * part 3: Sample_Test2
     * part 4: </string>
     */
   

    $stringPseudo = 'éèà';
    if (false === strpos($strXmlEnUs, $stringPseudo)) {
        echo "\r\n >>> START processXMLPseudo[".$strPathWpFilesEnUS."] \r\n";

        preg_match_all("/(<([\w]+)[^>]*>)(.*?)(<\/\\2>)/", $strXmlEnUs, $matchesEnUs, PREG_SET_ORDER);

        // Step : backup original en_US.xml to /path/to/backup/en_US.xml
        $sourceEnUs = $strPathWpFilesEnUS;
        list($tmp, $strPathRelativeWpFilesEnUS) = explode(WF::getWidgetPoolFactory()->getWPWebrootNoSlash(), $strPathWpFilesEnUS);
        $dest = $strPathBackup . '/' . WF::getWidgetPoolFactory()->getWPWebrootNoSlash() . $strPathRelativeWpFilesEnUS;
        $strPathBackupFile = $dest;
        toolPseudo_mkdirRecursive($strPathBackupFile);
        if (WF::getIOFactory()->getFile()->copyRecurse($sourceEnUs, $dest)) {
            echo("Backup source success: [" . $strPathRelativeWpFilesEnUS . "]\r\n");
        }

        $strXmlFinal = '<?xml version="1.0" encoding="utf-8"?>
<translations language="English">' . "\r\n";

        foreach ($matchesEnUs as $assocMatchesEnUs) {
            $strPseudoFinal = toolPseudo_getPseudo($assocMatchesEnUs[3]);
            $strXmlFinal .= '    ' . $assocMatchesEnUs[1] . $strPseudoFinal . $assocMatchesEnUs[4] . "\r\n";
        }

        $strXmlFinal .= '</translations>';
        //var_dump($strXmlFinal);
        /*$pattern = '/(<([\w]+)[^>]*>)(.*?)(<\/\\2>)/';
        $replacement = "$1{$strPrefix}$3{$strSuffix}$4";
        $strXml = preg_replace($pattern, $replacement, $strXml);*/

        if (WF::getIOFactory()->getFile()->writeDataSafe($sourceEnUs, 'w+', $strXmlFinal)) {
            echo("Pseudo string prepare success : [" . $strPathRelativeWpFilesEnUS . "]\r\n");
        }
        echo "\r\n >>> SUCCESS processXMLPseudo[".$strPathWpFilesEnUS."] \r\n";
    }else{
        echo "\r\n >>> SKIP processXMLPseudo[".$strPathWpFilesEnUS."]. Reason: File already pseudo. \r\n";
    }
}
/**
 * @param $strPathWpFilesEnUS
 * @param $strPathBackup
 * @throws WFIOFileException
 */
function processJSONPseudo($strPathWpFilesEnUS, $strPathBackup)
{
    $strPathWpFilesEnUSDirname = dirname($strPathWpFilesEnUS);

    // Get content from en_US.js
    $strJSONEnUs = file_get_contents($strPathWpFilesEnUS);
    $strJSONEnUs = removeWihteLineAndChangeNOrRToNR($strJSONEnUs);
    /**
     * The following is the example result of $matchesEnUs
     * matched: <string id="title"><![CDATA[Sample_Test]]></string>
     * part 1: <string id="title">
     * part 2: string
     * part 3: <![CDATA[Sample_Test]]>
     * part 4: </string>
     *
     * matched: <string id="title">Sample_Test2</string>
     * part 1: <string id="title">
     * part 2: string
     * part 3: Sample_Test2
     * part 4: </string>
     */
    ////preg_match_all("/(<([\w]+)[^>]*>)(.*?)(<\/\\2>)/", $strJSONEnUs, $matchesEnUs, PREG_SET_ORDER);


    $stringPseudo = 'éèà';
    if (false === strpos($strJSONEnUs, $stringPseudo)) {
        echo "\r\n >>> START processJSONPseudo[".$strPathWpFilesEnUS."] \r\n";

        // Step : backup original en_US.xml to /path/to/backup/en_US.xml
        $sourceEnUs = $strPathWpFilesEnUS;
        list($tmp, $strPathRelativeWpFilesEnUS) = explode(WF::getWidgetPoolFactory()->getWPWebrootNoSlash(), $strPathWpFilesEnUS);
        $dest = $strPathBackup . '/' . WF::getWidgetPoolFactory()->getWPWebrootNoSlash() . $strPathRelativeWpFilesEnUS;
        $strPathBackupFile = $dest;
        toolPseudo_mkdirRecursive($strPathBackupFile);
        if (WF::getIOFactory()->getFile()->copyRecurse($sourceEnUs, $dest)) {
            echo("Backup source success: [" . $strPathRelativeWpFilesEnUS . "]\r\n");
        }

        $strJSONFinal = '{' . "\r\n";

        $assocJSONEnUS = json_decode($strJSONEnUs, true);

        $arrJSONFinal = array();
        if(!empty($assocJSONEnUS)){
            foreach($assocJSONEnUS as $key => $value){
                $strPseudoFinal = toolPseudo_getPseudo($value, true);
                $arrJSONFinal[] = '"'.$key.'": "'.$strPseudoFinal.'"';
            }
            $strJSONFinal .= implode(",\r\n", $arrJSONFinal);
        }

        $strJSONFinal .= '}';
        //var_dump($strJSONFinal);exit();
        /*$pattern = '/(<([\w]+)[^>]*>)(.*?)(<\/\\2>)/';
        $replacement = "$1{$strPrefix}$3{$strSuffix}$4";
        $strXml = preg_replace($pattern, $replacement, $strXml);*/

        if (WF::getIOFactory()->getFile()->writeDataSafe($sourceEnUs, 'w+', $strJSONFinal)) {
            echo("Pseudo string prepare success : [" . $strPathRelativeWpFilesEnUS . "]\r\n");
        }
        echo "\r\n >>> SUCCESS processJSONPseudo[".$strPathWpFilesEnUS."] \r\n";
    }else{
        echo "\r\n >>> SKIP processJSONPseudo[".$strPathWpFilesEnUS."]. Reason: File already pseudo. \r\n";
    }
}

foreach($arrPathWpFilesEnUS as $strPathWpFilesEnUS) {
    $ext = pathinfo($strPathWpFilesEnUS, PATHINFO_EXTENSION);
    switch($ext){
        case 'xml':
            //processXMLPseudo($strPathWpFilesEnUS, $strPathBackup,$matchesEnUs);
            processXMLPseudo($strPathWpFilesEnUS, $strPathBackup);
            break;
        case 'js':
        case 'json':
            processJSONPseudo($strPathWpFilesEnUS, $strPathBackup);
            break;
    }

}

// app/l10n/en_US.js has already been compressed into app/app.js
// Do the following items to do the pseudo file
// 1. Backup app/app.js
// 2. Get content of app/l10n/en_US.js
// 3. Put en_US.js content into app/app.js

// 1. Backup app/app.js
echo ">>> START put app/l10n/en_US.js to app/app.js\r\n";
$stringPseudo = 'éèà';
$sourceAppJS = dirname(__FILE__).'/../../../'.WF::getWidgetPoolFactory()->getWPWebrootNoSlash().'/widgetBase/modTMLS/app/app.js';
list($tmp, $strPathRelativeWpFilesAppJS) = explode(WF::getWidgetPoolFactory()->getWPWebrootNoSlash(), $sourceAppJS);
//echo '$sourceAppJS::'.$sourceAppJS."\r\n";
//echo '$strPathRelativeWpFilesAppJS::'.$strPathRelativeWpFilesAppJS."\r\n";

$destAppJS = $strPathBackup . '/' . WF::getWidgetPoolFactory()->getWPWebrootNoSlash() . $strPathRelativeWpFilesAppJS;
$destAppOriginalJS = str_replace('app.js','app-original.js', $sourceAppJS);
//echo '$destAppJS::'.$destAppJS."\r\n";
//echo '$destAppOriginalJS::'.$destAppOriginalJS."\r\n";

$contentAppJS = file_get_contents($sourceAppJS);
if (false === strpos($contentAppJS, $stringPseudo)) {
    WF::getIOFactory()->getFile()->copyRecurse($sourceAppJS, $destAppOriginalJS);
}
toolPseudo_mkdirRecursive($destAppJS);
WF::getIOFactory()->getFile()->copyRecurse($destAppOriginalJS, $destAppJS);

// 3. Put en_US.js content into app/app.js
$strContentAppJS = file_get_contents($sourceAppJS);

if(false !== strpos($strContentAppJS,'{_Comment_Common')){
    $searchKeyWord = '{_Comment_Common';
putAppEnUSJsToAppAppJs($strContentAppJS, $searchKeyWord, $sourceAppJS);
}elseif(false !== strpos($strContentAppJS,'{ "_Comment_Common')){
    $searchKeyWord = '{ "_Comment_Common';
putAppEnUSJsToAppAppJs($strContentAppJS, $searchKeyWord, $sourceAppJS);
}else{
    echo("Skip put app/l10n/en_US.js to app/app.js. Reson:Failed to find _Comment_Common in app/app.js\r\n");
    exit();
}


echo("Pseudo string prepare proccess >>>>>>>>>>> END\r\n");