github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/i18n/i18n.go (about) 1 // FIXME: see https://godoc.org/golang.org/x/text/message/catalog and compare 2 // 3 // Translation tooling for internationalized sites/pages/components. 4 // 5 // Groups and Keys 6 // 7 // Groups are logical units of translation, loosely corresponding to an application or Go package. 8 // Group names can be any Go string but it is recommended that they are alphanumeric and specifically 9 // should not including whitespace, a colon or other punctuation characters. 10 // 11 // Keys are more loose in their requirments and can either be a token or the original language text. 12 // For example, you can use "title_this_is_a_test" as a key or "This is a Test". The former is 13 // more specific and can be used to avoid confusion between the same text appearing in different 14 // contexts and thus needing different translations; whereas the latter is easier to work with when 15 // rapidly putting in content since you can avoid providing any translation and the English text 16 // will still appear correctly and a corresopnding translation file can be created later. Pick your poison, 17 // but it's recommended that the larger your project, the more you should consider using surrogate tokens 18 // for translation keys (i.e. "title_something_here" not "Something Here"). 19 // 20 // Locales 21 // 22 // While no format is strictly enforced for locale names, RFC 3066 and ISO 639 should be used. 23 // Locale names are treated as case-insensitive (implementations should simply convert to lower case) 24 // but no other change is performed on locale strings, they are otherwise compared using 25 // normal string Go equality comparison. Apply the following rules to avoid confusion: 26 // 27 // Always use the shortest possible locale string representation which accurately represents the locale. 28 // ISO 639.1 has two-letter language codes and ISO 639.2 has three-letter codes - always 29 // prefer the two-letter code where it exists. E.g. English is "en" not "eng", Finnish is 30 // "fi" not "fin", however Filipino is "fil" as there is no ISO 639.1 (two-letter) code for it. 31 // 32 // A "subtag" should be used (with a dash) where it is necessary to distinguish between language variations. 33 // E.g. if you are providing a translation in Spanish you should simply use "es" as the locale. 34 // However if a Castilian Spanish version is needed for Spain, you can use "es-es" (Spanish - Spain), 35 // "en-gb" would be used for (English - United Kingdom), and so on. ISO 3166 two-letter country 36 // codes should be used (following the "shortest possible representation" concept.) 37 // 38 package i18n 39 40 import ( 41 "log" 42 43 "github.com/gocaveman/caveman/webutil" 44 ) 45 46 // documentation points: 47 // - needs to support vastly different sizes of scale from a few dozen or hundred words to large databases of text 48 // - registration and override mechanism so translations can be plugged in 49 // - organizing translations into groups so different packages can each provide translations in an appropriate namespace 50 // and avoid collisions. 51 // - the option to use regular English or other default language text as the key or a surrogate identifier, e.g. you can 52 // use either "This is a test." or "title_this_is_a_test" as the key. Each has its pros and cons - using default text 53 // is faster to prototype with because you don't need to maintain a translations file just to see the first language; 54 // whereas using a surrogate unique id/token ensures translations are specific and individual labels for example don't 55 // lose their context. (Using "Name" as a key could be very confusing for example because it may be the same in English 56 // but different in other languages depending on the context.) 57 // - string replacements (give example) 58 // - multiple or custom storage format, files or database or other arbitrary 59 // - default behavior for how pages can easily be translated, but also customizable by replacing out just the logic of 60 // what decides the locale for a page (put name of struct or method here) 61 // - support for determining what locales a page is translated into 62 // - editor for visual translations ui, with pluggable storage mechanisms (i.e can write to file or db) 63 // - a way to debug and see where text is being pulled from (although it is quite verbose and only appropriate 64 // during debugging) 65 66 // flat file -> sqlite would be an excellent choice here (although it is actually simpler and 67 // complex queries are not required, so... we'll see - possibly in the case of large datasets, 68 // but then the build time would be bad. yeah, simple in memory cache with LRU or something 69 // if it gets too big - at least the possibility of plugging that in - probably more the way to go) 70 71 // probably want to provide something in the context that can know what the current page's 72 // locale is, with defaults, and a way to override 73 74 // maybe a registry mechanism so other things can provide translations 75 76 // at least think through what happens if they want to have one page per translation 77 // instead of using string lookups, we should facilitate that (although doing it 78 // with the metadata maybe tricky - but give that some thought too);dont' need to die 79 // over it but it needs to be feasible if desired. 80 81 //////////// AHHHHHHHH - need to have good support for string replacement/arguments - Go templating is probably 82 // a good choice here, but context needs to be figured out. 83 84 // groups also need more thought out - in cases where we do {{$t.T "Stores"}} is this supposed to have an implied group? 85 // check it check them all? should it be the "default" group? Or should we just ignore groups altogether and treat it 86 // as a flat keyspace 87 88 var ErrNotFound = webutil.ErrNotFound 89 90 type LocaleGroupTranslator interface { 91 T(s string) string // Returns the text translated into one of the target locales or returns back the same text provided as-is. 92 } 93 94 // LocaleTranslator is aware of the current list of locales for the given situation (HTTP request or other) 95 // and can translate strings into the appropriate text. 96 type LocaleTranslator interface { 97 T2(g, s string) string // Returns the text translated into one of the target locales or returns back the same text provided as-is. 98 } 99 100 type DefaultLocaleTranslator struct { 101 Locales []string 102 Translator Translator 103 } 104 105 func (t *DefaultLocaleTranslator) T(g, s string) string { 106 ret, err := t.Translator.Translate(g, s, t.Locales...) 107 if err != nil { 108 log.Printf("Error calling Translator.Translate(%q): %v", s, err) 109 } 110 return ret 111 }