@@ -8,19 +8,32 @@ import (
88func runCheck (args []string ) error {
99 fs := flag .NewFlagSet ("check" , flag .ExitOnError )
1010 locale := fs .String ("locale" , "" , "Target locale code (required)" )
11+ policy := fs .String ("policy" , "" , "Policy level: experimental, shipping (optional)" )
1112 fs .Parse (args )
1213
1314 if * locale == "" {
1415 return fmt .Errorf ("--locale is required" )
1516 }
1617
18+ if * policy != "" && * policy != "experimental" && * policy != "shipping" {
19+ return fmt .Errorf ("--policy must be experimental or shipping" )
20+ }
21+
1722 root , err := repoRoot ()
1823 if err != nil {
1924 return err
2025 }
2126
27+ if * policy != "" {
28+ return reportCheckPolicy (root , * locale , * policy )
29+ }
30+ return reportCheckBasic (root , * locale )
31+ }
32+
33+ // reportCheckBasic is the original check: unused, stale, missing counts.
34+ func reportCheckBasic (root , locale string ) error {
2235 enPath := translationsPath (root , "en-us.yaml" )
23- localePath := translationsPath (root , * locale + ".yaml" )
36+ localePath := translationsPath (root , locale + ".yaml" )
2437
2538 enKeys , err := loadYAMLFlat (enPath )
2639 if err != nil {
@@ -36,31 +49,27 @@ func runCheck(args []string) error {
3649 return err
3750 }
3851
39- // Count unused keys.
4052 unusedCount := 0
4153 for k := range enKeys {
4254 if _ , found := refs [k ]; ! found {
4355 unusedCount ++
4456 }
4557 }
4658
47- // Count stale keys.
4859 staleCount := 0
4960 for k := range localeKeys {
5061 if _ , found := enKeys [k ]; ! found {
5162 staleCount ++
5263 }
5364 }
5465
55- // Count keys missing from locale.
5666 missingCount := 0
5767 for k := range enKeys {
5868 if _ , found := localeKeys [k ]; ! found {
5969 missingCount ++
6070 }
6171 }
6272
63- // Print results.
6473 passed := true
6574 printResult := func (label string , count int ) {
6675 status := "OK"
@@ -72,12 +81,148 @@ func runCheck(args []string) error {
7281 }
7382
7483 printResult ("unused keys" , unusedCount )
75- printResult ("stale keys in " + * locale , staleCount )
76- printResult ("keys missing from " + * locale , missingCount )
84+ printResult ("stale keys in " + locale , staleCount )
85+ printResult ("keys missing from " + locale , missingCount )
7786
7887 if passed {
7988 fmt .Println ("All checks passed." )
8089 return nil
8190 }
8291 return fmt .Errorf ("checks failed" )
8392}
93+
94+ // reportCheckPolicy runs policy-aware checks on a locale.
95+ //
96+ // Experimental policy:
97+ // - manifest valid
98+ // - locale file present
99+ // - no stale keys
100+ // - validate passes
101+ // - metadata coherent
102+ //
103+ // Shipping policy (all of the above plus):
104+ // - no missing keys
105+ // - no drifted keys
106+ func reportCheckPolicy (root , locale , policy string ) error {
107+ passed := true
108+ printResult := func (label string , ok bool , detail string ) {
109+ status := "OK"
110+ if ! ok {
111+ status = "FAIL"
112+ passed = false
113+ }
114+ if detail != "" {
115+ fmt .Printf (" %-35s %s %s\n " , label + ":" , status , detail )
116+ } else {
117+ fmt .Printf (" %-35s %s\n " , label + ":" , status )
118+ }
119+ }
120+
121+ fmt .Printf ("Policy check (%s) for %s:\n " , policy , locale )
122+
123+ // Manifest valid and locale status matches policy.
124+ m , manifestErr := loadManifest (root )
125+ printResult ("manifest valid" , manifestErr == nil , errString (manifestErr ))
126+ if manifestErr == nil {
127+ localeInfo , registered := m .Locales [locale ]
128+ if ! registered {
129+ printResult ("locale registered in manifest" , false , "not found in meta/locales.yaml" )
130+ } else if policy == "shipping" && localeInfo .Status != StatusShipping && localeInfo .Status != StatusSource {
131+ printResult ("locale status matches policy" , false ,
132+ fmt .Sprintf ("locale status is %q, shipping policy requires \" shipping\" or \" source\" " , localeInfo .Status ))
133+ }
134+ }
135+
136+ // Locale file present.
137+ localePath := translationsPath (root , locale + ".yaml" )
138+ enPath := translationsPath (root , "en-us.yaml" )
139+
140+ enKeys , err := loadYAMLFlat (enPath )
141+ if err != nil {
142+ return err
143+ }
144+
145+ localeKeys , localeErr := loadYAMLFlat (localePath )
146+ printResult ("locale file readable" , localeErr == nil , errString (localeErr ))
147+ if localeErr != nil {
148+ localeKeys = make (map [string ]string )
149+ }
150+
151+ // No stale keys.
152+ staleCount := 0
153+ for k := range localeKeys {
154+ if _ , found := enKeys [k ]; ! found {
155+ staleCount ++
156+ }
157+ }
158+ printResult ("no stale keys" , staleCount == 0 , countDetail (staleCount ))
159+
160+ // Validate passes.
161+ validateErr := reportValidateQuiet (root , locale )
162+ printResult ("validate passes" , validateErr == nil , errString (validateErr ))
163+
164+ // Load metadata for the drift check below. Metadata coherence is already
165+ // covered by the "validate passes" check above (validateLocale checks it).
166+ meta , _ := loadMetadata (root , locale )
167+
168+ // Shipping-only checks.
169+ if policy == "shipping" {
170+ // No missing keys.
171+ missingCount := 0
172+ for k := range enKeys {
173+ if _ , found := localeKeys [k ]; ! found {
174+ missingCount ++
175+ }
176+ }
177+ printResult ("no missing keys" , missingCount == 0 , countDetail (missingCount ))
178+
179+ // No drifted keys.
180+ if meta != nil {
181+ driftCount := 0
182+ for k := range localeKeys {
183+ enValue , inEn := enKeys [k ]
184+ storedSource , inMeta := meta [k ]
185+ if ! inEn || ! inMeta {
186+ continue
187+ }
188+ if enValue != storedSource {
189+ driftCount ++
190+ }
191+ }
192+ printResult ("no drifted keys" , driftCount == 0 , countDetail (driftCount ))
193+ }
194+ }
195+
196+ if passed {
197+ fmt .Printf ("All %s policy checks passed for %s.\n " , policy , locale )
198+ return nil
199+ }
200+ return fmt .Errorf ("%s policy checks failed for %s" , policy , locale )
201+ }
202+
203+ // reportValidateQuiet runs validate and returns an error summary without
204+ // printing individual errors.
205+ func reportValidateQuiet (root , locale string ) error {
206+ errors , err := validateLocale (root , locale )
207+ if err != nil {
208+ return err
209+ }
210+ if len (errors ) > 0 {
211+ return fmt .Errorf ("%d validation errors" , len (errors ))
212+ }
213+ return nil
214+ }
215+
216+ func errString (err error ) string {
217+ if err == nil {
218+ return ""
219+ }
220+ return err .Error ()
221+ }
222+
223+ func countDetail (count int ) string {
224+ if count == 0 {
225+ return ""
226+ }
227+ return fmt .Sprintf ("%d found" , count )
228+ }
0 commit comments