<?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: i18n.site</title>
    <description>The latest articles on DEV Community by i18n.site (@i18n-site).</description>
    <link>https://dev.to/i18n-site</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%2F1788183%2Fefc97ee9-f4d4-48a2-9af3-e3e5ae5dd79b.png</url>
      <title>DEV Community: i18n.site</title>
      <link>https://dev.to/i18n-site</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/i18n-site"/>
    <language>en</language>
    <item>
      <title>Pure Front-End Inverted Full-Text Search</title>
      <dc:creator>i18n.site</dc:creator>
      <pubDate>Mon, 14 Oct 2024 05:30:26 +0000</pubDate>
      <link>https://dev.to/i18n-site/pure-front-end-inverted-full-text-search-7ho</link>
      <guid>https://dev.to/i18n-site/pure-front-end-inverted-full-text-search-7ho</guid>
      <description>&lt;p&gt;Original Link: &lt;a href="https://i18n.site/blog/tech/search" rel="noopener noreferrer"&gt;https://i18n.site/blog/tech/search&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequence
&lt;/h2&gt;

&lt;p&gt;After several weeks of development, &lt;a href="////i18n.site"&gt;i18n.site&lt;/a&gt; (a purely static markdown multilingual translation &amp;amp; website building tool) now supports pure front-end full-text search.&lt;/p&gt;

&lt;p&gt;&lt;a href="//p.3ti.site/1727600475.avif" class="article-body-image-wrapper"&gt;&lt;img src="//p.3ti.site/1727600475.avif"&gt;&lt;/a&gt;&lt;a href="//p.3ti.site/1727602760.avif" class="article-body-image-wrapper"&gt;&lt;img src="//p.3ti.site/1727602760.avif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article will share the technical implementation of &lt;code&gt;i18n.site&lt;/code&gt;'s pure front-end full-text search. Visit &lt;a href="////i18n.site"&gt;i18n.site&lt;/a&gt; to experience the search functionality.&lt;/p&gt;

&lt;p&gt;Code is open-source: &lt;a href="////github.com/i18n-site/ie/tree/main/qy"&gt;Search kernel&lt;/a&gt; / &lt;a href="////github.com/i18n-site/plugin/tree/main/qy"&gt;Interactive interface&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  An Overview of Serverless Full-Text Search Solutions
&lt;/h2&gt;

&lt;p&gt;For small and medium-sized purely static websites such as documents/personal blogs, building a self-built full-text search backend is too heavy, and service-free full-text search is the more common choice.&lt;/p&gt;

&lt;p&gt;Serverless full-text search solutions are divided into two main categories:&lt;/p&gt;

&lt;p&gt;The first involves third-party search service providers like &lt;a href="////algolia.com"&gt;algolia.com&lt;/a&gt; that offer front-end components for full-text search.&lt;/p&gt;

&lt;p&gt;Such services require payment based on search volume and are often unavailable to users in mainland China due to compliance issues.&lt;/p&gt;

&lt;p&gt;They cannot be used offline or on intranets, and have significant limitations. This article will not elaborate further.&lt;/p&gt;

&lt;p&gt;The second category is pure front-end full-text search.&lt;/p&gt;

