timsquad 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +288 -0
- package/README.md +158 -151
- package/dist/commands/compile.d.ts +3 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/compile.js +170 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +95 -5
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/full.js +1 -0
- package/dist/commands/full.js.map +1 -1
- package/dist/commands/git/pr.js +6 -5
- package/dist/commands/git/pr.js.map +1 -1
- package/dist/commands/git/release.js +2 -7
- package/dist/commands/git/release.js.map +1 -1
- package/dist/commands/improve.js +2 -2
- package/dist/commands/improve.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/log.d.ts.map +1 -1
- package/dist/commands/log.js +2 -2
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +6 -2
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/retro.js +8 -8
- package/dist/commands/retro.js.map +1 -1
- package/dist/commands/session.js +3 -3
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/skills.d.ts +12 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +228 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/status.js +1 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +23 -1
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/daemon/entry.js +3 -3
- package/dist/daemon/entry.js.map +1 -1
- package/dist/daemon/event-queue.d.ts.map +1 -1
- package/dist/daemon/event-queue.js +2 -2
- package/dist/daemon/event-queue.js.map +1 -1
- package/dist/daemon/index.d.ts +4 -2
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +214 -52
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/jsonl-watcher.d.ts +1 -0
- package/dist/daemon/jsonl-watcher.d.ts.map +1 -1
- package/dist/daemon/jsonl-watcher.js.map +1 -1
- package/dist/daemon/meta-cache.d.ts +1 -0
- package/dist/daemon/meta-cache.d.ts.map +1 -1
- package/dist/daemon/meta-cache.js +9 -0
- package/dist/daemon/meta-cache.js.map +1 -1
- package/dist/daemon/session-notes.d.ts +33 -0
- package/dist/daemon/session-notes.d.ts.map +1 -0
- package/dist/daemon/session-notes.js +74 -0
- package/dist/daemon/session-notes.js.map +1 -0
- package/dist/daemon/session-state.d.ts +27 -0
- package/dist/daemon/session-state.d.ts.map +1 -0
- package/dist/daemon/session-state.js +165 -0
- package/dist/daemon/session-state.js.map +1 -0
- package/dist/daemon/shutdown.d.ts.map +1 -1
- package/dist/daemon/shutdown.js +9 -1
- package/dist/daemon/shutdown.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-generator.d.ts +4 -0
- package/dist/lib/agent-generator.d.ts.map +1 -1
- package/dist/lib/agent-generator.js +52 -3
- package/dist/lib/agent-generator.js.map +1 -1
- package/dist/lib/compile-rules.d.ts +66 -0
- package/dist/lib/compile-rules.d.ts.map +1 -0
- package/dist/lib/compile-rules.js +114 -0
- package/dist/lib/compile-rules.js.map +1 -0
- package/dist/lib/compiler.d.ts +105 -0
- package/dist/lib/compiler.d.ts.map +1 -0
- package/dist/lib/compiler.js +368 -0
- package/dist/lib/compiler.js.map +1 -0
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/project.d.ts.map +1 -1
- package/dist/lib/project.js +8 -3
- package/dist/lib/project.js.map +1 -1
- package/dist/lib/skill-generator.d.ts.map +1 -1
- package/dist/lib/skill-generator.js +22 -1
- package/dist/lib/skill-generator.js.map +1 -1
- package/dist/lib/template.d.ts.map +1 -1
- package/dist/lib/template.js +6 -0
- package/dist/lib/template.js.map +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +12 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/project.d.ts +1 -1
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +2 -0
- package/dist/types/project.js.map +1 -1
- package/package.json +4 -4
- package/templates/base/agents/base/tsq-architect.md +2 -2
- package/templates/base/agents/overlays/domain/mobile/_common.md +13 -0
- package/templates/base/knowledge/checklists/plan-quality.md +31 -0
- package/templates/base/knowledge/checklists/stability-verification.md +14 -0
- package/templates/base/skills/controller/SKILL.md +111 -0
- package/templates/base/skills/controller/references/README.md +35 -0
- package/templates/base/skills/controller/rules/README.md +18 -0
- package/templates/base/skills/mobile/dart/SKILL.md +69 -0
- package/templates/base/skills/mobile/dart/rules/async-patterns.md +112 -0
- package/templates/base/skills/mobile/dart/rules/code-style.md +96 -0
- package/templates/base/skills/mobile/dart/rules/null-safety.md +84 -0
- package/templates/base/skills/mobile/dart/rules/type-system.md +111 -0
- package/templates/base/skills/mobile/flutter/SKILL.md +89 -0
- package/templates/base/skills/mobile/flutter/ci-cd/SKILL.md +82 -0
- package/templates/base/skills/mobile/flutter/ci-cd/references/ci-cd-pipeline.md +314 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/code-signing.md +106 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/codemagic-setup.md +116 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/fastlane-setup.md +105 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/github-actions.md +112 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/store-deployment.md +106 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/versioning.md +107 -0
- package/templates/base/skills/mobile/flutter/i18n/SKILL.md +78 -0
- package/templates/base/skills/mobile/flutter/i18n/references/i18n-architecture.md +225 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/arb-files.md +182 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/locale-switching.md +226 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/localization-setup.md +137 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/plural-gender.md +159 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/text-direction.md +199 -0
- package/templates/base/skills/mobile/flutter/monitoring/SKILL.md +81 -0
- package/templates/base/skills/mobile/flutter/monitoring/references/monitoring-architecture.md +269 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/analytics.md +227 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/crashlytics-setup.md +195 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/logging.md +258 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/performance-monitoring.md +248 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/sentry-integration.md +249 -0
- package/templates/base/skills/mobile/flutter/networking/SKILL.md +88 -0
- package/templates/base/skills/mobile/flutter/networking/references/api-client-architecture.md +305 -0
- package/templates/base/skills/mobile/flutter/networking/rules/caching.md +212 -0
- package/templates/base/skills/mobile/flutter/networking/rules/connectivity.md +213 -0
- package/templates/base/skills/mobile/flutter/networking/rules/dio-setup.md +159 -0
- package/templates/base/skills/mobile/flutter/networking/rules/error-handling.md +209 -0
- package/templates/base/skills/mobile/flutter/networking/rules/interceptors.md +205 -0
- package/templates/base/skills/mobile/flutter/networking/rules/retrofit-patterns.md +194 -0
- package/templates/base/skills/mobile/flutter/push-notifications/SKILL.md +87 -0
- package/templates/base/skills/mobile/flutter/push-notifications/references/notification-architecture.md +340 -0
- package/templates/base/skills/mobile/flutter/push-notifications/references/platform-setup.md +286 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/background-processing.md +308 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/deep-linking.md +217 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/fcm-setup.md +164 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/local-notifications.md +262 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-handling.md +210 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-permissions.md +246 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/rich-notifications.md +320 -0
- package/templates/base/skills/mobile/flutter/references/freezed-patterns.md +162 -0
- package/templates/base/skills/mobile/flutter/references/project-structure.md +170 -0
- package/templates/base/skills/mobile/flutter/rules/animations.md +112 -0
- package/templates/base/skills/mobile/flutter/rules/architecture.md +121 -0
- package/templates/base/skills/mobile/flutter/rules/navigation-routing.md +117 -0
- package/templates/base/skills/mobile/flutter/rules/performance.md +112 -0
- package/templates/base/skills/mobile/flutter/rules/platform-adaptive.md +126 -0
- package/templates/base/skills/mobile/flutter/rules/state-management.md +110 -0
- package/templates/base/skills/mobile/flutter/rules/testing.md +131 -0
- package/templates/base/skills/mobile/flutter/rules/widget-conventions.md +122 -0
- package/templates/base/skills/mobile/flutter/security/SKILL.md +86 -0
- package/templates/base/skills/mobile/flutter/security/references/mobile-security-checklist.md +168 -0
- package/templates/base/skills/mobile/flutter/security/rules/api-key-protection.md +206 -0
- package/templates/base/skills/mobile/flutter/security/rules/authentication.md +248 -0
- package/templates/base/skills/mobile/flutter/security/rules/data-protection.md +271 -0
- package/templates/base/skills/mobile/flutter/security/rules/obfuscation.md +213 -0
- package/templates/base/skills/mobile/flutter/security/rules/secure-storage.md +171 -0
- package/templates/base/skills/mobile/flutter/security/rules/ssl-pinning.md +197 -0
- package/templates/base/skills/stability-verification/SKILL.md +64 -0
- package/templates/base/skills/stability-verification/references/release-checklist.md +34 -0
- package/templates/base/skills/stability-verification/references/security-fix-patterns.md +112 -0
- package/templates/base/skills/stability-verification/rules/verification-layers.md +67 -0
- package/templates/base/skills/stability-verification/rules/verification-workflow.md +69 -0
- package/templates/base/skills/stability-verification/scripts/verify.sh +294 -0
- package/templates/platforms/claude-code/CLAUDE.md.template +25 -0
- package/templates/platforms/claude-code/rules/build-gate.md +28 -0
- package/templates/platforms/claude-code/rules/completion-verification.md +30 -0
- package/templates/platforms/claude-code/rules/context-monitor.md +23 -0
- package/templates/platforms/claude-code/rules/plan-review.md +45 -0
- package/templates/platforms/claude-code/rules/quality-guards.md +43 -0
- package/templates/platforms/claude-code/rules/session-notes.md +18 -0
- package/templates/platforms/claude-code/rules/skill-suggest.md +27 -0
- package/templates/platforms/claude-code/scripts/build-gate.sh +73 -0
- package/templates/platforms/claude-code/scripts/completion-guard.sh +93 -0
- package/templates/platforms/claude-code/scripts/phase-guard.sh +79 -0
- package/templates/platforms/claude-code/scripts/safe-guard.sh +83 -0
- package/templates/platforms/claude-code/scripts/skill-rules.json +85 -0
- package/templates/platforms/claude-code/scripts/skill-suggest.sh +105 -0
- package/templates/platforms/claude-code/settings.json +111 -3
- package/templates/project-types/mobile-app/config.yaml +123 -0
- package/templates/project-types/mobile-app/process/workflow.xml +191 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: GitHub Actions
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: "CI 없음 → 릴리스 품질 불안정, GitHub Actions → 코드 변경마다 자동 검증"
|
|
5
|
+
tags: github-actions, ci, workflow, flutter-action, cache, artifact
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## GitHub Actions
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (CI 없음 → 릴리스 품질 불안정, GitHub Actions → 코드 변경마다 자동 검증)**
|
|
11
|
+
|
|
12
|
+
Flutter 빌드 워크플로우, flutter-action, 테스트-빌드-업로드 파이프라인, 캐시.
|
|
13
|
+
|
|
14
|
+
### 테스트 워크플로우
|
|
15
|
+
|
|
16
|
+
**Incorrect (Flutter 수동 설치, 캐시 없음):**
|
|
17
|
+
```yaml
|
|
18
|
+
on: push
|
|
19
|
+
jobs:
|
|
20
|
+
test:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- run: git clone https://github.com/flutter/flutter.git
|
|
24
|
+
- run: flutter test # 매번 전체 다운로드 → 느림
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Correct (flutter-action + 캐시):**
|
|
28
|
+
```yaml
|
|
29
|
+
# .github/workflows/test.yml
|
|
30
|
+
name: Test & Analyze
|
|
31
|
+
on:
|
|
32
|
+
pull_request:
|
|
33
|
+
branches: [main, develop]
|
|
34
|
+
concurrency:
|
|
35
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
36
|
+
cancel-in-progress: true
|
|
37
|
+
jobs:
|
|
38
|
+
test:
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
timeout-minutes: 15
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- uses: subosito/flutter-action@v2
|
|
44
|
+
with:
|
|
45
|
+
flutter-version: "3.27.0"
|
|
46
|
+
cache: true
|
|
47
|
+
- uses: actions/cache@v4
|
|
48
|
+
with:
|
|
49
|
+
path: ${{ env.PUB_CACHE }}
|
|
50
|
+
key: pub-${{ runner.os }}-${{ hashFiles('**/pubspec.lock') }}
|
|
51
|
+
- run: flutter pub get
|
|
52
|
+
- run: flutter analyze --no-fatal-infos
|
|
53
|
+
- run: flutter test --coverage
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 릴리스 워크플로우 (iOS + Android)
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
# .github/workflows/ios-release.yml — 태그 푸시 트리거
|
|
60
|
+
name: iOS Release
|
|
61
|
+
on: { push: { tags: ["v*"] } }
|
|
62
|
+
jobs:
|
|
63
|
+
ios-build:
|
|
64
|
+
runs-on: macos-latest
|
|
65
|
+
timeout-minutes: 45
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v4
|
|
68
|
+
- uses: subosito/flutter-action@v2
|
|
69
|
+
with: { flutter-version: "3.27.0", cache: true }
|
|
70
|
+
- uses: ruby/setup-ruby@v1
|
|
71
|
+
with: { ruby-version: "3.2", bundler-cache: true, working-directory: ios }
|
|
72
|
+
- run: |
|
|
73
|
+
VERSION=${GITHUB_REF#refs/tags/v}
|
|
74
|
+
flutter build ipa --release --build-name=$VERSION --build-number=$GITHUB_RUN_NUMBER
|
|
75
|
+
cd ios && bundle exec fastlane beta
|
|
76
|
+
env:
|
|
77
|
+
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
|
|
78
|
+
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
# .github/workflows/android-release.yml
|
|
83
|
+
name: Android Release
|
|
84
|
+
on: { push: { tags: ["v*"] } }
|
|
85
|
+
jobs:
|
|
86
|
+
android-build:
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
timeout-minutes: 30
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/checkout@v4
|
|
91
|
+
- uses: actions/setup-java@v4
|
|
92
|
+
with: { distribution: "temurin", java-version: "17" }
|
|
93
|
+
- uses: subosito/flutter-action@v2
|
|
94
|
+
with: { flutter-version: "3.27.0", cache: true }
|
|
95
|
+
- run: echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/release.keystore
|
|
96
|
+
- run: flutter build appbundle --release --build-number=${{ github.run_number }}
|
|
97
|
+
- uses: r0adkll/upload-google-play@v1
|
|
98
|
+
with:
|
|
99
|
+
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT }}
|
|
100
|
+
packageName: com.yourapp.id
|
|
101
|
+
releaseFiles: build/app/outputs/bundle/release/app-release.aab
|
|
102
|
+
track: internal
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 규칙
|
|
106
|
+
|
|
107
|
+
- `subosito/flutter-action@v2` — Flutter SDK 설치 + `cache: true`
|
|
108
|
+
- pub 캐시 — `actions/cache@v4` + `pubspec.lock` 해시 키
|
|
109
|
+
- `concurrency` — 동일 PR 중복 빌드 취소
|
|
110
|
+
- `timeout-minutes` — 테스트 15분, iOS 45분, Android 30분
|
|
111
|
+
- 태그 `v*` 트리거 — 릴리스 빌드, PR → 테스트만
|
|
112
|
+
- 시크릿 — `${{ secrets.* }}` 로 주입, 하드코딩 금지
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Store Deployment
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: "배포 실수 → 심사 거절/사용자 불만, 체계적 배포 → 안정적 출시"
|
|
5
|
+
tags: app-store, play-store, testflight, deployment, rollout, metadata
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Store Deployment
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (배포 실수 → 심사 거절/사용자 불만, 체계적 배포 → 안정적 출시)**
|
|
11
|
+
|
|
12
|
+
TestFlight 업로드, Play Store 트랙 관리, 메타데이터, 단계적 출시 전략.
|
|
13
|
+
|
|
14
|
+
### TestFlight (iOS) 배포
|
|
15
|
+
|
|
16
|
+
**Incorrect (Xcode에서 수동 업로드):**
|
|
17
|
+
```
|
|
18
|
+
Xcode > Archive > Distribute App → 수동 반복, 재현 불가
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (Fastlane + API Key):**
|
|
22
|
+
```ruby
|
|
23
|
+
lane :beta do
|
|
24
|
+
api_key = app_store_connect_api_key(
|
|
25
|
+
key_id: ENV["ASC_KEY_ID"],
|
|
26
|
+
issuer_id: ENV["ASC_ISSUER_ID"],
|
|
27
|
+
key_filepath: ENV["ASC_KEY_PATH"],
|
|
28
|
+
)
|
|
29
|
+
build_app(workspace: "Runner.xcworkspace", scheme: "Runner",
|
|
30
|
+
export_method: "app-store")
|
|
31
|
+
upload_to_testflight(
|
|
32
|
+
api_key: api_key,
|
|
33
|
+
skip_waiting_for_build_processing: true,
|
|
34
|
+
groups: ["Internal Testers"],
|
|
35
|
+
changelog: ENV["RELEASE_NOTES"] || "Bug fixes and improvements",
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Play Store 트랙 전략
|
|
41
|
+
|
|
42
|
+
**Incorrect (바로 production):**
|
|
43
|
+
```ruby
|
|
44
|
+
supply(track: "production", aab: "app-release.aab")
|
|
45
|
+
# → 테스트 없이 전체 배포 → 크래시 위험
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Correct (단계적 트랙 승격):**
|
|
49
|
+
```ruby
|
|
50
|
+
lane :internal do
|
|
51
|
+
supply(track: "internal", aab: "app-release.aab",
|
|
52
|
+
json_key: ENV["GOOGLE_PLAY_JSON_KEY_PATH"])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
lane :promote_production do |options|
|
|
56
|
+
supply(track: "internal", track_promote_to: "production",
|
|
57
|
+
rollout: options[:rollout] || "0.01",
|
|
58
|
+
json_key: ENV["GOOGLE_PLAY_JSON_KEY_PATH"])
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
lane :increase_rollout do |options|
|
|
62
|
+
supply(track: "production", rollout: options[:rollout],
|
|
63
|
+
json_key: ENV["GOOGLE_PLAY_JSON_KEY_PATH"])
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 단계적 출시 전략
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Day 0: internal (팀) → 크래시율/기능 확인
|
|
71
|
+
Day 1: closed testing → 소규모 외부 테스터
|
|
72
|
+
Day 3: production 1% → 크래시 모니터링
|
|
73
|
+
Day 5: production 5% → ANR 비율 확인
|
|
74
|
+
Day 7: production 20% → 사용자 피드백
|
|
75
|
+
Day 10: production 50%
|
|
76
|
+
Day 14: production 100%
|
|
77
|
+
|
|
78
|
+
중단 기준: 크래시율 > 1%, ANR > 0.5%, 평점 급락
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 메타데이터 관리
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
fastlane/metadata/
|
|
85
|
+
├── android/en-US/ # title.txt, short_description.txt, full_description.txt
|
|
86
|
+
├── android/ko/
|
|
87
|
+
├── ios/en-US/ # name.txt, subtitle.txt, description.txt, keywords.txt
|
|
88
|
+
├── ios/ko/
|
|
89
|
+
└── screenshots/en-US/ # iPhone6.5/, iPhone5.5/, iPad12.9/
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
lane :update_metadata do
|
|
94
|
+
deliver(skip_binary_upload: true, skip_screenshots: false,
|
|
95
|
+
overwrite_screenshots: true, api_key_path: "fastlane/api_key.json")
|
|
96
|
+
end
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 규칙
|
|
100
|
+
|
|
101
|
+
- TestFlight 자동 업로드 — `upload_to_testflight` + API Key, 수동 금지
|
|
102
|
+
- Play Store 트랙 순서 — internal → closed → open → production
|
|
103
|
+
- 단계적 출시 — 1%부터 시작, 크래시율 모니터링 후 확대
|
|
104
|
+
- 메타데이터 코드 관리 — `fastlane/metadata/` 텍스트 파일
|
|
105
|
+
- 릴리스 노트 — CHANGELOG에서 추출, 수동 작성 금지
|
|
106
|
+
- 크래시율 1% 초과 → 즉시 rollout 중단
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Versioning
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: "버전 관리 부재 → 빌드 추적 불가, 체계적 버전 → 출시 이력 완전 추적"
|
|
5
|
+
tags: versioning, semver, build-number, changelog, git-tag, pubspec
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Versioning
|
|
9
|
+
|
|
10
|
+
**Impact: MEDIUM (버전 관리 부재 → 빌드 추적 불가, 체계적 버전 → 출시 이력 완전 추적)**
|
|
11
|
+
|
|
12
|
+
pubspec.yaml 시맨틱 버저닝, 빌드 넘버 자동 증가, CHANGELOG 생성, git 태그 연동.
|
|
13
|
+
|
|
14
|
+
### pubspec.yaml 버전 형식
|
|
15
|
+
|
|
16
|
+
**Incorrect (빌드 넘버 누락):**
|
|
17
|
+
```yaml
|
|
18
|
+
version: 1.0.0 # → 스토어 업로드 시 구분 불가, 수동 관리 실수
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (시맨틱 버전 + 빌드 넘버):**
|
|
22
|
+
```yaml
|
|
23
|
+
version: 1.2.3+45
|
|
24
|
+
# major(1) — 호환 불가 변경
|
|
25
|
+
# minor(2) — 새 기능 (하위 호환)
|
|
26
|
+
# patch(3) — 버그 수정
|
|
27
|
+
# build(45) — CI 빌드 넘버 (스토어 구분용, 항상 증가)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 빌드 넘버 자동 증가
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# 전략 1: CI 빌드 넘버 (권장)
|
|
34
|
+
flutter build appbundle --build-number=${{ github.run_number }} # GitHub Actions
|
|
35
|
+
flutter build ipa --build-number=$PROJECT_BUILD_NUMBER # Codemagic
|
|
36
|
+
|
|
37
|
+
# 전략 2: 타임스탬프 (YYYYMMDDNN)
|
|
38
|
+
BUILD_NUMBER=$(date +%Y%m%d)$(printf "%02d" $BUILD_COUNT)
|
|
39
|
+
|
|
40
|
+
# 전략 3: Git 커밋 카운트
|
|
41
|
+
BUILD_NUMBER=$(git rev-list --count HEAD)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### CI에서 버전 오버라이드
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
TAG=${GITHUB_REF#refs/tags/v} # v1.2.3 → 1.2.3
|
|
48
|
+
flutter build ipa --release \
|
|
49
|
+
--build-name=$TAG \
|
|
50
|
+
--build-number=${{ github.run_number }}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 런타임 버전 접근
|
|
54
|
+
|
|
55
|
+
```dart
|
|
56
|
+
import 'package:package_info_plus/package_info_plus.dart';
|
|
57
|
+
|
|
58
|
+
class AppVersion {
|
|
59
|
+
static Future<String> getFullVersion() async {
|
|
60
|
+
final info = await PackageInfo.fromPlatform();
|
|
61
|
+
return '${info.version}+${info.buildNumber}'; // "1.2.3+45"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### CHANGELOG 자동 생성
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
# .github/workflows/changelog.yml (태그 푸시 시)
|
|
70
|
+
- uses: orhun/git-cliff-action@v3
|
|
71
|
+
with:
|
|
72
|
+
config: cliff.toml
|
|
73
|
+
args: --verbose
|
|
74
|
+
env:
|
|
75
|
+
OUTPUT: CHANGELOG.md
|
|
76
|
+
- uses: softprops/action-gh-release@v2
|
|
77
|
+
with:
|
|
78
|
+
body_path: CHANGELOG.md
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Git 태그 전략
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git tag -a v1.2.3 -m "Release v1.2.3"
|
|
85
|
+
git push origin v1.2.3
|
|
86
|
+
|
|
87
|
+
# 태그 규칙:
|
|
88
|
+
# v1.2.3 → production
|
|
89
|
+
# v1.2.3-rc1 → release candidate
|
|
90
|
+
# v1.2.3-beta.1 → beta (TestFlight/Internal)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 플랫폼별 주의
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
iOS: CFBundleVersion = build-number, 동일 버전 재업로드 불가
|
|
97
|
+
Android: versionCode = 정수, 감소 불가 (한번 올리면 되돌릴 수 없음)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 규칙
|
|
101
|
+
|
|
102
|
+
- `version: major.minor.patch+buildNumber` 형식 유지
|
|
103
|
+
- 빌드 넘버는 CI에서 자동 증가 — 수동 관리 금지
|
|
104
|
+
- `--build-name` + `--build-number` CI 오버라이드
|
|
105
|
+
- git 태그 `v*` — 릴리스와 1:1 매핑
|
|
106
|
+
- CHANGELOG 자동 생성 — conventional commits 기반
|
|
107
|
+
- 빌드 넘버 절대 감소 불가 — 스토어 거부
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: i18n
|
|
3
|
+
description: |
|
|
4
|
+
Flutter 국제화(i18n) 가이드라인.
|
|
5
|
+
flutter_localizations, intl, ARB 파일 관리,
|
|
6
|
+
RTL 레이아웃, 복수형/성별 처리, 런타임 로캘 전환.
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
tags: [flutter, i18n, l10n, localization, arb, intl, rtl]
|
|
9
|
+
user-invocable: false
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Internationalization (i18n) & Localization
|
|
13
|
+
|
|
14
|
+
Flutter 다국어 지원 통합 가이드.
|
|
15
|
+
ARB 파일 기반 번역 관리, ICU MessageFormat, RTL 대응, 런타임 로캘 전환.
|
|
16
|
+
|
|
17
|
+
## Philosophy
|
|
18
|
+
|
|
19
|
+
- 텍스트는 코드에 없다 — 모든 사용자 노출 문자열은 ARB 파일에
|
|
20
|
+
- 로캘은 상태 — Riverpod provider로 관리, SharedPreferences로 영속화
|
|
21
|
+
- RTL은 기본 — Directionality 위젯 활용, 하드코딩된 padding/margin 금지
|
|
22
|
+
- 번역은 프로세스 — 개발자 → 번역가 → 검수, CI에서 누락 키 자동 감지
|
|
23
|
+
|
|
24
|
+
## Resources
|
|
25
|
+
|
|
26
|
+
5개 규칙 + 1개 참조. i18n 파이프라인 전체를 커버.
|
|
27
|
+
|
|
28
|
+
| Priority | Type | Resource | Description |
|
|
29
|
+
|----------|------|----------|-------------|
|
|
30
|
+
| CRITICAL | rule | [localization-setup](rules/localization-setup.md) | flutter_localizations, intl, l10n.yaml, pubspec generate |
|
|
31
|
+
| CRITICAL | rule | [arb-files](rules/arb-files.md) | ARB 파일 구조, @placeholder, 네이밍 컨벤션, gen-l10n |
|
|
32
|
+
| HIGH | rule | [text-direction](rules/text-direction.md) | RTL 대응, EdgeInsetsDirectional, AlignmentDirectional |
|
|
33
|
+
| HIGH | rule | [plural-gender](rules/plural-gender.md) | ICU MessageFormat, plural, select, ordinal |
|
|
34
|
+
| MEDIUM | rule | [locale-switching](rules/locale-switching.md) | LocaleNotifier, SharedPreferences, 런타임 전환 |
|
|
35
|
+
| — | ref | [i18n-architecture](references/i18n-architecture.md) | 디렉토리 구조, 번역 워크플로우, CI 검증 |
|
|
36
|
+
|
|
37
|
+
## Quick Rules
|
|
38
|
+
|
|
39
|
+
### 설정
|
|
40
|
+
- `pubspec.yaml` 에 `generate: true` + `flutter_localizations`, `intl` 의존성
|
|
41
|
+
- `l10n.yaml` 로 ARB 디렉토리, 출력 클래스, 기본 로캘 지정
|
|
42
|
+
- `MaterialApp.localizationsDelegates` + `supportedLocales` 필수 설정
|
|
43
|
+
|
|
44
|
+
### ARB 파일
|
|
45
|
+
- `app_en.arb` 가 기본 (template ARB), `app_ko.arb` 등 로캘별 추가
|
|
46
|
+
- 키 네이밍: `featureName_context` (예: `matchDetail_inviteButton`)
|
|
47
|
+
- `@placeholder` 메타데이터로 동적 값 타입 명시
|
|
48
|
+
- `flutter gen-l10n` 으로 자동 생성 (`AppLocalizations` 클래스)
|
|
49
|
+
|
|
50
|
+
### RTL 대응
|
|
51
|
+
- `EdgeInsetsDirectional.only(start: 16)` (left/right 대신 start/end)
|
|
52
|
+
- `AlignmentDirectional.centerStart` (centerLeft 대신)
|
|
53
|
+
- 아이콘 미러링: `Directionality` 감지 후 조건부 Transform
|
|
54
|
+
|
|
55
|
+
### 복수형/성별
|
|
56
|
+
- ICU `{count, plural, =0{no items} =1{1 item} other{{count} items}}`
|
|
57
|
+
- `{gender, select, male{his} female{her} other{their}}`
|
|
58
|
+
- ARB 파일에 직접 ICU 구문 작성, intl이 자동 파싱
|
|
59
|
+
|
|
60
|
+
### 로캘 전환
|
|
61
|
+
- `LocaleNotifier` (Riverpod StateNotifier) → 현재 로캘 관리
|
|
62
|
+
- `SharedPreferences` 로 선택 로캘 영속화
|
|
63
|
+
- 앱 재시작 없이 `MaterialApp.locale` 변경으로 즉시 반영
|
|
64
|
+
|
|
65
|
+
## Checklist
|
|
66
|
+
|
|
67
|
+
| Priority | Item |
|
|
68
|
+
|----------|------|
|
|
69
|
+
| CRITICAL | `pubspec.yaml` 에 `generate: true` 설정 |
|
|
70
|
+
| CRITICAL | `l10n.yaml` 생성 (arb-dir, template-arb-file, output-localization-file) |
|
|
71
|
+
| CRITICAL | `MaterialApp.localizationsDelegates` 3종 등록 |
|
|
72
|
+
| CRITICAL | 모든 사용자 노출 문자열이 ARB 파일에 존재 |
|
|
73
|
+
| HIGH | `@placeholder` 메타데이터 완전성 (타입, 예시, 설명) |
|
|
74
|
+
| HIGH | RTL 레이아웃에서 Directional 위젯 사용 |
|
|
75
|
+
| HIGH | 복수형/성별 표현이 ICU MessageFormat 사용 |
|
|
76
|
+
| MEDIUM | SharedPreferences 로캘 영속화 |
|
|
77
|
+
| MEDIUM | CI에서 누락 키 자동 감지 |
|
|
78
|
+
| MEDIUM | 번역 워크플로우 문서화 (개발자 → 번역가 → 검수) |
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: i18n Architecture & Translation Workflow
|
|
3
|
+
category: reference
|
|
4
|
+
source: internal
|
|
5
|
+
tags: architecture, directory, workflow, ci, missing-keys, translation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# i18n Architecture & Translation Workflow
|
|
9
|
+
|
|
10
|
+
i18n 서비스 아키텍처. 디렉토리 구조, 번역 워크플로우, CI 자동 검증, 누락 키 감지.
|
|
11
|
+
|
|
12
|
+
## Key Concepts
|
|
13
|
+
|
|
14
|
+
- **ARB 중심**: 모든 번역의 원본은 ARB 파일 (코드에 문자열 하드코딩 금지)
|
|
15
|
+
- **코드 생성**: `flutter gen-l10n` → 타입 안전한 `AppLocalizations` 클래스 자동 생성
|
|
16
|
+
- **CI 검증**: 빌드 파이프라인에서 누락 키, 미사용 키, 포맷 오류 자동 감지
|
|
17
|
+
- **번역 프로세스**: 개발자 (키 추가) → 번역가 (번역) → 검수 (QA) → 머지
|
|
18
|
+
|
|
19
|
+
## Directory Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
lib/
|
|
23
|
+
├── l10n/
|
|
24
|
+
│ ├── app_en.arb # 기본 (template) — 모든 키 + 메타데이터
|
|
25
|
+
│ ├── app_ko.arb # 한국어
|
|
26
|
+
│ ├── app_ms.arb # 말레이어
|
|
27
|
+
│ ├── app_id.arb # 인도네시아어
|
|
28
|
+
│ └── app_zh.arb # 중국어 (간체)
|
|
29
|
+
│
|
|
30
|
+
├── core/
|
|
31
|
+
│ └── l10n/
|
|
32
|
+
│ ├── locale_notifier.dart # LocaleNotifier (Riverpod)
|
|
33
|
+
│ ├── app_locale.dart # AppLocale enum (지원 로캘 목록)
|
|
34
|
+
│ ├── l10n_extension.dart # BuildContext.l10n extension
|
|
35
|
+
│ └── locale_observer.dart # 시스템 로캘 변경 감지
|
|
36
|
+
│
|
|
37
|
+
├── features/
|
|
38
|
+
│ └── settings/
|
|
39
|
+
│ └── presentation/
|
|
40
|
+
│ └── screens/
|
|
41
|
+
│ └── language_settings_screen.dart # 언어 설정 UI
|
|
42
|
+
│
|
|
43
|
+
└── .dart_tool/
|
|
44
|
+
└── flutter_gen/
|
|
45
|
+
└── gen_l10n/ # 자동 생성 (커밋 X)
|
|
46
|
+
├── app_localizations.dart
|
|
47
|
+
├── app_localizations_en.dart
|
|
48
|
+
├── app_localizations_ko.dart
|
|
49
|
+
└── ...
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Translation Workflow
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
┌──────────────────────────────────────────────────────────┐
|
|
56
|
+
│ 번역 워크플로우 │
|
|
57
|
+
├──────────┬────────────────┬──────────────┬───────────────┤
|
|
58
|
+
│ 1. 개발자 │ 2. 번역가 │ 3. 검수 │ 4. 머지 │
|
|
59
|
+
├──────────┼────────────────┼──────────────┼───────────────┤
|
|
60
|
+
│ app_en.arb│ app_ko.arb │ 화면 확인 │ PR 승인 │
|
|
61
|
+
│ 키 추가 │ app_ms.arb │ 문맥 확인 │ CI 통과 │
|
|
62
|
+
│ @메타데이터│ app_id.arb │ 길이 확인 │ 머지 │
|
|
63
|
+
│ 설명 작성 │ 번역 작성 │ RTL 확인 │ 배포 │
|
|
64
|
+
├──────────┼────────────────┼──────────────┼───────────────┤
|
|
65
|
+
│ PR 생성 │ 번역 PR 생성 │ 리뷰 코멘트 │ gen-l10n 실행 │
|
|
66
|
+
└──────────┴────────────────┴──────────────┴───────────────┘
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 단계별 상세
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
1. 개발자 (키 추가)
|
|
73
|
+
- app_en.arb에 새 키 + @메타데이터 추가
|
|
74
|
+
- description에 화면 위치, 용도, 맥락 설명
|
|
75
|
+
- placeholder에 type, example 명시
|
|
76
|
+
- 스크린샷/화면 위치 정보 첨부 (번역가 맥락 전달)
|
|
77
|
+
|
|
78
|
+
2. 번역가 (번역)
|
|
79
|
+
- 각 로캘 ARB 파일에 번역 추가
|
|
80
|
+
- ICU 복수형/성별 규칙 적용 (언어별)
|
|
81
|
+
- placeholder 위치 조정 (언어 어순에 맞게)
|
|
82
|
+
|
|
83
|
+
3. 검수 (QA)
|
|
84
|
+
- 실제 화면에서 번역 확인 (텍스트 잘림, 줄바꿈)
|
|
85
|
+
- RTL 레이아웃 확인 (해당 시)
|
|
86
|
+
- 복수형/성별 변형 모두 확인
|
|
87
|
+
- 문화적 적절성 확인
|
|
88
|
+
|
|
89
|
+
4. 머지
|
|
90
|
+
- CI 자동 검증 통과 확인
|
|
91
|
+
- flutter gen-l10n 빌드 성공 확인
|
|
92
|
+
- 머지 후 릴리스 포함
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## CI Automated Verification
|
|
96
|
+
|
|
97
|
+
### 누락 키 감지 스크립트
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
#!/bin/bash
|
|
101
|
+
# scripts/check_l10n.sh — CI에서 실행
|
|
102
|
+
|
|
103
|
+
set -e
|
|
104
|
+
|
|
105
|
+
echo "=== i18n Key Verification ==="
|
|
106
|
+
|
|
107
|
+
# 1. gen-l10n 빌드 테스트
|
|
108
|
+
flutter gen-l10n
|
|
109
|
+
echo "✓ gen-l10n succeeded"
|
|
110
|
+
|
|
111
|
+
# 2. 누락 키 체크 (template ARB 기준)
|
|
112
|
+
TEMPLATE="lib/l10n/app_en.arb"
|
|
113
|
+
ERRORS=0
|
|
114
|
+
|
|
115
|
+
for ARB in lib/l10n/app_*.arb; do
|
|
116
|
+
if [ "$ARB" = "$TEMPLATE" ]; then continue; fi
|
|
117
|
+
|
|
118
|
+
LOCALE=$(basename "$ARB" .arb | sed 's/app_//')
|
|
119
|
+
|
|
120
|
+
# template의 비-메타 키 추출
|
|
121
|
+
TEMPLATE_KEYS=$(grep -oP '^\s*"(?!@|@@)\K[^"]+' "$TEMPLATE" | sort)
|
|
122
|
+
LOCALE_KEYS=$(grep -oP '^\s*"(?!@|@@)\K[^"]+' "$ARB" | sort)
|
|
123
|
+
|
|
124
|
+
# 누락 키 찾기
|
|
125
|
+
MISSING=$(comm -23 <(echo "$TEMPLATE_KEYS") <(echo "$LOCALE_KEYS"))
|
|
126
|
+
if [ -n "$MISSING" ]; then
|
|
127
|
+
echo "✗ $LOCALE: Missing keys:"
|
|
128
|
+
echo "$MISSING" | sed 's/^/ /'
|
|
129
|
+
ERRORS=$((ERRORS + 1))
|
|
130
|
+
else
|
|
131
|
+
echo "✓ $LOCALE: All keys present"
|
|
132
|
+
fi
|
|
133
|
+
done
|
|
134
|
+
|
|
135
|
+
if [ $ERRORS -gt 0 ]; then
|
|
136
|
+
echo "FAIL: $ERRORS locale(s) have missing keys"
|
|
137
|
+
exit 1
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
echo "=== All i18n checks passed ==="
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 미사용 키 감지
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
#!/bin/bash
|
|
147
|
+
# scripts/check_unused_l10n.sh
|
|
148
|
+
|
|
149
|
+
TEMPLATE="lib/l10n/app_en.arb"
|
|
150
|
+
UNUSED=0
|
|
151
|
+
|
|
152
|
+
# template의 키 목록 추출
|
|
153
|
+
KEYS=$(grep -oP '^\s*"(?!@|@@)\K[^"]+' "$TEMPLATE")
|
|
154
|
+
|
|
155
|
+
for KEY in $KEYS; do
|
|
156
|
+
# Dart 코드에서 사용 여부 확인 (.l10n.$KEY 또는 l10n.$KEY)
|
|
157
|
+
if ! grep -rq "\.$KEY" lib/ --include="*.dart" 2>/dev/null; then
|
|
158
|
+
echo "⚠ Potentially unused: $KEY"
|
|
159
|
+
UNUSED=$((UNUSED + 1))
|
|
160
|
+
fi
|
|
161
|
+
done
|
|
162
|
+
|
|
163
|
+
if [ $UNUSED -gt 0 ]; then
|
|
164
|
+
echo "WARNING: $UNUSED potentially unused key(s)"
|
|
165
|
+
fi
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## ARB Key Organization
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
// 키 그룹화 전략 (featureName_ 접두사로 자연 정렬)
|
|
172
|
+
{
|
|
173
|
+
"@@locale": "en",
|
|
174
|
+
|
|
175
|
+
// === Common (공통) ===
|
|
176
|
+
"common_cancelButton": "Cancel",
|
|
177
|
+
"common_confirmButton": "Confirm",
|
|
178
|
+
"common_deleteButton": "Delete",
|
|
179
|
+
"common_loadingMessage": "Loading...",
|
|
180
|
+
"common_retryButton": "Retry",
|
|
181
|
+
|
|
182
|
+
// === Error (에러) ===
|
|
183
|
+
"error_networkTimeout": "Network timeout. Please try again.",
|
|
184
|
+
"error_serverError": "Server error. Please try later.",
|
|
185
|
+
"error_unauthorized": "Please log in again.",
|
|
186
|
+
|
|
187
|
+
// === Match (매치) ===
|
|
188
|
+
"matchDetail_inviteButton": "Invite to Match",
|
|
189
|
+
"matchDetail_playerCount": "{count, plural, =0{No players} =1{1 player} other{{count} players}}",
|
|
190
|
+
"matchList_emptyState": "No matches available",
|
|
191
|
+
"matchList_title": "Available Matches",
|
|
192
|
+
|
|
193
|
+
// === Settings (설정) ===
|
|
194
|
+
"settings_languageTitle": "Language",
|
|
195
|
+
"settings_systemLanguage": "System Language"
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Common Pitfalls
|
|
200
|
+
|
|
201
|
+
1. **gen-l10n 미실행**: ARB 수정 후 코드 생성 안 하면 IDE 자동완성 미반영
|
|
202
|
+
2. **@@locale 누락**: ARB 파일에 로캘 코드 없으면 파일명에서 유추 (명시 권장)
|
|
203
|
+
3. **ICU 구문 오류**: 중괄호 불일치, other 누락 → gen-l10n 빌드 실패
|
|
204
|
+
4. **텍스트 길이**: 독일어 등 긴 번역 → UI 레이아웃 깨짐 (Expanded/Flexible 사용)
|
|
205
|
+
5. **날짜/숫자 포맷**: ARB placeholder format 미지정 → 로캘별 포맷 미적용
|
|
206
|
+
6. **컨텍스트 없는 description**: 번역가가 맥락 모르면 오역 → 화면 위치/용도 필수
|
|
207
|
+
7. **하드코딩된 문자열**: 로그, 에러 메시지도 ARB로 (사용자 노출 가능성)
|
|
208
|
+
8. **synthetic-package 이해**: `.dart_tool/` 에 생성 → `.gitignore` 에 이미 포함 (커밋 X)
|
|
209
|
+
|
|
210
|
+
## Examples
|
|
211
|
+
|
|
212
|
+
### 최소 구현 체크리스트
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
[ ] pubspec.yaml: flutter_localizations + intl + generate: true
|
|
216
|
+
[ ] l10n.yaml: arb-dir, template-arb-file, output-localization-file
|
|
217
|
+
[ ] lib/l10n/app_en.arb: 기본 키 + @메타데이터
|
|
218
|
+
[ ] lib/l10n/app_ko.arb (등): 번역 ARB
|
|
219
|
+
[ ] MaterialApp: localizationsDelegates (4종) + supportedLocales
|
|
220
|
+
[ ] core/l10n/: LocaleNotifier + AppLocale enum
|
|
221
|
+
[ ] main.dart: SharedPreferences 초기화 + ProviderScope override
|
|
222
|
+
[ ] 설정 화면: 언어 선택 UI
|
|
223
|
+
[ ] CI: 누락 키 검증 스크립트
|
|
224
|
+
[ ] 위젯 테스트: RTL 레이아웃 확인
|
|
225
|
+
```
|