使用 PHP 关联数组创建翻译 class

Creating a translation class with PHP associative array

我写了一个非常简单的翻译 class,应该 return 与我赋予它的短语相关的含义。在引擎盖下,它在构造时将 csv 的翻译加载到关联数组中。根据翻译请求,它检查数组。如果短语作为数组中的键存在,则 return 是它的值,即它的翻译。如果短语不作为键存在,它会再次从文件中加载数组(因为可能有新的翻译),再次检查键。如果它没有再次找到密钥,该短语将按原样 returned。

<?php

class Translate{

    function __construct() {       
        $this->loadTranslations();
    }

    public function get($message, $lang = "de"): string{          
        if(key_exists($message, self::$de)){           
            return self::$de[$message];
        }
        else {
            //Load translations again
            $this->loadTranslations();
            if(isset(self::$de[$message])){
                return self::$de[$message];
            }
            else {
                return $message;
            }
        }
    }


    protected static $de = [];   

    protected  function loadTranslations() {       
        $file = fopen(__DIR__ . "/../data/de.csv", "r");      
        if($file){            
            while($line = fgets($file)){               
                  $en_de = explode(":", $line);                 
                  self::$de[array_shift($en_de)] = array_shift($en_de);        
              }
          }         

          fclose($file);

    }
}

$t = new Translate();
echo $t->get("Hello") . PHP_EOL;

de.csv的内容是这样的:

"Hi": "Hallo"
"Hello": "Hallo"

问题是当要求翻译时,class 总是 return 给定的短语。当我转储数组时,短语作为键在那里,但是访问 $array[$phrase] 没有成功,因为 PHP 没有在数组中找到键!

问题在于,在您的 CSV 文件中,文本周围有引号,因此尽管 Hello 存在,但它实际上作为 "Hello" 存储在翻译数组中,因此不会匹配。

您可以重做您的翻译文件以不包含引号,或者您可以使用 fgetcsv() 的功能来阅读它并删除任何周围的引号(使用 : 作为分隔符) ...

protected  function loadTranslations() {
    $file = fopen(__DIR__ . "/a.csv", "r");
    if($file){
        while([$key, $trans] = fgetcsv($file, null, ":", '"')){
            self::$de[$key] = $trans;
        }
    }

    fclose($file);
}

只看获取翻译的代码,您可以缩短它。首先检查翻译是否已加载,然后 return 翻译 - 使用 ?? 说明是否未找到,然后 return 原始消息...

public function get($message, $lang = "de"): string{
    if(!isset(self::$de)){
        $this->loadTranslations();
    }
    return self::$de[$message] ?? $message;
}

你的 csv 在我看来更像 json。

我可能会将文件调整为永久 json,但在那之前,只需手动将其转换为 json 字符串,然后对其进行解码以创建键值对。

self::$de = json_decode(
    '{' . implode(',', file(__DIR__ . "/a.csv")) . '}',
    true
);

换句话说,使所有语言文件都有效json。这样您就可以立即对整个文件内容进行 cal json_decode() 并且数组已准备就绪。将您的文件保持为当前格式意味着单独隔离文件中的每一行文本并调用一个函数来解析它 -- 每次都要做太多的工作。

请始终如一地将您的 class 变量写在 class 的顶部。

$de 不应该是一个变量名——我假设它指的是一种特定的语言。 $lang() 应该用于指定用户所需的语言并搜索适当的文件名。


编辑:

我真的不能夸大将您的文件转换为有效文件的好处 json - 它只会让一切变得更干净。这是您的代码的重写。我不同意使用静态 class 变量,也不同意在不知道将要使用什么的情况下加载语言的构造函数。如前所述,不应该有引用特定语言的变量 ($de)。 class 变量 $translations 应该是一个包含子数组的关联数组,以便您可以永久地同时加载和访问多个翻译。

未经测试的建议:

class Translate{
    protected $translations = [];

    protected function loadTranslations($lang)
    {
        $filePath = __DIR__ . '/' . $lang . '.json';
        if (file_exists($filePath)) {
            $this->translations[$lang] = json_decode(file_get_contents($filePath), true);
        }
    }

    public function get($message, $lang = "de"): string
    {
        if (!isset($this->translations[$lang])) {
            $this->loadTranslations($lang);
        }
        return $this->translations[$lang][$message] ?? $message;
    }

    // e.g. $newTrans = ['Good Day' => 'Guten Tag', ...]
    public function set($lang, $newTrans)
    {
        if (!isset($this->translations[$lang])) {
            $this->loadTranslations($lang);
        }
        $this->translations[$lang] += $newTrans;  // insert or overwrite key-value pair(s)
        file_put_contents(__DIR__ . '/' . $lang . '.json', json_encode($this->translations[$lang]));  // commit to file
    }
}

$t = new Translate();
echo $t->get("Hello") . PHP_EOL;