@@ -78,6 +78,9 @@ func validateDocBodyFencedLiterals(source, translated string) error {
7878 }
7979 sourceStructure := summarizeDocChunkStructure (source )
8080 translatedStructure := summarizeDocChunkStructure (translated )
81+ if ! sameI18NProtocolMarkers (source , translated ) {
82+ return fmt .Errorf ("i18n placeholder mismatch" )
83+ }
8184 if ! slices .Equal (sourceStructure .listShapes , translatedStructure .listShapes ) {
8285 return fmt .Errorf ("list structure mismatch: source=%v translated=%v" , sourceStructure .listShapes , translatedStructure .listShapes )
8386 }
@@ -236,6 +239,12 @@ func validateDocChunkTranslation(source, translated string) error {
236239 sourceLower := strings .ToLower (source )
237240 translatedLower := strings .ToLower (translated )
238241 for _ , token := range docsProtocolTokens {
242+ if token == "__OC_I18N_" {
243+ if ! sameI18NProtocolMarkers (source , translated ) {
244+ return fmt .Errorf ("protocol token leaked: %s" , token )
245+ }
246+ continue
247+ }
239248 tokenLower := strings .ToLower (token )
240249 if strings .Contains (sourceLower , tokenLower ) {
241250 continue
@@ -278,6 +287,16 @@ func validateDocChunkTranslation(source, translated string) error {
278287 return nil
279288}
280289
290+ func sameI18NProtocolMarkers (source , translated string ) bool {
291+ if ! sameStringMultiset (placeholderRe .FindAllString (source , - 1 ), placeholderRe .FindAllString (translated , - 1 )) {
292+ return false
293+ }
294+ sourceResidual := placeholderRe .ReplaceAllString (source , "" )
295+ translatedResidual := placeholderRe .ReplaceAllString (translated , "" )
296+ return strings .Count (strings .ToLower (sourceResidual ), "__oc_i18n_" ) ==
297+ strings .Count (strings .ToLower (translatedResidual ), "__oc_i18n_" )
298+ }
299+
281300func sameStringMultiset (left , right []string ) bool {
282301 if len (left ) != len (right ) {
283302 return false
0 commit comments