You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP提取带标签的TMX内容遇阻,求解决方案

基于浏览器的TMX编辑器标签提取异常问题解决

问题背景

我正在开发一款基于浏览器的TMX(翻译记忆库)编辑器,当源或目标文本段包含<bpt>/<ept>这类标签时,内容提取脚本出现异常。

包含标签的源/目标字符串示例:

<tuv xml:lang="BG">
  <seg><bpt x="1" i="1" type="italic"/>Формантът -лар/-лар- (от тюрк. -lar) е слабо представен в българската словообразувателна система.<ept i="1"/></seg>
</tuv>
<tuv xml:lang="EN-GB">
  <seg><bpt x="1" i="1" type="italic"/>The -lar/-lar- formant (from the Turkic -lar) is sparsely represented in the Bulgarian word-building system.<ept i="1"/></seg>
</tuv>

如果源/目标段无标签,提取逻辑很简单:

$sourceText = $TU['tuv'][0]['seg'];
$targetText = $TU['tuv'][1]['seg'];

但当<seg>内包含标签时,seg会被解析为数组,无法直接提取文本+标签的完整内容,最终需要保留文本与标签的结构,方便用户编辑文本、移动或删除标签。

测试代码

我的测试代码如下:

$uploadedFile = "user_files/sample_tmx.tmx";

$xmlStr = file_get_contents($uploadedFile);
$xmlObj = simplexml_load_string($xmlStr);
$arrXml = $util->objectsIntoArray($xmlObj);
$TUs = $arrXml['body']['tu'];

$pattern = '~<.*?>~';

foreach($TUs as $TU) {

    if(is_array($TU['tuv'][0]['seg'])) {

        var_dump($TU['tuv'][0]['seg']);

        $pattern = '~<.*?>~';
        $uncleanSource = $TU['tuv'][0]['seg'];
        $uncleanTarget = $TU['tuv'][1]['seg'];

        foreach($uncleanSource as $unclean) {
            //var_dump($unclean);
        }

        //$sourceText = preg_replace($pattern, "&lt;TAG&gt;", $uncleanSource);
        //$targetText = preg_replace($pattern, "&lt;TAG&gt;", $uncleanTarget);
    }
    else {
        $sourceText = $TU['tuv'][0]['seg'];
        $targetText = $TU['tuv'][1]['seg'];
    }

    echo "<p>".$sourceText." = ".$targetText."</p>";
}

示例TMX文件

测试用的sample_tmx.tmx内容:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tmx SYSTEM "tmx14.dtd" >
<tmx version="1.4">
<header creationtool="TDC Analysis Package" creationtoolversion="org.gs4tr.tm3.tmx.Version" segtype="sentence" o-tmf="unknown" adminlang="EN-US" srclang="BG" datatype="unknown" creationdate="20221006T184234Z" >
</header>
<body>
<tu creationdate="20201101T133734Z" creationid="Cheeseus" changedate="20201103T151745Z" changeid="Cheeseus" usagecount="0">
    <prop type="nextMd5Checksum">e82d0ed6d711aa59310d1e8f4478537e</prop>
    <prop type="previousMd5Checksum">39b279e324e9f6cd27351287502eefcb</prop>
    <tuv xml:lang="BG">
      <seg><bpt x="1" i="1" type="italic"/>Формантът -лар/-лар- (от тюрк. -lar) е слабо представен в българската словообразувателна система.<ept i="1"/></seg>
    </tuv>
    <tuv xml:lang="EN-GB">
      <seg><bpt x="1" i="1" type="italic"/>The -lar/-lar- formant (from the Turkic -lar) is sparsely represented in the Bulgarian word-building system.<ept i="1"/></seg>
    </tuv>
  </tu>
  <tu creationdate="20080812T111221Z" creationid="Cheeseus" changedate="20190825T065920Z" changeid="Cheeseus" usagecount="0">
    <tuv xml:lang="BG">
      <seg>ПАРТНЬОРИ</seg>
    </tuv>
    <tuv xml:lang="EN-GB">
      <seg>PARTNERS</seg>
    </tuv>
  </tu>
</body>
</tmx>

解决方案

方法1:直接获取<seg>的原始XML内容

SimpleXMLElement自带asXML()方法,可以直接获取节点的完整XML内容,无需转数组处理:

$uploadedFile = "user_files/sample_tmx.tmx";

$xmlStr = file_get_contents($uploadedFile);
$xmlObj = simplexml_load_string($xmlStr);

// 遍历所有tu节点
foreach($xmlObj->body->tu as $tu) {
    // 获取源语言和目标语言的seg节点
    $sourceSeg = $tu->tuv[0]->seg;
    $targetSeg = $tu->tuv[1]->seg;
    
    // 获取seg的完整XML内容,包含标签和文本
    $sourceText = $sourceSeg->asXML();
    $targetText = $targetSeg->asXML();
    
    // 可选:去掉外层的<seg>标签,只保留内部内容
    $sourceText = preg_replace('/^<seg>|<\/seg>$/', '', $sourceText);
    $targetText = preg_replace('/^<seg>|<\/seg>$/', '', $targetText);
    
    echo "<p>".$sourceText." = ".$targetText."</p>";
}

这种方法能直接保留所有标签和文本的原始结构,避免转数组带来的结构丢失问题,更适合后续编辑操作。

方法2:处理转数组后的seg内容

如果必须使用objectsIntoArray转成数组处理,可以遍历seg的子元素,拼接标签和文本:

function parseSegArray($segArray) {
    $result = '';
    foreach($segArray as $key => $value) {
        if(is_array($value)) {
            // 构建标签字符串,比如<bpt x="1" i="1" type="italic"/>
            $tag = "<{$key}";
            foreach($value['@attributes'] as $attrKey => $attrVal) {
                $tag .= " {$attrKey}=\"{$attrVal}\"";
            }
            $tag .= "/>";
            $result .= $tag;
        } else {
            // 普通文本直接拼接
            $result .= $value;
        }
    }
    return $result;
}

// 在循环中调用
if(is_array($TU['tuv'][0]['seg'])) {
    $sourceText = parseSegArray($TU['tuv'][0]['seg']);
    $targetText = parseSegArray($TU['tuv'][1]['seg']);
} else {
    $sourceText = $TU['tuv'][0]['seg'];
    $targetText = $TU['tuv'][1]['seg'];
}

这个函数会遍历seg数组的每个元素,将数组类型的标签转为XML字符串,文本直接拼接,最终得到完整的带标签内容。

总结

推荐使用第一种方法,直接利用SimpleXML的asXML()获取完整内容,代码更简洁,也能避免转数组带来的结构丢失问题,更适合后续的编辑操作。

内容的提问来源于stack exchange,提问作者cheeseus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 21:37:04