@esgettext/runtime 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +411 -0
  3. package/_bundles/runtime.js +1356 -0
  4. package/_bundles/runtime.js.map +1 -0
  5. package/_bundles/runtime.min.js +2 -0
  6. package/_bundles/runtime.min.js.map +1 -0
  7. package/api-docs/assets/highlight.css +127 -0
  8. package/api-docs/assets/main.js +59 -0
  9. package/api-docs/assets/navigation.js +1 -0
  10. package/api-docs/assets/search.js +1 -0
  11. package/api-docs/assets/style.css +1414 -0
  12. package/api-docs/classes/Textdomain.html +364 -0
  13. package/api-docs/index.html +222 -0
  14. package/api-docs/interfaces/Catalog.html +20 -0
  15. package/api-docs/interfaces/CatalogEntries.html +11 -0
  16. package/api-docs/interfaces/LocaleContainer.html +19 -0
  17. package/api-docs/interfaces/Placeholders.html +9 -0
  18. package/api-docs/modules.html +6 -0
  19. package/lib/core/browser-environment.d.ts +1 -0
  20. package/lib/core/browser-environment.js +12 -0
  21. package/lib/core/browser-environment.js.map +1 -0
  22. package/lib/core/catalog-cache.d.ts +10 -0
  23. package/lib/core/catalog-cache.js +39 -0
  24. package/lib/core/catalog-cache.js.map +1 -0
  25. package/lib/core/catalog-cache.spec.d.ts +1 -0
  26. package/lib/core/catalog-cache.spec.js +24 -0
  27. package/lib/core/catalog-cache.spec.js.map +1 -0
  28. package/lib/core/catalog-format.spec.d.ts +1 -0
  29. package/lib/core/catalog-format.spec.js +35 -0
  30. package/lib/core/catalog-format.spec.js.map +1 -0
  31. package/lib/core/catalog.d.ts +9 -0
  32. package/lib/core/catalog.js +3 -0
  33. package/lib/core/catalog.js.map +1 -0
  34. package/lib/core/data-viewlet.d.ts +11 -0
  35. package/lib/core/data-viewlet.js +57 -0
  36. package/lib/core/data-viewlet.js.map +1 -0
  37. package/lib/core/data-viewlet.spec.d.ts +1 -0
  38. package/lib/core/data-viewlet.spec.js +62 -0
  39. package/lib/core/data-viewlet.spec.js.map +1 -0
  40. package/lib/core/explode-locale.d.ts +3 -0
  41. package/lib/core/explode-locale.js +37 -0
  42. package/lib/core/explode-locale.js.map +1 -0
  43. package/lib/core/explode-locale.spec.d.ts +1 -0
  44. package/lib/core/explode-locale.spec.js +41 -0
  45. package/lib/core/explode-locale.spec.js.map +1 -0
  46. package/lib/core/germanic-plural.d.ts +1 -0
  47. package/lib/core/germanic-plural.js +8 -0
  48. package/lib/core/germanic-plural.js.map +1 -0
  49. package/lib/core/gettext-impl.d.ts +10 -0
  50. package/lib/core/gettext-impl.js +38 -0
  51. package/lib/core/gettext-impl.js.map +1 -0
  52. package/lib/core/gettext.spec.d.ts +1 -0
  53. package/lib/core/gettext.spec.js +391 -0
  54. package/lib/core/gettext.spec.js.map +1 -0
  55. package/lib/core/index.d.ts +4 -0
  56. package/lib/core/index.js +21 -0
  57. package/lib/core/index.js.map +1 -0
  58. package/lib/core/locale-container.d.ts +8 -0
  59. package/lib/core/locale-container.js +3 -0
  60. package/lib/core/locale-container.js.map +1 -0
  61. package/lib/core/path-separator.d.ts +1 -0
  62. package/lib/core/path-separator.js +12 -0
  63. package/lib/core/path-separator.js.map +1 -0
  64. package/lib/core/resolve-impl.d.ts +3 -0
  65. package/lib/core/resolve-impl.js +253 -0
  66. package/lib/core/resolve-impl.js.map +1 -0
  67. package/lib/core/resolve.spec.d.ts +1 -0
  68. package/lib/core/resolve.spec.js +345 -0
  69. package/lib/core/resolve.spec.js.map +1 -0
  70. package/lib/core/select-locale.d.ts +1 -0
  71. package/lib/core/select-locale.js +43 -0
  72. package/lib/core/select-locale.js.map +1 -0
  73. package/lib/core/select-locale.spec.d.ts +1 -0
  74. package/lib/core/select-locale.spec.js +27 -0
  75. package/lib/core/select-locale.spec.js.map +1 -0
  76. package/lib/core/set-locale-browser.spec.d.ts +1 -0
  77. package/lib/core/set-locale-browser.spec.js +17 -0
  78. package/lib/core/set-locale-browser.spec.js.map +1 -0
  79. package/lib/core/set-locale-node.spec.d.ts +1 -0
  80. package/lib/core/set-locale-node.spec.js +26 -0
  81. package/lib/core/set-locale-node.spec.js.map +1 -0
  82. package/lib/core/split-locale.d.ts +7 -0
  83. package/lib/core/split-locale.js +39 -0
  84. package/lib/core/split-locale.js.map +1 -0
  85. package/lib/core/split-locale.spec.d.ts +1 -0
  86. package/lib/core/split-locale.spec.js +59 -0
  87. package/lib/core/split-locale.spec.js.map +1 -0
  88. package/lib/core/textdomain.d.ts +55 -0
  89. package/lib/core/textdomain.js +277 -0
  90. package/lib/core/textdomain.js.map +1 -0
  91. package/lib/core/textdomain.spec.d.ts +1 -0
  92. package/lib/core/textdomain.spec.js +33 -0
  93. package/lib/core/textdomain.spec.js.map +1 -0
  94. package/lib/core/user-locales.d.ts +1 -0
  95. package/lib/core/user-locales.js +12 -0
  96. package/lib/core/user-locales.js.map +1 -0
  97. package/lib/index-browser.d.ts +2 -0
  98. package/lib/index-browser.js +44 -0
  99. package/lib/index-browser.js.map +1 -0
  100. package/lib/index.d.ts +2 -0
  101. package/lib/index.js +37 -0
  102. package/lib/index.js.map +1 -0
  103. package/lib/parser/index.d.ts +2 -0
  104. package/lib/parser/index.js +19 -0
  105. package/lib/parser/index.js.map +1 -0
  106. package/lib/parser/parse-json-catalog.d.ts +3 -0
  107. package/lib/parser/parse-json-catalog.js +37 -0
  108. package/lib/parser/parse-json-catalog.js.map +1 -0
  109. package/lib/parser/parse-json-catalog.spec.d.ts +1 -0
  110. package/lib/parser/parse-json-catalog.spec.js +41 -0
  111. package/lib/parser/parse-json-catalog.spec.js.map +1 -0
  112. package/lib/parser/parse-mo-catalog.d.ts +2 -0
  113. package/lib/parser/parse-mo-catalog.js +86 -0
  114. package/lib/parser/parse-mo-catalog.js.map +1 -0
  115. package/lib/parser/parse-mo-catalog.spec.d.ts +1 -0
  116. package/lib/parser/parse-mo-catalog.spec.js +115 -0
  117. package/lib/parser/parse-mo-catalog.spec.js.map +1 -0
  118. package/lib/transport/fs.d.ts +4 -0
  119. package/lib/transport/fs.js +18 -0
  120. package/lib/transport/fs.js.map +1 -0
  121. package/lib/transport/http.d.ts +4 -0
  122. package/lib/transport/http.js +27 -0
  123. package/lib/transport/http.js.map +1 -0
  124. package/lib/transport/http.spec.d.ts +1 -0
  125. package/lib/transport/http.spec.js +119 -0
  126. package/lib/transport/http.spec.js.map +1 -0
  127. package/lib/transport/index.d.ts +3 -0
  128. package/lib/transport/index.js +20 -0
  129. package/lib/transport/index.js.map +1 -0
  130. package/lib/transport/transport.interface.d.ts +3 -0
  131. package/lib/transport/transport.interface.js +3 -0
  132. package/lib/transport/transport.interface.js.map +1 -0
  133. package/lib-esm/core/browser-environment.d.ts +1 -0
  134. package/lib-esm/core/browser-environment.js +8 -0
  135. package/lib-esm/core/browser-environment.js.map +1 -0
  136. package/lib-esm/core/catalog-cache.d.ts +10 -0
  137. package/lib-esm/core/catalog-cache.js +36 -0
  138. package/lib-esm/core/catalog-cache.js.map +1 -0
  139. package/lib-esm/core/catalog-cache.spec.d.ts +1 -0
  140. package/lib-esm/core/catalog-cache.spec.js +22 -0
  141. package/lib-esm/core/catalog-cache.spec.js.map +1 -0
  142. package/lib-esm/core/catalog-format.spec.d.ts +1 -0
  143. package/lib-esm/core/catalog-format.spec.js +33 -0
  144. package/lib-esm/core/catalog-format.spec.js.map +1 -0
  145. package/lib-esm/core/catalog.d.ts +9 -0
  146. package/lib-esm/core/catalog.js +2 -0
  147. package/lib-esm/core/catalog.js.map +1 -0
  148. package/lib-esm/core/data-viewlet.d.ts +11 -0
  149. package/lib-esm/core/data-viewlet.js +54 -0
  150. package/lib-esm/core/data-viewlet.js.map +1 -0
  151. package/lib-esm/core/data-viewlet.spec.d.ts +1 -0
  152. package/lib-esm/core/data-viewlet.spec.js +60 -0
  153. package/lib-esm/core/data-viewlet.spec.js.map +1 -0
  154. package/lib-esm/core/explode-locale.d.ts +3 -0
  155. package/lib-esm/core/explode-locale.js +33 -0
  156. package/lib-esm/core/explode-locale.js.map +1 -0
  157. package/lib-esm/core/explode-locale.spec.d.ts +1 -0
  158. package/lib-esm/core/explode-locale.spec.js +39 -0
  159. package/lib-esm/core/explode-locale.spec.js.map +1 -0
  160. package/lib-esm/core/germanic-plural.d.ts +1 -0
  161. package/lib-esm/core/germanic-plural.js +4 -0
  162. package/lib-esm/core/germanic-plural.js.map +1 -0
  163. package/lib-esm/core/gettext-impl.d.ts +10 -0
  164. package/lib-esm/core/gettext-impl.js +34 -0
  165. package/lib-esm/core/gettext-impl.js.map +1 -0
  166. package/lib-esm/core/gettext.spec.d.ts +1 -0
  167. package/lib-esm/core/gettext.spec.js +389 -0
  168. package/lib-esm/core/gettext.spec.js.map +1 -0
  169. package/lib-esm/core/index.d.ts +4 -0
  170. package/lib-esm/core/index.js +5 -0
  171. package/lib-esm/core/index.js.map +1 -0
  172. package/lib-esm/core/locale-container.d.ts +8 -0
  173. package/lib-esm/core/locale-container.js +2 -0
  174. package/lib-esm/core/locale-container.js.map +1 -0
  175. package/lib-esm/core/path-separator.d.ts +1 -0
  176. package/lib-esm/core/path-separator.js +8 -0
  177. package/lib-esm/core/path-separator.js.map +1 -0
  178. package/lib-esm/core/resolve-impl.d.ts +3 -0
  179. package/lib-esm/core/resolve-impl.js +249 -0
  180. package/lib-esm/core/resolve-impl.js.map +1 -0
  181. package/lib-esm/core/resolve.spec.d.ts +1 -0
  182. package/lib-esm/core/resolve.spec.js +340 -0
  183. package/lib-esm/core/resolve.spec.js.map +1 -0
  184. package/lib-esm/core/select-locale.d.ts +1 -0
  185. package/lib-esm/core/select-locale.js +39 -0
  186. package/lib-esm/core/select-locale.js.map +1 -0
  187. package/lib-esm/core/select-locale.spec.d.ts +1 -0
  188. package/lib-esm/core/select-locale.spec.js +25 -0
  189. package/lib-esm/core/select-locale.spec.js.map +1 -0
  190. package/lib-esm/core/set-locale-browser.spec.d.ts +1 -0
  191. package/lib-esm/core/set-locale-browser.spec.js +15 -0
  192. package/lib-esm/core/set-locale-browser.spec.js.map +1 -0
  193. package/lib-esm/core/set-locale-node.spec.d.ts +1 -0
  194. package/lib-esm/core/set-locale-node.spec.js +24 -0
  195. package/lib-esm/core/set-locale-node.spec.js.map +1 -0
  196. package/lib-esm/core/split-locale.d.ts +7 -0
  197. package/lib-esm/core/split-locale.js +35 -0
  198. package/lib-esm/core/split-locale.js.map +1 -0
  199. package/lib-esm/core/split-locale.spec.d.ts +1 -0
  200. package/lib-esm/core/split-locale.spec.js +57 -0
  201. package/lib-esm/core/split-locale.spec.js.map +1 -0
  202. package/lib-esm/core/textdomain.d.ts +55 -0
  203. package/lib-esm/core/textdomain.js +274 -0
  204. package/lib-esm/core/textdomain.js.map +1 -0
  205. package/lib-esm/core/textdomain.spec.d.ts +1 -0
  206. package/lib-esm/core/textdomain.spec.js +31 -0
  207. package/lib-esm/core/textdomain.spec.js.map +1 -0
  208. package/lib-esm/core/user-locales.d.ts +1 -0
  209. package/lib-esm/core/user-locales.js +8 -0
  210. package/lib-esm/core/user-locales.js.map +1 -0
  211. package/lib-esm/index-browser.d.ts +2 -0
  212. package/lib-esm/index-browser.js +26 -0
  213. package/lib-esm/index-browser.js.map +1 -0
  214. package/lib-esm/index.d.ts +2 -0
  215. package/lib-esm/index.js +19 -0
  216. package/lib-esm/index.js.map +1 -0
  217. package/lib-esm/parser/index.d.ts +2 -0
  218. package/lib-esm/parser/index.js +3 -0
  219. package/lib-esm/parser/index.js.map +1 -0
  220. package/lib-esm/parser/parse-json-catalog.d.ts +3 -0
  221. package/lib-esm/parser/parse-json-catalog.js +32 -0
  222. package/lib-esm/parser/parse-json-catalog.js.map +1 -0
  223. package/lib-esm/parser/parse-json-catalog.spec.d.ts +1 -0
  224. package/lib-esm/parser/parse-json-catalog.spec.js +39 -0
  225. package/lib-esm/parser/parse-json-catalog.spec.js.map +1 -0
  226. package/lib-esm/parser/parse-mo-catalog.d.ts +2 -0
  227. package/lib-esm/parser/parse-mo-catalog.js +82 -0
  228. package/lib-esm/parser/parse-mo-catalog.js.map +1 -0
  229. package/lib-esm/parser/parse-mo-catalog.spec.d.ts +1 -0
  230. package/lib-esm/parser/parse-mo-catalog.spec.js +113 -0
  231. package/lib-esm/parser/parse-mo-catalog.spec.js.map +1 -0
  232. package/lib-esm/transport/fs.d.ts +4 -0
  233. package/lib-esm/transport/fs.js +15 -0
  234. package/lib-esm/transport/fs.js.map +1 -0
  235. package/lib-esm/transport/http.d.ts +4 -0
  236. package/lib-esm/transport/http.js +24 -0
  237. package/lib-esm/transport/http.js.map +1 -0
  238. package/lib-esm/transport/http.spec.d.ts +1 -0
  239. package/lib-esm/transport/http.spec.js +114 -0
  240. package/lib-esm/transport/http.spec.js.map +1 -0
  241. package/lib-esm/transport/index.d.ts +3 -0
  242. package/lib-esm/transport/index.js +4 -0
  243. package/lib-esm/transport/index.js.map +1 -0
  244. package/lib-esm/transport/transport.interface.d.ts +3 -0
  245. package/lib-esm/transport/transport.interface.js +2 -0
  246. package/lib-esm/transport/transport.interface.js.map +1 -0
  247. package/package.json +72 -0
  248. package/webpack.common.js +38 -0
  249. package/webpack.dev.js +9 -0
  250. package/webpack.prod.js +8 -0
