세 갈래의 결함을 한 브랜치에서 정리한다.
## 목록 컨텍스트 왕복 시 URL 상태 소실 ( @jiwonpapa 님께서 제보해주셨습니다.)
목록에서 상세·형제 상세(이전/다음)·작성/수정 폼에 다녀오면 보고 있던
page/search/category/filters 가 사라지던 문제를 전 도메인에서 수정했다.
- 엔진(engine-v1.54.2): `mergeQuery: true` 만 적고 `query` 를 생략하면 병합이
통째로 건너뛰어지던 함정을 교정 — `ActionDispatcher.handleNavigate`/`handleReplaceUrl`.
- 게시판·이커머스·페이지·회원·마이페이지·gdpr 등 9개 확장 레이아웃의 왕복 leg 전수
적용(mergeQuery: true). 의도적 리셋(검색/필터 초기화·탭 전환·프리셋)은 면제 주석으로 구분.
- 무한스크롤 목록(브랜드·상품 공통정보·고시정보)의 새로고침이 URL 검색·정렬을 떨구던
결함 수정.
- 재발 차단: audit 룰 `layout-list-context-navigate-merge-query`(목록 클러스터 자동 도출,
page/필터 URL 신호 4종) + `layout-navigate-path-absolute`(navigate path 동작 키워드 금지).
## cellChildren 등 반복 렌더에서 단일 바인딩 파이프 미적용 ( @glitter-gim 님께서 제보해주셨습니다.)
목록 표의 각 칸에 넣은 날짜·숫자 서식(`{{row.x | datetime(...)}}`)이 빈 값이 되거나
서식 없는 원본으로 나오던 문제를, 표현식 판정 로직이 엔진 전역에 복제되며 갈라진
구조적 결함으로 진단하고 판정 경로를 단일화했다(engine-v1.54.3).
- `RenderHelpers`(renderItemChildren·evaluateIfCondition)·`ConditionEvaluator`·
`DataBindingEngine.resolveObject`·`DynamicRenderer` props 5곳에 단일 바인딩 파이프 분기 추가.
- 계획: `g7-scalable-lobster.md`(렌더 경로 비대칭 결함 일괄 수정).
## 공개 문서 내부 도구 귀속 제거
release 에 포함되는 공개 문서(`docs/**`)에서 내부 audit 룰 ID 귀속 서술을
도구 비귀속 표현("정적 검사")으로 정리. 재발 차단 룰 `public-no-internal-audit-reference` 신설.
전 계층 테스트(PHPUnit·Vitest·Playwright)·회귀 테스트 동반, 버전/CHANGELOG/활성 디렉토리 동기 완료.
141 lines
5.1 KiB
Markdown
141 lines
5.1 KiB
Markdown
# Settings 카탈로그 다국어 자동 보강
|
|
|
|
## TL;DR (5초 요약)
|
|
|
|
```text
|
|
1. settings JSON 의 다국어 카탈로그 라벨(_cached_name 등)은 카탈로그 빌드 시점에 보강
|
|
2. helper: localize_catalog_field($field, $langKey) — 단일 함수 호출
|
|
3. 모듈/플러그인 Service 가 자기 빌드 메서드 안에서 직접 호출
|
|
4. lang key segment 는 settings JSON 의 식별자(id/code/key) 와 동일
|
|
5. 정적 검사가 호출 누락 검출 (경고)
|
|
```
|
|
|
|
## 사용법
|
|
|
|
### Helper
|
|
|
|
[`app/Helpers/locale_helpers.php`](../../app/Helpers/locale_helpers.php) 의 `localize_catalog_field()`:
|
|
|
|
```php
|
|
/**
|
|
* Settings 카탈로그 다국어 JSON 필드에 활성 언어팩 키 자동 보강.
|
|
*
|
|
* 운영자 편집값(비어있지 않은 값)은 보존, 부재한 locale 만 lang pack 에서 채움.
|
|
*
|
|
* @param array<string, string> $field ['ko' => '...', 'en' => '...']
|
|
* @param string $langKey 완전한 lang key (네임스페이스 prefix 포함)
|
|
* @return array<string, string> 보강된 다국어 JSON
|
|
*/
|
|
function localize_catalog_field(array $field, string $langKey): array;
|
|
```
|
|
|
|
### 모듈/플러그인 Service 사용 예 (실제 EcommerceSettingsService)
|
|
|
|
```php
|
|
private function getBuiltinPaymentMethods(): array
|
|
{
|
|
$defaults = $this->getDefaults();
|
|
$methods = $defaults['defaults']['order_settings']['payment_methods'] ?? [];
|
|
|
|
return array_map(function (array $method) {
|
|
$id = $method['id'];
|
|
|
|
return [
|
|
'id' => $id,
|
|
// 카탈로그 빌드 시점에 직접 보강
|
|
'name' => localize_catalog_field(
|
|
$method['_cached_name'] ?? ['ko' => $id, 'en' => $id],
|
|
"sirsoft-ecommerce::settings.payment_methods.{$id}.name",
|
|
),
|
|
'description' => localize_catalog_field(
|
|
$method['_cached_description'] ?? ['ko' => '', 'en' => ''],
|
|
"sirsoft-ecommerce::settings.payment_methods.{$id}.description",
|
|
),
|
|
// ...
|
|
];
|
|
}, $methods);
|
|
}
|
|
```
|
|
|
|
### 빌드 단계가 따로 없는 카탈로그 (currencies / countries)
|
|
|
|
settings 응답 빌드 메서드 안에서 inline foreach 로 호출:
|
|
|
|
```php
|
|
public function getAllSettings(): array
|
|
{
|
|
// ... settings 머지 ...
|
|
|
|
if (isset($settings['language_currency']['currencies'])) {
|
|
foreach ($settings['language_currency']['currencies'] as $idx => $currency) {
|
|
if (! empty($currency['code']) && isset($currency['name']) && is_array($currency['name'])) {
|
|
$settings['language_currency']['currencies'][$idx]['name'] = localize_catalog_field(
|
|
$currency['name'],
|
|
"sirsoft-ecommerce::settings.currencies.{$currency['code']}.name",
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $settings;
|
|
}
|
|
```
|
|
|
|
### Lang 파일 작성
|
|
|
|
`{module|plugin}/_bundled/{id}/{src/}lang/{ko,en}/settings.php`. **lang key 의 segment 는 settings JSON 의 식별자(id/code/key) 와 정확히 일치해야 함**.
|
|
|
|
```php
|
|
// modules/_bundled/sirsoft-ecommerce/src/lang/ko/settings.php
|
|
return [
|
|
'payment_methods' => [
|
|
'card' => ['name' => '신용카드', 'description' => '신용카드로 안전하게 결제'],
|
|
'dbank' => ['name' => '무통장입금', 'description' => '지정 계좌로 직접 입금'],
|
|
// settings JSON 의 id 를 그대로 사용
|
|
],
|
|
];
|
|
```
|
|
|
|
ja/zh 등 다른 locale 은 번들 lang pack 자동 빌드 (`build-language-pack.cjs`).
|
|
|
|
## 운영자 편집 보존
|
|
|
|
helper 동작:
|
|
|
|
- `field[locale]` 이 존재하고 비어있지 않으면 → 그대로 유지 (운영자 편집값 보존)
|
|
- 키 부재 또는 빈 문자열 → lang pack 에서 채움
|
|
- lang pack 에도 키 부재 → 변경 없음
|
|
|
|
운영자가 admin UI 에서 ja 라벨을 직접 입력했다면 보존. 입력 안한 locale 만 lang pack 으로 채움.
|
|
|
|
## 정적 검사
|
|
|
|
경고 수준으로 다음을 검출한다:
|
|
|
|
- `{modules|plugins}/_bundled/*/config/settings/defaults.json` 에 다국어 카탈로그 entry 발견
|
|
- 같은 확장의 `src/Services/*.php` 안에 `localize_catalog_field` 호출 없음
|
|
- → warning (호출 누락 안내)
|
|
|
|
신규 모듈/플러그인 개발자가 카탈로그 추가 시 helper 호출을 빠뜨리지 않도록 안내.
|
|
|
|
## 함정 — settings JSON id 와 lang key 일치
|
|
|
|
settings JSON 의 entry 식별자(id/code/key) 와 lang 파일의 segment 가 다르면 보강이 동작하지 않음.
|
|
|
|
```php
|
|
// defaults.json
|
|
{ "id": "dbank", "_cached_name": { "ko": "무통장입금", "en": "Bank Transfer" } }
|
|
|
|
// lang/ko/settings.php — 키가 'dbank' 이어야 함
|
|
'payment_methods' => [
|
|
'dbank' => ['name' => '무통장입금'], // ✓ id 와 일치
|
|
// 'bank_transfer' => [...] // ✗ 보강 안됨
|
|
],
|
|
```
|
|
|
|
## 참고
|
|
|
|
- Helper: [`localize_catalog_field()`](../../app/Helpers/locale_helpers.php)
|
|
- 적용 사례: [`EcommerceSettingsService::getBuiltinPaymentMethods()`](../../modules/_bundled/sirsoft-ecommerce/src/Services/EcommerceSettingsService.php), `getAllSettings()` inline 보강
|
|
- Feature 테스트: [`EcommerceSettingsLocalizationTest`](../../tests/Feature/Settings/EcommerceSettingsLocalizationTest.php)
|