169 lines
72 KiB
HTML
169 lines
72 KiB
HTML
<!doctype html><html lang=en dir=ltr class="docs-wrapper plugin-docs plugin-id-default docs-version-current docs-doc-page docs-doc-id-contribution/i18n" data-has-hydrated=false><meta charset=UTF-8><meta name=generator content="Docusaurus v3.7.0"><title data-rh=true>i18n | LiveCodes</title><meta data-rh=true name=viewport content="width=device-width, initial-scale=1.0"><meta data-rh=true name=twitter:card content=summary_large_image><meta data-rh=true property=og:url content=https://livecodes.io/docs/contribution/i18n><meta data-rh=true property=og:locale content=en><meta data-rh=true name=docusaurus_locale content=en><meta data-rh=true name=docsearch:language content=en><meta data-rh=true name=docusaurus_version content=current><meta data-rh=true name=docusaurus_tag content=docs-default-current><meta data-rh=true name=docsearch:version content=current><meta data-rh=true name=docsearch:docusaurus_tag content=docs-default-current><meta data-rh=true property=og:title content="i18n | LiveCodes"><meta data-rh=true name=description content="This document provides a guide on how to contribute to the translation of the app."><meta data-rh=true property=og:description content="This document provides a guide on how to contribute to the translation of the app."><link data-rh=true rel=icon href=/docs/img/favicon.ico><link data-rh=true rel=canonical href=https://livecodes.io/docs/contribution/i18n><link data-rh=true rel=alternate href=https://livecodes.io/docs/contribution/i18n hreflang=en><link data-rh=true rel=alternate href=https://livecodes.io/docs/contribution/i18n hreflang=x-default><link data-rh=true rel=preconnect href=https://H9Z2PKYS80-dsn.algolia.net crossorigin=anonymous><link rel=search type=application/opensearchdescription+xml title=LiveCodes href=/docs/opensearch.xml><script type=ea-placeholder id=ea-placeholder data-ea-publisher=livecodesio data-ea-manual=true></script><script src=https://unpkg.com/prettier@2.4.1/standalone.js async></script><script src=https://unpkg.com/prettier@2.4.1/parser-babel.js async></script><script src=https://unpkg.com/prettier@2.4.1/parser-html.js async></script><script src=https://media.ethicalads.io/media/client/ethicalads.min.js async defer></script><link rel=stylesheet href=/docs/assets/css/styles.61e3dfbf.css><script src=/docs/assets/js/runtime~main.a40c3f9c.js defer></script><script src=/docs/assets/js/main.af5c4c8b.js defer></script><body class=navigation-with-keyboard><script>!function(){var t,e=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();t=null!==e?e:"light",document.documentElement.setAttribute("data-theme",t)}(),function(){try{for(var[t,e]of new URLSearchParams(window.location.search).entries())if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id=__docusaurus><link rel=preload as=image href=/docs/img/livecodes-logo.svg><div role=region aria-label="Skip to main content"><a class=skipToContent_fXgn href=#__docusaurus_skipToContent_fallback>Skip to main content</a></div><nav aria-label=Main class="navbar navbar--fixed-top"><div class=navbar__inner><div class=navbar__items><button aria-label="Toggle navigation bar" aria-expanded=false class="navbar__toggle clean-btn" type=button><svg width=30 height=30 viewBox="0 0 30 30" aria-hidden=true><path stroke=currentColor stroke-linecap=round stroke-miterlimit=10 stroke-width=2 d="M4 7h22M4 15h22M4 23h22"/></svg></button><a class=navbar__brand href=/docs/><div class=navbar__logo><img src=/docs/img/livecodes-logo.svg alt="LiveCodes Logo" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src=/docs/img/livecodes-logo.svg alt="LiveCodes Logo" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><b class="navbar__title text--truncate">LiveCodes</b></a><a class="navbar__item navbar__link" href=/docs/overview>Docs</a><a class="navbar__item navbar__link" href=/docs/sdk>SDK</a><a href=https://blog.livecodes.io target=_self rel="noopener noreferrer" class="navbar__item navbar__link">Blog<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><a href=/docs/../stories target=_blank rel="noopener noreferrer" class="navbar__item navbar__link">Storybook<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a></div><div class="navbar__items navbar__items--right"><a href=/docs/../ target=_blank rel="noopener noreferrer" class="navbar__item navbar__link">App<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><a href=https://twitter.com/livecodes_io target=_blank rel="noopener noreferrer" class="navbar__item navbar__link">𝕏<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><a href=https://github.com/live-codes/livecodes target=_blank rel="noopener noreferrer" class="navbar__item navbar__link">GitHub<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><div class="toggle_vylO colorModeToggle_DEke"><button class="clean-btn toggleButton_gllP toggleButtonDisabled_aARS" type=button disabled title="Switch between dark and light mode (currently light mode)" aria-label="Switch between dark and light mode (currently light mode)" aria-live=polite aria-pressed=false><svg viewBox="0 0 24 24" width=24 height=24 class=lightToggleIcon_pyhR><path fill=currentColor d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg><svg viewBox="0 0 24 24" width=24 height=24 class=darkToggleIcon_wfgR><path fill=currentColor d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg></button></div><div class=navbarSearchContainer_Bca1><button type=button class="DocSearch DocSearch-Button" aria-label="Search (Command+K)"><span class=DocSearch-Button-Container><svg width=20 height=20 class=DocSearch-Search-Icon viewBox="0 0 20 20" aria-hidden=true><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke=currentColor fill=none fill-rule=evenodd stroke-linecap=round stroke-linejoin=round /></svg><span class=DocSearch-Button-Placeholder>Search</span></span><span class=DocSearch-Button-Keys></span></button></div></div></div><div role=presentation class=navbar-sidebar__backdrop></div></nav><div id=__docusaurus_skipToContent_fallback class="main-wrapper mainWrapper_z2l0"><div class=docsWrapper_hBAB><button aria-label="Scroll back to top" class="clean-btn theme-back-to-top-button backToTopButton_sjWU" type=button></button><div class=docRoot_UBD9><main class="docMainContainer_TBSr docMainContainerEnhanced_lQrH"><div class="container padding-top--md padding-bottom--lg"><div class=row><div class="col docItemCol_z5aJ"><div class=docItemContainer_c0TR><article><div class="tocCollapsible_ETCw theme-doc-toc-mobile tocMobile_ITEo"><button type=button class="clean-btn tocCollapsibleButton_TO0P">On this page</button></div><div class="theme-doc-markdown markdown"><header><h1>i18n</h1><div class="custom-content custom-content-header content_lMOZ undefined"><div data-ea-publisher=livecodesio data-ea-type=image class=horizontal data-ea-manual=true></div></div></header>
|
||
<p>This document provides a guide on how to contribute to the translation of the app.</p>
|
||
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=for-translators>For Translators<a href=#for-translators class=hash-link aria-label="Direct link to For Translators" title="Direct link to For Translators"></a></h2>
|
||
<p>Translators are responsible for translating the source texts on Lokalise.</p>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=contribute-to-translation>Contribute to Translation<a href=#contribute-to-translation class=hash-link aria-label="Direct link to Contribute to Translation" title="Direct link to Contribute to Translation"></a></h3>
|
||
<p>Please visit the <a href=https://app.lokalise.com/public/34958094667a72e9454592.95108106/ target=_blank rel="noopener noreferrer">Lokalise project page</a> to contribute to the translation of LiveCodes. You might find the <a href=https://docs.lokalise.com/en/articles/2967175-onboarding-guide-for-translators target=_blank rel="noopener noreferrer">Onboarding Guide for Translators</a> on Lokalise helpful.</p>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=add-a-new-language>Add a New Language<a href=#add-a-new-language class=hash-link aria-label="Direct link to Add a New Language" title="Direct link to Add a New Language"></a></h3>
|
||
<p>If you find that the language you want to translate to is not available on Lokalise, please kindly <a href="https://github.com/live-codes/livecodes/issues/new?template=i18n_request.yml" target=_blank rel="noopener noreferrer">raise an issue</a> in the repository with further details about the language you want to add.</p>
|
||
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=technical-overview>Technical Overview<a href=#technical-overview class=hash-link aria-label="Direct link to Technical Overview" title="Direct link to Technical Overview"></a></h2>
|
||
<p>The i18n framework <a href=https://www.i18next.com/ target=_blank rel="noopener noreferrer"><code>i18next</code></a> and the online translation collaboration platform <a href=https://lokalise.com/ target=_blank rel="noopener noreferrer">Lokalise</a> are used to manage the i18n of LiveCodes.</p>
|
||
<blockquote>
|
||
<p>It is recommended to read the related documentation of the above tools before continuing.</p>
|
||
</blockquote>
|
||
<p>The i18n workflow is designed to be <strong>source-based</strong>, which means that the source texts are extracted from the codebase and uploaded to Lokalise for translation. After the translation is complete, the translated texts are integrated back into the codebase. For more details, please refer to the <a href=#workflow>Workflow</a> section.</p>
|
||
<p>Two types of strings mentioned in the <a href=#strings>Strings</a> section are considered as the <strong>source texts</strong>, and English is the <strong>source language</strong>.</p>
|
||
<p>Extracted source texts are stored in two forms under <code>src/livecodes/i18n/locales/en</code>:</p>
|
||
<ul>
|
||
<li><code>.ts</code> files: used by the app to load the source texts and provide type-safety for TypeScript</li>
|
||
<li><code>.lokalise.json</code> files: used to upload the source texts to Lokalise</li>
|
||
</ul>
|
||
<blockquote>
|
||
<p>These files are generated and kept in sync with each other by the <code>i18n-export</code> npm script. See the <a href=#scripts>Scripts</a> section for more details.</p>
|
||
</blockquote>
|
||
<p>Other directories under <code>src/livecodes/i18n/locales</code> are used to store the translated texts in other target languages, which only contain <code>.ts</code> files.</p>
|
||
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=for-developers>For Developers<a href=#for-developers class=hash-link aria-label="Direct link to For Developers" title="Direct link to For Developers"></a></h2>
|
||
<p>Developers are responsible for implementing new features or making changes to the existing codebase. When adding new strings or modifying existing strings, developers should ensure that the newly-edited strings are properly extracted and saved.</p>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=strings>Strings<a href=#strings class=hash-link aria-label="Direct link to Strings" title="Direct link to Strings"></a></h3>
|
||
<p>Strings that need to be translated are located in both <code>src/livecodes/html/*.html</code> and other <code>.ts</code> files in <code>src/livecodes</code> (mostly in <code>src/livecodes/UI/</code>). These two different types of files, which also represent two types of translation methods, are handled differently in the i18n workflow:</p>
|
||
<h4 class="anchor anchorWithStickyNavbar_LWe7" id=element-level-translation-html-files>Element-level Translation (HTML Files)<a href=#element-level-translation-html-files class=hash-link aria-label="Direct link to Element-level Translation (HTML Files)" title="Direct link to Element-level Translation (HTML Files)"></a></h4>
|
||
<p>In these files, strings are wrapped inside HTML elements with <code>data-i18n</code> attribute and two optional attributes (<code>data-i18n-prop</code>, <code>data-i18n-interpolation</code>). For example:</p>
|
||
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style=--prism-color:#393A34;--prism-background-color:#f6f8fa><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-html codeBlock_bY9V thin-scrollbar" style=color:#393A34;background-color:#f6f8fa><code class=codeBlockLines_e6Vv><span class=token-line style=color:#393A34><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>div</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>class</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>modal-title</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>assets.heading</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain">Assets</span><span class="token tag punctuation" style=color:#393A34></</span><span class="token tag" style=color:#00009f>div</span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style=color:#393A34><span class="token plain"></span><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>input</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>id</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>search-assets</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>type</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>text</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>placeholder</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>Search</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>assets.search</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n-prop</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>placeholder</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f></span><span class="token tag punctuation" style=color:#393A34>/></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style=color:#393A34><span class="token plain"></span><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>div</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>class</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>description</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>backup.backup.desc</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n-prop</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>innerHTML</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> Backup LiveCodes data, so that it can be later restored on this or other devices. </span><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>br</span><span class="token tag" style=color:#00009f> </span><span class="token tag punctuation" style=color:#393A34>/></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> Please visit the</span><br></span><span class=token-line style=color:#393A34><span class="token plain"> </span><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>a</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>href</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>{{DOCS_BASE_URL}}features/backup-restore</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>target</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>_blank</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>rel</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>noopener</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f> </span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain">documentations</span><span class="token tag punctuation" style=color:#393A34></</span><span class="token tag" style=color:#00009f>a</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f> </span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> for details.</span><br></span><span class=token-line style=color:#393A34><span class="token plain"></span><span class="token tag punctuation" style=color:#393A34></</span><span class="token tag" style=color:#00009f>div</span><span class="token tag punctuation" style=color:#393A34>></span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<h5 class="anchor anchorWithStickyNavbar_LWe7" id=keys>Keys<a href=#keys class=hash-link aria-label="Direct link to Keys" title="Direct link to Keys"></a></h5>
|
||
<p>The <code>data-i18n</code> attribute is used to specify the <strong>key</strong> of the string and is a <strong>period-separated</strong> string with each part being a <strong>lowerCamelCase</strong> word.</p>
|
||
<h5 class="anchor anchorWithStickyNavbar_LWe7" id=value>Value<a href=#value class=hash-link aria-label="Direct link to Value" title="Direct link to Value"></a></h5>
|
||
<p>The value of the corresponding attribute of the element is used as the <strong>default / fallback value</strong> of the string.</p>
|
||
<h5 class="anchor anchorWithStickyNavbar_LWe7" id=props>Props<a href=#props class=hash-link aria-label="Direct link to Props" title="Direct link to Props"></a></h5>
|
||
<p>The <code>data-i18n-prop</code> attribute is a <strong>space-separated list</strong> of properties that should be translated. If it is not present, the string will be translated as the <code>textContent</code> of the element. When two or more properties are specified, a "full key" (<code><key>.<property></code>, or <code><key>#<property></code> on Lokalise) will be used to identify the string. For example, for the following element:</p>
|
||
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style=--prism-color:#393A34;--prism-background-color:#f6f8fa><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-html codeBlock_bY9V thin-scrollbar" style=color:#393A34;background-color:#f6f8fa><code class=codeBlockLines_e6Vv><span class=token-line style=color:#393A34><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>div</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>title</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>This is a title</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>key.to.title</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n-prop</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>title textContent</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> This is a content</span><br></span><span class=token-line style=color:#393A34><span class="token plain"></span><span class="token tag punctuation" style=color:#393A34></</span><span class="token tag" style=color:#00009f>div</span><span class="token tag punctuation" style=color:#393A34>></span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<p>The string will be identified as <code>key.to.title#title</code> and <code>key.to.title#textContent</code> on Lokalise.</p>
|
||
<h6 class="anchor anchorWithStickyNavbar_LWe7" id=abstract-html-tags>Abstract HTML Tags<a href=#abstract-html-tags class=hash-link aria-label="Direct link to Abstract HTML Tags" title="Direct link to Abstract HTML Tags"></a></h6>
|
||
<p>When the <code>data-i18n-prop</code> attribute is <code>innerHTML</code>, HTML tags inside the value will be abstracted during exporting, making the final source texts more readable. For example, the following value:</p>
|
||
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style=--prism-color:#393A34;--prism-background-color:#f6f8fa><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-html codeBlock_bY9V thin-scrollbar" style=color:#393A34;background-color:#f6f8fa><code class=codeBlockLines_e6Vv><span class=token-line style=color:#393A34><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>div</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>class</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>description</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>backup.backup.desc</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n-prop</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>innerHTML</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> Backup LiveCodes data, so that it can be later restored on this or other devices. </span><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>br</span><span class="token tag" style=color:#00009f> </span><span class="token tag punctuation" style=color:#393A34>/></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> Please visit the</span><br></span><span class=token-line style=color:#393A34><span class="token plain"> </span><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>a</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>href</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>{{DOCS_BASE_URL}}features/backup-restore</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>target</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>_blank</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>rel</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>noopener</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f> </span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain">documentations</span><span class="token tag punctuation" style=color:#393A34></</span><span class="token tag" style=color:#00009f>a</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f> </span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> for details.</span><br></span><span class=token-line style=color:#393A34><span class="token plain"></span><span class="token tag punctuation" style=color:#393A34></</span><span class="token tag" style=color:#00009f>div</span><span class="token tag punctuation" style=color:#393A34>></span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<p>will be abstracted to:</p>
|
||
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style=--prism-color:#393A34;--prism-background-color:#f6f8fa><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-html codeBlock_bY9V thin-scrollbar" style=color:#393A34;background-color:#f6f8fa><code class=codeBlockLines_e6Vv><span class=token-line style=color:#393A34><span class="token plain">Backup LiveCodes data, so that it can be later restored on this or other devices. <1></1> Please visit the <2>documentations</2> for details.</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<p>Although overriding specified attributes or adding new ones to the corresponding element is supported (<code><2 href="https://example.org">documentations</2></code>), it is not recommended to do so unless necessary (e.g., docs link for different languages).</p>
|
||
<h5 class="anchor anchorWithStickyNavbar_LWe7" id=interpolation>Interpolation<a href=#interpolation class=hash-link aria-label="Direct link to Interpolation" title="Direct link to Interpolation"></a></h5>
|
||
<p>Interpolation is used to insert dynamic content into the string. The <code>data-i18n-interpolation</code> attribute is a <strong>JSON object string</strong> that contains the key-value pairs of the dynamic content. For example:</p>
|
||
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style=--prism-color:#393A34;--prism-background-color:#f6f8fa><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-html codeBlock_bY9V thin-scrollbar" style=color:#393A34;background-color:#f6f8fa><code class=codeBlockLines_e6Vv><span class=token-line style=color:#393A34><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>div</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>class</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>share-encoded-url-expiry</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> </span><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>span</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>class</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>{{warnClass}}</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>share.characters</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain">{{urlLength}} characters</span><span class="token tag punctuation" style=color:#393A34></</span><span class="token tag" style=color:#00009f>span</span><span class="token tag" style=color:#00009f></span><br></span><span class=token-line style=color:#393A34><span class="token tag" style=color:#00009f> </span><span class="token tag punctuation" style=color:#393A34>></span><span class="token tag punctuation" style=color:#393A34><</span><span class="token tag" style=color:#00009f>a</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>href</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>#</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag" style=color:#00009f> </span><span class="token tag attr-name" style=color:#00a4db>data-i18n</span><span class="token tag attr-value punctuation attr-equals" style=color:#393A34>=</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag attr-value" style=color:#e3116c>share.shortURL</span><span class="token tag attr-value punctuation" style=color:#393A34>"</span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain">Get short URL</span><span class="token tag punctuation" style=color:#393A34></</span><span class="token tag" style=color:#00009f>a</span><span class="token tag punctuation" style=color:#393A34>></span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"></span><span class="token tag punctuation" style=color:#393A34></</span><span class="token tag" style=color:#00009f>div</span><span class="token tag punctuation" style=color:#393A34>></span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<p>In related TypeScript files, the <code>data-i18n-interpolation</code> attribute should be set as follows:</p>
|
||
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style=--prism-color:#393A34;--prism-background-color:#f6f8fa><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style=color:#393A34;background-color:#f6f8fa><code class=codeBlockLines_e6Vv><span class=token-line style=color:#393A34><span class="token plain">charactersSpan</span><span class="token punctuation" style=color:#393A34>.</span><span class="token plain">dataset</span><span class="token punctuation" style=color:#393A34>.</span><span class="token plain">i18nInterpolation </span><span class="token operator" style=color:#393A34>=</span><span class="token plain"> </span><span class="token constant" style=color:#36acaa>JSON</span><span class="token punctuation" style=color:#393A34>.</span><span class="token function" style=color:#d73a49>stringify</span><span class="token punctuation" style=color:#393A34>(</span><span class="token punctuation" style=color:#393A34>{</span><span class="token plain"> urlLength </span><span class="token punctuation" style=color:#393A34>}</span><span class="token punctuation" style=color:#393A34>)</span><span class="token punctuation" style=color:#393A34>;</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<h4 class="anchor anchorWithStickyNavbar_LWe7" id=string-level-translation-typescript-files>String-level Translation (TypeScript Files)<a href=#string-level-translation-typescript-files class=hash-link aria-label="Direct link to String-level Translation (TypeScript Files)" title="Direct link to String-level Translation (TypeScript Files)"></a></h4>
|
||
<p>In these files, strings are wrapped inside <code>window.deps.translateString(key, value, interpolation)</code> function calls. For example:</p>
|
||
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style=--prism-color:#393A34;--prism-background-color:#f6f8fa><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style=color:#393A34;background-color:#f6f8fa><code class=codeBlockLines_e6Vv><span class=token-line style=color:#393A34><span class="token plain">window</span><span class="token punctuation" style=color:#393A34>.</span><span class="token plain">deps</span><span class="token punctuation" style=color:#393A34>.</span><span class="token function" style=color:#d73a49>translateString</span><span class="token punctuation" style=color:#393A34>(</span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> </span><span class="token string" style=color:#e3116c>'namespace:file.key1.subkey1'</span><span class="token punctuation" style=color:#393A34>,</span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> </span><span class="token string" style=color:#e3116c>'default <strong>value</strong>, {{interpol}}'</span><span class="token punctuation" style=color:#393A34>,</span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> </span><span class="token punctuation" style=color:#393A34>{</span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> isHTML</span><span class="token operator" style=color:#393A34>:</span><span class="token plain"> </span><span class="token boolean" style=color:#36acaa>true</span><span class="token punctuation" style=color:#393A34>,</span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> interpol</span><span class="token operator" style=color:#393A34>:</span><span class="token plain"> </span><span class="token string" style=color:#e3116c>'abc'</span><span class="token punctuation" style=color:#393A34>,</span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> </span><span class="token punctuation" style=color:#393A34>}</span><span class="token punctuation" style=color:#393A34>,</span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"></span><span class="token punctuation" style=color:#393A34>)</span><span class="token punctuation" style=color:#393A34>;</span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style=color:#393A34><span class="token plain">window</span><span class="token punctuation" style=color:#393A34>.</span><span class="token plain">deps</span><span class="token punctuation" style=color:#393A34>.</span><span class="token function" style=color:#d73a49>translateString</span><span class="token punctuation" style=color:#393A34>(</span><span class="token string" style=color:#e3116c>'core.login.successWithName'</span><span class="token punctuation" style=color:#393A34>,</span><span class="token plain"> </span><span class="token string" style=color:#e3116c>'Logged in as: {{name}}'</span><span class="token punctuation" style=color:#393A34>,</span><span class="token plain"> </span><span class="token punctuation" style=color:#393A34>{</span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"> name</span><span class="token operator" style=color:#393A34>:</span><span class="token plain"> displayName</span><span class="token punctuation" style=color:#393A34>,</span><span class="token plain"></span><br></span><span class=token-line style=color:#393A34><span class="token plain"></span><span class="token punctuation" style=color:#393A34>}</span><span class="token punctuation" style=color:#393A34>)</span><span class="token punctuation" style=color:#393A34>;</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<p>The function is completely type-safe:</p>
|
||
<ul>
|
||
<li>The first argument is <code>key</code>, which is validated with valid keys in <code>i18n/locales/en/</code>.</li>
|
||
<li>The second argument is <code>value</code>. With the <code>key</code> provided, the type of <code>value</code> will be narrowed down to the value in the translation file to ensure all occurrences of the key have the same value. If this is an HTML string (with the <code>isHTML</code> attribute set to <code>true</code>), the value will automatically be abstracted when exporting the translation.</li>
|
||
<li>The third argument is <code>interpolation</code>, which could be omitted when the value doesn't contain any interpolation. Otherwise, it should be an object whose attributes are inferred from the <code>value</code>. Moreover, an additional <code>isHTML</code> boolean attribute is added to indicate whether the <code>value</code> contains HTML tags and should be abstracted when exporting the translation.</li>
|
||
</ul>
|
||
<p>To provide better readability and maintainability, only string-level translation will be used in .ts files when it comes to dynamic content. For static content, element-level translation is still the best choice.</p>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=scripts>Scripts<a href=#scripts class=hash-link aria-label="Direct link to Scripts" title="Direct link to Scripts"></a></h3>
|
||
<p>Several npm scripts are available to facilitate the i18n workflow:</p>
|
||
<ul>
|
||
<li><a href=/docs/assets/files/vscode-intellisense-9573a05c29631b61eab8c1ea3da9c50d.js target=_blank><code>vscode-intellisense</code></a>: Generates the <code>html.html-data.json</code> file in the <code>.vscode</code> folder to enhance intellisense for the <code>data-i18n</code> attribute in HTML files in VSCode.</li>
|
||
<li><a href=/docs/assets/files/i18n-export-83977ebfdbe21c25c30819abc6321af1.js target=_blank><code>i18n-export</code></a>: Extracts source texts from the codebase and generates <code>.ts</code> and <code>.lokalise.json</code> files under <code>src/livecodes/i18n/locales/en</code>.<!-- -->
|
||
<ul>
|
||
<li>The <code>--save-tmp</code> flag can be used to save the extracted source texts to <code>i18n/locales/tmp</code> instead of <code>en</code> for debugging purposes.</li>
|
||
</ul>
|
||
</li>
|
||
<li><a href=/docs/assets/files/i18n-upload-6d83a823535487ee2d3034a686d86062.mjs target=_blank><code>i18n-update-push</code> / <code>i18n-upload.mjs</code></a>: <strong>(Only used in CI and should not be run locally)</strong> Pushes the source texts in <code>.lokasile.json</code> files to Lokalise.<!-- -->
|
||
<ul>
|
||
<li>The <code><branch></code> argument is required to specify the branch to push to.</li>
|
||
<li>The <code>--force</code> flag can be used to skip the check for environment variable <code>CI</code> and allow running the script locally.</li>
|
||
</ul>
|
||
</li>
|
||
<li><a href=/docs/assets/files/i18n-import-28359d2898f8ffcd1048c32f5d6a3401.mjs target=_blank><code>i18n-update-pull</code> / <code>i18n-import.mjs</code></a>: <strong>(Only used in CI and should not be run locally)</strong> Pulls the translated texts from Lokalise and updates the <code>.ts</code> files under <code>src/livecodes/i18n/locales</code>. <em>Outdated translation will be deprecated during import.</em>
|
||
<ul>
|
||
<li>The <code><branch></code> argument is required to specify the branch to pull from.</li>
|
||
<li>The <code>--force</code> flag can be used to skip the check for environment variable <code>CI</code> and allow running the script locally.</li>
|
||
<li>The <code>--local</code> flag can be used to let the script use local resources in the directory defined by the <code>LOKALISE_TEMP</code> environment variable instead of fetching from Lokalise.</li>
|
||
</ul>
|
||
</li>
|
||
<li><a href=/docs/assets/files/i18n-exclude-e5d517ec6b92591b6019979892ee8d93.js target=_blank><code>i18n-exclude</code></a>: <strong>(Only used in other scripts and should not be run locally)</strong> Excludes all other i18n locales except for English from type checking, as they might stay outdated and cause errors.<!-- -->
|
||
<ul>
|
||
<li>The <code><phase></code> argument is required to specify whether the script is to exclude files or revert the exclusion. Valid values are <code>pre</code> and <code>post</code>.</li>
|
||
<li>This script only works when environment variable <code>BUILD_INCLUDE_LOCALES</code> is <strong>NOT</strong> set to <code>true</code>.</li>
|
||
</ul>
|
||
</li>
|
||
<li><a href=/docs/assets/files/i18n-lokalise-json-331642ee9c64647d271c8928cd4d8880.mjs target=_blank><code>i18n-lokalise-json</code></a>: Generates <code>.lokalise.json</code> files from codebase for manually authoring translations on Lokalise.<!-- -->
|
||
<ul>
|
||
<li>Arguments to the script are required to specify the languages that the <code>.lokalise.json</code> files are generated from.</li>
|
||
<li>You can also use <code>all</code> to generate <code>.lokalise.json</code> files for all languages.</li>
|
||
<li>This script <strong>should not</strong> be used for the source language English. Use <code>i18n-export</code> instead.</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>Please run <code>i18n-export</code> before pushing changes to the codebase to ensure that the source texts are up-to-date.</p>
|
||
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=for-maintainers>For Maintainers<a href=#for-maintainers class=hash-link aria-label="Direct link to For Maintainers" title="Direct link to For Maintainers"></a></h2>
|
||
<p>Maintainers are responsible for managing the i18n workflow and ensuring the quality of translations.</p>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=workflow>Workflow<a href=#workflow class=hash-link aria-label="Direct link to Workflow" title="Direct link to Workflow"></a></h3>
|
||
<p>We consider the i18n process to consist of two parts:</p>
|
||
<h4 class="anchor anchorWithStickyNavbar_LWe7" id=no-source-update>No-Source Update<a href=#no-source-update class=hash-link aria-label="Direct link to No-Source Update" title="Direct link to No-Source Update"></a></h4>
|
||
<p>This means there are no changes to the source code/texts, only translations are updated. Adding new languages or updating existing translations are examples of this part.</p>
|
||
<p>In such cases, there is a scheduled workflow <a href=/docs/assets/files/i18n-update-scheduled-fc9b46482ca124aee2d68a17c3a1b69f.yml target=_blank><code>i18n-update-scheduled</code></a> to handle this. The workflow will sync from the <code>master</code> branch on Lokalise to the <code>i18n/develop</code> branch on the codebase, then automatically create a PR if there are any changes.</p>
|
||
<p>Basically, maintainers only need to focus on the following for this part:</p>
|
||
<ul>
|
||
<li>Reviewing PRs created by the <code>i18n-update-scheduled</code> workflow</li>
|
||
<li>Do merging on Lokalise after they consider the translation for a specific feature is ready, before commenting <code>.i18n-update-pull</code> to trigger the <code>i18n-update-pull</code> workflow</li>
|
||
</ul>
|
||
<h4 class="anchor anchorWithStickyNavbar_LWe7" id=source-update>Source Update<a href=#source-update class=hash-link aria-label="Direct link to Source Update" title="Direct link to Source Update"></a></h4>
|
||
<p>This means new changes are made to the source code/texts. In this case, maintainers should follow the steps below:</p>
|
||
<ol>
|
||
<li>Developers work on the source code, developing new features or modifying existing ones. They use <code>data-i18n</code> and <code>window.deps.translateString</code> to mark the strings that need to be translated.</li>
|
||
<li>Once a new feature or version is ready, developers run <code>npm run i18n-export</code> to extract and update all marked strings, then export them to <code>.lokalise.json</code> and <code>.ts</code> files.</li>
|
||
<li>Developers commit and push the changes to the repository. A feature PR is created and reviewed, and related tests, checks, and manual review could be carried out.</li>
|
||
<li>After the PR is merged, an auto-generated comment will notify maintainers to comment <code>.i18n-update-push</code> to trigger the <code>i18n-update-push</code> workflow when they think it is ready.</li>
|
||
<li>Maintainers comment <code>.i18n-update-push</code> to trigger the <code>i18n-update-push</code> workflow. The workflow will create a new branch named <code>i18n/<owner>/<head-branch></code>, run <code>npm run i18n-export</code> again to ensure the source texts are up-to-date, and push the changes. Then, it will push the changes to the <code>i18n/<owner>/<head-branch></code> branch on Lokalise.</li>
|
||
<li>Translators can start translating the texts on Lokalise.</li>
|
||
<li>Once the translation is complete, maintainers can comment <code>.i18n-update-pull</code> to trigger the <code>i18n-update-pull</code> workflow. The workflow will pull the translated texts from Lokalise, update the <code>.ts</code> files under <code>src/livecodes/i18n/locales</code>, and commit the changes to the <code>i18n/<owner>/<head-branch></code> branch. Then, it will create a PR to merge the changes back to the default branch <code>develop</code>.</li>
|
||
<li>Maintainers should perform a final review on the i18n PR and merge it if everything is fine. Meanwhile, a merging from the <code>i18n/<owner>/<head-branch></code> to <code>master</code> should also be done to keep the <code>master</code> branch on Lokalise up-to-date.</li>
|
||
</ol>
|
||
<h4 class="anchor anchorWithStickyNavbar_LWe7" id=minor-fixes--updates>Minor Fixes / Updates<a href=#minor-fixes--updates class=hash-link aria-label="Direct link to Minor Fixes / Updates" title="Direct link to Minor Fixes / Updates"></a></h4>
|
||
<p>Sometimes there is already an ongoing main prerelease branch with many features being developed and translated on Lokalise, and a minor fix or update to the prerelease branch is needed. In this case, maintainers should follow the steps below:</p>
|
||
<ol>
|
||
<li>Switch to <code>develop</code> branch.</li>
|
||
<li>Do <code>i18n-export</code> and upload corresponding <code>.lokalise.json</code> to the prerelease branch of the Lokalise project through web UI.</li>
|
||
<li>Affected entries will be updated and <a href=https://docs.lokalise.com/en/articles/3684557-translation-statuses-translated-verified-reviewed-and-completed#verified-and-unverified target=_blank rel="noopener noreferrer">marked as <code>unverified</code></a>.</li>
|
||
<li>Provide correct translations in other languages on Lokalise.</li>
|
||
</ol>
|
||
<p>Here we do not want an extra i18n branch for simplicity, nor need to pull from Lokalise as we always consider English source strings from codebase as the latest version and do not recommend modifying them on Lokalise directly.</p>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=github-actions-ci>Github Actions (CI)<a href=#github-actions-ci class=hash-link aria-label="Direct link to Github Actions (CI)" title="Direct link to Github Actions (CI)"></a></h3>
|
||
<p>Four i18n-related workflows are set up in the repository:</p>
|
||
<ul>
|
||
<li><a href=/docs/assets/files/i18n-update-notify-a3777449a43532d607ba41185a4d7760.yml target=_blank><code>i18n-update-notify</code></a>: Creates a comment on merged PRs to notify maintainers to trigger the <code>i18n-update-push</code> workflow.</li>
|
||
<li><a href=/docs/assets/files/i18n-update-push-0ccaf936f8b7727f7a8e5c8a4a944707.yml target=_blank><code>i18n-update-push</code></a>: Creates a new branch named <code>i18n/<owner>/<head-branch></code>, runs <code>npm run i18n-export</code> again to ensure the source texts are up-to-date, pushes the changes on git, then pushes the changes to the <code>i18n/<owner>/<head-branch></code> branch on Lokalise.</li>
|
||
<li><a href=/docs/assets/files/i18n-update-pull-d89075850c83dbf05af9b0d6b67c1d1e.yml target=_blank><code>i18n-update-pull</code></a>: Pulls the translated texts from Lokalise, updates the <code>.ts</code> files under <code>src/livecodes/i18n/locales</code>, commits the changes to the <code>i18n/<owner>/<head-branch></code> branch, then creates a PR to merge the changes back to the default branch <code>develop</code>.</li>
|
||
<li><a href=/docs/assets/files/i18n-update-scheduled-fc9b46482ca124aee2d68a17c3a1b69f.yml target=_blank><code>i18n-update-scheduled</code></a>: Syncs between the <code>master</code> branch on Lokalise and the <code>i18n/develop</code> branch on the codebase, then automatically creates a PR if there are any changes.</li>
|
||
</ul>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=hashing-and-cache>Hashing and Cache<a href=#hashing-and-cache class=hash-link aria-label="Direct link to Hashing and Cache" title="Direct link to Hashing and Cache"></a></h3>
|
||
<p>After production build, <a href=/docs/assets/files/hash-9dd0148ddb2f39842f39f389ec4236cc.js target=_blank>file hashes are added</a> to all files in <code>build/livecodes/</code> directory. The hash is the checksum of the file content. So if the file content does not change, it will get the same hash across builds.</p>
|
||
<p>This assumes that all files to be hashed are in that directory (without nesting), and that they are referenced in code using placeholders like <code>{{hash:file-name.js}}</code>:</p>
|
||
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style=--prism-color:#393A34;--prism-background-color:#f6f8fa><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style=color:#393A34;background-color:#f6f8fa><code class=codeBlockLines_e6Vv><span class=token-line style=color:#393A34><span class="token keyword" style=color:#00009f>const</span><span class="token plain"> mod </span><span class="token operator" style=color:#393A34>=</span><span class="token plain"> </span><span class="token keyword" style=color:#00009f>await</span><span class="token plain"> </span><span class="token keyword" style=color:#00009f>import</span><span class="token punctuation" style=color:#393A34>(</span><span class="token plain">baseUrl </span><span class="token operator" style=color:#393A34>+</span><span class="token plain"> </span><span class="token string" style=color:#e3116c>'{{hash:file-name.js}}'</span><span class="token punctuation" style=color:#393A34>)</span><span class="token punctuation" style=color:#393A34>;</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<p>Then, these files are <a href=https://github.com/live-codes/livecodes/tree/develop/src/_headers target=_blank rel="noopener noreferrer">aggressively cached</a> (for 1 year).</p>
|
||
<p>So, any file that has not changed will continue to be served from cache even for later releases.</p>
|
||
<p>File hashing is also applied to translation files during build, by auto-generating a path loader file that contains hard-coded hash placeholders for each translation file.</p>
|
||
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=for-those-who-forked-the-repo>For Those Who Forked the Repo<a href=#for-those-who-forked-the-repo class=hash-link aria-label="Direct link to For Those Who Forked the Repo" title="Direct link to For Those Who Forked the Repo"></a></h2>
|
||
<p>This repository is utilizing <a href=https://github.com/apps/livecodes-ci target=_blank rel="noopener noreferrer">LiveCodes CI</a> Github App to ensure the i18n workflow functions properly.</p>
|
||
<p>For forked repositories, maintainers should set up their own Lokalise project and Github App (see <a href=https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.mdx#authenticating-with-github-app-generated-tokens target=_blank rel="noopener noreferrer">here</a>) to handle the i18n workflow. Changes to related workflow files are necessary.</p>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=secrets-and-variables-checklist>Secrets and Variables Checklist<a href=#secrets-and-variables-checklist class=hash-link aria-label="Direct link to Secrets and Variables Checklist" title="Direct link to Secrets and Variables Checklist"></a></h3>
|
||
<h4 class="anchor anchorWithStickyNavbar_LWe7" id=repository-secrets>Repository Secrets<a href=#repository-secrets class=hash-link aria-label="Direct link to Repository Secrets" title="Direct link to Repository Secrets"></a></h4>
|
||
<ul>
|
||
<li><code>LOKALISE_API_TOKEN</code></li>
|
||
<li><code>CI_APP_ID</code></li>
|
||
<li><code>CI_APP_PRIVATE_KEY</code></li>
|
||
</ul>
|
||
<h4 class="anchor anchorWithStickyNavbar_LWe7" id=repository-variables>Repository Variables<a href=#repository-variables class=hash-link aria-label="Direct link to Repository Variables" title="Direct link to Repository Variables"></a></h4>
|
||
<ul>
|
||
<li><code>LOKALISE_PROJECT_ID</code></li>
|
||
</ul></div><footer class="theme-doc-footer docusaurus-mt-lg"><div class="row margin-top--sm theme-doc-footer-edit-meta-row"><div class=col><a href=https://github.com/live-codes/livecodes/tree/develop/docs/docs/contribution/i18n.mdx target=_blank rel="noopener noreferrer" class=theme-edit-this-page><svg fill=currentColor height=20 width=20 viewBox="0 0 40 40" class=iconEdit_Z9Sw aria-hidden=true><g><path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"/></g></svg>Edit this page</a></div><div class="col lastUpdated_JAkA"></div></div></footer></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages"></nav></div></div><div class="col col--3"><div class="tableOfContents_bqdL thin-scrollbar theme-doc-toc-desktop"><ul class="table-of-contents table-of-contents__left-border"><li><a href=#for-translators class="table-of-contents__link toc-highlight">For Translators</a><ul><li><a href=#contribute-to-translation class="table-of-contents__link toc-highlight">Contribute to Translation</a><li><a href=#add-a-new-language class="table-of-contents__link toc-highlight">Add a New Language</a></ul><li><a href=#technical-overview class="table-of-contents__link toc-highlight">Technical Overview</a><li><a href=#for-developers class="table-of-contents__link toc-highlight">For Developers</a><ul><li><a href=#strings class="table-of-contents__link toc-highlight">Strings</a><li><a href=#scripts class="table-of-contents__link toc-highlight">Scripts</a></ul><li><a href=#for-maintainers class="table-of-contents__link toc-highlight">For Maintainers</a><ul><li><a href=#workflow class="table-of-contents__link toc-highlight">Workflow</a><li><a href=#github-actions-ci class="table-of-contents__link toc-highlight">Github Actions (CI)</a><li><a href=#hashing-and-cache class="table-of-contents__link toc-highlight">Hashing and Cache</a></ul><li><a href=#for-those-who-forked-the-repo class="table-of-contents__link toc-highlight">For Those Who Forked the Repo</a><ul><li><a href=#secrets-and-variables-checklist class="table-of-contents__link toc-highlight">Secrets and Variables Checklist</a></ul></ul><div class="custom-content custom-content-toc content_kfNa">
|
||
<div class=sponsors>
|
||
<div class=sponsors-title>Sponsors</div>
|
||
<ul>
|
||
|
||
<li><a href=https://livecodes.io/docs/sponsor title="Become a sponsor">Your Logo</a></li>
|
||
</ul>
|
||
</div>
|
||
</div></div></div></div></div></main></div></div></div><footer class="footer footer--dark"><div class="container container-fluid"><div class="row footer__links"><div class="col footer__col"><div class=footer__title>Docs</div><ul class="footer__items clean-list"><li class=footer__item><a class=footer__link-item href=/docs/overview>Overview</a><li class=footer__item><a class=footer__link-item href=/docs/why>Why Another Playground?</a><li class=footer__item><a class=footer__link-item href=/docs/getting-started>Getting Started</a><li class=footer__item><a class=footer__link-item href=/docs/features>Features</a><li class=footer__item><a class=footer__link-item href=/docs/languages>Languages</a><li class=footer__item><a class=footer__link-item href=/docs/sdk>SDK</a></ul></div><div class="col footer__col"><div class=footer__title>LiveCodes</div><ul class="footer__items clean-list"><li class=footer__item><a href=/docs/../ target=_blank rel="noopener noreferrer" class=footer__link-item>App<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><li class=footer__item><a href=/docs/../?new target=_blank rel="noopener noreferrer" class=footer__link-item>Starter Templates<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><li class=footer__item><a href="/docs/../?screen=import" target=_blank rel="noopener noreferrer" class=footer__link-item>Import...<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><li class=footer__item><a class=footer__link-item href=/docs/features/ai>AI Code Assistant 🪄</a><li class=footer__item><a class=footer__link-item href=/docs/bookmarklet>Bookmarklet</a><li class=footer__item><span style="display: flex; gap: 0.5em; align-items: baseline;"><a href=/docs/llms.txt target=_blank class=footer__link-item>llms.txt</a>-<a href=/docs/llms-full.txt target=_blank class=footer__link-item>llms-full.txt</a></span></ul></div><div class="col footer__col"><div class=footer__title>Info</div><ul class="footer__items clean-list"><li class=footer__item><a class=footer__link-item href=/docs/credits>Credits</a><li class=footer__item><a class=footer__link-item href=/docs/license>License</a><li class=footer__item><a class=footer__link-item href=/docs/sponsor>Sponsor 💚</a><li class=footer__item><a class=footer__link-item href=/docs/contact>Contact</a><li class=footer__item><a class=footer__link-item href=/docs/about>About us</a></ul></div><div class="col footer__col"><div class=footer__title>More</div><ul class="footer__items clean-list"><li class=footer__item><a href=https://blog.livecodes.io target=_self rel="noopener noreferrer" class=footer__link-item>Blog<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><li class=footer__item><a href=https://github.com/live-codes/livecodes target=_blank rel="noopener noreferrer" class=footer__link-item>GitHub<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><li class=footer__item><a href=https://twitter.com/livecodes_io target=_blank rel="noopener noreferrer" class=footer__link-item>𝕏 / Twitter<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><li class=footer__item><a href=https://dev.to/livecodes_io target=_blank rel="noopener noreferrer" class=footer__link-item>Dev<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><li class=footer__item><a href=https://www.npmjs.com/package/livecodes target=_blank rel="noopener noreferrer" class=footer__link-item>npm<svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24" class=iconExternalLink_nPIU><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a><li class=footer__item><a href=https://status.livecodes.io target=_blank rel="noopener noreferrer" class="footer__link-item status-link"><span>Status</span><svg width=13.5 height=13.5 aria-hidden=true viewBox="0 0 24 24"><path fill=currentColor d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></svg></a></ul></div></div><div class="footer__bottom text--center"><div class=footer__copyright><br> Released under the MIT License <br>
|
||
Copyright © 2025
|
||
<a href=https://github.com/hatemhosny target=_blank rel="noopener noreferrer">Hatem Hosny</a></div></div></div></footer></div> |