[{"data":1,"prerenderedAt":699},["ShallowReactive",2],{"navigation":3,"/posts/2025-02-24-release-notes-v0.20.0":166,"/posts/2025-02-24-release-notes-v0.20.0-surround":696},[4],{"title":5,"path":6,"stem":7,"children":8,"page":165},"Posts","/posts","posts",[9,13,17,21,25,29,33,37,41,45,49,53,57,61,65,69,73,77,81,85,89,93,97,101,105,109,113,117,121,125,129,133,137,141,145,149,153,157,161],{"title":10,"path":11,"stem":12},"New API client library: Perl","/posts/2012-01-03-perl-api-library","posts/2012-01-03-perl-api-library",{"title":14,"path":15,"stem":16},"'Good' vs 'Strong' passwords","/posts/2012-01-25-good-passwords","posts/2012-01-25-good-passwords",{"title":18,"path":19,"stem":20},"Protecting your credentials from criminals","/posts/2012-01-30-protecting-credentials","posts/2012-01-30-protecting-credentials",{"title":22,"path":23,"stem":24},"Onetime Secret is Now Opensource","/posts/2013-01-30-onetimesecret-is-now-opensource","posts/2013-01-30-onetimesecret-is-now-opensource",{"title":26,"path":27,"stem":28},"Major UI update (with mobile support)","/posts/2013-02-04-ui-update-mobile","posts/2013-02-04-ui-update-mobile",{"title":30,"path":31,"stem":32},"Copying server data from the command-line, safely","/posts/2013-02-12-copy-server-data-command-line","posts/2013-02-12-copy-server-data-command-line",{"title":34,"path":35,"stem":36},"Privacy Policy Update (June 2024)","/posts/2024-06-15-privacy-policy-update","posts/2024-06-15-Privacy Policy-Update",{"title":38,"path":39,"stem":40},"Onetime Secret v0.16.0 Release - Modern UI and Enhanced Development","/posts/2024-07-29-release-notes-v0.16.0","posts/2024-07-29-release-notes-v0.16.0",{"title":42,"path":43,"stem":44},"Open-Source First Development Model","/posts/2024-08-14-opensource-first-model","posts/2024-08-14-opensource-first-model",{"title":46,"path":47,"stem":48},"Onetime Secret v0.17.0: Foundation for the Future","/posts/2024-08-31-release-notes-v0.17.0","posts/2024-08-31-release-notes-v0.17.0",{"title":50,"path":51,"stem":52},"So the custom font was a whoopsies","/posts/2024-09-03-custom-font-a-whoopsies","posts/2024-09-03-custom-font-a-whoopsies",{"title":54,"path":55,"stem":56},"UI/UX Updates - September '24","/posts/2024-09-04-ux-ui-updates","posts/2024-09-04-ux-ui-updates",{"title":58,"path":59,"stem":60},"Data Privacy Regulations: A New Framework for UI Design","/posts/2024-09-05-constraints","posts/2024-09-05-constraints",{"title":62,"path":63,"stem":64},"A verifiable error in our signup flow","/posts/2024-09-06-mistake-were-made-signups","posts/2024-09-06-mistake-were-made-signups",{"title":66,"path":67,"stem":68},"Support for ASCII QR Codes","/posts/2024-09-08-qr-code","posts/2024-09-08-qr-code",{"title":70,"path":71,"stem":72},"Denial of Service (DoS) Attack: A brief summary","/posts/2024-09-09-denial-of-service","posts/2024-09-09-denial-of-service",{"title":74,"path":75,"stem":76},"Denial of Service (DoS) Attack: Continued Adventure","/posts/2024-09-12-ddos-day-4","posts/2024-09-12-ddos-day-4",{"title":78,"path":79,"stem":80},"Denial of Service (DoS) Attack: Day 5","/posts/2024-09-13-ddos-day-5","posts/2024-09-13-ddos-day-5",{"title":82,"path":83,"stem":84},"Onetime Secret v0.18.0: Enjoying the Vue","/posts/2024-10-03-release-notes-v0.18.0","posts/2024-10-03-release-notes-v0.18.0",{"title":86,"path":87,"stem":88},"Installing Onetime Secret: The Complete Guide (Part 1 of 5)","/posts/2024-10-08-installation-series-1of5-standalone","posts/2024-10-08-installation-series-1of5-standalone",{"title":90,"path":91,"stem":92},"Ugly By Nature: Why Our Blog Graphics Look Terrible (On Purpose)","/posts/2024-10-15-ai-generated-graphics-transparency","posts/2024-10-15-ai-generated-graphics-transparency",{"title":94,"path":95,"stem":96},"Expanding Horizons: Introducing US Data Locality for Onetime Secret","/posts/2024-10-23-us-data-center","posts/2024-10-23-us-data-center",{"title":98,"path":99,"stem":100},"Industry Practices We Deliberately Avoid","/posts/2024-11-01-industry-practices-we-deliberately-avoid","posts/2024-11-01-industry-practices-we-deliberately-avoid",{"title":102,"path":103,"stem":104},"Why we don't have a cookie notice","/posts/2024-11-05-privacy-by-design-no-cookie-notice","posts/2024-11-05-privacy-by-design-no-cookie-notice",{"title":106,"path":107,"stem":108},"Onetime Secret v0.19.0: Custom Branding & Type Safety","/posts/2025-01-24-release-notes-v0.19.0","posts/2025-01-24-release-notes-v0.19.0",{"title":110,"path":111,"stem":112},"Onetime Secret v0.20.0: Internationalization","/posts/2025-02-24-release-notes-v0.20.0","posts/2025-02-24-release-notes-v0.20.0",{"title":114,"path":115,"stem":116},"Canada Region Now Available","/posts/2025-03-03-canada-region-now-available","posts/2025-03-03-canada-region-now-available",{"title":118,"path":119,"stem":120},"Aotearoa New Zealand Region Now Available","/posts/2025-04-03-aotearoa-new-zealand-region-available","posts/2025-04-03-aotearoa-new-zealand-region-available",{"title":122,"path":123,"stem":124},"Homepage Update: We're Going Regional","/posts/2025-05-05-homepage-update-going-regional","posts/2025-05-05-homepage-update-going-regional",{"title":126,"path":127,"stem":128},"Domain Strategy Reengineered: Building Verification into Our Architecture","/posts/2025-05-14-homepage-post-as-in-after","posts/2025-05-14-homepage-post-as-in-after",{"title":130,"path":131,"stem":132},"Verifying Authentic Onetime Secret Services","/posts/2025-05-15-distrust-and-double-verify","posts/2025-05-15-distrust-and-double-verify",{"title":134,"path":135,"stem":136},"Claude Roasts our tests","/posts/2025-05-21-claude-roasts-our-tests","posts/2025-05-21-claude-roasts-our-tests",{"title":138,"path":139,"stem":140},"JSON Trouble on the Default-lines","/posts/2025-06-07-json-trouble-on-the-default-lines","posts/2025-06-07-json-trouble-on-the-default-lines",{"title":142,"path":143,"stem":144},"OneTimeSecret v0.22.4: Password Settings & Disabling Homepage","/posts/2025-09-16-release-notes-v0.22.4","posts/2025-09-16-release-notes-v0.22.4",{"title":146,"path":147,"stem":148},"The Ramshackler's Guide to i18n","/posts/2025-12-12-how-we-handle-translations","posts/2025-12-12-how-we-handle-translations",{"title":150,"path":151,"stem":152},"Onetime Secret v0.23: Config Migration and API Improvements","/posts/2026-01-18-release-notes-v0.23","posts/2026-01-18-release-notes-v0.23",{"title":154,"path":155,"stem":156},"UK Region Now Available","/posts/2026-02-06-uk-region-now-available","posts/2026-02-06-uk-region-now-available",{"title":158,"path":159,"stem":160},"Onetime Secret v0.24.0: A New Foundation","/posts/2026-03-04-release-notes-v0.24.0","posts/2026-03-04-release-notes-v0.24.0",{"title":162,"path":163,"stem":164},"Mea gulpa: a big bowl of humble broth (or, API-carumba: an upgrade story)","/posts/2026-03-27-mea-gulpa-api-carumba","posts/2026-03-27-mea-gulpa-api-carumba",false,{"id":167,"title":110,"authors":168,"badge":174,"body":176,"date":687,"description":182,"extension":688,"headline":689,"icon":689,"image":690,"layout":691,"meta":692,"navigation":693,"path":111,"readingTime":311,"seo":694,"stem":112,"__hash__":695},"posts/posts/2025-02-24-release-notes-v0.20.0.md",[169],{"name":170,"to":171,"avatar":172},"Delano","https://blog.onetimesecret.com/about",{"src":173},"/img/portrait-profile-pic-delano-2025-m.png",{"label":175},"Release",{"type":177,"value":178,"toc":679},"minimark",[179,183,192,197,202,211,214,217,233,241,244,249,252,272,277,286,298,507,513,522,555,567,571,575,578,589,593,609,613,616,640,644,667,670,675],[180,181,182],"p",{},"Today we're announcing Onetime Secret v0.20.0, a release focused on renewed language support and enhanced type safety and error handling.",[180,184,185],{},[186,187,191],"a",{"href":188,"rel":189},"https://github.com/onetimesecret/onetimesecret/releases/tag/v0.20.0",[190],"nofollow","Full v0.20.0 release notes",[193,194,196],"h2",{"id":195},"key-improvements","Key Improvements",[198,199,201],"h3",{"id":200},"internationalization-support","Internationalization Support",[180,203,204,205,210],{},"We're pleased to report that Onetime Secret now supports multiple languages... again for the first time. We've had translations since 2017 ",[186,206,209],{"href":207,"rel":208},"https://onetimesecret.com/translations",[190],"thanks to our community contributors"," but a lot has changed since then, including replacing the entire frontend with a new Vue 3 implementation. That plus the addition of new features and content meant we needed to circle back and update our translations.",[180,212,213],{},"This work was originally planned as a stretch goal for 2025, but we decided to tackle it sooner to make our service more accessible globally. This internationalization effort is a key priority for us, ensuring Onetime Secret is available to users worldwide and removing language barriers that might otherwise prevent people from securing their sensitive information.",[180,215,216],{},"For v0.20.0, we've enabled support for:",[218,219,220,224,227,230],"ul",{},[221,222,223],"li",{},"English (en)",[221,225,226],{},"French (France) (fr_FR)",[221,228,229],{},"French (Canada) (fr_CA)",[221,231,232],{},"German (Austria) (de_AT)",[180,234,235,236,240],{},"Other languages are temporarily disabled as we work to complete their translations. We'll be re-enabling them in future updates as they become more complete. If you'd like to check on the status of a specific language or contribute to this effort, please visit our ",[186,237,239],{"href":207,"rel":238},[190],"translation page",".",[180,242,243],{},"In our earlier versions, we supported a wide range of languages including Arabic, Bulgarian, Catalan, Czech, Danish, German, Greek, Spanish, Hebrew, Hungarian, Italian, Japanese, Dutch, Polish, Portuguese (Brazil and Portugal), Russian, Slovenian, Swedish, Turkish, Ukrainian, Vietnamese, and Chinese. We're working to update these translations and will be re-enabling them in upcoming releases.",[245,246,248],"h4",{"id":247},"translation-process","Translation Process",[180,250,251],{},"Our translation workflow combined several approaches:",[218,253,254,263,266,269],{},[221,255,256,257,262],{},"Machine translation services (",[186,258,261],{"href":259,"rel":260},"https://www.deepl.com/en/translator",[190],"DeepL",")",[221,264,265],{},"AI assistance (Claude 3.5 Sonnet and 3.7)",[221,267,268],{},"Manual review and corrections",[221,270,271],{},"Community contributions",[273,274,276],"h5",{"id":275},"tools","Tools",[180,278,279,280,285],{},"The ",[186,281,284],{"href":282,"rel":283},"https://github.com/lokalise/i18n-ally",[190],"i18n Ally extension"," from Lokalise has been very useful for two stages of the work: reviewing translation files locally and extracting hardcoded strings into the translation files.",[180,287,288,289,297],{},"We leaned heavily on the power of ",[186,290,293],{"href":291,"rel":292},"https://github.com/jqlang/jq",[190],[294,295,296],"code",{},"jq",", the command-line JSON processor. Who knew it could run powerful scripts like this:",[299,300,305],"pre",{"className":301,"code":302,"language":303,"meta":304,"style":304},"language-javascript shiki shiki-themes material-theme-lighter houston one-dark-pro","// Sample from our harmonization script\njq -r '\n  # Start with the main locale file (en.json)\n  . as $main_locale\n  # Create a new object\n  | {}\n  # Add all keys from main locale\n  | . * ($main_locale | map_values(null))\n  # Override with any existing values from target locale\n  | . * ($current_locale | map_values(.))\n' \"$REFERENCE_FILE\" > temp_file.json\n","javascript","",[294,306,307,316,333,344,357,377,386,409,443,471,495],{"__ignoreMap":304},[308,309,312],"span",{"class":310,"line":311},"line",1,[308,313,315],{"class":314},"s9XRh","// Sample from our harmonization script\n",[308,317,319,322,326,329],{"class":310,"line":318},2,[308,320,296],{"class":321},"sFOsI",[308,323,325],{"class":324},"svn82"," -",[308,327,328],{"class":321},"r",[308,330,332],{"class":331},"szjKe"," '\n",[308,334,336,340],{"class":310,"line":335},3,[308,337,339],{"class":338},"sC_Oy","  # Start with the main locale file (en.json",[308,341,343],{"class":342},"sHfUk",")\n",[308,345,347,351,354],{"class":310,"line":346},4,[308,348,350],{"class":349},"simXI","  .",[308,352,353],{"class":321}," as",[308,355,356],{"class":321}," $main_locale\n",[308,358,360,364,367,370,374],{"class":310,"line":359},5,[308,361,363],{"class":362},"sCVsf","  # ",[308,365,366],{"class":321},"Create",[308,368,369],{"class":321}," a",[308,371,373],{"class":372},"sH1nl"," new",[308,375,376],{"class":321}," object\n",[308,378,380,383],{"class":310,"line":379},6,[308,381,382],{"class":324},"  |",[308,384,385],{"class":349}," {}\n",[308,387,389,391,394,397,400,403,406],{"class":310,"line":388},7,[308,390,363],{"class":362},[308,392,393],{"class":321},"Add",[308,395,396],{"class":321}," all",[308,398,399],{"class":321}," keys",[308,401,402],{"class":321}," from",[308,404,405],{"class":321}," main",[308,407,408],{"class":321}," locale\n",[308,410,412,414,417,420,423,426,429,433,436,440],{"class":310,"line":411},8,[308,413,382],{"class":324},[308,415,416],{"class":349}," .",[308,418,419],{"class":324}," *",[308,421,422],{"class":362}," (",[308,424,425],{"class":321},"$main_locale",[308,427,428],{"class":324}," |",[308,430,432],{"class":431},"sbomi"," map_values",[308,434,435],{"class":362},"(",[308,437,439],{"class":438},"sQhQs","null",[308,441,442],{"class":362},"))\n",[308,444,446,448,451,455,458,461,464,466,469],{"class":310,"line":445},9,[308,447,363],{"class":362},[308,449,450],{"class":321},"Override",[308,452,454],{"class":453},"s6Icb"," with",[308,456,457],{"class":321}," any",[308,459,460],{"class":321}," existing",[308,462,463],{"class":321}," values",[308,465,402],{"class":321},[308,467,468],{"class":321}," target",[308,470,408],{"class":321},[308,472,474,476,478,480,482,485,487,489,491,493],{"class":310,"line":473},10,[308,475,382],{"class":324},[308,477,416],{"class":349},[308,479,419],{"class":324},[308,481,422],{"class":362},[308,483,484],{"class":321},"$current_locale",[308,486,428],{"class":324},[308,488,432],{"class":431},[308,490,435],{"class":362},[308,492,240],{"class":349},[308,494,442],{"class":362},[308,496,498,501,504],{"class":310,"line":497},11,[308,499,500],{"class":331},"'",[308,502,503],{"class":338}," \"$REFERENCE_FILE\" > temp_file.jso",[308,505,506],{"class":342},"n\n",[180,508,435,509,262],{},[510,511,512],"strong",{},"In the interest of transparency, while I understand this script when analyzing it line by line, crafting it from scratch would have been challenging within our timeline constraints. This efficient solution was developed with assistance from Claude 3.5 Sonnet.",[180,514,515,516,521],{},"We wrote a couple bash scripts around the jq commands to actually do the work of harmonizing the JSON locales files. ",[186,517,520],{"href":518,"rel":519},"https://github.com/onetimesecret/onetimesecret/blob/v0.20.0/src/locales/scripts/harmonize-locale-file",[190],"This was a huge time saver"," and allowed us to focus on the actual translations. This updates the Dutch translation file with any missing keys from the English file, including the order and hierarchical structure of the keys:",[299,523,527],{"className":524,"code":525,"language":526,"meta":304,"style":304},"language-bash shiki shiki-themes material-theme-lighter houston one-dark-pro","  $ ./src/locales/scripts/harmonize-locale-file -v -c src/locales/nl.json\n  Repaired src/locales/nl.json\n","bash",[294,528,529,548],{"__ignoreMap":304},[308,530,531,535,538,542,545],{"class":310,"line":311},[308,532,534],{"class":533},"sABnp","  $",[308,536,537],{"class":338}," ./src/locales/scripts/harmonize-locale-file",[308,539,541],{"class":540},"sd32E"," -v",[308,543,544],{"class":540}," -c",[308,546,547],{"class":338}," src/locales/nl.json\n",[308,549,550,553],{"class":310,"line":318},[308,551,552],{"class":533},"  Repaired",[308,554,547],{"class":338},[180,556,557,558,563,564,240],{},"If you have suggestions about our internationalization efforts or would like to contribute to improving translations, please visit our ",[186,559,562],{"href":560,"rel":561},"https://onetimesecret.com/feedback",[190],"feedback page"," or our ",[186,565,239],{"href":207,"rel":566},[190],[198,568,570],{"id":569},"also-in-this-release","Also in this release",[245,572,574],{"id":573},"enhanced-error-handling-type-safety","Enhanced Error Handling & Type Safety",[180,576,577],{},"We've significantly improved:",[218,579,580,583,586],{},[221,581,582],{},"Error handling in API requests",[221,584,585],{},"Type safety throughout the application",[221,587,588],{},"Configuration options for Sentry monitoring",[245,590,592],{"id":591},"configuration-changes","Configuration Changes",[180,594,595,596,599,600,603,604,608],{},"Note that we've renamed the ",[294,597,598],{},"services"," configuration key to ",[294,601,602],{},"diagnostics"," in etc/config.yaml, and we've introduced several new environment variables to support internationalization and enhanced monitoring. See the ",[186,605,607],{"href":188,"rel":606},[190],"full v0.20.0 release notes"," for details.",[193,610,612],{"id":611},"new-to-onetime-secret","New to Onetime Secret?",[180,614,615],{},"Onetime Secret lets you share sensitive information that's automatically destroyed after it's viewed.",[218,617,618,626],{},[221,619,620,621],{},"Try our hosted service at ",[186,622,625],{"href":623,"rel":624},"https://onetimesecret.com",[190],"onetimesecret.com",[221,627,628,629,634,635,240],{},"Self-host your own instance by forking our ",[186,630,633],{"href":631,"rel":632},"https://github.com/onetimesecret/onetimesecret",[190],"GitHub repository"," or official ",[186,636,639],{"href":637,"rel":638},"https://hub.docker.com/r/onetimesecret/onetimesecret",[190],"Docker image",[193,641,643],{"id":642},"additional-resources","Additional Resources",[218,645,646,653,660],{},[221,647,648],{},[186,649,652],{"href":650,"rel":651},"https://docs.onetimesecret.com/docs/",[190],"Documentation",[221,654,655],{},[186,656,659],{"href":657,"rel":658},"https://status.onetimesecret.com/",[190],"Status",[221,661,662],{},[186,663,666],{"href":664,"rel":665},"https://github.com/orgs/onetimesecret/projects/1",[190],"Roadmap",[180,668,669],{},"Thank you to everyone who contributed to this release. We appreciate all your support and feedback.",[671,672],"image-modal",{"alt":304,"src":673,"width":674},"/img/blog/2025/release-0.20.svg",600,[676,677,678],"style",{},"html pre.shiki code .s9XRh, html code.shiki .s9XRh{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEF0F98F;--shiki-default-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic}html pre.shiki code .sFOsI, html code.shiki .sFOsI{--shiki-light:#90A4AE;--shiki-default:#4BF3C8;--shiki-dark:#E06C75}html pre.shiki code .svn82, html code.shiki .svn82{--shiki-light:#39ADB5;--shiki-default:#EEF0F9;--shiki-dark:#56B6C2}html pre.shiki code .szjKe, html code.shiki .szjKe{--shiki-light:#39ADB5;--shiki-default:#FFD493;--shiki-dark:#98C379}html pre.shiki code .sC_Oy, html code.shiki .sC_Oy{--shiki-light:#91B859;--shiki-default:#FFD493;--shiki-dark:#98C379}html pre.shiki code .sHfUk, html code.shiki .sHfUk{--shiki-light:#91B859;--shiki-default:#FFFFFF;--shiki-dark:#FFFFFF}html pre.shiki code .simXI, html code.shiki .simXI{--shiki-light:#39ADB5;--shiki-default:#EEF0F9;--shiki-dark:#ABB2BF}html pre.shiki code .sCVsf, html code.shiki .sCVsf{--shiki-light:#90A4AE;--shiki-default:#EEF0F9;--shiki-dark:#ABB2BF}html pre.shiki code .sH1nl, html code.shiki .sH1nl{--shiki-light:#39ADB5;--shiki-default:#54B9FF;--shiki-dark:#C678DD}html pre.shiki code .sbomi, html code.shiki .sbomi{--shiki-light:#6182B8;--shiki-default:#00DAEF;--shiki-dark:#61AFEF}html pre.shiki code .sQhQs, html code.shiki .sQhQs{--shiki-light:#39ADB5;--shiki-default:#FFD493;--shiki-dark:#D19A66}html pre.shiki code .s6Icb, html code.shiki .s6Icb{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#54B9FF;--shiki-default-font-style:inherit;--shiki-dark:#C678DD;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sABnp, html code.shiki .sABnp{--shiki-light:#E2931D;--shiki-default:#00DAEF;--shiki-dark:#61AFEF}html pre.shiki code .sd32E, html code.shiki .sd32E{--shiki-light:#91B859;--shiki-default:#FFD493;--shiki-dark:#D19A66}",{"title":304,"searchDepth":318,"depth":318,"links":680},[681,685,686],{"id":195,"depth":318,"text":196,"children":682},[683,684],{"id":200,"depth":335,"text":201},{"id":569,"depth":335,"text":570},{"id":611,"depth":318,"text":612},{"id":642,"depth":318,"text":643},"2025-02-24","md",null,{"src":673},"post",{},true,{"title":110,"description":182},"L32LTLliOxgb9ue6iuU6ENx2xIbbzCd7rfOA233FgP4",[697,698],{"title":106,"path":107,"stem":108,"children":-1},{"title":114,"path":115,"stem":116,"children":-1},1781425298572]