&lt;p&gt;Currently, common pure front-end full-text search tools include &lt;a href="////lunrjs.com"&gt;lunrjs&lt;/a&gt; and &lt;a href="////github.com/weixsong/elasticlunr.js"&gt;ElasticLunr.js&lt;/a&gt; (a secondary development based on &lt;code&gt;lunrjs&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lunrjs&lt;/code&gt; has two methods for building indexes, both with their own issues.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pre-built index files&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because the index includes all the words from the documents, it is large in size.&lt;br&gt;
   Every time a document is added or modified, a new index file must be loaded.&lt;br&gt;
   This increases user waiting time and consumes a significant amount of bandwidth.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Loading documents and building indexes on the fly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Building an index is a computationally intensive task, and rebuilding it with each access can cause noticeable delays, leading to a poor user experience.&lt;/p&gt;



&lt;p&gt;In addition to &lt;code&gt;lunrjs&lt;/code&gt;, there are other full-text search solutions, such as:&lt;/p&gt;

&lt;p&gt;&lt;a href="////www.fusejs.io"&gt;fusejs&lt;/a&gt;, which searches by calculating the similarity between strings.&lt;/p&gt;

&lt;p&gt;This solution has poor performance and is not suitable for full-text search (refer to &lt;a href="////stackoverflow.com/questions/70984437/fuse-js-takes-10-seconds-with-semi-long-queries"&gt;Fuse.js Long query takes over 10 seconds, how to optimize?&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="////github.com/tinysearch/tinysearch"&gt;TinySearch&lt;/a&gt;, which uses a Bloom filter for searching, cannot perform prefix searches (e.g., entering &lt;code&gt;goo&lt;/code&gt; to search for &lt;code&gt;good&lt;/code&gt; or &lt;code&gt;google&lt;/code&gt;) and cannot achieve an autocomplete effect.&lt;/p&gt;

&lt;p&gt;Due to the drawbacks of existing solutions, &lt;code&gt;i18n.site&lt;/code&gt; has developed a new pure front-end full-text search solution with the following features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Supports multi-language search, with a compact size; the search kernel, when packaged with &lt;code&gt;gzip&lt;/code&gt;, is only &lt;code&gt;6.9KB&lt;/code&gt; (in comparison, &lt;code&gt;lunrjs&lt;/code&gt; is &lt;code&gt;25KB&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Builds an inverted index based on &lt;code&gt;IndexedDB&lt;/code&gt;, with low memory usage and fast performance&lt;/li&gt;
&lt;li&gt;When documents are added/modified, only the added or modified documents are re-indexed, reducing the amount of calculations&lt;/li&gt;
&lt;li&gt;Supports prefix search, allowing real-time display of search results as the user types&lt;/li&gt;
&lt;li&gt;Offline Availability&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Details of the &lt;code&gt;i18n.site&lt;/code&gt; technical implementation will be introduced below.&lt;/p&gt;
&lt;h2&gt;
  
  
  Multilingual Word Segmentation
&lt;/h2&gt;

&lt;p&gt;Word segmentation uses the browser's native &lt;code&gt;Intl.Segmenter&lt;/code&gt;, which is supported by all mainstream browsers.&lt;/p&gt;

&lt;p&gt;&lt;a href="//p.3ti.site/1727667759.avif" class="article-body-image-wrapper"&gt;&lt;img src="//p.3ti.site/1727667759.avif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;coffeescript&lt;/code&gt; code for word segmentation is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SEG = new Intl.Segmenter 0, granularity: "word"

seg = (txt) =&amp;gt;
  r = []
  for {segment} from SEG.segment(txt)
    for i from segment.split('.')
      i = i.trim()
      if i and !'|`'.includes(i) and !/\p{P}/u.test(i)
        r.push i
  r

export default seg

export segqy = (q) =&amp;gt;
  seg q.toLocaleLowerCase()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/\p{P}/&lt;/code&gt; is a regular expression that matches punctuation marks, including: &lt;code&gt;! " # $ % &amp;amp; ' ( ) * + , - . / : ; &amp;lt; = &amp;gt; ? @ [ \ ] ^ _&lt;/code&gt; { | } ~. &lt;code&gt;.&amp;lt;/p&amp;gt;&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;&lt;/code&gt;split('.')&lt;code&gt;is because&lt;/code&gt;Firefox&lt;code&gt;browser word segmentation does not segment&lt;/code&gt;.` .&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Index Construction
&lt;/h2&gt;

&lt;p&gt;Five object storage tables are created in &lt;code&gt;IndexedDB&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;word&lt;/code&gt;: id - word&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;doc&lt;/code&gt;: id - document URL - document version number&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docWord&lt;/code&gt;: document id - array of word ids&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prefix&lt;/code&gt;: prefix - array of word ids&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rindex&lt;/code&gt;: word id - document id - array of line numbers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By passing in an array of document &lt;code&gt;url&lt;/code&gt; and version number &lt;code&gt;ver&lt;/code&gt;, the &lt;code&gt;doc&lt;/code&gt; table is checked for the document's existence. If it does not exist, an inverted index is created. Simultaneously, the inverted index for documents not passed in is removed.&lt;/p&gt;

&lt;p&gt;This method allows for incremental indexing, reducing the computational load.&lt;/p&gt;

&lt;p&gt;In the front-end interface, a progress bar for index loading can be displayed to avoid lag during the initial load. See "Animated Progress Bar, Based on a Single progress + Pure CSS Implementation" &lt;a href="////dev.to/i18n-site/a-single-progress-uses-pure-css-to-achieve-animation-effects-2oo"&gt;English&lt;/a&gt; / &lt;a href="////juejin.cn/post/7413586285954154522"&gt;Chinese&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  IndexedDB High Concurrent Writing
&lt;/h3&gt;

&lt;p&gt;The project is developed based on the asynchronous encapsulation of IndexedDB, &lt;a href="////www.npmjs.com/package/idb"&gt;idb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;IndexedDB reads and writes are asynchronous. When creating an index, documents are loaded concurrently to build the index.&lt;/p&gt;

&lt;p&gt;To avoid data loss due to concurrent writes, you can refer to the following &lt;code&gt;coffeescript&lt;/code&gt; code, which adds a &lt;code&gt;ing&lt;/code&gt; cache between reading and writing to intercept competitive writes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;br&gt;
pusher = =&amp;gt;&lt;br&gt;
  ing = new Map()&lt;br&gt;
  (table, id, val)=&amp;gt;&lt;br&gt;
    id_set = ing.get(id)&lt;br&gt;
    if id_set&lt;br&gt;
      id_set.add val&lt;br&gt;
      return&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id_set = new Set([val])
ing.set id, id_set
pre = await table.get(id)
li = pre?.li or []

loop
  to_add = [...id_set]
  li.push(...to_add)
  await table.put({id,li})
  for i from to_add
    id_set.delete i
  if not id_set.size
    ing.delete id
    break
return
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;rindexPush = pusher()&lt;br&gt;
prefixPush = pusher()&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Precision and Recall
&lt;/h2&gt;

&lt;p&gt;The search first segments the keywords entered by the user.&lt;/p&gt;

&lt;p&gt;Assuming there are &lt;code&gt;N&lt;/code&gt; words after segmentation, the results are first returned with all keywords, followed by results with &lt;code&gt;N-1&lt;/code&gt;, &lt;code&gt;N-2&lt;/code&gt;, ..., &lt;code&gt;1&lt;/code&gt; keywords.&lt;/p&gt;

&lt;p&gt;The search results displayed first ensure query precision, while subsequent loaded results (click the "Load More" button) ensure recall.&lt;/p&gt;

&lt;p&gt;&lt;a href="//p.3ti.site/1727684564.avif" class="article-body-image-wrapper"&gt;&lt;img src="//p.3ti.site/1727684564.avif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  On-Demand Loading
&lt;/h2&gt;

&lt;p&gt;To improve response speed, the search uses the &lt;code&gt;yield&lt;/code&gt; generator to implement on-demand loading, returning results after each &lt;code&gt;limit&lt;/code&gt; query.&lt;/p&gt;

&lt;p&gt;Note that after each &lt;code&gt;yield&lt;/code&gt;, a new &lt;code&gt;IndexedDB&lt;/code&gt; query transaction must be opened for the next search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prefix Real-Time Search
&lt;/h2&gt;

&lt;p&gt;To display search results in real-time as the user types, for example, showing words like &lt;code&gt;words&lt;/code&gt; and &lt;code&gt;work&lt;/code&gt; that start with &lt;code&gt;wor&lt;/code&gt; when &lt;code&gt;wor&lt;/code&gt; is entered.&lt;/p&gt;

&lt;p&gt;&lt;a href="//p.3ti.site/1727684944.avif" class="article-body-image-wrapper"&gt;&lt;img src="//p.3ti.site/1727684944.avif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The search kernel uses the &lt;code&gt;prefix&lt;/code&gt; table for the last word after segmentation to find all words with that prefix and search sequentially.&lt;/p&gt;

&lt;p&gt;An anti-shake function, &lt;code&gt;debounce&lt;/code&gt; (implemented as follows), is used in the front-end interaction to reduce the frequency of searches triggered by user input, thus minimizing computational load.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;js&lt;br&gt;
export default (wait, func) =&amp;gt; {&lt;br&gt;
  var timeout;&lt;br&gt;
  return function(...args) {&lt;br&gt;
    clearTimeout(timeout);&lt;br&gt;
    timeout = setTimeout(func.bind(this, ...args), wait);&lt;br&gt;
  };&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Offline Availability
&lt;/h2&gt;

&lt;p&gt;The index table does not store the original text, only words, reducing storage space.&lt;/p&gt;

&lt;p&gt;Highlighting search results requires reloading the original text, and using &lt;code&gt;service worker&lt;/code&gt; can avoid repeated network requests.&lt;/p&gt;

&lt;p&gt;Also, because &lt;code&gt;service worker&lt;/code&gt; caches all articles, once a search is performed, the entire website, including search functionality, becomes offline available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimization for Displaying MarkDown Documents
&lt;/h2&gt;

&lt;p&gt;The pure front-end search solution provided by &lt;code&gt;i18n.site&lt;/code&gt; is optimized for &lt;code&gt;MarkDown&lt;/code&gt; documents.&lt;/p&gt;

&lt;p&gt;When displaying search results, the chapter name is shown, and clicking navigates to that chapter.&lt;/p&gt;

&lt;p&gt;&lt;a href="//p.3ti.site/1727686552.avif" class="article-body-image-wrapper"&gt;&lt;img src="//p.3ti.site/1727686552.avif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The pure front-end implementation of inverted full-text search, without the need for a server, is very suitable for small to medium-sized websites such as documents and personal blogs.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;i18n.site&lt;/code&gt;'s open-source self-developed pure front-end search is compact, responsive, and addresses the various shortcomings of current pure front-end full-text search solutions, providing a better user experience.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>nlp</category>
      <category>web</category>
      <category>programming</category>
    </item>
    <item>
      <title>Animated progress bar, based on a single progress + pure css implementation</title>
      <dc:creator>i18n.site</dc:creator>
      <pubDate>Thu, 12 Sep 2024 14:02:35 +0000</pubDate>
      <link>https://dev.to/i18n-site/a-single-progress-uses-pure-css-to-achieve-animation-effects-2oo</link>
      <guid>https://dev.to/i18n-site/a-single-progress-uses-pure-css-to-achieve-animation-effects-2oo</guid>
      <description>&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/i18n-site/embed/WNqWevp?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  pug
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p
  input#rangeInput(type="range", min="0", max="100", value="50")
  progress#progressBar(value="50", max="100")
p
  progress(max="100", value="10")
  progress(max="100", value="40")
  progress(max="100", value="80")
  progress(max="100", value="100")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  css
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;progress[value],
progress[value]::-webkit-progress-bar {
  border-radius: 10px;
  box-shadow: 0 0 3px inset #ccc;
  background: url('data:image/svg+xml,&amp;lt;svg xmlns="http://www.w3.org/2000/svg"&amp;gt;&amp;lt;defs&amp;gt;&amp;lt;linearGradient id="a" x1="0%" y1="0%" x2="100%" y2="0%"&amp;gt;&amp;lt;stop offset="0%" stop-color="%23fff"/&amp;gt;&amp;lt;stop offset="50%" stop-color="%23eee"/&amp;gt;&amp;lt;stop offset="100%" stop-color="%23fff"/&amp;gt;&amp;lt;animateTransform attributeName="gradientTransform" type="translate" from="-1 0" to="1 0" dur="2s" repeatCount="indefinite" additive="sum"/&amp;gt;&amp;lt;/linearGradient&amp;gt;&amp;lt;/defs&amp;gt;&amp;lt;rect width="100%" height="100%" fill="url(%23a)"/&amp;gt;&amp;lt;/svg&amp;gt;');
}

progress[value] {
  width: 200px;
  border: 2px solid #eee;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  overflow: hidden;
  height: 20px;
  --pbarbg: url('data:image/svg+xml,&amp;lt;svg xmlns="http://www.w3.org/2000/svg" width="26" height="20" preserveAspectRatio="xMidYMid"&amp;gt;&amp;lt;defs&amp;gt;&amp;lt;pattern id="a" patternUnits="userSpaceOnUse" width="100" height="100"&amp;gt;&amp;lt;g transform="translate(7.8)"&amp;gt;&amp;lt;path fill="%23ddd" d="M-40-10h10v120h-10z" transform="rotate(20 50 50) scale(1.2)"/&amp;gt;&amp;lt;path fill="%23eee" d="M-30-10h10v120h-10z" transform="rotate(20 50 50) scale(1.2)"/&amp;gt;&amp;lt;path fill="%23ddd" d="M-20-10h10v120h-10z" transform="rotate(20 50 50) scale(1.2)"/&amp;gt;&amp;lt;path fill="%23eee" d="M-10-10H0v120h-10z" transform="rotate(20 50 50) scale(1.2)"/&amp;gt;&amp;lt;path fill="%23ddd" d="M0-10h10v120H0z" transform="rotate(20 50 50) scale(1.2)"/&amp;gt;&amp;lt;path fill="%23eee" d="M10-10h10v120H10z" transform="rotate(20 50 50) scale(1.2)"/&amp;gt;&amp;lt;path fill="%23ddd" d="M20-10h10v120H20z" transform="rotate(20 50 50) scale(1.2)"/&amp;gt;&amp;lt;animateTransform attributeName="transform" type="translate" values="0 0;26 0" keyTimes="0;1" repeatCount="indefinite" dur="2s"/&amp;gt;&amp;lt;/g&amp;gt;&amp;lt;/pattern&amp;gt;&amp;lt;/defs&amp;gt;&amp;lt;path fill="url(%23a)" d="M0 0h26v20H0z"/&amp;gt;&amp;lt;/svg&amp;gt;');
}

progress[value]::-webkit-progress-value {
  box-shadow: 0 0 3px inset #999;
  border-radius: 10px;
  background: var(--pbarbg);
  transition: all 0.5s;
}

progress::-moz-progress-bar {
  box-shadow: 0 0 3px inset #999;
  border-radius: 10px;
  background: var(--pbarbg);
  transition: all 0.5s;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  About
&lt;/h2&gt;

&lt;p&gt;This is a progress bar I developed for the markdown translation/website building tool &lt;a href="////i18n.site"&gt;i18n.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>animation</category>
    </item>
    <item>
      <title>i18n.site · MarkDown Translation and Website Building Tool, Now Online !</title>
      <dc:creator>i18n.site</dc:creator>
      <pubDate>Fri, 16 Aug 2024 03:19:51 +0000</pubDate>
      <link>https://dev.to/i18n-site/i18nsite-markdown-translation-and-website-building-tool-now-online-1iif</link>
      <guid>https://dev.to/i18n-site/i18nsite-markdown-translation-and-website-building-tool-now-online-1iif</guid>
      <description>&lt;p&gt;After more than half a year of development, &lt;a href="////i18n.site"&gt;https://i18n.site&lt;/a&gt; has come online.&lt;/p&gt;

&lt;p&gt;Currently, two open source command-line tools have been implemented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;i18&lt;/code&gt;: MarkDown Command-line translation tool&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i18n.site&lt;/code&gt;: Multi-language static document site generator, &lt;strong&gt;optimized for the reading experience&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Translation can perfectly maintain the format of &lt;code&gt;Markdown&lt;/code&gt;. It can identify file modifications and only translate files with changes.&lt;/p&gt;

&lt;p&gt;The translation is editable; if you modify the original text and machine-translate it again, the manual modifications to the translation will not be overwritten (if this paragraph of the original text has not been modified).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/login/oauth/authorize?client_id=Ov23liuGAmK0plc9FgB3&amp;amp;scope=user:email,user:follow,public_repo" rel="noopener noreferrer"&gt;Click here to authorize automatically follow i18n.site's github&lt;/a&gt; and will &lt;strong&gt;receive bonus $50&lt;/strong&gt; .&lt;/p&gt;

&lt;h2&gt;
  
  
  Origin
&lt;/h2&gt;

&lt;p&gt;In the Internet era, the whole world is a market, and multilingualism and localization are basic skills.&lt;/p&gt;

&lt;p&gt;The existing translation management tools are too heavyweight. For programmers who rely on &lt;code&gt;git&lt;/code&gt; for version management, they still prefer the command-line.&lt;/p&gt;

&lt;p&gt;So, I developed a translation tool &lt;code&gt;i18&lt;/code&gt; and built a multi-language static site generator &lt;code&gt;i18n.site&lt;/code&gt; based on the translation tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fp.3ti.site%2F1723777556.avif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fp.3ti.site%2F1723777556.avif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is just the beginning, and there is much more to do.&lt;/p&gt;

&lt;p&gt;For example, by connecting the static document site with social media and email subscriptions, users can be reached in a timely manner when updates are released.&lt;/p&gt;

&lt;p&gt;For example, multi-language forums and work order systems can be embedded in any webpage, allowing users to communicate without barriers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source
&lt;/h2&gt;

&lt;p&gt;The front-end, back-end, and command-line &lt;a href="https://i18n.site/i18n.site/c/src" rel="noopener noreferrer"&gt;codes are all open source&lt;/a&gt; (the translation model is not open source yet).&lt;/p&gt;

&lt;p&gt;The technology stack used is as follows:&lt;/p&gt;

&lt;p&gt;Front-end &lt;a href="https://svelte.dev" rel="noopener noreferrer"&gt;svelte&lt;/a&gt;, &lt;a href="https://stylus-lang.com" rel="noopener noreferrer"&gt;stylus&lt;/a&gt;, &lt;a href="https://github.com/pugjs/pug" rel="noopener noreferrer"&gt;pug&lt;/a&gt;, &lt;a href="https://github.com/vitejs/vite" rel="noopener noreferrer"&gt;vite&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The command-line and back-end are developed based on rust.&lt;/p&gt;

&lt;p&gt;Back-end &lt;a href="https://github.com/tokio-rs/axum" rel="noopener noreferrer"&gt;axum&lt;/a&gt;, &lt;a href="https://github.com/tower-rs/tower-http/releases" rel="noopener noreferrer"&gt;tower-http&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Command-line &lt;a href="https://docs.rs/boa_engine" rel="noopener noreferrer"&gt;embedded js engine boa_engine&lt;/a&gt;, &lt;a href="https://github.com/fjall-rs/fjall" rel="noopener noreferrer"&gt;embedded database fjall&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Server VPS &lt;a href="https://my.contabo.com" rel="noopener noreferrer"&gt;contabo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Database &lt;a href="https://kvrocks.apache.org" rel="noopener noreferrer"&gt;kvrocks&lt;/a&gt;, &lt;a href="https://mariadb.org" rel="noopener noreferrer"&gt;mariadb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Mail sending self-built SMTP &lt;a href="https://github.com/albertito/chasquid" rel="noopener noreferrer"&gt;chasquid&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contact Us
&lt;/h2&gt;

&lt;p&gt;When new products are launched, problems are inevitable.&lt;/p&gt;

&lt;p&gt;Feel free to contact us via the Google Forum: &lt;a href="https://groups.google.com/u/2/g/i18n-site" rel="noopener noreferrer"&gt;groups.google.com/u/2/g/i18n.site&lt;/a&gt;&lt;/p&gt;

</description>
      <category>i18n</category>
      <category>markdown</category>
      <category>svelte</category>
      <category>rust</category>
    </item>
  </channel>
</rss>
