langtool.pl 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. #!/usr/bin/perl
  2. # Processes language_xx.h files into language.cpp and language.h
  3. use strict;
  4. use warnings;
  5. my @langs = ("en","cz","it","es","pl");
  6. sub parselang
  7. {
  8. my ($filename) = @_;
  9. open(my $fh, '<:encoding(UTF-8)', $filename)
  10. # open(my $fh, '<', $filename)
  11. or die "Could not open file '$filename' $!";
  12. # Create a new hash reference.
  13. my $out = {};
  14. while (my $line = <$fh>) {
  15. chomp $line;
  16. next if (index($line, 'MSG') == -1);
  17. $line =~ /(?is)\#define\s*(\S*)\s*(.*)/;
  18. my $symbol = $1;
  19. my $v = $2;
  20. next if (! defined $symbol or length($symbol) == 0);
  21. # Trim whitespaces from both sides
  22. $v =~ s/^\s+|\s+$//g;
  23. #$string =~ s/" MACHINE_NAME "/Prusa i3/;
  24. $v =~ s/" FIRMWARE_URL "/https:\/\/github.com\/prusa3d\/Prusa-i3-Plus\//;
  25. $v =~ s/" PROTOCOL_VERSION "/1.0/;
  26. $v =~ s/" STRINGIFY\(EXTRUDERS\) "/1/;
  27. $v =~ s/" MACHINE_UUID "/00000000-0000-0000-0000-000000000000/;
  28. ${$out}{$symbol} = $v;
  29. }
  30. return $out;
  31. }
  32. sub pgm_is_whitespace
  33. {
  34. my ($c) = @_;
  35. if (! defined($c)) {
  36. print "pgm_is_whitespace: undefined\n";
  37. exit(1);
  38. }
  39. return $c == ord(' ') || $c == ord('\t') || $c == ord('\r') || $c == ord('\n');
  40. }
  41. sub pgm_is_interpunction
  42. {
  43. my ($c) = @_;
  44. return $c == ord('.') || $c == ord(',') || $c == ord(';') || $c == ord('?') || $c == ord('!');
  45. }
  46. sub break_text_fullscreen
  47. {
  48. my $lines = [];
  49. my ($text_str) = @_;
  50. if (! defined($text_str) || length($text_str) < 2) {
  51. return $lines;
  52. }
  53. $text_str =~ s/^"//;
  54. $text_str =~ s/([^\\])"/$1/;
  55. $text_str =~ s/\\"/"/;
  56. my @msg = unpack("W*", $text_str);
  57. #my @msg = split("", $text_str);
  58. my $len = $#msg + 1;
  59. my $i = 0;
  60. LINE:
  61. while ($i < $len) {
  62. while ($i < $len && pgm_is_whitespace($msg[$i])) {
  63. $i += 1;
  64. }
  65. if ($i == $len) {
  66. # End of the message.
  67. last LINE;
  68. }
  69. my $msgend2 = $i + ((20 > $len) ? $len : 20);
  70. my $msgend = $msgend2;
  71. if ($msgend < $len && ! pgm_is_whitespace($msg[$msgend]) && ! pgm_is_interpunction($msg[$msgend])) {
  72. # Splitting a word. Find the start of the current word.
  73. while ($msgend > $i && ! pgm_is_whitespace($msg[$msgend - 1])) {
  74. $msgend -= 1;
  75. }
  76. if ($msgend == $i) {
  77. # Found a single long word, which cannot be split. Just cut it.
  78. $msgend = $msgend2;
  79. }
  80. }
  81. my $outstr = substr($text_str, $i, $msgend - $i);
  82. $i = $msgend;
  83. $outstr =~ s/~/ /g;
  84. #print "Output string: $outstr \n";
  85. push @$lines, $outstr;
  86. }
  87. return $lines;
  88. }
  89. my %texts;
  90. my $num_languages = 0;
  91. foreach my $lang (@langs) {
  92. my $symbols = parselang("language_$lang.h");
  93. foreach my $key (keys %{$symbols}) {
  94. if (! (exists $texts{$key})) {
  95. $texts{$key} = [];
  96. }
  97. my $strings = $texts{$key};
  98. die "Symbol $key defined first in $lang, undefined in the preceding language files."
  99. if (scalar(@$strings) != $num_languages);
  100. push @$strings, ${$symbols}{$key};
  101. }
  102. $num_languages += 1;
  103. foreach my $key (keys %texts) {
  104. my $strings = $texts{$key};
  105. if (scalar(@$strings) != $num_languages) {
  106. # die "Symbol $key undefined in $lang."
  107. print "Symbol $key undefined in language \"$lang\". Using the english variant.\n";
  108. push @$strings, ${$strings}[0];
  109. }
  110. }
  111. }
  112. my $filename = 'language_all.h';
  113. open(my $fh, '>', $filename) or die "Could not open file '$filename' $!";
  114. # For the programmatic access to the program memory, read
  115. # http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html
  116. print $fh <<END
  117. #ifndef LANGUAGE_ALL_H
  118. #define LANGUAGE_ALL_H
  119. #define LANG_NUM (${num_languages})
  120. extern unsigned char lang_selected;
  121. #define LANG_TABLE_SELECT_EXPLICIT(TABLE, LANG) ((const char*)(pgm_read_ptr(TABLE + (LANG))))
  122. #define LANG_TABLE_SELECT(TABLE) LANG_TABLE_SELECT_EXPLICIT(TABLE, lang_selected)
  123. END
  124. ;
  125. foreach my $key (sort(keys %texts)) {
  126. my $strings = $texts{$key};
  127. if (@{$strings} == grep { $_ eq ${$strings}[0] } @{$strings}) {
  128. # All strings are English.
  129. print $fh "extern const char* const ${key}_LANG_TABLE[1];\n";
  130. print $fh "#define $key LANG_TABLE_SELECT_EXPLICIT(${key}_LANG_TABLE, 0)\n";
  131. } else {
  132. print $fh "extern const char* const ${key}_LANG_TABLE[LANG_NUM];\n";
  133. print $fh "#define $key LANG_TABLE_SELECT(${key}_LANG_TABLE)\n";
  134. print $fh "#define ${key}_EXPLICIT(LANG) LANG_TABLE_SELECT_EXPLICIT(${key}_LANG_TABLE, LANG)\n"
  135. if ($key eq "MSG_LANGUAGE_NAME" || $key eq "MSG_LANGUAGE_SELECT");
  136. }
  137. }
  138. print $fh <<END
  139. extern char* CAT2(const char *s1,const char *s2);
  140. extern char* CAT4(const char *s1,const char *s2,const char *s3,const char *s4);
  141. #endif //LANGUAGE_ALL.H
  142. END
  143. ;
  144. close $fh;
  145. print ".h created\n";
  146. $filename = 'language_all.cpp';
  147. open($fh, '>', $filename) or die "Could not open file '$filename' $!";
  148. print $fh <<'END'
  149. #include <avr/pgmspace.h>
  150. #include "configuration_prusa.h"
  151. #include "language_all.h"
  152. #define LCD_WIDTH 20
  153. extern unsigned char lang_selected;
  154. END
  155. ;
  156. my @keys = sort(keys %texts);
  157. foreach my $key (@keys) {
  158. my $strings = $texts{$key};
  159. if (@{$strings} == grep { $_ eq ${$strings}[0] } @{$strings}) {
  160. # Shrink the array to a single value.
  161. $strings = [${$strings}[0]];
  162. }
  163. for (my $i = 0; $i <= $#{$strings}; $i ++) {
  164. my $suffix = uc($langs[$i]);
  165. if ($i == 0 || ${$strings}[$i] ne ${$strings}[0]) {
  166. print $fh "const char ${key}_${suffix}[] PROGMEM = ${$strings}[$i];\n";
  167. }
  168. }
  169. my $langnum = $#{$strings}+1;
  170. if ($langnum == $#langs+1) {
  171. $langnum = "LANG_NUM";
  172. }
  173. print $fh "const char * const ${key}_LANG_TABLE[$langnum] PROGMEM = {\n";
  174. for (my $i = 0; $i <= $#{$strings}; $i ++) {
  175. my $suffix = uc($langs[$i]);
  176. if ($i == 0 || ${$strings}[$i] ne ${$strings}[0]) {
  177. print $fh "\t${key}_${suffix}";
  178. } else {
  179. print $fh "\t${key}_EN";
  180. }
  181. print $fh ',' if $i < $#{$strings};
  182. print $fh "\n";
  183. }
  184. print $fh "};\n\n";
  185. }
  186. print $fh <<'END'
  187. char langbuffer[LCD_WIDTH+1];
  188. char* CAT2(const char *s1,const char *s2) {
  189. unsigned char len=0;
  190. strncpy_P(langbuffer+len,s1,LCD_WIDTH-len);
  191. len+=strlen_P(s1);
  192. strncpy_P(langbuffer+len,s2,LCD_WIDTH-len);
  193. return langbuffer;
  194. }
  195. char* CAT4(const char *s1,const char *s2,const char *s3,const char *s4) {
  196. unsigned char len=0;
  197. strncpy_P(langbuffer+len,s1,LCD_WIDTH-len);
  198. len+=strlen_P(s1);
  199. strncpy_P(langbuffer+len,s2,LCD_WIDTH-len);
  200. len+=strlen_P(s2);
  201. strncpy_P(langbuffer+len,s3,LCD_WIDTH-len);
  202. len+=strlen_P(s3);
  203. strncpy_P(langbuffer+len,s4,LCD_WIDTH-len);
  204. return langbuffer;
  205. }
  206. END
  207. ;
  208. print ".cpp created.\nDone!\n";
  209. for my $lang (0 .. $#langs) {
  210. print "Language: $langs[$lang]\n";
  211. foreach my $key (@keys) {
  212. my $strings = $texts{$key};
  213. my $message = ${$strings}[$lang];
  214. if ($lang == 0 || ${$strings}[0] ne $message) {
  215. # If the language is not English, don't show the non-translated message.
  216. my $lines = break_text_fullscreen($message);
  217. my $nlines = @{$lines};
  218. if ($nlines > 1) {
  219. print "Multi-line message: $message. Breaking to $nlines lines:\n";
  220. print "\t$_\n" foreach (@{$lines});
  221. }
  222. }
  223. }
  224. }
  225. sub break_text_fullscreen