Skip to content

Commit

Permalink
improve parsing and serializing process
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanhofer committed May 13, 2023
1 parent 386b6de commit 2134664
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 20 deletions.
8 changes: 4 additions & 4 deletions dist/index.js

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

2 changes: 2 additions & 0 deletions example/i18n/de/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ 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}}",
}

export default de
2 changes: 2 additions & 0 deletions example/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ 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}}",
}

export default en
20 changes: 19 additions & 1 deletion example/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,33 @@ type RootTranslation = {
/**
* H​i​ ​{​n​a​m​e​}​!​ ​P​l​e​a​s​e​ ​l​e​a​v​e​ ​a​ ​s​t​a​r​ ​i​f​ ​y​o​u​ ​l​i​k​e​ ​t​h​i​s​ ​p​r​o​j​e​c​t​:​ ​h​t​t​p​s​:​/​/​g​i​t​h​u​b​.​c​o​m​/​i​v​a​n​h​o​f​e​r​/​t​y​p​e​s​a​f​e​-​i​1​8​n
* @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
}

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
}

export type Formatters = {}
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
"name": "inlang-plugin-typesafe-i18n",
"type": "module",
"scripts": {
"dev": "concurrently -n build,test -c auto \"npm run dev:watch\" \"npm run dev:test\"",
"dev:test": "vitest",
"dev:watch": "DEV=true tsx ./esbuild.ts",
"dev": "concurrently -n build,test -c auto \"npm run dev:plugin\" \"npm run tsc:watch\"",
"dev:plugin": "DEV=true tsx ./esbuild.ts",
"build": "tsx ./esbuild.ts",
"test": "npm run tsc && npm run vitest",
"vitest": "vitest run",
"vitest:watch": "vitest",
"tsc": "tsc --noEmit",
"tsc:watch": "tsc --watch --noEmit",
"prepare": "husky install"
},
"devDependencies": {
Expand Down
25 changes: 20 additions & 5 deletions src/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { describe, test, expect } from "vitest"
import nodeFs from "node:fs/promises"
import { query } from "@inlang/core/query"
import { setupConfig } from '@inlang/core/config'
import type { Resource } from '@inlang/core/ast'
import type { Placeholder, Resource } from '@inlang/core/ast'
import { mockEnvironment, testConfig } from "@inlang/core/test"
import fs from "node:fs/promises"

Expand Down Expand Up @@ -46,10 +46,20 @@ describe("plugin", async () => {
test("should be possible to query a message", () => {
const message = query(referenceResource).get({ id: "HI" })
expect(message).toBeDefined()
expect(message?.pattern.elements[0].value).toBe(
"Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n"
expect(message!.pattern.elements[0].value).toBe("Hi ")
expect((message!.pattern.elements[1].body as Placeholder)!.name).toBe(
"name"
)
expect(message!.pattern.elements[2].value).toBe(
"! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n"
)
})

test("should be possible to query a message with plural part", () => {
const message = query(referenceResource).get({ id: "PLURAL_FULL" })
expect(message).toBeDefined()
expect(message!.pattern.elements[0].value).toBe("{{zero|one|two|few|many|other}}")
})
})

describe("writeResources()", async () => {
Expand All @@ -61,7 +71,11 @@ describe("plugin", async () => {
type: "Message",
pattern: {
type: "Pattern",
elements: [{ type: "Text", value: "Newly created message" }],
elements: [
{ type: "Text", value: "Newly created message with " },
{ type: "Placeholder", body: { type: 'VariableReference', name: "variable" } },
{ type: "Text", value: "!" },
],
},
},
})
Expand All @@ -78,7 +92,8 @@ describe("plugin", async () => {
`./example/i18n/${config.referenceLanguage}/index.ts`,
{ encoding: "utf-8" }
)) as string
expect(module.includes('"new-message": "Newly created message"')).toBeTruthy()

expect(module.includes('"new-message": "Newly created message with {variable}!"')).toBeTruthy()
})
})
})
65 changes: 58 additions & 7 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { InlangConfig } from "@inlang/core/config"
import type { InlangEnvironment } from "@inlang/core/environment"
import type * as ast from "@inlang/core/ast"
import { getConfig, getLocaleInformation } from "typesafe-i18n/config"
import { experimentalParseMessage, type ParameterPart, type PluralPart } from "typesafe-i18n/parser"
import { createPlugin } from "@inlang/core/plugin"
import type { readdir } from "node:fs/promises"

Expand Down Expand Up @@ -117,14 +118,52 @@ const parseResource = (
}

const parseMessage = (id: string, value: string): ast.Message => {
// TODO: also parse variables
const parsedMessage = experimentalParseMessage(value)

return {
type: "Message",
id: {
type: "Identifier",
name: id,
},
pattern: { type: "Pattern", elements: [{ type: "Text", value: value }] },
pattern: {
type: "Pattern",
elements: parsedMessage.map(part => {
switch (part.kind) {
case 'parameter':
return parseParameter(part)
case 'text':
return { type: "Text", value: part.content }
case 'plural':
return parsePlural(part)
}
})
},
}
}

const parseParameter = (parameterPart: ParameterPart): ast.Placeholder => {
return {
type: "Placeholder",
body: {
type: 'VariableReference',
name: parameterPart.key,
metadata: {
types: parameterPart.types,
optional: parameterPart.optional,
transforms: parameterPart.transforms,
}
}
}
}

const parsePlural = (pluralPart: PluralPart): ast.Text => {
return {
type: "Text",
value: `{{${[pluralPart.zero, pluralPart.one, pluralPart.two, pluralPart.few, pluralPart.many, pluralPart.other]
.filter(Boolean)
.join('|')
}}}`
}
}

Expand Down Expand Up @@ -172,21 +211,33 @@ const serializeResource = (resource: ast.Resource): string => {
return JSON.stringify(json, null, 3)
}

function serializeMessage(message: ast.Message): [id: string, value: string] {
const serializeMessage = (message: ast.Message): [id: string, value: string] => {
return [message.id.name, serializePattern(message.pattern)]
}

function serializePattern(pattern: ast.Pattern): string {
const serializePattern = (pattern: ast.Pattern): string => {
return pattern.elements.map(serializePatternElement).join("")
}

function serializePatternElement(
const serializePatternElement = (
element: ast.Pattern["elements"][number]
): string {
): string => {
switch (element.type) {
case "Text":
return element.value
case "Placeholder":
return `{${element.body.name}}`
return serializePlaceholder(element)
}
}

const serializePlaceholder = ({ body: { name, metadata = {} } }: ast.Placeholder): string => {
let str = name
if (metadata.optional) str += "?"
if (metadata.types?.length > 0) {
str += `:${metadata.types.join("|")}`
}
if (metadata.transforms?.length > 0) {
str += `|${metadata.transforms.join("|")}`
}
return `{${str}}`
}

0 comments on commit 2134664

Please sign in to comment.