check-i18n-style.escript 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. #!/usr/bin/env escript
  2. %% called from check-i18n-style.sh
  3. -mode(compile).
  4. % -define(YELLOW, "\e[33m"). % not used
  5. -define(RED, "\e[31m").
  6. -define(RESET, "\e[39m").
  7. main([Files0]) ->
  8. io:format(user, "checking i18n file styles~n", []),
  9. _ = put(errors, 0),
  10. Files = string:tokens(Files0, "\n"),
  11. ok = load_hocon(),
  12. ok = lists:foreach(fun check/1, Files),
  13. case get(errors) of
  14. 1 ->
  15. logerr("1 error found~n", []);
  16. N when is_integer(N) andalso N > 1 ->
  17. logerr("~p errors found~n", [N]);
  18. _ ->
  19. io:format(user, "~nOK~n", [])
  20. end.
  21. load_hocon() ->
  22. Dir = "_build/default/lib/hocon/ebin",
  23. File = filename:join([Dir, "hocon.beam"]),
  24. case filelib:is_regular(File) of
  25. true ->
  26. code:add_path(Dir),
  27. ok;
  28. false ->
  29. die("HOCON is not compiled in " ++ Dir ++ "~n")
  30. end.
  31. die(Msg) ->
  32. die(Msg, []).
  33. die(Msg, Args) ->
  34. ok = logerr(Msg, Args),
  35. halt(1).
  36. logerr(Fmt, Args) ->
  37. io:format(standard_error, "~n" ++ ?RED ++ "ERROR: " ++ Fmt ++ ?RESET, Args),
  38. N = get(errors),
  39. _ = put(errors, N + 1),
  40. ok.
  41. check(File) ->
  42. io:format(user, ".", []),
  43. {ok, C} = hocon:load(File),
  44. maps:foreach(fun check_one_field/2, C),
  45. ok.
  46. check_one_field(Name, Field) ->
  47. maps:foreach(fun(SubName, DescAndLabel) ->
  48. check_desc_and_label([Name, ".", SubName], DescAndLabel)
  49. end, Field).
  50. check_desc_and_label(Name, D) ->
  51. case maps:keys(D) -- [<<"desc">>, <<"label">>] of
  52. [] ->
  53. ok;
  54. Unknown ->
  55. die("~s: unknown tags ~p~n", [Name, Unknown])
  56. end,
  57. ok = check_desc(Name, D),
  58. ok = check_label(Name, D).
  59. check_label(_Name, #{<<"label">> := _Label}) ->
  60. ok;
  61. check_label(_Name, _) ->
  62. %% some may not have label
  63. ok.
  64. check_desc(Name, #{<<"desc">> := Desc}) ->
  65. do_check_desc(Name, Desc);
  66. check_desc(Name, _) ->
  67. die("~s: no 'desc'~n", [Name]).
  68. do_check_desc(Name, #{<<"zh">> := Zh, <<"en">> := En}) ->
  69. ok = check_desc_string(Name, "zh", Zh),
  70. ok = check_desc_string(Name, "en", En);
  71. do_check_desc(Name, _) ->
  72. die("~s: missing 'zh' or 'en'~n", [Name]).
  73. check_desc_string(Name, Tr, <<>>) ->
  74. logerr("~s.~s: empty string~n", [Name, Tr]);
  75. check_desc_string(Name, Tr, BinStr) ->
  76. Str = unicode:characters_to_list(BinStr, utf8),
  77. Err = fun(Reason) ->
  78. logerr("~s.~s: ~s~n", [Name, Tr, Reason])
  79. end,
  80. case Str of
  81. [$\s | _] ->
  82. Err("remove leading whitespace");
  83. [$\n | _] ->
  84. Err("remove leading line-break");
  85. "<br/>" ->
  86. Err("remove leading <br/>");
  87. "<br />" ->
  88. Err("remove leading <br />");
  89. _ ->
  90. ok
  91. end,
  92. case lists:reverse(Str) of
  93. [$\s | _] ->
  94. Err("remove trailing whitespace");
  95. [$\n | _] ->
  96. Err("remove trailing line-break");
  97. ">/rb<" ++ _ ->
  98. Err("remove trailing <br/>");
  99. ">/ rb<" ++ _ ->
  100. Err("remove trailing <br />");
  101. _ ->
  102. ok
  103. end.