Files
dafuweng-saiadmin6.x/server/app/api/util/ApiLang.php

124 lines
4.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace app\api\util;
use support\Request;
/**
* API 多语言(兼容 Webman 多语言配置)
* 根据请求头 langzh=中文en=英文)返回对应文案;
* 无 lang 请求头时使用 config('translation.locale') 推断zh_CN/zh→中文en→英文
*/
class ApiLang
{
private const LANG_HEADER = 'lang';
private const LANG_EN = 'en';
private const LANG_ZH = 'zh';
/** @var array<string, array<string, string>> lang => [ key => message ] */
private static array $messages = [];
/**
* 从请求中获取语言:优先读 header lang否则按 Webman config('translation.locale') 推断
*/
public static function getLang(?Request $request = null): string
{
$request = $request ?? (function_exists('request') ? request() : null);
if ($request !== null) {
$lang = $request->header(self::LANG_HEADER);
if ($lang !== null && $lang !== '') {
$lang = strtolower(trim((string) $lang));
if ($lang === self::LANG_EN) {
return self::LANG_EN;
}
if ($lang === self::LANG_ZH || $lang === 'chs') {
return self::LANG_ZH;
}
}
}
$locale = (string) (function_exists('config') ? config('translation.locale', 'zh_CN') : 'zh_CN');
return stripos($locale, 'en') !== false ? self::LANG_EN : self::LANG_ZH;
}
/**
* 翻译文案(对外接口 message
* - 推荐:抛英文 key如 USER_NOT_FOUND根据 lang 返回对应语言
* - 兼容仍抛中文原文时lang=en 按旧映射翻译,否则原样返回
*
* 语言文件优先从 Webman config('translation.path')/api/{lang}.php 加载
*/
public static function translate(string $message, ?Request $request = null): string
{
$lang = self::getLang($request);
$map = self::loadMessages($lang);
if (isset($map[$message])) {
return (string) $map[$message];
}
// 若传入的是中文/原文,则按固定规则生成英文 keyMSG_XXXXXXXX再翻译
$key = self::toMsgKey($message);
if ($key !== null && isset($map[$key])) {
return (string) $map[$key];
}
return $message;
}
/**
* 加载某语言的 API 文案推荐key=英文value=对应语言文案)
*/
private static function loadMessages(string $locale): array
{
if (isset(self::$messages[$locale])) {
return self::$messages[$locale];
}
$path = null;
if (function_exists('config')) {
$base = rtrim((string) config('translation.path', ''), DIRECTORY_SEPARATOR);
if ($base !== '') {
$path = $base . DIRECTORY_SEPARATOR . 'api' . DIRECTORY_SEPARATOR . $locale . '.php';
}
}
if ($path !== null && is_file($path)) {
self::$messages[$locale] = require $path;
return self::$messages[$locale];
}
// 回退到 app/api/lang/{lang}.php同样使用英文 key
$fallback = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $locale . '.php';
self::$messages[$locale] = is_file($fallback) ? (require $fallback) : [];
return self::$messages[$locale];
}
/**
* 将原文转换为英文 key只包含英文字符/数字/下划线MSG_XXXXXXXXcrc32
* 若入参已经是英文 key则返回 null表示无需转换
*/
private static function toMsgKey(string $message): ?string
{
$trim = trim($message);
if ($trim === '') {
return null;
}
// 已经是英文错误码 key只允许 A-Z/0-9/_且至少 3 位)
if (preg_match('/^[A-Z0-9_]{3,}$/', $trim) === 1) {
return null;
}
return 'MSG_' . strtoupper(sprintf('%08X', crc32($trim)));
}
/**
* 带占位符的翻译,如 translateParams('当前玩家余额%s小于%s无法继续游戏', [$coin, $minCoin])
* 先翻译再替换en 文案使用 %s 占位)
*/
public static function translateParams(string $message, array $params = [], ?Request $request = null): string
{
$translated = self::translate($message, $request);
if ($params !== []) {
$translated = sprintf($translated, ...$params);
}
return $translated;
}
}