@@ -224,24 +224,24 @@ func (source *Source) LocaleFiles() (LocaleFiles, error) {
224224 }
225225 }
226226
227- parser := Parser {
227+ reducer := Reducer {
228228 SourceFile : source .File ,
229229 Extension : source .Extension ,
230230 }
231- parser .Initialize ()
231+ reducer .Initialize ()
232232
233- if err := parser . Search (); err != nil {
233+ if err := reducer . Reduce (); err != nil {
234234 return nil , err
235235 }
236236
237237 var localeFiles LocaleFiles
238238 for _ , path := range filePaths {
239239
240- if ! parser .MatchesPath (path ) {
240+ if ! reducer .MatchesPath (path ) {
241241 continue
242242 }
243243
244- temporaryLocaleFile , err := parser .Eval (path )
244+ temporaryLocaleFile , err := reducer .Eval (path )
245245 if err != nil {
246246 return nil , err
247247 }
@@ -254,7 +254,7 @@ func (source *Source) LocaleFiles() (LocaleFiles, error) {
254254 if Debug {
255255 fmt .Println (fmt .Sprintf (
256256 "RFC:'%s', Name:'%s', Tag;'%s', Pattern:'%s'" ,
257- localeFile .RFC , localeFile .Name , localeFile .Tag , parser .Matcher .String (),
257+ localeFile .RFC , localeFile .Name , localeFile .Tag , reducer .Matcher .String (),
258258 ))
259259 }
260260
@@ -498,155 +498,237 @@ func (source *Source) setUploadParams(localeFile *LocaleFile) (*phraseapp.Upload
498498 return uploadParams , nil
499499}
500500
501- // Parser Algorithm, Stateful
502- // parser := &Parser{ SourceFile: file, Extension: ext }
503- // parser.Initialize()
504- // parser.Search()
505- // parser.Eval( path )
506- type Parser struct {
501+ // Print out
502+ func printSummary (summary * phraseapp.SummaryType ) {
503+ newItems := []int64 {
504+ summary .LocalesCreated ,
505+ summary .TranslationsUpdated ,
506+ summary .TranslationKeysCreated ,
507+ summary .TranslationsCreated ,
508+ }
509+ var changed bool
510+ for _ , item := range newItems {
511+ if item > 0 {
512+ changed = true
513+ }
514+ }
515+ if changed || Debug {
516+ printMessage ("Locales created: " , fmt .Sprintf ("%d" , summary .LocalesCreated ))
517+ printMessage (" - Keys created: " , fmt .Sprintf ("%d" , summary .TranslationKeysCreated ))
518+ printMessage (" - Translations created: " , fmt .Sprintf ("%d" , summary .TranslationsCreated ))
519+ printMessage (" - Translations updated: " , fmt .Sprintf ("%d" , summary .TranslationsUpdated ))
520+ fmt .Print ("\n " )
521+ }
522+ }
523+
524+ func printMessage (msg , stat string ) {
525+ fmt .Print (msg )
526+ ct .Foreground (ct .Green , true )
527+ fmt .Print (stat )
528+ ct .ResetColor ()
529+ }
530+
531+ // Reducer Algorithm:
532+ // reducer := &Reducer{
533+ // SourceFile: file,
534+ // Extension: extension,
535+ // }
536+ // reducer.Initialize()
537+ // reducer.Reduce()
538+ // if reducer.MatchesPath(path) {
539+ // reducer.Eval(path)
540+ // }
541+ type Reducer struct {
507542 SourceFile string
508543 Extension string
509544 Tokens []string
510- Buffer [] string
545+ Reductions [] * Reduction
511546 Matcher * regexp.Regexp
512547}
513548
514- func (parser * Parser ) Initialize () {
549+ type Reduction struct {
550+ Original string
551+ Matcher * regexp.Regexp
552+ }
553+
554+ func (reducer * Reducer ) Initialize () {
515555 tokens := []string {}
516- for _ , token := range strings .Split (parser .SourceFile , "/" ) {
556+ for _ , token := range strings .Split (reducer .SourceFile , "/" ) {
517557 if token == "." || token == "" {
518558 continue
519559 }
520560 tokens = append (tokens , token )
521561 }
522- parser .Tokens = tokens
523- parser .Buffer = []string {}
562+ reducer .Tokens = tokens
524563}
525564
526- func (parser * Parser ) Search () error {
527- for _ , token := range parser .Tokens {
528- head := strings .Replace (token , "." , "_PHRASEAPP_REGEXP_DOT_" , 1 )
529- // if next is part of the placeholder dsl then it can be expanded and will be
530- // converted to a regexp, else we found a path part that is already a static string
531- nextRegexp := parser .ConvertToRegexp (head )
532- if nextRegexp != "" {
533- head = nextRegexp
534-
535- // if it is the end of the path and matching the extension
536- // e.g. config/.yml we convert it to config/.*.yml
537- } else if strings .TrimSpace (token ) == parser .Extension {
538- head = ".*" + head
565+ func (reducer * Reducer ) Reduce () error {
566+ reducer .Reductions = []* Reduction {}
567+ heads := []string {}
568+ for _ , token := range reducer .Tokens {
569+ head , matcher , err := reducer .toRegexp (token )
570+ if err != nil {
571+ return err
539572 }
540-
541- head = parser .SanitizeRegexp (head )
542- head = strings .Replace (head , "_PHRASEAPP_REGEXP_DOT_" , "[.]" , 1 )
543- parser .Buffer = append (parser .Buffer , head )
573+ heads = append (heads , head )
574+ reducer .Reductions = append (reducer .Reductions , & Reduction {
575+ Original : token ,
576+ Matcher : matcher ,
577+ })
544578 }
545579
546- fileAsRegexp := strings .Join (parser .Buffer , "[/\\ \\ ]" )
547- matcherString := strings .Trim (fileAsRegexp , "[/\\ \\ ]" )
548-
549- reMatcher , err := regexp .Compile (matcherString )
580+ reMatcher , err := reducer .wholeMatcher (heads )
550581 if err != nil {
551582 return err
552583 }
553-
554- // valid regular expression
555- parser .Matcher = reMatcher
584+ reducer .Matcher = reMatcher
556585
557586 return nil
558587}
559588
560- func (parser * Parser ) ConvertToRegexp (part string ) string {
589+ func (reducer * Reducer ) MatchesPath (path string ) bool {
590+ return reducer .Matcher .MatchString (path )
591+ }
592+
593+ func (reducer * Reducer ) Eval (path string ) (* LocaleFile , error ) {
594+ tagged := reducer .unify (path )
595+
596+ name := tagged ["locale_name" ]
597+ rfc := tagged ["locale_code" ]
598+ tag := tagged ["tag" ]
599+
600+ localeFile := & LocaleFile {}
601+ if name != "" {
602+ localeFile .Name = name
603+ }
604+
605+ if rfc != "" {
606+ localeFile .RFC = rfc
607+ }
608+
609+ if tag != "" {
610+ localeFile .Tag = tag
611+ }
612+
613+ return localeFile , nil
614+ }
615+
616+ // Private Reducer Methods
617+ func (reducer * Reducer ) toRegexp (token string ) (string , * regexp.Regexp , error ) {
618+ head := strings .Replace (token , "." , "_PHRASEAPP_REGEXP_DOT_" , 1 )
619+ nextRegexp := reducer .convertToGroupRegexp (head )
620+ if nextRegexp != "" {
621+ head = nextRegexp
622+ }
623+ head = reducer .sanitizeRegexp (head )
624+ head = strings .Replace (head , "_PHRASEAPP_REGEXP_DOT_" , "[.]" , 1 )
625+ if strings .HasPrefix (head , "[.]" ) {
626+ head = ".*" + head
627+ }
628+ matcher , err := regexp .Compile (head )
629+ if err != nil {
630+ return "" , nil , err
631+ }
632+
633+ return head , matcher , nil
634+ }
635+
636+ func (reducer * Reducer ) convertToGroupRegexp (part string ) string {
561637 groups := placeholderRegexp .FindAllString (part , - 1 )
562638 if len (groups ) <= 0 {
563639 return ""
564640 }
641+
565642 for _ , group := range groups {
566643 replacer := fmt .Sprintf ("(?P%s.+)" , group )
567644 part = strings .Replace (part , group , replacer , 1 )
568645 }
646+
569647 return part
570648}
571649
572- func (parser * Parser ) SanitizeRegexp (token string ) string {
650+ func (reducer * Reducer ) sanitizeRegexp (token string ) string {
573651 newToken := strings .Replace (token , "**" , "__PHRASE_DOUBLE_STAR__" , - 1 )
574652 newToken = strings .Replace (newToken , "*" , ".*" , - 1 )
575653 newToken = strings .Replace (newToken , ".." , "." , - 1 )
576654 newToken = strings .Replace (newToken , "__PHRASE_DOUBLE_STAR__" , ".*" , - 1 )
577655 return newToken
578656}
579657
580- func (parser * Parser ) MatchesPath (path string ) bool {
581- return parser .Matcher .MatchString (path )
658+ func (reducer * Reducer ) wholeMatcher (heads []string ) (* regexp.Regexp , error ) {
659+ fileAsRegexp := strings .Join (heads , "[/\\ \\ ]" )
660+ matcherString := strings .Trim (fileAsRegexp , "[/\\ \\ ]" )
661+ reMatcher , err := regexp .Compile (matcherString )
662+ if err != nil {
663+ return nil , err
664+ }
665+ return reMatcher , nil
582666}
583667
584- func (parser * Parser ) Eval (path string ) (* LocaleFile , error ) {
585- tagged := parser .TagMatches (path )
586- name := tagged ["locale_name" ]
587- rfc := tagged ["locale_code" ]
588- tag := tagged ["tag" ]
668+ func (reducer * Reducer ) unify (path string ) map [string ]string {
669+ tagged := map [string ]string {}
589670
590- localeFile := & LocaleFile {}
591- if name != "" {
592- localeFile .Name = name
593- }
671+ tokens := strings .Split (path , "/" )
672+ reductions := reducer .Reductions
594673
595- if rfc != "" {
596- localeFile .RFC = rfc
674+ if strings .Contains (reducer .SourceFile , "**" ) || reducer .fileContainsStar () {
675+ for idx , reduction := range reductions {
676+ if reduction .Original == "**" {
677+ break
678+ }
679+ partlyTagged := reducer .tagMatches (reduction , tokens [idx ])
680+ tagged = reducer .updateTaggedMatches (tagged , partlyTagged )
681+ }
597682 }
598683
599- if tag != "" {
600- localeFile .Tag = tag
684+ offset := 0
685+ for i := len (reductions ) - 1 ; i >= 0 ; i -- {
686+ reduction := reductions [i ]
687+ idx := len (tokens ) - offset - 1
688+ partlyTagged := reducer .tagMatches (reduction , tokens [idx ])
689+ tagged = reducer .updateTaggedMatches (tagged , partlyTagged )
690+
691+ if i == 0 {
692+ break
693+ }
694+ offset += 1
601695 }
602696
603- return localeFile , nil
697+ return tagged
698+ }
699+
700+ func (reducer * Reducer ) fileContainsStar () bool {
701+ last := reducer .Reductions [len (reducer .Reductions )- 1 ]
702+ return strings .Contains (reducer .SourceFile , "*" ) && ! strings .Contains (last .Original , "*" )
604703}
605704
606- // @return named matches for the given path matching the file pattern
607- // example: {"locale_name" : "English", "locale_code" : "en-Gb", "tag" : "my_tag"}
608- func (parser * Parser ) TagMatches (path string ) map [string ]string {
705+ func (reducer * Reducer ) tagMatches (reduction * Reduction , token string ) map [string ]string {
609706 tagged := map [string ]string {}
610- namedMatches := parser .Matcher .SubexpNames ()
611- subMatches := parser .Matcher .FindStringSubmatch (path )
707+ namedMatches := reduction .Matcher .SubexpNames ()
708+ subMatches := reduction .Matcher .FindStringSubmatch (token )
612709 for i , subMatch := range subMatches {
613710 if subMatch != "" {
614- if strings .Contains (subMatch , separator ) {
615- subSlice := strings .Split (subMatch , separator )
616- subMatch = subSlice [len (subSlice )- 1 ]
617- }
618711 tagged [namedMatches [i ]] = subMatch
619712 }
620713 }
621714 return tagged
622715}
623716
624- // print out
625- func printSummary (summary * phraseapp.SummaryType ) {
626- newItems := []int64 {
627- summary .LocalesCreated ,
628- summary .TranslationsUpdated ,
629- summary .TranslationKeysCreated ,
630- summary .TranslationsCreated ,
631- }
632- var changed bool
633- for _ , item := range newItems {
634- if item > 0 {
635- changed = true
636- }
717+ func (reducer * Reducer ) updateTaggedMatches (original , updater map [string ]string ) map [string ]string {
718+ localeCode := updater ["locale_code" ]
719+ localeName := updater ["locale_name" ]
720+ tag := updater ["tag" ]
721+
722+ if original ["locale_code" ] == "" {
723+ original ["locale_code" ] = strings .Trim (localeCode , "/" )
637724 }
638- if changed || Debug {
639- printMessage ("Locales created: " , fmt .Sprintf ("%d" , summary .LocalesCreated ))
640- printMessage (" - Keys created: " , fmt .Sprintf ("%d" , summary .TranslationKeysCreated ))
641- printMessage (" - Translations created: " , fmt .Sprintf ("%d" , summary .TranslationsCreated ))
642- printMessage (" - Translations updated: " , fmt .Sprintf ("%d" , summary .TranslationsUpdated ))
643- fmt .Print ("\n " )
725+
726+ if original ["locale_name" ] == "" {
727+ original ["locale_name" ] = strings .Trim (localeName , "/" )
644728 }
645- }
646729
647- func printMessage (msg , stat string ) {
648- fmt .Print (msg )
649- ct .Foreground (ct .Green , true )
650- fmt .Print (stat )
651- ct .ResetColor ()
730+ if original ["tag" ] == "" {
731+ original ["tag" ] = strings .Trim (tag , "/" )
732+ }
733+ return original
652734}
0 commit comments