Skip to content

Commit

Permalink
fix: don't replace octothorpe coming from variable (#1850)
Browse files Browse the repository at this point in the history
  • Loading branch information
thekip committed Feb 15, 2024
1 parent 685730f commit e321729
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 17 deletions.
19 changes: 19 additions & 0 deletions packages/core/src/interpolate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ describe("interpolate", () => {
expect(plural({ value: 2 })).toEqual("2 Books")
})

it("should not replace `#` symbol passed in the variable in the jsx expression", () => {
const plural = prepare(
"{value, plural, one {There is a notification in <1>{documentTitle}</1>} other {There are # notifications in <1>{documentTitle}</1>}}"
)

expect(plural({ value: 1, documentTitle: "Title #1" })).toEqual(
"There is a notification in <1>Title #1</1>"
)
expect(plural({ value: 2, documentTitle: "Title #1" })).toEqual(
"There are 2 notifications in <1>Title #1</1>"
)
})

it("should replace more than one octothorpe symbols in message", () => {
const plural = prepare("{value, plural, one {} other {# and #}}")

expect(plural({ value: 2 })).toEqual("2 and 2")
})

it("when a value is defined (even when empty) plural will return it. Conversely, if a value is not defined, plural defaults to 'other'", () => {
const plural = prepare("{value, plural, =0 {} other {#% discount}}")
expect(plural({ value: 0 })).toEqual("")
Expand Down
57 changes: 40 additions & 17 deletions packages/core/src/interpolate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { CompiledMessage, Formats, Locales, Values } from "./i18n"
import { date, number, plural, type PluralOptions } from "./formats"
import { isString } from "./essentials"
import { unraw } from "unraw"
import { CompiledIcuChoices } from "@lingui/message-utils/compileMessage"

export const UNICODE_REGEX = /\\u[a-fA-F0-9]{4}|\\x[a-fA-F0-9]{2}/g

const OCTOTHORPE_PH = "%__lingui_octothorpe__%"

const getDefaultFormats = (
locale: string,
passedLocales?: Locales,
Expand All @@ -22,7 +25,7 @@ const getDefaultFormats = (
? style("number")
: undefined
const valueStr = number(locales, value, numberFormat)
return message.replace("#", valueStr)
return message.replace(new RegExp(OCTOTHORPE_PH, "g"), valueStr)
}

return {
Expand Down Expand Up @@ -51,16 +54,12 @@ const getDefaultFormats = (
value: string,
format: string | Intl.DateTimeFormatOptions
): string => date(locales, value, style(format)),

undefined: undefinedFormatter,
} as const
}

const selectFormatter = (value: string, rules: Record<string, any>) =>
rules[value] ?? rules.other

const undefinedFormatter = (value: unknown) => value

/**
* @param translation compiled message
* @param locale Locale of message
Expand All @@ -78,26 +77,50 @@ export function interpolate(
return (values: Values = {}, formats?: Formats): string => {
const formatters = getDefaultFormats(locale, locales, formats)

const formatMessage = (message: CompiledMessage | number | undefined) => {
if (!Array.isArray(message)) return message
const formatMessage = (tokens: CompiledMessage | number | undefined) => {
if (!Array.isArray(tokens)) return tokens

return message.reduce<string>((message, token) => {
if (isString(token)) return message + token
return tokens.reduce<string>((message, token) => {
if (token === "#") {
return message + OCTOTHORPE_PH
}

if (isString(token)) {
return message + token
}

const [name, type, format] = token

let interpolatedFormat: any = {}
if (format != null && typeof format === "object") {
Object.entries(format).forEach(([key, value]) => {
interpolatedFormat[key] = formatMessage(value)
})

if (
type === "plural" ||
type === "selectordinal" ||
type === "select"
) {
Object.entries(format as CompiledIcuChoices).forEach(
([key, value]) => {
interpolatedFormat[key] = formatMessage(value)
}
)
} else {
interpolatedFormat = format
}
// @ts-ignore TS2538: Type undefined cannot be used as an index type.
const formatter = formatters[type]
const value = formatter(values[name], interpolatedFormat)
if (value == null) return message

let value: unknown

if (type) {
// run formatter, such as plural, number, etc.
const formatter = (formatters as any)[type]
value = formatter(values[name], interpolatedFormat)
} else {
// simple placeholder variable interpolation eq {variableName}
value = values[name]
}

if (value == null) {
return message
}

return message + value
}, "")
Expand Down

0 comments on commit e321729

Please sign in to comment.