detect_translatable_string.coffee 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. module.exports = class DetectTranslatableString
  2. # coffeelint: disable=detect_translatable_string
  3. rule:
  4. name: 'detect_translatable_string'
  5. level: 'ignore'
  6. message: 'The following string looks like it should be marked as translatable via __(...)'
  7. description: '''
  8. '''
  9. constructor: ->
  10. @callTokens = []
  11. tokens: ['STRING', 'CALL_START', 'CALL_END']
  12. lintToken: (token, tokenApi) ->
  13. [type, tokenValue] = token
  14. if type in ['CALL_START', 'CALL_END']
  15. @trackCall token, tokenApi
  16. return
  17. return false if @isInIgnoredMethod()
  18. return @lintString(token, tokenApi)
  19. lintString: (token, tokenApi) ->
  20. [type, tokenValue] = token
  21. # Remove quotes.
  22. string = tokenValue[1..-2]
  23. # Ignore strings with less than two words.
  24. return false if string.split(' ').length < 2
  25. # Ignore strings that are being used as exception; unlike Ruby exceptions, these should not reach the user.
  26. return false if tokenApi.peek(-3)[1] == 'throw'
  27. return false if tokenApi.peek(-2)[1] == 'throw'
  28. return false if tokenApi.peek(-1)[1] == 'throw'
  29. # Ignore strings that are being used for comparison
  30. return false if tokenApi.peek(-1)[1] == '=='
  31. # String interpolation is handled via concatenation, ignore such strings.
  32. return false if tokenApi.peek(1)[1] == '+'
  33. return false if tokenApi.peek(2)[1] == '+'
  34. BLOCKLIST = [
  35. # Only look at strings starting with upper case letters
  36. /^[^A-Z]/,
  37. # # Ignore strings starting with three upper case letters like SELECT, POST etc.
  38. # /^[A-Z]{3}/,
  39. ]
  40. return false if BLOCKLIST.some (entry) ->
  41. #console.log([string, entry, string.match(entry), token, tokenApi.peek(-1), tokenApi.peek(1)])
  42. string.match(entry)
  43. # console.log(tokenApi.peek(-3))
  44. # console.log(tokenApi.peek(-2))
  45. # console.log(tokenApi.peek(-1))
  46. # console.log(token)
  47. return { context: "Found: #{token[1]}" }
  48. ignoredMethods: {
  49. '__': true,
  50. 'log': true,
  51. 'T': true,
  52. 'controllerBind': true,
  53. 'error': true, # App.Log.error
  54. 'set': true, # App.Config.set
  55. 'translateInline': true,
  56. 'translateContent': true,
  57. 'translatePlain': true,
  58. }
  59. isInIgnoredMethod: ->
  60. #console.log(@callTokens)
  61. for t in @callTokens
  62. return true if t.isIgnoredMethod
  63. return false
  64. trackCall: (token, tokenApi) ->
  65. if token[0] is 'CALL_START'
  66. p = tokenApi.peek(-1)
  67. token.isIgnoredMethod = p and @ignoredMethods[p[1]]
  68. @callTokens.push(token)
  69. else
  70. @callTokens.pop()
  71. return null