packDir = base_path('lang-packs/g7-test-namespace-ja'); File::ensureDirectoryExists($this->packDir.'/backend/ja'); File::ensureDirectoryExists($this->packDir.'/frontend/partial'); File::put( $this->packDir.'/backend/ja/messages.php', " 'こんにちは'];\n" ); File::put( $this->packDir.'/frontend/partial/admin.json', json_encode(['settings' => ['title' => 'タイトル(JA)']], JSON_UNESCAPED_UNICODE) ); LanguagePack::create([ 'identifier' => 'g7-test-namespace-ja', 'vendor' => 'g7', 'name' => 'Test Namespace JA', 'version' => '1.0.0', 'scope' => LanguagePackScope::Module->value, 'target_identifier' => 'sirsoft-ecommerce', 'locale' => 'ja', 'locale_name' => 'Japanese', 'locale_native_name' => '日本語', 'text_direction' => 'ltr', 'is_protected' => false, 'manifest' => [ 'identifier' => 'g7-test-namespace-ja', 'scope' => 'module', 'target_identifier' => 'sirsoft-ecommerce', 'locale' => 'ja', 'requires' => ['depends_on_core_locale' => false], ], 'status' => LanguagePackStatus::Active->value, 'source_type' => LanguagePackSourceType::Bundled->value, 'installed_at' => now(), 'activated_at' => now(), ]); // Registry 캐시 무효화 (setUp 이전에 부트된 싱글톤 회피) app()->forgetInstance(LanguagePackRegistry::class); } protected function tearDown(): void { if (File::isDirectory($this->packDir)) { File::deleteDirectory($this->packDir); } parent::tearDown(); } /** * #1 회귀: MergeFrontendLanguage 가 module 팩 데이터를 target_identifier 로 wrap 한다. * * wrap 누락 시 활성 ja 팩의 admin 키가 root 에 평탄 병합되어 sirsoft-ecommerce.admin * 경로 하위가 ko 데이터만 보유하게 됨. wrap 적용 시 sirsoft-ecommerce 키 하위에 * 정확히 병합되어야 한다. */ public function test_merge_frontend_language_wraps_module_pack_with_target_identifier(): void { $merger = app(MergeFrontendLanguage::class); $merged = $merger([], 'sirsoft-admin_basic', 'ja'); $this->assertArrayHasKey('sirsoft-ecommerce', $merged, 'module 팩은 target_identifier 키 하위로 wrap 되어야 함'); $this->assertSame('タイトル(JA)', $merged['sirsoft-ecommerce']['admin']['settings']['title'] ?? null); } /** * #2 회귀: 모듈 자체 ko/en namespace 등록을 덮어쓰지 않는다. * * loadTranslationsFrom() 직접 호출 시 FileLoader hint 가 덮어써져 ko 가 raw key 로 * 떨어지지만, addNamespaceFallbackPath 사용 시 ko/en 정상 + ja 보완 모두 동작해야 한다. */ public function test_active_module_pack_does_not_overwrite_namespace_hint_for_existing_locales(): void { $translator = app('translator'); $this->assertInstanceOf(LanguagePackTranslator::class, $translator); // 모듈 자체 src/lang 등록을 시뮬레이션 (TranslationServiceProvider 가 부팅 시 수행하는 작업) // 활성 디렉토리 우선, 없으면 _bundled 로 폴백 (테스트 환경에서 ecommerce 미설치인 경우 대비) $ecommerceLangPath = base_path('modules/sirsoft-ecommerce/src/lang'); if (! is_dir($ecommerceLangPath)) { $ecommerceLangPath = base_path('modules/_bundled/sirsoft-ecommerce/src/lang'); } $translator->addNamespace('sirsoft-ecommerce', $ecommerceLangPath); // 언어팩 fallback 등록 (LanguagePackServiceProvider::registerActivePack 가 수행) $translator->addNamespaceFallbackPath( namespace: 'sirsoft-ecommerce', locale: 'ja', path: $this->packDir.'/backend/ja', ); // ko: 모듈 자체 src/lang/ko/messages.php 의 키가 보존되어야 함 (덮어쓰지 않음) app()->setLocale('ko'); $this->assertNotSame( 'sirsoft-ecommerce::messages.cart.empty', trans('sirsoft-ecommerce::messages.cart.empty'), 'ko 호출이 raw key 로 떨어지면 namespace hint 가 덮어써졌다는 증거' ); // ja: 언어팩 fallback 에서 키가 보완되어야 함 app()->setLocale('ja'); $this->assertSame('こんにちは', trans('sirsoft-ecommerce::messages.greeting')); } }