From 08d7c20dd7bf6670dcc2319ffa3ef100d2ff6510 Mon Sep 17 00:00:00 2001 From: Amer Harb Date: Mon, 27 Nov 2023 09:49:16 +0100 Subject: [PATCH 1/3] make languages a static Store class --- webapp/src/app/api/Store.ts | 56 +++++++++++++++++++ webapp/src/app/api/messages/route.ts | 4 ++ webapp/src/app/api/pull-request/route.ts | 1 + .../api/translations/[lang]/[msgId]/route.ts | 4 +- .../src/app/api/translations/[lang]/route.ts | 4 +- 5 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 webapp/src/app/api/Store.ts diff --git a/webapp/src/app/api/Store.ts b/webapp/src/app/api/Store.ts new file mode 100644 index 00000000..325887de --- /dev/null +++ b/webapp/src/app/api/Store.ts @@ -0,0 +1,56 @@ +import { envVarNotFound } from '@/utils/util'; +import fs from 'fs/promises'; +import { parse } from 'yaml'; +import { simpleGit, SimpleGit, SimpleGitOptions } from 'simple-git'; + +const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound('REPO_PATH'); +const MAIN_BRANCH = 'main'; + +export class Store { + public static async getLanguage(lang: string) { + let languages: Map>; + if (!globalThis.languages) { + console.debug('Initializing languages'); + const options: Partial = { + baseDir: REPO_PATH, + binary: 'git', + maxConcurrentProcesses: 1, + trimmed: false, + }; + const git: SimpleGit = simpleGit(options); + console.debug('git checkout main pull...'); + await git.checkout(MAIN_BRANCH); + console.debug('git pull...'); + await git.pull(); + console.debug('git done checkout main branch and pull'); + languages = new Map>(); + globalThis.languages = languages; + } else { + console.debug('find languages in Memory'); + languages = globalThis.languages; + } + + let translations: Record; + if (!languages.has(lang)) { + console.debug('read language[' + lang +'] from file'); + // TODO: read this from .lyra.yml setting file in client repo + const yamlPath = REPO_PATH + `/src/locale/${lang}.yml`; + + const yamlBuf = await fs.readFile(yamlPath); + // TODO: change parsing to be flattened map of key to value, instead of object + // so key will be like 'key1.key2.key3' and value will be 'translated text' + // this will reduce the cost of looping for the object every time we need to save a message + translations = parse(yamlBuf.toString()) as Record; + languages.set(lang, translations); + } else { + console.debug('read language [' + lang + '] from Memory'); + translations = languages.get(lang) ?? Store.throwLangNotFound(lang); + } + + return translations; + } + + private static throwLangNotFound(lang: string): never { + throw new Error(`Language ${lang} not found`); + } +} diff --git a/webapp/src/app/api/messages/route.ts b/webapp/src/app/api/messages/route.ts index ff93e4b2..ef44c01c 100644 --- a/webapp/src/app/api/messages/route.ts +++ b/webapp/src/app/api/messages/route.ts @@ -10,10 +10,14 @@ const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound('REPO_PATH'); export async function GET() { const messages: MessageData[] = []; + // TODO: read path or src from .lyra.yml setting file in client repo for await (const item of getMessageFiles(REPO_PATH + '/src')) { messages.push(...readTypedMessages(item)); } + // TODO: change data instruction to be a map of key to value, instead of object + // message id is the key, and value is an object with default and params + // example: { 'key1.key2.key3': { default: 'default text', params: [] }} return NextResponse.json({ data: messages, }); diff --git a/webapp/src/app/api/pull-request/route.ts b/webapp/src/app/api/pull-request/route.ts index 5ae236e0..33ca5594 100644 --- a/webapp/src/app/api/pull-request/route.ts +++ b/webapp/src/app/api/pull-request/route.ts @@ -39,6 +39,7 @@ export async function POST() { const languages = globalThis.languages; for (const lang of languages.keys()) { const yamlPath = REPO_PATH + `/src/locale/${lang}.yml`; + // TODO: when language obj become a map of key to value, then it need to be converted to object before stringify const yamlOutput = stringify(languages.get(lang), { doubleQuotedAsJSON: true, singleQuote: true, diff --git a/webapp/src/app/api/translations/[lang]/[msgId]/route.ts b/webapp/src/app/api/translations/[lang]/[msgId]/route.ts index 0505d1da..148e9454 100644 --- a/webapp/src/app/api/translations/[lang]/[msgId]/route.ts +++ b/webapp/src/app/api/translations/[lang]/[msgId]/route.ts @@ -1,4 +1,4 @@ -import { getLanguage } from '@/app/api/languages'; +import { Store } from '@/app/api/Store'; import { NextRequest, NextResponse } from 'next/server'; export async function PUT( @@ -15,7 +15,7 @@ export async function PUT( const { text } = payload; const objKeyPath = msgId.split('.'); - let curObj = await getLanguage(lang); + let curObj = await Store.getLanguage(lang); objKeyPath.forEach((key, index) => { if (index == objKeyPath.length - 1) { curObj[key] = text; diff --git a/webapp/src/app/api/translations/[lang]/route.ts b/webapp/src/app/api/translations/[lang]/route.ts index a6b0bbfb..56e7dace 100644 --- a/webapp/src/app/api/translations/[lang]/route.ts +++ b/webapp/src/app/api/translations/[lang]/route.ts @@ -1,4 +1,4 @@ -import { getLanguage } from '@/app/api/languages'; +import { Store } from '@/app/api/Store'; import { NextRequest, NextResponse } from 'next/server'; export async function GET( @@ -6,7 +6,7 @@ export async function GET( context: { params: { lang: string; msgId: string } }, ) { const lang = context.params.lang; - const langObj = await getLanguage(lang); + const langObj = await Store.getLanguage(lang); const flattenLangObj = flattenObject(langObj); return NextResponse.json({ From 2b6c69d8c33a07413fbf20539d2f03ad7da46a13 Mon Sep 17 00:00:00 2001 From: Amer Harb Date: Mon, 27 Nov 2023 09:59:48 +0100 Subject: [PATCH 2/3] remove unused --- webapp/src/app/[lang]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/app/[lang]/page.tsx b/webapp/src/app/[lang]/page.tsx index dcd310e9..2a7efb99 100644 --- a/webapp/src/app/[lang]/page.tsx +++ b/webapp/src/app/[lang]/page.tsx @@ -53,7 +53,7 @@ export default function Home({ params }: { params: { lang: string } }) { key={msg.id} message={msg} onSave={async (text) => { - const res = await fetch( + await fetch( `/api/translations/${params.lang}/${msg.id}`, { body: JSON.stringify({ From 39c5fd7e480020d12c58b9295d83a8a441ce19e7 Mon Sep 17 00:00:00 2001 From: Amer Harb Date: Mon, 27 Nov 2023 14:18:03 +0100 Subject: [PATCH 3/3] remote languages --- webapp/src/app/[lang]/page.tsx | 1 - webapp/src/app/api/Store.ts | 17 +++++------ webapp/src/app/api/languages.ts | 51 --------------------------------- 3 files changed, 9 insertions(+), 60 deletions(-) delete mode 100644 webapp/src/app/api/languages.ts diff --git a/webapp/src/app/[lang]/page.tsx b/webapp/src/app/[lang]/page.tsx index 2a7efb99..9a0ce79e 100644 --- a/webapp/src/app/[lang]/page.tsx +++ b/webapp/src/app/[lang]/page.tsx @@ -66,7 +66,6 @@ export default function Home({ params }: { params: { lang: string } }) { }, ); - await res.json(); setTranslations((cur) => ({ ...cur, [msg.id]: text, diff --git a/webapp/src/app/api/Store.ts b/webapp/src/app/api/Store.ts index 325887de..3eb5f2dd 100644 --- a/webapp/src/app/api/Store.ts +++ b/webapp/src/app/api/Store.ts @@ -1,6 +1,7 @@ -import { envVarNotFound } from '@/utils/util'; +/* global globalThis */ import fs from 'fs/promises'; import { parse } from 'yaml'; +import { envVarNotFound, logDebug } from '@/utils/util'; import { simpleGit, SimpleGit, SimpleGitOptions } from 'simple-git'; const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound('REPO_PATH'); @@ -10,7 +11,7 @@ export class Store { public static async getLanguage(lang: string) { let languages: Map>; if (!globalThis.languages) { - console.debug('Initializing languages'); + logDebug('Initializing languages'); const options: Partial = { baseDir: REPO_PATH, binary: 'git', @@ -18,21 +19,21 @@ export class Store { trimmed: false, }; const git: SimpleGit = simpleGit(options); - console.debug('git checkout main pull...'); + logDebug('git checkout main pull...'); await git.checkout(MAIN_BRANCH); - console.debug('git pull...'); + logDebug('git pull...'); await git.pull(); - console.debug('git done checkout main branch and pull'); + logDebug('git done checkout main branch and pull'); languages = new Map>(); globalThis.languages = languages; } else { - console.debug('find languages in Memory'); + logDebug('find languages in Memory'); languages = globalThis.languages; } let translations: Record; if (!languages.has(lang)) { - console.debug('read language[' + lang +'] from file'); + logDebug('read language[' + lang +'] from file'); // TODO: read this from .lyra.yml setting file in client repo const yamlPath = REPO_PATH + `/src/locale/${lang}.yml`; @@ -43,7 +44,7 @@ export class Store { translations = parse(yamlBuf.toString()) as Record; languages.set(lang, translations); } else { - console.debug('read language [' + lang + '] from Memory'); + logDebug('read language [' + lang + '] from Memory'); translations = languages.get(lang) ?? Store.throwLangNotFound(lang); } diff --git a/webapp/src/app/api/languages.ts b/webapp/src/app/api/languages.ts deleted file mode 100644 index cf49905a..00000000 --- a/webapp/src/app/api/languages.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* global globalThis */ -import fs from 'fs/promises'; -import { parse } from 'yaml'; -import { envVarNotFound, logDebug } from '@/utils/util'; -import { simpleGit, SimpleGit, SimpleGitOptions } from 'simple-git'; - -const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound('REPO_PATH'); -const MAIN_BRANCH = 'main'; - -export async function getLanguage(lang: string) { - let languages: Map>; - if (!globalThis.languages) { - logDebug('Initializing languages'); - const options: Partial = { - baseDir: REPO_PATH, - binary: 'git', - maxConcurrentProcesses: 1, - trimmed: false, - }; - const git: SimpleGit = simpleGit(options); - logDebug('git checkout main pull...'); - await git.checkout(MAIN_BRANCH); - logDebug('git pull...'); - await git.pull(); - logDebug('git done checkout main branch and pull'); - languages = new Map>(); - globalThis.languages = languages; - } else { - logDebug('read languages from globalThis'); - languages = globalThis.languages; - } - - let translations: Record; - if (!languages.has(lang)) { - logDebug('read languages from file'); - const yamlPath = REPO_PATH + `/src/locale/${lang}.yml`; - - const yamlBuf = await fs.readFile(yamlPath); - translations = parse(yamlBuf.toString()) as Record; - languages.set(lang, translations); - } else { - logDebug('read languages from Memory'); - translations = languages.get(lang) ?? throwLangNotFound(lang); - } - - return translations; -} - -function throwLangNotFound(lang: string): never { - throw new Error(`Language ${lang} not found`); -}