1313* [ Basic usage] ( #basic-usage )
1414* [ Message formatting] ( #message-formatting )
1515* [ Message loading] ( #message-loading )
16- * [ Manual messages creation] ( #manual-messages-creation )
17- * [ Loading messages from classpath or file system] ( #loading-messages-from-classpath-or-file-system )
18- * [ Single message file with multiple locales] ( #single-message-file-with-multiple-locales )
19- * [ Watching for file changes] ( #watching-for-file-changes )
16+ * [ Manual messages creation] ( #manual-messages-creation )
17+ * [ Loading messages from classpath or file system] ( #loading-messages-from-classpath-or-file-system )
18+ * [ Single message file with multiple locales] ( #single-message-file-with-multiple-locales )
19+ * [ Watching for file changes] ( #watching-for-file-changes )
2020* [ Message resolution] ( #message-resolution )
21- * [ Message fallbacks] ( #message-fallbacks )
22- * [ Prefixed queries] ( #prefixed-queries )
23- * [ Localized queries] ( #localized-queries )
21+ * [ Message fallbacks] ( #message-fallbacks )
22+ * [ Prefixed queries] ( #prefixed-queries )
23+ * [ Localized queries] ( #localized-queries )
2424* [ Message references] ( #message-references )
25- * [ Reference resolution order] ( #reference-resolution-order )
26- * [ References in a prefixed file] ( #references-in-a-prefixed-file )
25+ * [ Reference resolution order] ( #reference-resolution-order )
26+ * [ References in a prefixed file] ( #references-in-a-prefixed-file )
2727* [ Type based formatters] ( #type-based-formatters )
2828* [ Missing messages] ( #missing-messages )
29- * [ Missing message handler] ( #missing-message-handler )
30- * [ Missing message detection] ( #missing-message-detection )
29+ * [ Missing message handler] ( #missing-message-handler )
30+ * [ Missing message detection] ( #missing-message-detection )
3131* [ Whitespace normalization] ( #whitespace-normalization )
3232* [ DevMode] ( #devmode )
3333
34-
3534## Installation
3635
3736Add to your ` build.gradle ` :
3837
3938``` gradle
4039dependencies {
41- implementation "com.coditory.quark:quark-i18n:0.0.8 "
40+ implementation "com.coditory.quark:quark-i18n:$version "
4241}
4342```
4443
4544## Basic usage
4645
4746``` java
4847I18nMessagePack messagePack = I18nMessagePack . builder()
49- .scanClassPath(" /i18n/messages-{locale}.yml" )
50- .setDefaultLocale(Locales . EN_US )
51- .build();
48+ .scanClassPath(" /i18n/messages-{locale}.yml" )
49+ .setDefaultLocale(Locales . EN_US )
50+ .build();
5251
5352// ...when request arrives
5453I18nMessages messages = messagePack. localize(req. getLocale());
54+
5555print(messages. getMessage(" greeting" , userName));
5656```
5757
@@ -92,31 +92,33 @@ Messages can be created in 3 ways:
9292
9393``` java
9494I18nMessagePack messages = I18nMessagePack . builder()
95- .addMessage(Loacles . EN_US , " hello" , " Hello {0}" )
96- .addMessage(Loacles . PL_PL , " hello" , " Cześć {0}" )
97- .setDefaultLocale(PL_PL )
98- .build();
95+ .addMessage(Loacles . EN_US , " hello" , " Hello {0}" )
96+ .addMessage(Loacles . PL_PL , " hello" , " Cześć {0}" )
97+ .setDefaultLocale(PL_PL )
98+ .build();
9999
100- messages. getMessage(Loacles . EN_US , " hello" , userName);
100+ messages.
101+
102+ getMessage(Loacles . EN_US , " hello" ,userName);
101103```
102104
103105If you want to quickly load messages from a nested map (for example fetched from a document storage)
104106you can use ` I18nParsers.parseEntries(map, locale) ` to translate nested the map keys into localized message paths.
105107
106108``` java
107109I18nMessagePack messages = I18nMessagePack . builder()
108- .addMessages(I18nParsers . parseEntries(Map . of(" hello" , " Hello {0}" ), EN_US ))
109- .build();
110+ .addMessages(I18nParsers . parseEntries(Map . of(" hello" , " Hello {0}" ), EN_US ))
111+ .build();
110112```
111113
112114### Loading messages from classpath or file system
113115
114116``` java
115117I18nMessagePack messages = I18nMessagePack . builder()
116- .scanClassPath(" /i18n/messages-{locale}.yml" )
117- .scanFileSystem(" ./overriddes/messages-{locale}.yml" )
118- .setDefaultLocale(PL_PL )
119- .build();
118+ .scanClassPath(" /i18n/messages-{locale}.yml" )
119+ .scanFileSystem(" ./overriddes/messages-{locale}.yml" )
120+ .setDefaultLocale(PL_PL )
121+ .build();
120122```
121123
122124Localization based path placeholders are used to assign all messages in the file to file's locale.
@@ -147,6 +149,7 @@ When path pattern does not contain one of localization placeholders (`{locale}`,
147149then locale is parsed from the last segment of the message path:
148150
149151Example in ` yml `
152+
150153``` yml
151154homepage.title :
152155 pl-PL : Strona główna
@@ -159,8 +162,12 @@ To reload messages in file change use:
159162
160163` ` ` java
161164I18nMessagePack.builder()
162- .scanFileSystem("i18n/*")
163- .buildAndWatchForChanges();
165+ .
166+
167+ scanFileSystem("i18n/*")
168+ .
169+
170+ buildAndWatchForChanges();
164171```
165172
166173ATM, it works for messages loaded from filesystem only, but for add your own implementation of ` WatchableI18nLoader ` .
@@ -173,10 +180,10 @@ If there is still no match then the default locale (followed by a less strict de
173180
174181``` java
175182I18nMessagePack messages = I18nMessagePack . builder()
176- .scanClassPath(" /i18n/messages-{locale}.yml" )
177- .setDefaultLocale(PL_PL )
178- .addFallbackKeyPrefix(" glossary" )
179- .build();
183+ .scanClassPath(" /i18n/messages-{locale}.yml" )
184+ .setDefaultLocale(PL_PL )
185+ .addFallbackKeyPrefix(" glossary" )
186+ .build();
180187
181188String message = messages. getMessage(Locales . en_US, " hello" );
182189```
@@ -194,10 +201,10 @@ Sometimes it is useful to specify a common path prefix for all unmatched queries
194201
195202``` java
196203I18nMessagePack messages = I18nMessagePack . builder()
197- .scanClassPath(" /i18n/messages-{locale}.yml" )
198- .setDefaultLocale(PL_PL )
199- .addMessageFallbackKeyPrefix(" common" )
200- .build();
204+ .scanClassPath(" /i18n/messages-{locale}.yml" )
205+ .setDefaultLocale(PL_PL )
206+ .addMessageFallbackKeyPrefix(" common" )
207+ .build();
201208
202209String message = messages. getMessage(Locales . en_US, " hello" );
203210```
@@ -217,9 +224,9 @@ Sometimes it's useful to prefix all queries with some path, like in the example:
217224
218225``` java
219226I18nMessagePack messages = I18nMessagePack . builder()
220- .scanClassPath(" /i18n/messages-{locale}.yml" )
221- .setDefaultLocale(PL_PL )
222- .build();
227+ .scanClassPath(" /i18n/messages-{locale}.yml" )
228+ .setDefaultLocale(PL_PL )
229+ .build();
223230
224231I18nMessagePack homepageMessages = messages. prefixQueries(" pages.homepage" );
225232String homepageTitle = homepageMessages. getMessage(en_US, " title" );
@@ -241,9 +248,9 @@ Sometimes it's useful to apply common locale to all queries:
241248
242249``` java
243250I18nMessagePack messagePack = I18nMessagePack . builder()
244- .scanClassPath(" /i18n/messages-{locale}.yml" )
245- .setDefaultLocale(PL_PL )
246- .build();
251+ .scanClassPath(" /i18n/messages-{locale}.yml" )
252+ .setDefaultLocale(PL_PL )
253+ .build();
247254
248255// ...when request arrives
249256I18nMessages messages = messagePack. localize(req. getLocale());
@@ -255,15 +262,16 @@ Query localization mechanism can be used together with query prefixes:
255262
256263``` java
257264I18nMessages messages = messagePack
258- .prefixQueries(" pages.homepage" )
259- .localize(req. getLocale());
265+ .prefixQueries(" pages.homepage" )
266+ .localize(req. getLocale());
260267```
261268
262269## Message references
263270
264271Message references are the way to reuse text across multiple messages.
265272
266273Example in yml:
274+
267275``` yml
268276# Common entries
269277company :
@@ -274,7 +282,7 @@ about-company: "${company.name} was established on ${company.established}"
274282` ` `
275283
276284` ` ` java
277- messages.getMessage("about-company") == "ACME was established on 1988"
285+ messages.getMessage("about-company") =="ACME was established on 1988"
278286```
279287
280288- It's not a part of ICU standard
@@ -283,7 +291,8 @@ messages.getMessage("about-company") == "ACME was established on 1988"
283291 with ` i18nMessagePackBuilder.addReferenceFallbackKeyPrefix() ` ([ example] ( #sample-reference-resolution ) )
284292- References in prefixed files are prefixed as
285293 well ` ${foo} -> ${<file-prefix>.foo} ` ([ example] ( #sample-reference-resolution-from-a-prefixed-file ) )
286- - References can have a short notation ` $common.reference ` and long one ` ${common.reference} ` . The long one is useful when
294+ - References can have a short notation ` $common.reference ` and long one ` ${common.reference} ` . The long one is useful
295+ when
287296 there reference is placed next to ` [a-zA-Z0-9-_] ` , like in ` abc${common.reference}abc$ ` .
288297
289298### Reference resolution order
@@ -292,11 +301,11 @@ Let's configure messages:
292301
293302``` java
294303I18nMessagePack messagePack = I18nMessagePack . builder()
295- .addMessage(EN_US , " msg" , " ${company.name} was established on 1988" )
296- .scanClassPath(" /i18n/messages-{locale}.yml" )
297- .setDefaultLocale(PL_PL )
298- .addFallbackKeyPrefix(" fallback" )
299- .build();
304+ .addMessage(EN_US , " msg" , " ${company.name} was established on 1988" )
305+ .scanClassPath(" /i18n/messages-{locale}.yml" )
306+ .setDefaultLocale(PL_PL )
307+ .addFallbackKeyPrefix(" fallback" )
308+ .build();
300309```
301310
302311Locations used to find the message:
@@ -314,10 +323,10 @@ If the reference is defined in a message stored in a prefixed file it will be au
314323
315324``` java
316325I18nMessagePack messagePack = I18nMessagePack . builder()
317- .scanClassPathLocation(" i18n/{prefix}/message_{locale}.yml" )
318- .setDefaultLocale(PL_PL )
319- .addFallbackKeyPrefix(" fallback" )
320- .build();
326+ .scanClassPathLocation(" i18n/{prefix}/message_{locale}.yml" )
327+ .setDefaultLocale(PL_PL )
328+ .addFallbackKeyPrefix(" fallback" )
329+ .build();
321330```
322331
323332and file ` i18n/company/message_en-US.yml ` contains
@@ -340,6 +349,7 @@ Locations used to find the message:
340349## Type based formatters
341350
342351You can map arguments by their type using argument transformers:
352+
343353- transformation is located in the definition order
344354- only the arguments used in the message are transformed
345355- transformation is transitive - one value can be transformed multiple times
@@ -348,11 +358,13 @@ Example:
348358
349359``` java
350360I18nMessages messages = I18nMessagePack . builder()
351- .addMessage(EN , " msg" , " {0,number,00000.00000}" )
352- .addArgumentTransformer(Foo , (foo) - > foo. getSomeNumber())
353- .buildLocalized(EN );
361+ .addMessage(EN , " msg" , " {0,number,00000.00000}" )
362+ .addArgumentTransformer(Foo , (foo) - > foo. getSomeNumber())
363+ .buildLocalized(EN );
364+
365+ messages.
354366
355- messages . getMessage(" msg" , new Foo (123.456 )) == " 00123.45600"
367+ getMessage(" msg" ,new Foo (123.456 ))== " 00123.45600"
356368```
357369
358370## Missing messages
@@ -366,7 +378,9 @@ When message is missing, exception is thrown. This mechanism can be changed with
366378i18nMessagePackBuilder. setMissingMessageHandler(customHandler);
367379
368380// ...or simply return message path when message is missing
369- i18nMessagePackBuilder. usePathOnMissingMessage();
381+ i18nMessagePackBuilder.
382+
383+ usePathOnMissingMessage();
370384```
371385
372386### Missing message detection
@@ -375,6 +389,7 @@ It's important to find about missing messages as quickly as possible
375389and avoid finding them on production.
376390
377391That's why there is an option to detect them during build phase:
392+
378393```
379394i18nMessagePackBuilder.validateNoMissingMessages() - throws exception on missing message
380395i18nMessagePackBuilder.logMissingMessages() - simply logs a report about missing messages
@@ -384,14 +399,25 @@ i18nMessagePackBuilder.logMissingMessages() - simply logs a report about missing
384399
385400``` java
386401I18nMessagePack . builder()
387- .addMessage(EN_US , " hello" , " Hello" )
388- .addMessage(PL_PL , " hello" , " Cześć" )
389- .addMessage(DE_DE , " bye" , " Tschüss" )
390- .logMissingMessages()
391- .build();
402+ .
403+
404+ addMessage(EN_US , " hello" ," Hello" )
405+ .
406+
407+ addMessage(PL_PL , " hello" ," Cześć" )
408+ .
409+
410+ addMessage(DE_DE , " bye" ," Tschüss" )
411+ .
412+
413+ logMissingMessages()
414+ .
415+
416+ build();
392417```
393418
394419Will generate following report:
420+
395421```
396422Missing Messages
397423================
@@ -414,17 +440,31 @@ You can skip them using a custom missing message detector:
414440
415441``` java
416442I18nMissingMessagesDetector detector = I18nMissingMessagesDetector . builder()
417- .skipPath(skipPath)
418- .logMissingMessages()
419- .build()
443+ .skipPath(skipPath)
444+ .logMissingMessages()
445+ .build()
420446
421- I18nMessagePack . builder()
422- .addMessage(EN_US , " a.b.c.d" , " MISSING" )
423- .addMessage(EN_US , " x" , " X" )
424- .addMessage(EN_GB , " x" , " X" )
425- .addMessage(PL_PL , " x" , " X" )
426- .detectMissingMessages(detector)
427- .build();
447+ I18nMessagePack .
448+
449+ builder()
450+ .
451+
452+ addMessage(EN_US , " a.b.c.d" ," MISSING" )
453+ .
454+
455+ addMessage(EN_US , " x" ," X" )
456+ .
457+
458+ addMessage(EN_GB , " x" ," X" )
459+ .
460+
461+ addMessage(PL_PL , " x" ," X" )
462+ .
463+
464+ detectMissingMessages(detector)
465+ .
466+
467+ build();
428468
429469// to skip a.b.c.d use one of sample path patterns as a skipPath:
430470// - "a.b.c.d",
@@ -453,12 +493,12 @@ You can use file watching capabilities to speed up the development cycle:
453493
454494``` java
455495I18nMessagePackBuidler messagesBuilder = I18nMessagePack . builder()
456- .setDefaultLocale(EN_US );
457- // ... other common settings
496+ .setDefaultLocale(EN_US );
497+ // ... other common settings
458498
459499I18nMessagePack messages = devMode
460- ? messagesBuilder. scanFileSystem(" src/main/resources/i18n/*" ). buildAndWatchForChanges()
461- : messagesBuilder. scanClassPath(" i18n/*" ). build();
500+ ? messagesBuilder. scanFileSystem(" src/main/resources/i18n/*" ). buildAndWatchForChanges()
501+ : messagesBuilder. scanClassPath(" i18n/*" ). build();
462502```
463503
464504Following setup will load messages directly from project structure and watch for changes.
0 commit comments