在 PHP 8.1 引入原生枚举之后,我们可以把一组相关的常量以及与之关联的行为放在一起。当多个枚举项都需要类似的翻译逻辑时,如果逐个写重复代码会很难维护。借助枚举自身的方法定义或 trait,就能轻松复用翻译逻辑。

在枚举中直接定义翻译方法
枚举可以像普通类一样声明方法。我们可以在每个 case 上绑定翻译映射,然后提供一个统一的方法返回当前语言的文本。
<?php
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
case Archived = 'archived';
// 翻译映射,按语言代码组织
private function translations(): array
{
return [
'zh' => [
self::Draft->name => '草稿',
self::Published->name => '已发布',
self::Archived->name => '已归档',
],
'en' => [
self::Draft->name => 'Draft',
self::Published->name => 'Published',
self::Archived->name => 'Archived',
],
];
}
// 复用翻译逻辑的方法
public function label(string $lang = 'zh'): string
{
$map = $this->translations();
return $map[$lang][$this->name] ?? $this->value;
}
}
echo Status::Published->label('zh'); // 输出:已发布
echo Status::Draft->label('en'); // 输出:Draft
使用 trait 在多个枚举间复用
如果多个枚举都需要同样的翻译机制,可以把公共方法抽到 trait 中,再让各个枚举 use 它。
<?php
trait TranslatableEnum
{
// 子类需实现此方法提供映射
abstract private function translations(): array;
public function label(string $lang = 'zh'): string
{
$map = $this->translations();
return $map[$lang][$this->name] ?? $this->value;
}
}
enum Role: string
{
use TranslatableEnum;
case Admin = 'admin';
case User = 'user';
private function translations(): array
{
return [
'zh' => [
self::Admin->name => '管理员',
self::User->name => '普通用户',
],
'en' => [
self::Admin->name => 'Admin',
self::User->name => 'User',
],
];
}
}
echo Role::Admin->label('zh'); // 输出:管理员
通过接口约束翻译行为
为了让调用方不依赖具体枚举类,可以定义接口,让枚举实现它,从而统一调用方式。
<?php
interface Localized
{
public function label(string $lang = 'zh'): string;
}
enum Category: string implements Localized
{
case Tech = 'tech';
case Life = 'life';
public function label(string $lang = 'zh'): string
{
$map = [
'zh' => [self::Tech->name => '科技', self::Life->name => '生活'],
'en' => [self::Tech->name => 'Tech', self::Life->name => 'Life'],
];
return $map[$lang][$this->name] ?? $this->value;
}
}
function showLabel(Localized $e): void
{
echo $e->label('zh');
}
showLabel(Category::Tech); // 输出:科技
小结
在 PHP 枚举中复用方法并不复杂。对于单一枚举,直接写方法最直观;多个枚举共享逻辑时用 trait 抽离;需要多态调用就配合接口。这样翻译等通用逻辑就能保持集中且易于修改。