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