1 /**
2
3 MultiMarkdown 6 -- MultiMarkdown - lightweight markup processor.
4
5 @file i18n.h
6
7 @brief Provide rudimentary ability to provide translation string functionality.
8 This file enables calculating hash values of built in strings in order to allow
9 swapping out certain strings based on user-specified languages at runtime.
10
11 This does slow down compiling, as multiple hash strings are compiled (seemingly
12 quite slowly). But, to my understanding and testing, it does not affect the
13 speed when actually running MMD. This should allow translating an arbitrary
14 number of strings into an arbitrary number of languages without a performance
15 penalty.
16
17
18 @author Fletcher T. Penney
19 @bug
20
21 **/
22
23 /*
24
25 Copyright © 2016 - 2017 Fletcher T. Penney.
26
27 */
28
29
30 #ifndef I18N_MULTIMARKDOWN_6_H
31 #define I18N_MULTIMARKDOWN_6_H
32
33
34 #include <string.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38
39
40 #define kNumberOfLanguages 7
41 #define kNumberOfStrings 4
42 #define kLanguage 0
43
44 //!< #define this in order to disable translations -- speeds up compiling
45 // #define I18N_DISABLED
46
47
48 // Hash function from http://lolengine.net/blog/2011/12/20/cpp-constant-string-hash
49 // via http://stackoverflow.com/questions/2826559/compile-time-preprocessor-hashing-of-string
50
51
52 #ifdef I18N_DISABLED
53 #define LC(x) x
54 #define LANG_FROM_STR(x) 0
55 #else
56 #define H1(s,i,x) (x*65599u+(uint8_t)s[(i)<strlen(s)?strlen(s)-1-(i):strlen(s)])
57 #define H4(s,i,x) H1(s,i,H1(s,i+1,H1(s,i+2,H1(s,i+3,x))))
58 #define H16(s,i,x) H4(s,i,H4(s,i+4,H4(s,i+8,H4(s,i+12,x))))
59 #define H64(s,i,x) H16(s,i,H16(s,i+16,H16(s,i+32,H16(s,i+48,x))))
60 #define H256(s,i,x) H64(s,i,H64(s,i+64,H64(s,i+128,H64(s,i+192,x))))
61
62 #define HASH(s) ((uint32_t)(H64(s,0,0)^(H64(s,0,0)>>16)))
63
64 #define LC(x) Translate(HASH(x), scratch->language)
65
66 //#define LC(x) TranslateTest(__COUNTER__, __FILE__, __LINE__, __FUNCTION__ , x)
67
68 #define LANG_FROM_STR(x) i18n_language_from_string(x)
69
70
71 // Create the dictionary array
72 static const char * lc_lookup[kNumberOfLanguages * kNumberOfStrings] = {
73 "return to body", // English
74 "Regresar al texto", // Español
75 "Zum Haupttext", // Deutsch
76 "Retour au texte principal", // Français
77 "Ga terug naar tekstlichaam", // Nederlands
78 "Återgå till textkroppen", // Svenska
79 "חזור/י לגוף הטקסט", // Hebrew - עברית
80
81 "see footnote", // English
82 "Ver nota a pie de página", // Español
83 "Siehe Fußnote", // Deutsch
84 "Voir note de bas de page", // Français
85 "Zie vootnot", // Nederlands
86 "Se fotnot", // Svenska
87 "ראה/י הערה", // Hebrew - עברית
88
89 "see citation", // English
90 "Ver referencia", // Español
91 "Siehe Zitat", // Deutsch
92 "Voir citation", // Français
93 "Zie citaat", // Nederlands
94 "Se citat", // Svenska
95 "ראה/י ציטוט", // Hebrew - עברית
96
97 "see glossary", // English
98 "Ver glosario", // Español
99 "Siehe Glossar", // Deutsch
100 "Voir glossaire", // Français
101 "Zie woordenlijst", // Nederlands
102 "Se ordlista", // Svenska
103 "ראה/י מילון מונחים", // Hebrew - עברית
104 };
105
106
107 // Used for development when a new string is added to the dictionary and
108 // we need to know the hash
TranslateTest(int c,char * file,int line,const char func[],char * x)109 static inline const char * TranslateTest(int c, char * file, int line, const char func[], char * x) {
110 fprintf(stderr, "%s: %d (%s) -> %d\n", file, line, func, c);
111 unsigned long h = HASH(x);
112 fprintf(stderr, "hash '%s' -> %lu\n", x, h);
113
114 return lc_lookup[(c * kNumberOfLanguages) + kLanguage];
115 }
116
117
118 // Given a hash and language, return the proper string
Translate(unsigned long x,int l)119 static inline const char * Translate(unsigned long x, int l) {
120 switch (x) {
121 case 3219553713:
122 return lc_lookup[0 * kNumberOfLanguages + l];
123 case 657226305:
124 return lc_lookup[1 * kNumberOfLanguages + l];
125 case 2977473004:
126 return lc_lookup[2 * kNumberOfLanguages + l];
127 case 3851221863:
128 return lc_lookup[3 * kNumberOfLanguages + l];
129 default:
130 return "localization error";
131 }
132 }
133
134
135 #endif
136
137
138 // Based on ISO 639-1 names
139 // https://en.wikipedia.org/wiki/ISO_639-1
140 enum lc_languages {
141 LC_EN = 0, //!< English is default
142 LC_ES, //!< Español
143 LC_DE, //!< Deutsch
144 LC_FR, //!< Français
145 LC_NL, //!< Nederlands
146 LC_SV, //!< Svenska
147 LC_HE, //!< Hebrew - עברית
148 };
149
150
151 // MMD expects a lower case 2 letter code in the metadata or command-line arguments
i18n_language_from_string(const char * l)152 static inline short i18n_language_from_string(const char * l) {
153 if (strcmp(l, "es") == 0) {
154 return LC_ES;
155 } else if (strcmp(l, "de") == 0) {
156 return LC_DE;
157 } else if (strcmp(l, "fr") == 0) {
158 return LC_FR;
159 } else if (strcmp(l, "he") == 0) {
160 return LC_HE;
161 } else if (strcmp(l, "nl") == 0) {
162 return LC_NL;
163 } else if (strcmp(l, "sv") == 0) {
164 return LC_SV;
165 }
166
167 return 0;
168 }
169
170 #endif
171