<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jelili Adesina</title>
    <description>The latest articles on DEV Community by Jelili Adesina (@jelilio).</description>
    <link>https://dev.to/jelilio</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1451959%2F3d53a397-5a57-4442-a980-c300411125ef.jpg</url>
      <title>DEV Community: Jelili Adesina</title>
      <link>https://dev.to/jelilio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jelilio"/>
    <language>en</language>
    <item>
      <title>Implementing Soft Delete in Spring WebFlux with R2DBC</title>
      <dc:creator>Jelili Adesina</dc:creator>
      <pubDate>Tue, 12 Nov 2024 21:12:42 +0000</pubDate>
      <link>https://dev.to/jelilio/implementing-soft-delete-in-spring-webflux-with-r2dbc-53gg</link>
      <guid>https://dev.to/jelilio/implementing-soft-delete-in-spring-webflux-with-r2dbc-53gg</guid>
      <description>&lt;p&gt;Data management is a fundamental component in software development, especially when handling records that need removal from active use. Instead of permanently deleting records (a method known as “hard delete”), many applications use a technique called “soft delete.” The “soft delete” approach is a widely used solution that marks records as inactive without permanently removing them, enabling easy data recovery and historical tracking.&lt;/p&gt;

&lt;p&gt;Currently, unlike Spring Data JPA and Hibernate, Spring Data R2DBC does not offer built-in annotations for automatically handling soft-delete. As a result, developers resort to the use of custom repository implementations or queries to achieve similar functionality.&lt;/p&gt;

&lt;p&gt;In this article, we’ll examine soft delete, its benefits, and how to implement it in a Spring WebFlux application with R2DBC.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Soft Delete
&lt;/h2&gt;

&lt;p&gt;Soft delete is a data management method where records are flagged as inactive or “deleted” without being removed from the database. Typically, this involves adding a field to the entity, like deleted (a boolean) or deletedDate (a timestamp), to indicate that a record is no longer active. Instead of permanently deleting data, a soft delete marks the record as logically deleted, hiding it from standard queries while preserving it for potential recovery or auditing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of Soft Delete
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Data Recovery: Soft delete allows for easy restoration of data. If a record is accidentally deleted, it can be quickly “undeleted” by resetting the flag, ensuring that no data is permanently lost.&lt;/li&gt;
&lt;li&gt;Historical Data: Soft delete provides an audit trail. Organisations often need to keep historical data for compliance or reporting purposes, and soft delete enables this without crowding active data.&lt;/li&gt;
&lt;li&gt;Data Integrity: In systems with complex relationships, permanently deleting a record can cause broken links and data inconsistencies. Soft delete addresses this by keeping related data intact while marking the deleted records as inactive.&lt;/li&gt;
&lt;li&gt;Security and Compliance: Regulations often require data to be retained for specific periods. Soft delete enables developers to meet these compliance needs without making the data available to regular users.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to implement Soft Delete with Spring Reactive and R2DBC
&lt;/h2&gt;

&lt;p&gt;If you’re interested in implementing this yourself, I’ve prepared a starter code — a simple blog application with basic CRUD endpoints and unit test cases. You can access the starter code from my GitHub repository using this &lt;a href="https://github.com/jelilio/spring-webflux-blog/tree/starter" rel="noopener noreferrer"&gt;link&lt;/a&gt;. So, let's get right to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Add a Field to Mark Records as Deleted
&lt;/h3&gt;

&lt;p&gt;To implement this, add a field in your entity class to represent the deletion status. A more effective approach is to create an abstract class (&lt;strong&gt;AbstractSoftDeletableEntity&lt;/strong&gt;), define the deletion status field within it, and have your entity class extend this abstract class. This field could either be a boolean (deleted) indicating whether the record is deleted or a timestamp (deletedDate) to specify when it was deleted. I recommend using a timestamp, as it provides the added detail of when the deletion took place:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Step 2: Modify the entity class to extend the abstract class
&lt;/h3&gt;

&lt;p&gt;Modifying the entity class to extend the &lt;strong&gt;AbstractSoftDeletableEntity&lt;/strong&gt; creates a level of abstraction and separation of concern, thus adhering to the Single Responsibility Principle of object-oriented design.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Step 3: Create a Generic Custom Repository Extending the SimpleR2dbcRepository
&lt;/h3&gt;

&lt;p&gt;Many resources on implementing soft delete recommend using a custom repository for each entity, which can be cumbersome and hard to manage when an application has numerous entities. A better approach is to use a generic repository interface while providing custom implementations for the basic methods such as &lt;strong&gt;counts&lt;/strong&gt;, &lt;strong&gt;deleteById&lt;/strong&gt;, &lt;strong&gt;deleteAll&lt;/strong&gt;, &lt;strong&gt;findById&lt;/strong&gt;, and others.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In the snippet above, I have the &lt;strong&gt;SoftDeleteRepositoryImpl&lt;/strong&gt; implementing the &lt;strong&gt;SoftDeleteRepository&lt;/strong&gt; interface, which provides another abstraction layer by listing abstract methods enhanced for soft delete operation.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Step 4: Modifying the Entity Repository to extend the Custom Repository Interface
&lt;/h3&gt;

&lt;p&gt;Finally, modify the main repository interface by extending the &lt;strong&gt;SoftDeleteRepository&lt;/strong&gt; and providing the entity class name and the id data type as the generic type arguments. It also provides a default implementation for the &lt;strong&gt;findById&lt;/strong&gt;, &lt;strong&gt;findAll&lt;/strong&gt;, &lt;strong&gt;deleteById&lt;/strong&gt;, and &lt;strong&gt;deleteAll&lt;/strong&gt; methods to make use of the soft delete custom implementations defined in the &lt;strong&gt;SoftDeleteRepository&lt;/strong&gt; interface.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Step 5: Implement Soft Delete in the Service Layer
&lt;/h3&gt;

