Files
2026-07-01 10:30:32 +09:00

9.5 KiB

다국어 키 추출 (extract-i18n-keys)

레이아웃 JSON 파일에서 사용된 다국어 키를 추출하고 언어 파일과 비교하여 누락된 키를 보고합니다.

0단계: 추출 대상 판별

⚠️ CRITICAL: 추출 대상 경로 확인

대상 경로 유형

경로 패턴 유형 언어 파일 위치
modules/[vendor-module]/resources/layouts/**/*.json 모듈 레이아웃 modules/[vendor-module]/resources/lang/*.json
modules/_bundled/[vendor-module]/resources/layouts/**/*.json _bundled 모듈 레이아웃 modules/_bundled/[vendor-module]/resources/lang/*.json
templates/[vendor-template]/layouts/**/*.json 템플릿 레이아웃 templates/[vendor-template]/lang/*.json
templates/_bundled/[vendor-template]/layouts/**/*.json _bundled 템플릿 레이아웃 templates/_bundled/[vendor-template]/lang/*.json
resources/layouts/**/*.json 코어 레이아웃 lang/{ko,en}.json (+ lang/partial/{ko,en}/*.json)

$ARGUMENTS 처리

  • 경로가 지정된 경우: 해당 경로의 JSON 파일만 대상
  • 경로가 지정되지 않은 경우: 사용자에게 대상 경로 문의

1단계: 레이아웃 파일 수집

1.1 대상 파일 탐색

# 모듈 레이아웃 예시
find modules/sirsoft-ecommerce/resources/layouts -name "*.json" -type f

# 템플릿 레이아웃 예시
find templates/sirsoft-admin_basic/layouts -name "*.json" -type f

1.2 파일 목록 생성

수집된 파일 목록을 정리:

  • 메인 레이아웃 파일
  • partials 디렉토리 내 파일
  • 중첩된 모든 JSON 파일

2단계: 다국어 키 추출

2.1 추출 패턴

다음 패턴의 다국어 키를 추출합니다:

패턴 설명 예시
$t:namespace.key.path 즉시 번역 $t:sirsoft-ecommerce.admin.product.name
$t:defer:namespace.key.path 지연 번역 $t:defer:common.save

2.2 추출 정규식

// $t: 패턴 추출 (defer 포함)
const regex = /\$t:(?:defer:)?([a-zA-Z][a-zA-Z0-9_.-]*(?:\.[a-zA-Z0-9_-]+)+)/g;

2.3 키 분류

추출된 키를 네임스페이스별로 분류:

키 패턴 분류 저장 위치
sirsoft-ecommerce.* 모듈 전용 modules/sirsoft-ecommerce/resources/lang/*.json
common.* 공통 키 코어 또는 모듈 lang 파일
messages.* 메시지 코어 또는 모듈 lang 파일
currency.* 통화 코어 또는 모듈 lang 파일
validation.* 검증 코어 또는 모듈 lang 파일

2.4 모듈 네임스페이스 검증 (CRITICAL)

모듈 레이아웃에서 사용된 다국어 키가 올바른 네임스페이스를 사용하는지 검증합니다.

⚠️ CRITICAL: 모듈 레이아웃에서 다국어 키 사용 시 반드시 모듈 식별자 접두사 포함
❌ 금지 ✅ 올바른 사용
$t:common.save (모듈에서 코어 키 직접 참조) $t:sirsoft-ecommerce.common.save (모듈 식별자 접두사)
$t:admin.product.name (네임스페이스 누락) $t:sirsoft-ecommerce.admin.product.name

검증 항목:

  • 모듈 레이아웃 내 $t: 키가 해당 모듈 식별자로 시작하는지 확인
  • $t:common.* 형태로 코어 키를 직접 참조하면 경고 (모듈 lang 파일이 아닌 코어 lang에서 찾게 됨)
  • 예외: $t:common.*이 코어 키를 의도적으로 참조하는 경우 (명시적 확인 필요)

3단계: 언어 파일 분석

3.1 언어 파일 읽기

# 모듈 언어 파일
modules/[vendor-module]/resources/lang/ko.json
modules/[vendor-module]/resources/lang/en.json

# 코어 프론트엔드 언어 파일 (모듈/플러그인과 동일한 구조: 엔트리 + partial 분할)
lang/ko.json
lang/en.json
lang/partial/ko/*.json  # 선택적 — 엔트리에서 $partial 디렉티브로 참조
lang/partial/en/*.json

3.2 기존 키 추출

JSON 파일에서 모든 키 경로를 추출:

// 중첩된 객체에서 키 경로 추출
function extractKeys(obj, prefix = '') {
  const keys = [];
  for (const [key, value] of Object.entries(obj)) {
    const path = prefix ? `${prefix}.${key}` : key;
    if (typeof value === 'object' && value !== null) {
      keys.push(...extractKeys(value, path));
    } else {
      keys.push(path);
    }
  }
  return keys;
}

4단계: 비교 및 분석

4.1 누락 키 식별

레이아웃에서 사용된 키 vs 언어 파일에 존재하는 키 비교:

사용된 키 - 존재하는 키 = 누락된 키

4.2 미사용 키 식별 (선택)

언어 파일에 존재하지만 레이아웃에서 사용되지 않는 키:

존재하는 키 - 사용된 키 = 미사용 키

5단계: 결과 보고

5.1 보고서 형식

## 다국어 키 추출 결과

### 추출 대상
- 레이아웃 경로: [경로]
- 검사 파일 수: X개
- 언어 파일: ko.json, en.json

### 추출된 키 요약
- 총 사용된 키: XXX개
- 모듈 전용 키 (sirsoft-ecommerce.*): XXX개
- 공통 키 (common.*): XX개
- 메시지 키 (messages.*): XX개
- 기타 키: XX개

### 언어 파일 현황
- ko.json 존재 키: XXX개
- en.json 존재 키: XXX개

### 누락된 키 (CRITICAL)
- 총 누락: XX개 (XX.X% 누락률)

#### 모듈 전용 키 누락 (sirsoft-ecommerce.*)
| 키 | 사용 위치 |
|----|----------|
| sirsoft-ecommerce.admin.product.xxx | file.json:line |
| ... | ... |

#### 공통 키 누락 (common.*)
| 키 | 사용 위치 |
|----|----------|
| common.xxx | file.json:line |
| ... | ... |

### 미사용 키 (참고)
- 총 미사용: XX개
| 키 | 위치 |
|----|------|
| sirsoft-ecommerce.xxx.unused | ko.json |

5.2 키 생성 템플릿 제공

누락된 키에 대한 JSON 템플릿 생성:

// ko.json에 추가할 키
{
  "admin": {
    "product": {
      "missing_key_1": "[한국어 번역 필요]",
      "missing_key_2": "[한국어 번역 필요]"
    }
  }
}

// en.json에 추가할 키
{
  "admin": {
    "product": {
      "missing_key_1": "[English translation needed]",
      "missing_key_2": "[English translation needed]"
    }
  }
}

6단계: 추출 스크립트 (참고)

Node.js 추출 스크립트 예시

const fs = require('fs');
const path = require('path');

// 디렉토리 내 모든 JSON 파일 찾기
function findJsonFiles(dir, files = []) {
  const items = fs.readdirSync(dir);
  for (const item of items) {
    const fullPath = path.join(dir, item);
    if (fs.statSync(fullPath).isDirectory()) {
      findJsonFiles(fullPath, files);
    } else if (item.endsWith('.json')) {
      files.push(fullPath);
    }
  }
  return files;
}

// $t: 키 추출
function extractI18nKeys(content) {
  const regex = /\$t:(?:defer:)?([a-zA-Z][a-zA-Z0-9_.-]*(?:\.[a-zA-Z0-9_-]+)+)/g;
  const keys = new Set();
  let match;
  while ((match = regex.exec(content)) !== null) {
    keys.add(match[1]);
  }
  return keys;
}

// JSON에서 키 경로 추출
function extractExistingKeys(obj, prefix = '', namespace = '') {
  const keys = new Set();
  for (const [key, value] of Object.entries(obj)) {
    const path = prefix ? `${prefix}.${key}` : key;
    const fullPath = namespace ? `${namespace}.${path}` : path;
    if (typeof value === 'object' && value !== null) {
      const childKeys = extractExistingKeys(value, path, namespace);
      childKeys.forEach(k => keys.add(k));
    } else {
      keys.add(fullPath);
    }
  }
  return keys;
}

// 메인 실행
const layoutDir = process.argv[2] || 'modules/sirsoft-ecommerce/resources/layouts';
const langDir = process.argv[3] || 'modules/sirsoft-ecommerce/resources/lang';
const namespace = process.argv[4] || 'sirsoft-ecommerce';

// 레이아웃에서 사용된 키 추출
const layoutFiles = findJsonFiles(layoutDir);
const usedKeys = new Set();
for (const file of layoutFiles) {
  const content = fs.readFileSync(file, 'utf-8');
  const keys = extractI18nKeys(content);
  keys.forEach(k => usedKeys.add(k));
}

// 언어 파일에서 존재하는 키 추출
const koJson = JSON.parse(fs.readFileSync(path.join(langDir, 'ko.json'), 'utf-8'));
const existingKeys = extractExistingKeys(koJson, '', namespace);

// 비교
const missingKeys = [...usedKeys].filter(k => !existingKeys.has(k));
const unusedKeys = [...existingKeys].filter(k => !usedKeys.has(k));

console.log('=== 다국어 키 추출 결과 ===');
console.log(`사용된 키: ${usedKeys.size}개`);
console.log(`존재하는 키: ${existingKeys.size}개`);
console.log(`누락된 키: ${missingKeys.length}개`);
console.log(`미사용 키: ${unusedKeys.length}개`);

if (missingKeys.length > 0) {
  console.log('\n=== 누락된 키 목록 ===');
  missingKeys.sort().forEach(k => console.log(k));
}

핵심 원칙

⚠️ CRITICAL:
- 모든 $t: 패턴 추출 (defer 포함)
- 모듈 네임스페이스와 공통 키 모두 추출
- 모듈 레이아웃: $t:키에 모듈 식별자 접두사 필수 ($t:sirsoft-ecommerce.xxx)
- _bundled 디렉토리 레이아웃도 추출 대상에 포함
- 정확한 키 경로 비교 (중첩 객체 고려)
- 누락 키에 대한 실용적인 템플릿 제공
- ko.json과 en.json 모두 검사

사용 예시

# 모듈 레이아웃 키 추출
/extract-i18n-keys modules/sirsoft-ecommerce/resources/layouts

# 특정 레이아웃 파일만 추출
/extract-i18n-keys modules/sirsoft-ecommerce/resources/layouts/admin/admin_ecommerce_product_form.json

# 템플릿 레이아웃 키 추출
/extract-i18n-keys templates/sirsoft-admin_basic/layouts