Skip to content

Commit

Permalink
arg escaping (bugfix when an arg is an url with ':')
Browse files Browse the repository at this point in the history
  • Loading branch information
eddow committed Jun 5, 2024
1 parent 7f93bd0 commit a4ad06a
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 24 deletions.
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

Generic i18n library managing the fullstack interaction in a CI/CD pace. The dictionaries are stored in a DB edited by the translators through a(/the same) web application - managing translation errors, missing keys, ...

- 1.0.x - ~~alpha~~
- 1.1.x - [beta](https://www.youtube.com/watch?v=1gSZfX91zYk)
- 1.1.5 - The library finally has well-set entry points and export bundles
- 1.1.8 - UMD-able

[![view on npm](https://badgen.net/npm/v/omni18n)](https://www.npmjs.org/package/omni18n)
[![npm module downloads](https://badgen.net/npm/dt/omni18n)](https://www.npmjs.org/package/omni18n)
Expand Down
6 changes: 1 addition & 5 deletions docs/umd.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,7 @@ A script-less way to use the library is by providing the arguments (`locales`, `

We speak about the "blink" when the page is just loaded and still displayed in its native language for half a second before being translated in the target language.

[_For now_](#todo), the solution needs to specify manually all the locales who shouldn't blink.

```html
<script src="dictionary_hu.js"></script>
```
[_For now_](#todo), the best solution is to let the texts empty when an `i18n` attribute is specified

Also, as many mobile webapp tend to let the resource loading at the end of the page, hurrying the translation by inserting a `translatePage` between the page content and the late loads (audio/scripts/...) can show useful.

Expand Down
14 changes: 12 additions & 2 deletions src/client/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,17 @@ export function translate(context: TContext, args: any[]): string {

export function translator(context: TContext): Translator {
Object.freeze(context)
function escapeArgs(args: any[]) {
// TODO? replace more that \ and :
return args.map((a) => (typeof a === 'string' ? a.replace(/([\\:])/g, '\\$1') : a))
}
const translation = context.key
? function (...args: any[]): string {
return translate(context, args)
return translate(context, escapeArgs(args))
}
: function (key?: TextKey, ...args: any[]): string {
if (!key) throw new TranslationError('Root translator called without key')
if (typeof key === 'string') return translate({ ...context, key }, args)
if (typeof key === 'string') return translate({ ...context, key }, escapeArgs(args))
return translate({ ...context, key }, args)
}
const primitive = <Translator>new Proxy(translation, {
Expand Down Expand Up @@ -232,3 +236,9 @@ export function bulkDictionary<T extends Translatable = Translatable>(
}
return dictionaryToTranslation(current, key)
}

export function split2(s: string, sep: string) {
const ndx = s.indexOf(sep)
if (ndx === -1) return [s]
return [s.slice(0, ndx), s.slice(ndx + sep.length)]
}
24 changes: 14 additions & 10 deletions src/client/interpolation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { reportMissing, reportError, translate } from './helpers'
import { reportMissing, reportError, translate, split2 } from './helpers'
import { TContext, TranslationError } from './types'

export const formats: Record<'date' | 'number' | 'relative', Record<string, object>> = {
Expand Down Expand Up @@ -165,9 +165,9 @@ function objectArgument(
if (typeof arg === 'object') return arg
// Here we throw as it means the code gave a wrong argument
if (typeof arg !== 'string') throw new TranslationError(`Invalid argument type: ${typeof arg}`)
if (!/:/.test(arg)) return arg
if (!/:[^\/\\]/.test(arg)) return arg
return Object.fromEntries(
arg.split(',').map((part) => part.split(':', 2).map((part) => unescape(part.trim())))
arg.split(',').map((part) => split2(part, ':').map((part) => unescape(part.trim())))
)
}

Expand All @@ -189,13 +189,17 @@ export function interpolate(context: TContext, text: string, args: any[]): strin
const escapements: Record<string, string> = { '/': '/' },
unescapements: Record<number, string> = {}
let escapementCounter = 0
placeholder = placeholder.replace(/\\(.)/g, (_, c) => {
if (!escapements[c]) {
unescapements[escapementCounter] = c
escapements[c] = '\u0004' + escapementCounter++ + '\u0005'
}
return escapements[c]
})
function escaped(s: string) {
return s.replace(/\\(.)/g, (_, c) => {
if (!escapements[c]) {
unescapements[escapementCounter] = c
escapements[c] = '\u0004' + escapementCounter++ + '\u0005'
}
return escapements[c]
})
}
placeholder = escaped(placeholder)
args = args.map((arg) => (typeof arg === 'string' ? escaped(arg) : arg))
function unescape(s: string) {
return s
.replace(/\u0003/g, '\n')
Expand Down
9 changes: 5 additions & 4 deletions src/umd/client.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { split2 } from 'src/client/helpers'
import {
I18nClient,
CondensedDictionary,
Expand Down Expand Up @@ -47,7 +48,7 @@ export function translatePage() {
for (const element of document.querySelectorAll('[i18n]')) {
const parts = element.getAttribute('i18n')!.split(',')
for (const part of parts) {
const [attr, key] = part.split(':', 2).map((k) => k.trim())
const [attr, key] = split2(part, ':').map((k) => k.trim())
if (attr === 'html') element.innerHTML = T[key]()
if (key) element.setAttribute(attr, T[key]())
else element.textContent = T[attr]()
Expand All @@ -59,7 +60,7 @@ export function translatePage() {
for (const element of document.querySelectorAll('[i18n]')) {
const parts = element.getAttribute('i18n')!.split(',')
for (const part of parts) {
const [attr, key] = part.split(':', 2).map((k) => k.trim())
const [attr, key] = split2(part, ':').map((k) => k.trim())
if (attr === 'html' || !key) element.textContent = ''
}
}
Expand All @@ -80,7 +81,7 @@ export function translatePage() {
localeName = new Intl.DisplayNames(locale, { type: 'language' }).of(locale),
selected = usedLocale === locale ? 'selected' : ''
selectionList.push(`
<button class="locale ${selected}" onclick="OmnI18n.setLocale('${locale}')" title="${localeName}">
<button class="locale ${selected}" onclick="OmnI18n.setLocale('${locale}')">
<span class="flag">${flagsStr}</span>
<span class="name">${localeName}</span>
</button>
Expand All @@ -102,7 +103,7 @@ export function translatePage() {
`,
localeName = new Intl.DisplayNames(locale, { type: 'language' }).of(locale)
currentLocaleElm.innerHTML = `
<button class="locale" title="${localeName}">
<button class="locale">
<span class="flag">${flagsStr}</span>
<span class="name">${localeName}</span>
</button>
Expand Down

0 comments on commit a4ad06a

Please sign in to comment.