Files
2026-05-11 11:29:41 +09:00

185 lines
6.8 KiB
PHP

<?php
/**
* 다국어 라벨 해석 헬퍼.
*
* 두 가지 도메인을 단일 시그니처로 처리한다:
* - settings JSON (사용자 편집 가능): `value: ['ko'=>..., 'en'=>...]` + `fallbackKey: 'sirsoft-ecommerce::settings.countries.KR.name'`
* - registry payload (시스템 정의): `nameKey: 'notification.channels.mail.name'`
*
* @since 7.0.0-beta.4
*/
if (! function_exists('localized_label')) {
/**
* 다국어 라벨을 활성 locale 기준으로 해석합니다.
*
* 우선순위: nameKey __() > value[locale] > fallbackKey __() > value[fallback_locale] > value 첫 키
*
* @param array<string, string>|null $value ['ko' => ..., 'en' => ...] 형태 (settings JSON)
* @param string|null $nameKey lang key 직접 선언 (registry payload)
* @param string|null $fallbackKey $value 의 활성 locale 키 부재 시 __() 호출 키
* @param string|null $locale 명시 locale (기본: app()->getLocale())
*/
function localized_label(
?array $value = null,
?string $nameKey = null,
?string $fallbackKey = null,
?string $locale = null,
): string {
// Laravel app 미초기화 환경 (PHPUnit\Framework\TestCase 직접 상속 등) 도 안전하게 처리.
// 명시 인자 → app locale → fallback_locale → 'ko'
$hasLaravelApp = function (): bool {
try {
return function_exists('app') && app() instanceof \Illuminate\Contracts\Foundation\Application;
} catch (\Throwable) {
return false;
}
};
if ($locale === null) {
if ($hasLaravelApp()) {
try {
$locale = app()->getLocale();
} catch (\Throwable) {
// 무시
}
if ($locale === null) {
try {
$locale = config('app.fallback_locale', 'ko');
} catch (\Throwable) {
$locale = 'ko';
}
}
} else {
$locale = 'ko';
}
}
$tryTranslate = static function (string $key) use ($hasLaravelApp, $locale): ?string {
if (! $hasLaravelApp()) {
return null;
}
try {
$translated = __($key, [], $locale);
return is_string($translated) ? $translated : null;
} catch (\Throwable) {
return null;
}
};
// 1. nameKey 우선 (registry payload — 데이터에 다국어 JSON 없음)
if ($nameKey !== null && $nameKey !== '') {
$translated = $tryTranslate($nameKey);
if ($translated !== null && $translated !== $nameKey) {
return $translated;
}
}
// 2. value 의 활성 locale 키 (settings JSON)
if ($value !== null && isset($value[$locale]) && $value[$locale] !== '') {
return $value[$locale];
}
// 3. fallbackKey __()
if ($fallbackKey !== null && $fallbackKey !== '') {
$translated = $tryTranslate($fallbackKey);
if ($translated !== null && $translated !== $fallbackKey) {
return $translated;
}
}
// 4. value 의 fallback_locale → 첫 키
if ($value !== null) {
$fallback = 'ko';
if ($hasLaravelApp()) {
try {
$fallback = config('app.fallback_locale', 'ko');
} catch (\Throwable) {
// 'ko' 유지
}
}
if (isset($value[$fallback]) && $value[$fallback] !== '') {
return $value[$fallback];
}
$first = reset($value);
return is_string($first) ? $first : '';
}
return '';
}
}
if (! function_exists('localize_catalog_field')) {
/**
* Settings 카탈로그 다국어 JSON 필드에 활성 언어팩의 모든 활성 locale 키를 채워줍니다.
*
* 단일 string 반환이 아니라 **다국어 배열 자체** 를 반환 — settings 응답에 다국어 JSON 으로
* 그대로 노출되어야 하므로. (운영자가 admin UI 에서 모든 locale 편집 가능해야 함)
*
* 운영자 편집값(비어있지 않은 값) 은 보존, 부재한 locale 만 lang pack 에서 자동 채움.
*
* 사용 예시:
* ```php
* // EcommerceSettingsService::getBuiltinPaymentMethods() 안에서
* $cachedName = localize_catalog_field(
* $method['_cached_name'] ?? ['ko' => $id, 'en' => $id],
* "sirsoft-ecommerce::settings.payment_methods.{$id}.name",
* );
* ```
*
* @param array<string, string> $field ['ko' => '...', 'en' => '...'] 다국어 JSON
* @param string $langKey 완전한 lang key (네임스페이스 prefix 포함)
* @return array<string, string> 보강된 다국어 JSON
*
* @since 7.0.0-beta.4
*/
function localize_catalog_field(array $field, string $langKey): array
{
$locales = config('app.translatable_locales', config('app.supported_locales', ['ko', 'en']));
if (! is_array($locales) || empty($locales)) {
$locales = ['ko', 'en'];
}
foreach ($locales as $locale) {
// 운영자 편집값 보존 — 키가 존재하고 비어있지 않으면 skip
if (isset($field[$locale]) && $field[$locale] !== '') {
continue;
}
try {
$translated = __($langKey, [], $locale);
if (is_string($translated) && $translated !== $langKey) {
$field[$locale] = $translated;
}
} catch (\Throwable) {
// Laravel app 미초기화 환경 — skip
}
}
return $field;
}
}
if (! function_exists('localized_payload')) {
/**
* Registry payload 단축 helper — entry 의 {field} (다국어 JSON) 와 {field}_key (lang key) 를 자동 처리.
*
* @param array<string, mixed> $entry ['name' => [...], 'name_key' => '...']
* @param string $field 처리할 필드명 (기본: 'name')
* @param string|null $locale
*/
function localized_payload(array $entry, string $field = 'name', ?string $locale = null): string
{
$value = $entry[$field] ?? null;
$nameKey = $entry["{$field}_key"] ?? null;
return localized_label(
value: is_array($value) ? $value : null,
nameKey: is_string($nameKey) ? $nameKey : null,
locale: $locale,
);
}
}