&lt;p&gt;Once you’ve completed the steps above, you’re all set. No further implementation is needed at the domain service layer. Notably, both the controller and unit test cases also required no modifications.&lt;/p&gt;

&lt;p&gt;To fetch all records (including deleted records) or deleted records, you can make use of query methods as shown in the snippet below:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Soft delete is an effective and flexible method for managing data without permanently deleting it, making it ideal for applications needing data recovery, compliance, or historical data tracking. In this guide, we discussed what soft delete is, its benefits, and how it can be implemented in a Spring WebFlux application with R2DBC.&lt;/p&gt;

&lt;p&gt;You can find the complete source code on &lt;a href="https://github.com/jelilio/spring-webflux-blog" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>spring</category>
      <category>java</category>
      <category>r2dbc</category>
      <category>webflux</category>
    </item>
    <item>
      <title>Internationalisation in Java</title>
      <dc:creator>Jelili Adesina</dc:creator>
      <pubDate>Tue, 30 Jul 2024 21:50:15 +0000</pubDate>
      <link>https://dev.to/jelilio/internationalisation-in-java-2oec</link>
      <guid>https://dev.to/jelilio/internationalisation-in-java-2oec</guid>
      <description>&lt;p&gt;Internationalisation (i18n) refers to designing and preparing software to be easily adapted to various languages, regions, and cultures without requiring engineering changes to the code. This is usually followed by localisation (l10n), which involves adapting the internationalised software to a specific locale, including translating the text, adjusting for local conventions, and modifying other locale-specific elements.&lt;/p&gt;

&lt;p&gt;The goal is to make the software flexible enough to support multiple locales by separating the core logic from locale-specific elements like language and cultural conventions.&lt;/p&gt;

&lt;h2&gt;
  
  
  ResourceBundle
&lt;/h2&gt;

&lt;p&gt;Localising a text message in a plain Java program is a bit straightforward using the ResourceBundle class provided by the programming language. The ResourceBundle class makes it easy to load, locale-specific, key-value attributes defined in properties files. These property files are known as &lt;strong&gt;resource bundles&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ResourceBundle&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
        &lt;span class="nc"&gt;ResourceBundle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBundle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"messages"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FRANCE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"greeting.hello"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bonjour le monde"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;greetingUsername&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;       &lt;span class="nc"&gt;MessageFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"greeting.username"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Ayo"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bonjour Ayo"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;greetingUsername&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using i8n-resource-bundle
&lt;/h2&gt;

&lt;p&gt;Another method of localising a text message in Java program is using third-party libraries. One of such library is i18n-resource-bundle. This library is an implementation over the ResourceBundle discussed earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency
&lt;/h3&gt;

&lt;p&gt;Add the dependency below in your pom.xml if you are using Maven&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.github.jelilio&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;i18n-resource-bundle&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.0.2&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefer Gradle, use this instead;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'io.github.jelilio:i18n-resource-bundle:0.0.2'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  MessageSource
&lt;/h3&gt;

&lt;p&gt;i8n-resource-bundle provides a &lt;strong&gt;MessageSource&lt;/strong&gt; interface that defines several methods for resolving messages. It has two implementations, &lt;strong&gt;ResourceBundleMessageSource&lt;/strong&gt; and &lt;strong&gt;ReloadableResourceBundleMessageSource&lt;/strong&gt;. Both implementations accesses the resource bundles using specified basenames similar to Java ResourceBundle. The ResourceBundleMessageSource resolve messages form resource bundles for different locales by relying on Java’s ResourceBundle implementation in combination with MessageFormat for message parsing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ResourceBundleMessageSource&lt;/span&gt; &lt;span class="n"&gt;messageSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ResourceBundleMessageSource&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;messageSource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setBasenames&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"messages"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageSource&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"greeting.hello"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FRANCE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bonjour le monde"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;greetingUsername&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageSource&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"greeting.username"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]{&lt;/span&gt;&lt;span class="s"&gt;"Ayo"&lt;/span&gt;&lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FRANCE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bonjour Ayo"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;greetingUsername&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ReloadableResourceBundleMessageSource
&lt;/h3&gt;

&lt;p&gt;Unlike ResourceBundleMessageSource, the ReloadableResourceBundleMessageSource uses Java’s Properties instances as its custom data structure for messages loading them using a different strategies that allow the reloading of the properties files based on timestamp changes and a specific character encoding without the need of restarting the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ReloadableResourceBundleMessageSource&lt;/span&gt; &lt;span class="n"&gt;messageSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ReloadableResourceBundleMessageSource&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;messageSource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setBasenames&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"messages"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageSource&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"greeting.hello"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FRANCE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bonjour le monde"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;greetingUsername&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageSource&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"greeting.username"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]{&lt;/span&gt;&lt;span class="s"&gt;"Ayo"&lt;/span&gt;&lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;US&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bonjour Ayo"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;greetingUsername&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this brief guide, we learned to implement internationalisation (i18n) in Java applications using the &lt;em&gt;ResourceBundle&lt;/em&gt; and i18n-resource-bundle. we learned how the resource bundles are resolved based on supplied locale names and saw an example in action.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/javase/tutorial/i18n/resbundle/index.html" rel="noopener noreferrer"&gt;The Java™ Tutorials - Isolating Locale-Specific Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jelilio/i18n-resource-bundle" rel="noopener noreferrer"&gt;i18n-resource-bundle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jelilio/i18n-in-java" rel="noopener noreferrer"&gt;Source code: i18n-in-java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>internationalisation</category>
      <category>programming</category>
      <category>localisation</category>
    </item>
  </channel>
</rss>
