Skip to content

Commit

Permalink
add support for nested dictionaries
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanhofer committed May 13, 2023
1 parent 2134664 commit 479642f
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 48 deletions.
6 changes: 3 additions & 3 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion example/i18n/de/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import type { Translation } from '../i18n-types'
const de: Translation = {
// this is an example Translation, just rename or delete this folder if you want
HI: 'Hallo {name}! Bitte hinterlasse einen Stern, wenn dir das Projekt gefällt: https://github.com/ivanhofer/typesafe-i18n',
PLURAL: "Hallo {{Banane|Bananen}}",
PLURAL_FULL: "{{zero|one|two|few|many|other}}",
nested: {
PLURAL: "Hallo {{Banane|Bananen}}",
}
}

export default de
4 changes: 3 additions & 1 deletion example/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import type { BaseTranslation } from '../i18n-types'
const en: BaseTranslation = {
// TODO: your translations go here
HI: 'Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n',
PLURAL: "hello banana{{|s}}",
PLURAL_FULL: "{{zero|one|two|few|many|other}}",
nested: {
PLURAL: "hello banana{{s}}",
}
}

export default en
26 changes: 15 additions & 11 deletions example/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,35 @@ type RootTranslation = {
* @param {string} name
*/
HI: RequiredParams<'name'>
/**
* "h​e​l​l​o​ ​b​a​n​a​n​a​{​{​|​s​}​}​"
* @param {string | number | boolean} 0
*/
PLURAL: string
/**
* "{​{​z​e​ro​|​o​n​e​|​t​w​o​|​f​e​w​|​m​a​n​y​|​o​t​h​e​r​}​}"
* @param {string | number | boolean} 0
*/
*/
PLURAL_FULL: string
nested: {
/**
* "h​e​l​l​o​ ​b​a​n​a​n​a​{​{​|​s​}​}​"
* @param {string | number | boolean} 0
*/
PLURAL: string
}
}

export type TranslationFunctions = {
/**
* Hi {name}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n
*/
HI: (arg: { name: string }) => LocalizedString
/**
* Hallo {{Banane|Bananen}}
*/
PLURAL: (arg: string | number | boolean) => LocalizedString
/**
* {{zero|one|two|few|many|other}}
*/
*/
PLURAL_FULL: (arg: string | number | boolean) => LocalizedString
nested: {
/**
* Hallo {{Banane|Bananen}}
*/
PLURAL: (arg: string | number | boolean) => LocalizedString
}
}

export type Formatters = {}
42 changes: 35 additions & 7 deletions src/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ describe("plugin", async () => {
expect(message).toBeDefined()
expect(message!.pattern.elements[0].value).toBe("{{zero|one|two|few|many|other}}")
})

test("should be possible to query a nested message with plural part", () => {
const message = query(referenceResource).get({ id: "nested.PLURAL" })
expect(message).toBeDefined()
expect(message!.pattern.elements[0].value).toBe("hello banana")
expect(message!.pattern.elements[1].value).toBe("{{s}}")
})
})

describe("writeResources()", async () => {
Expand All @@ -79,13 +86,7 @@ describe("plugin", async () => {
},
},
})
const updatedResources = [
...resources.filter(
(resource) =>
resource.languageTag.name !== config.referenceLanguage
),
updatedReferenceResource as Resource,
]
const updatedResources = [updatedReferenceResource as Resource]
await config.writeResources({ config, resources: updatedResources })
const module =
(await env.$fs.readFile(
Expand All @@ -95,5 +96,32 @@ describe("plugin", async () => {

expect(module.includes('"new-message": "Newly created message with {variable}!"')).toBeTruthy()
})

test("should serialize a nested resource", async () => {
const [updatedReferenceResource] = query(referenceResource)
.create({
message: {
id: { type: "Identifier", name: "nested.plural.message" },
type: "Message",
pattern: {
type: "Pattern",
elements: [
{ type: "Text", value: "{{Banane|Bananen}}" },
],
},
},
})
const updatedResources = [updatedReferenceResource as Resource]
await config.writeResources({ config, resources: updatedResources })
const module =
(await env.$fs.readFile(
`./example/i18n/${config.referenceLanguage}/index.ts`,
{ encoding: "utf-8" }
)) as string

expect(module.includes('"nested": {')).toBeTruthy()
expect(module.includes('"plural": {')).toBeTruthy()
expect(module.includes('message": "{{Banane|Bananen}}"')).toBeTruthy()
})
})
})
25 changes: 22 additions & 3 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,16 @@ const parseResource = (
},
body: Object.entries(flatJson).map(([id, value]) =>
parseMessage(id, value)
),
).flat(),
}
}

const parseMessage = (id: string, value: string): ast.Message => {
const parseMessage = (id: string, value: string | Record<string, string>): ast.Message | ast.Message[] => {
if (typeof value === 'object')
return Object.entries(value).map(([entryId, entryValue]) =>
parseMessage(`${id}.${entryId}`, entryValue)
).flat()

const parsedMessage = experimentalParseMessage(value)

return {
Expand Down Expand Up @@ -206,7 +211,21 @@ export default ${locale}`
}

const serializeResource = (resource: ast.Resource): string => {
const json = Object.fromEntries(resource.body.map(serializeMessage))
const json = {} as Record<string, any>

resource.body.map(serializeMessage).forEach(([id, value]) => {
const idParts = id.split('.')
let current = json
for (let i = 0; i < idParts.length; i++) {
const part = idParts[i]
if (i === idParts.length - 1) {
current[part] = value
} else {
if (!current[part]) current[part] = {}
current = current[part]
}
}
})
// stringify the object with beautification
return JSON.stringify(json, null, 3)
}
Expand Down
22 changes: 0 additions & 22 deletions test.ts

This file was deleted.

0 comments on commit 479642f

Please sign in to comment.