Skip to content

Commit

Permalink
better server API
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-burel committed May 27, 2024
1 parent 3e4fb56 commit 91dd205
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 30 deletions.
74 changes: 50 additions & 24 deletions shared/i18n/translate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const findString = (key: string, strings: Translation[]) => {
.find(t => t.key === key)
}

/**
* "Hello {foobar }", {foobar: "world"} => "hello world"
*/
const applyTemplate = ({
t,
values,
Expand All @@ -28,6 +31,7 @@ const applyTemplate = ({
key: string
}) => {
try {
// Detect any text within {}
return template(t, { interpolate: /{([\s\S]+?)}/g })(values)
} catch (error) {
console.error(error)
Expand All @@ -37,8 +41,6 @@ const applyTemplate = ({

/**
* @deprecated Use makeTea with a parsed locale using a dictionary of translations
* @param locale
* @returns
*/
export const makeTranslatorFunc =
(locale: Locale): StringTranslator =>
Expand Down Expand Up @@ -71,32 +73,56 @@ export const makeTranslatorFunc =
return result as StringTranslatorResult
}


/** Matches anything betwen { }, tolerate spaces, allow multiple interpolations */
const INTERPOLATION_REGEX = /{([\s\S]+?)}/g
/**
* TODO: see the perf impact of computing the templates
* Precomputing is probably useless, but we could cache computations
*/
function injectValues(str: string, values: Record<string, any>) {
return template(str, { interpolate: INTERPOLATION_REGEX })(values)
}

export function makeTea(locale: LocaleParsed) {
return function t(key: string, values: Record<string, any> = {}, fallback?: string) {
const { dict, ...rest } = locale
let result: Partial<StringTranslatorResult> = { key, locale: rest }
const stringObject = dict[key]
if (stringObject) {
result = { ...result, ...stringObject }
} else {

/**
* Get the full translation with metadata, HTML and clean versions
*/
function getMessage(key: string, values: Record<string, any> = {}, fallback?: string) {
// get the string or template
let result: StringTranslatorResult = {
key,
locale: { id: locale.id, label: locale.label },
t: fallback || ""
}
const translation = locale.dict[key]
if (!translation) {
result.missing = true
if (fallback) {
result.t = fallback
} else {
result = {
...result,
...translation,
}
}

const injectValues = (s: string) =>
values ? applyTemplate({ t: s, values, locale, key }) : s

if (result.t) {
const t = injectValues(result.t)
result.t = t
result.tClean = injectValues(result.tClean ?? t)
result.tHtml = injectValues(result.tHtml ?? t)
}

// TODO: this object seems super heavy
console.log(result, stringObject, key)
// interpolate values
const t = injectValues(result.t, values)
// handle tClean and tHTML variations
result.t = t
result.tClean = result.tClean ? injectValues(result.tClean, values) : result.t
result.tHtml = result.tHtml ? injectValues(result.tHtml, values) : result.t
return result as StringTranslatorResult
}
/**
* Shortcut to get a translated string
*
* Prefer "getMessage" for more elaborate usage (HTML content, handling missing values...)
*/
function t(key: string, values: Record<string, any> = {}, fallback?: string) {
let result = locale.dict[key]?.t || fallback || ""
result = injectValues(result, values)
return result

}
return { t, getMessage }
}
2 changes: 2 additions & 0 deletions shared/i18n/typings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export interface Translation {
key: string
t: string
tHtml?: string,
tClean?: string
}

/**
Expand Down
4 changes: 2 additions & 2 deletions surveyform/src/app/[lang]/(mainLayout)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export async function generateMetadata({
}): Promise<Metadata | undefined> {
const { t, error } = await rscTeapot()
if (error) return defaultMetadata
const title = t("general.title").t
const description = t("general.description").t
const title = t("general.title")
const description = t("general.description")
const metadata = { ...defaultMetadata, title, description };
return metadata;
}
Expand Down
8 changes: 4 additions & 4 deletions surveyform/src/i18n/components/ServerT.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { rscLocaleCached } from "~/lib/api/rsc-fetchers";
export async function rscTeapot({ contexts }: { contexts?: Array<string> } = {}) {
const { locale, error } = await rscLocaleCached({ contexts })
if (error) return { error }
const t = makeTea(locale)
return { t }
const { t, getMessage } = makeTea(locale)
return { t, getMessage }
}

/**
Expand All @@ -21,8 +21,8 @@ export async function rscTeapot({ contexts }: { contexts?: Array<string> } = {})
* TODO: this is just a draft to check that everything is plugged
* we should handle edge cases : children fallback, adding data attributes etc.
*/
export async function ServerT({ token }: { token: string }) {
export async function ServerT({ token, values, fallback }: { token: string, values?: Record<string, any>, fallback?: string }) {
const { t, error } = await rscTeapot()
if (error) return <span>Can't load locales</span>
return <span {...{ [DATA_TOKEN_ATTR]: token }}>{t(token).t}</span>
return <span {...{ [DATA_TOKEN_ATTR]: token }}>{t(token, values, fallback)}</span>
}

0 comments on commit 91dd205

Please sign in to comment.