package/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ This software is Copyright (C) 2020 by Guido Flohr.
2
+
3
+ This is free software, licensed under:
4
+
5
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE, Version 2, December 2004
6
+
7
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
8
+ Version 2, December 2004
9
+
10
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
11
+
12
+ Everyone is permitted to copy and distribute verbatim or modified
13
+ copies of this license document, and changing it is allowed as long
14
+ as the name is changed.
15
+
16
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
17
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
18
+
19
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
package/README.md ADDED
@@ -0,0 +1,411 @@
1
+ # @esgettext/runtime <!-- omit in toc -->
2
+
3
+ GNU gettext-alike translation runtime library.
4
+
5
+ ## Table of Contents <!-- omit in toc -->
6
+
7
+ - [API Documentation](#api-documentation)
8
+ - [Internationalizing Hello World](#internationalizing-hello-world)
9
+ - [Choosing a Textdomain](#choosing-a-textdomain)
10
+ - [Install the Library](#install-the-library)
11
+ - [Import the Library](#import-the-library)
12
+ - [Prepare Your Sources](#prepare-your-sources)
13
+ - [Translation Methods](#translation-methods)
14
+ - [Simple Translations With `_()`](#simple-translations-with-_)
15
+ - [Variable Interpolation With `_x()`](#variable-interpolation-with-_x)
16
+ - [Plural Forms With `_nx()`](#plural-forms-with-_nx)
17
+ - [Message Context With `_p()`](#message-context-with-_p)
18
+ - [Specific Locale with `_l`](#specific-locale-with-_l)
19
+ - [TODO: Gender-Specific Translations](#todo-gender-specific-translations)
20
+ - [Selecting the Preferred Language with `selectLocale()`](#selecting-the-preferred-language-with-selectlocale)
21
+ - [Internationalizing a Library](#internationalizing-a-library)
22
+ - [Frequently-Asked Questions](#frequently-asked-questions)
23
+ - [Why do Template Strings not Work?](#why-do-template-strings-not-work)
24
+ - [What Does the Error "template literals with embedded expressions are not allowed as arguments to gettext functions because they are not constant" Mean?](#what-does-the-error-template-literals-with-embedded-expressions-are-not-allowed-as-arguments-to-gettext-functions-because-they-are-not-constant-mean)
25
+ - [Copyright](#copyright)
26
+
27
+ ## API Documentation
28
+
29
+ If you are already familiar with the concepts of the esgettext runtime library,
30
+ you can go straight to the [API documentation](https://gflohr.github.io/esgettext/packages/runtime/api-docs/globals.html).
31
+
32
+ ## Internationalizing Hello World
33
+
34
+ You have written this little piece of JavaScript:
35
+
36
+ ```javascript
37
+ console.log('Hello, world!');
38
+ ```
39
+
40
+ What are the steps needed to internationalize it with this library?
41
+
42
+ ### Choosing a Textdomain
43
+
44
+ First, you have to choose a unique identifier for your project so that the
45
+ translation catalogs will have a unique name. You are almost free
46
+ in what you are choosing but you have to keep in mind that the textdomain
47
+ will be part of a URI or filename and therefore a couple of rules apply:
48
+
49
+ - A textdomain _must not_ contain a slash ("/").
50
+ - A textdomain _should not_ contain a colon (":"), because of Windows.
51
+ - A textdomain _should not_ contain a backslash ("\"), because of Windows.
52
+ - A textdomain _should not_ contain binary characters, because of common sense.
53
+
54
+ In general, you should only use lowercase characters that are valid inside
55
+ hostnames, namely "a-z", "0-9", the hyphen "-", and the dot ".".
56
+
57
+ If possible, follow this advice:
58
+
59
+ 1. If your organization has a domain, use the reverse(!) domain name followed by the name of your product for example "com.example.hello"
60
+ 2. Otherwise, if your project sources are publicly hosted use the reverse domain name of your hoster followed by an identifier of your project. For example, the textdomain for `https://github.com/gflohr/esgettext` would then be "com.github.gflohr.esgettext".
61
+ 3. Otherwise, use common sense.
62
+
63
+ We will use the the third rule and pick the textdomain "hello".
64
+
65
+ ### Install the Library
66
+
67
+ You normally install the library with `npm` or `yarn`.
68
+
69
+ With `npm`:
70
+
71
+ ```shell
72
+ $ npm install --save esgettext
73
+ ```
74
+
75
+ Or with `yarn`:
76
+
77
+ ```shell
78
+ $ yarn add esgettext
79
+ ```
80
+
81
+ ### Import the Library
82
+
83
+ How to import the library, depends on your environment.
84
+
85
+ If you have the `import` keyword:
86
+
87
+ ```javascript
88
+ import Textdomain from '@esgettext/runtime';
89
+ ```
90
+
91
+ If you can use `require`:
92
+
93
+ ```javascript
94
+ const esgettext = require('@esgettext/runtime');
95
+ const Textdomain = esgettext.Textdomain;
96
+ ```
97
+
98
+ _FIXME! Is this correct?_
99
+
100
+ If you are writing javascript loaded by a browser:
101
+
102
+ ```html
103
+ <script src="https://cdn.jsdelivr.net/npm/@esgettext/runtime/_bundles/runtime.min.js"></script>
104
+ <script>
105
+ var Textdomain = esgettext.Textdomain;
106
+ // ... your code follows.
107
+ </script>
108
+ ```
109
+
110
+ If you want a specific version, you can do like this:
111
+
112
+ ```html
113
+ <script src="https://cdn.jsdelivr.net/npm/@esgettext/runtime@0.1.0/_bundles/runtime.min.js"></script>
114
+ <script>
115
+ var Textdomain = esgettext.Textdomain;
116
+ // ... your code follows.
117
+ </script>
118
+ ```
119
+
120
+ ### Prepare Your Sources
121
+
122
+ Change your `hello.js` to read like this:
123
+
124
+ ```javascript
125
+ import { Textdomain } from '@esgettext/runtime';
126
+
127
+ Textdomain.locale = 'fr';
128
+ const gtx = Textdomain.getInstance('hello');
129
+ gtx.bindtextdomain('/assets/locale');
130
+ gtx.resolve().then(function () {
131
+ console.log(gtx._('Hello, world!'));
132
+ });
133
+ ```
134
+
135
+ _Hint:_ If you cannot use `import`, use one of the other techniques shown
136
+ above to make the `Textdomain` class available in your source!
137
+
138
+ What is happening here?
139
+
140
+ First, you set the locale (resp. language) to the desired value. Here we
141
+ choose "fr" for French. See the section [Selecting the Preferred Language with `selectLocale()`](#selecting-the-preferred-language-with-selectlocale)
142
+ for a more flexible way to select the user's locale.
143
+
144
+ You then get an instance of a `Textdomain` object. You cannot use the regular
145
+ constructor because it is private! The argument to the
146
+ [`getInstance()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#getinstance) class
147
+ method is the textdomain you have chosen.
148
+
149
+ You then have to tell the library where to find translations for the "hello"
150
+ textdomain. You do that with the instance method
151
+ [`bindtextdomain()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#bindtextdomain) that
152
+ receives a (base) directory as its argument. The actual translation catalog
153
+ would then be searched at `/assets/locale/fr/LC_MESSAGES/hello.json` (or
154
+ `hello.mo` depending on your environment).
155
+
156
+ Don't worry that there are no translations at the moment. Failure is handled
157
+ gracefully by the library falling back to using the original, untranslated
158
+ strings. See the [docs for tools](../tools/README.md) for
159
+ instructions on how to create translation catalogs.
160
+
161
+ You then have to [`resolve()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#resolve) the translations. The method returns a promise,
162
+ and your actual code should be moved into the promise's `then()` method.
163
+
164
+ Now that everything is loaded you can translate all messages by replacing
165
+ `'some string'` with `gtx._('some string')`. That's it!
166
+
167
+ ### Translation Methods
168
+
169
+ The method [`_()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_) is the simplest but by far not the only translation method
170
+ that esgettext has to offer.
171
+
172
+ #### Simple Translations With [`_()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_)
173
+
174
+ The method [`_()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_) has already been introduced:
175
+
176
+ ```javascript
177
+ console.log(gtx._('Hello, world!'));
178
+ ```
179
+
180
+ It returns the translation of its argument or just the argument if no
181
+ translation can be found.
182
+
183
+ #### Variable Interpolation With [`_x()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_x)
184
+
185
+ Imagine you want to express RGB colors in human-readable form. The esgettext
186
+ way to do that goes like this:
187
+
188
+ ```javascript
189
+ console.log(gtx._x(
190
+ 'red: {r}, green: {g}, blue: {b}', {
191
+ r: red,
192
+ g: green,
193
+ b: blue
194
+ }
195
+ );
196
+ ```
197
+
198
+ You use placeholders surrounded by curly braces (`{}`), and provide an object
199
+ as an additional argument where the keys are the placeholder names and the
200
+ values, the respective values to be interpolated.
201
+
202
+ Placeholder names must be valid C identifiers: They must begin with a
203
+ lower- or uppercase letter ("a" to "z", "A" to "Z") or an underscore ("\_")
204
+ followed by an arbitrary number of characters from the same set or decimal
205
+ digits ("0" to "9"). In regular espression syntax: `/^[_a-zA-Z][_a-zA-Z0-9]*$/`.
206
+
207
+ #### Plural Forms With [`_nx()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_nx)
208
+
209
+ Translating a string with plural expressions is unfortunately somewhat
210
+ convoluted:
211
+
212
+ ```javascript
213
+ console.log(
214
+ gtx._nx('One file copied', '{count} files copied.', count, {
215
+ count: count,
216
+ }),
217
+ );
218
+ ```
219
+
220
+ Count, count, count, count.
221
+
222
+ If the variable `count` has the value 42, the above would yield in English:
223
+ "42 files copied.". If `count` had the value 1, it would yield: "One file
224
+ copied.".
225
+
226
+ The method [`_nx()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_) has this signature:
227
+
228
+ ```javascript
229
+ _nx(
230
+ (msgid: string),
231
+ (msgidPlural: string),
232
+ (numberOfItems: number),
233
+ (placeholders: Object),
234
+ );
235
+ ```
236
+
237
+ Many times, the placeholder name will equal the variable name, so that you see
238
+ that name once inside the plural string as the placeholder, then as the
239
+ argument `numberOfItems`, then as the key in the placeholder hash, and again
240
+ as the value for that key.
241
+
242
+ Note that there is also a method [`_n()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_n) that does the same as [`_nx()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_nx) but
243
+ without inpterpolation but it only exists for completeness and is useless for
244
+ practical purposes.
245
+
246
+ #### Message Context With [`_p()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_p)
247
+
248
+ It is sometimes possible that one English sentence can have multiple, different
249
+ meanings in another language. For example "Sun" can mean our planet's star or
250
+ the abbreviation of "Sunday". In order to allow translators to provide
251
+ accurate translations for each meaning, you can distinguish them by message
252
+ context:
253
+
254
+ ```javascript
255
+ weekday = gtx._p('wday', 'Sun');
256
+ star = gtx._p('star', 'Sun');
257
+ ```
258
+
259
+ The first argument is the context (a free-form string), the second is the
260
+ string to translate.
261
+
262
+ It is actually sufficient to use a context just for one of the two cases.
263
+
264
+ There are also methods [`_px()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_px), when you need placeholders or [`_npx()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_npx), when
265
+ you need placeholders and plural forms in addition to a message context.
266
+
267
+ #### Specific Locale with [`_l`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_l)
268
+
269
+ Most of the time, the locale resp. language is set just once by setting the
270
+ static property `locale`:
271
+
272
+ ```javascript
273
+ Textdomain.locale = 'fr';
274
+ console.log(gtx._('Hello, world!'));
275
+ ```
276
+
277
+ Alternatively, you can pass the locale with every call to a translation
278
+ method:
279
+
280
+ ```javascript
281
+ console.log(gtx._l('fr', 'Hello, world!'));
282
+ ```
283
+
284
+ There is not just [`_l()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_l), but actually all of the above mentioned methods have
285
+ versions with a leading `_l`, like
286
+ [`_lx()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_lx)
287
+ or as complicated as
288
+ [`_lnpx()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_lnpx).
289
+
290
+ One use-case for the
291
+ [`_l()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_l)
292
+ family of methods
293
+ would be a web server. A web server handles requests asynchronously, and a
294
+ global locale doesn't make sense. Instead, the language is typically bound
295
+ to the request or response and should be taken from there.
296
+
297
+ #### TODO: Gender-Specific Translations
298
+
299
+ This is on the todo list for a future version. You will be able to do something
300
+ like:
301
+
302
+ ```javascript
303
+ msg = gtx._g(
304
+ user.gender,
305
+ 'They have liked your photo.',
306
+ 'She has liked your photo.',
307
+ );
308
+ ```
309
+
310
+ ### Selecting the Preferred Language with [`selectLocale()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#selectLocale)
311
+
312
+ Negotiating the preferred locale can be performed with the help of esgettext.
313
+ If your application supports the locales "en-US", "en-GB", "fr-FR", and
314
+ "de-DE", you can select a suitable locale for the current user by calling
315
+ [`Textdomain.selectlocale`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#selectLocale)
316
+
317
+ ```javascript
318
+ Textdomain.locale = Textdomain.selectLocale(['en-US', 'en-GB', 'fr-FR']);
319
+ ```
320
+
321
+ [`Textdomain.selectLocale()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#selectLocale)
322
+ will return the most suitable locale for the current user. For browser code,
323
+ the browser will be queried for the user language preferences, for server
324
+ code the environment variables `LANGUAGE`, `LC_ALL`, `LANG`, and `LC_MESSAGES`
325
+ will be queried in that order.
326
+
327
+ You can also explicitly specify, which locales the user has requested by
328
+ passing a second argument:
329
+
330
+ ```javascript
331
+ Textdomain.locale = Textdomain.selectLocale(
332
+ ['en-US', 'en-GB', 'fr-FR'], // Supported by the application.
333
+ ['de-DE', 'fr-FR'],
334
+ ); // Requested by the user.
335
+ ```
336
+
337
+ ## Internationalizing a Library
338
+
339
+ Internationalizing a library is very simple. Take this sample library:
340
+
341
+ ```javascript
342
+ function greet(name) {
343
+ return `Hello, ${name}`;
344
+ }
345
+ ```
346
+
347
+ Internationalized, it would look like this:
348
+
349
+ ```javascript
350
+ import { Textdomain } from '@esgettext/runtime';
351
+
352
+ const gtx = Textdomain.getInstance('hello-library');
353
+ gtx.bindtextdomain('/assets/locale');
354
+
355
+ function greet(name) {
356
+ return gtx._x('Hello, {name}', { name: name });
357
+ }
358
+ ```
359
+
360
+ The main differences to an internationalized application are:
361
+
362
+ - You do not set [Textdomain.locale](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#locale) because the main application (the application that loads your library) does it.
363
+ - You do not call [resolve()](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#resolve) because whenever the main application calls `resolve()`, the catalogs for your library will also be loaded.
364
+
365
+ ## Frequently-Asked Questions
366
+
367
+ ### Why do Template Strings not Work?
368
+
369
+ A common error is to use template strings with interpolations as arguments to
370
+ the translation functions, for example:
371
+
372
+ ```javascript
373
+ console.log(gtx._(`red: ${red}, green: ${green}, blue: ${blue}`);
374
+ ```
375
+
376
+ That seems to work for English but the string never gets translated.
377
+
378
+ The reason is that the argument to
379
+ [`_()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_)
380
+ is the lookup key into the translation database but that key has to be constant.
381
+ The method receives an argument like "red: 127, green: 63, blue: 31" because
382
+ the JavaScript engine has already interpolated the variables into the string.
383
+ But that string does not exist in the database.
384
+
385
+ You have to use
386
+ [`_x()`](https://gflohr.github.io/esgettext/packages/runtime/api-docs/classes/textdomain.html#_x)
387
+ instead:
388
+
389
+ ```javascript
390
+ console.log(gtx._x(
391
+ 'red: {r}, green: {g}, blue: {b}', {
392
+ r: red,
393
+ g: green,
394
+ b: blue
395
+ }
396
+ );
397
+ ```
398
+
399
+ ### What Does the Error "template literals with embedded expressions are not allowed as arguments to gettext functions because they are not constant" Mean?
400
+
401
+ See [Why do Template Strings not Work?](#why-do-template-strings-not-work)
402
+ above! The extractor `esgettext-xgettext` complains that you are using a
403
+ template string with interpolated expressions.
404
+
405
+ ## Copyright
406
+
407
+ Copyright (C) 2020 Guido Flohr <guido.flohr@cantanea.com>, all
408
+ rights reserved.
409
+
410
+ This software is available under the terms and conditions of the
411
+ [WTFPL](http://www.wtfpl.net/about).