From 425f734476e6c052d75d920ac3cece8610b02191 Mon Sep 17 00:00:00 2001 From: Pontus Abrahamsson Date: Sat, 22 Mar 2025 13:14:03 +0100 Subject: [PATCH 1/3] Handle complex keys --- examples/test2/languine.json | 12 ++ examples/test2/languine.lock | 19 +++ examples/test2/messages/de.json | 24 +++ examples/test2/messages/en.json | 24 +++ .../cli/src/parsers/__tests__/json.test.ts | 2 +- packages/cli/src/parsers/core/flatten.test.ts | 144 ++++++++++++++++++ packages/cli/src/parsers/core/flatten.ts | 116 +++++++++++--- 7 files changed, 320 insertions(+), 21 deletions(-) create mode 100644 examples/test2/languine.json create mode 100644 examples/test2/languine.lock create mode 100644 examples/test2/messages/de.json create mode 100644 examples/test2/messages/en.json create mode 100644 packages/cli/src/parsers/core/flatten.test.ts diff --git a/examples/test2/languine.json b/examples/test2/languine.json new file mode 100644 index 00000000..21366b52 --- /dev/null +++ b/examples/test2/languine.json @@ -0,0 +1,12 @@ +{ + "locale": { + "source": "en", + "targets": ["de"] + }, + "files": { + "json": { + "include": ["messages/[locale].json"] + } + } + } + \ No newline at end of file diff --git a/examples/test2/languine.lock b/examples/test2/languine.lock new file mode 100644 index 00000000..702eb446 --- /dev/null +++ b/examples/test2/languine.lock @@ -0,0 +1,19 @@ +version: 1 +files: + messages/en.json: + chat.poll.allow-multiple: b12390f674492adc8dadac6b407e040d + chat.poll.create-poll: 87dde47c3c24ba7cc629ea2dacab3806 + chat.poll.create-poll\.title: 6cb41bdaa77f037dfa8ec7db1bd346cd + chat.poll.create-poll\.question: a97ea56b0e00b2379736ae60869ff66a + chat.poll.create-poll\.options: dae8ace18bdcbcc6ae5aece263e14fe8 + chat.poll.create-poll\.placeholder\.add: ec211f7c20af43e742bf2570c3cb84f9 + test.*: 73f0a5236a013c8ee6af7d96fee67df5 + test.image/*, \.jpg, \.jpeg, \.png, \.gif, \.svg, \.webp: fff0d600f8a0b5e19e88bfb821dd1157 + test.\.mp4, \.mov, \.avi, \.mkv, \.webm, \.mpeg: 554cfab3938e21d9270bd6b75931f96f + test.\.mp3, \.wav, \.ogg, \.flac, \.aac, \.wma, \.m4a, \.midi, \.alac: b22f0418e8ac915eb66f829d262d14a2 + test.\.pdf: abdf095626d4a4ab2bcd4167b128c1f1 + test.\.docx: 8c7a7a618a6d4acfe85b4b4b1048846d + test.\.csv: 01b1b6ebeb388697cca6712b58439069 + test.\.pptx: a9bec49e3e6504112d702f6449b79631 + test.\.xlsx: 552c070e14cf0a9151d1b49064e4cde0 + test.\.zip: daf4de58883c030f033f07e61debf025 diff --git a/examples/test2/messages/de.json b/examples/test2/messages/de.json new file mode 100644 index 00000000..8717b33a --- /dev/null +++ b/examples/test2/messages/de.json @@ -0,0 +1,24 @@ +{ + "chat": { + "poll": { + "allow-multiple": "Mehrere Antworten zulassen?", + "create-poll": "Umfrage", + "create-poll.title": "Umfrage erstellen", + "create-poll.question": "Frage", + "create-poll.options": "Optionen", + "create-poll.placeholder.add": "Hinzufügen" + } + }, + "test": { + "*": "Alle Dateitypen zulassen", + "image/*, .jpg, .jpeg, .png, .gif, .svg, .webp": "Bilder", + ".mp4, .mov, .avi, .mkv, .webm, .mpeg": "Videos", + ".mp3, .wav, .ogg, .flac, .aac, .wma, .m4a, .midi, .alac": "Audio", + ".pdf": ".pdf", + ".docx": ".docx", + ".csv": ".csv", + ".pptx": ".pptx", + ".xlsx": ".xlsx", + ".zip": ".zip" + } +} diff --git a/examples/test2/messages/en.json b/examples/test2/messages/en.json new file mode 100644 index 00000000..6ce96ffd --- /dev/null +++ b/examples/test2/messages/en.json @@ -0,0 +1,24 @@ +{ + "chat": { + "poll": { + "allow-multiple": "Allow Multiple Answers?", + "create-poll": "Poll", + "create-poll.title": "Create Poll", + "create-poll.question": "Question", + "create-poll.options": "Options", + "create-poll.placeholder.add": "Add" + } + }, + "test": { + "*": "Allow all file types", + "image/*, .jpg, .jpeg, .png, .gif, .svg, .webp": "Images", + ".mp4, .mov, .avi, .mkv, .webm, .mpeg": "Videos", + ".mp3, .wav, .ogg, .flac, .aac, .wma, .m4a, .midi, .alac": "Audio", + ".pdf": ".pdf", + ".docx": ".docx", + ".csv": ".csv", + ".pptx": ".pptx", + ".xlsx": ".xlsx", + ".zip": ".zip" + } +} diff --git a/packages/cli/src/parsers/__tests__/json.test.ts b/packages/cli/src/parsers/__tests__/json.test.ts index e720e346..67059ba4 100644 --- a/packages/cli/src/parsers/__tests__/json.test.ts +++ b/packages/cli/src/parsers/__tests__/json.test.ts @@ -150,7 +150,7 @@ describe("JSON Parser", () => { expect(result).toEqual({ "special@key": "value", "with spaces": "test", - "with.dot": "works", + "with\\.dot": "works", }); }); }); diff --git a/packages/cli/src/parsers/core/flatten.test.ts b/packages/cli/src/parsers/core/flatten.test.ts new file mode 100644 index 00000000..9e83b502 --- /dev/null +++ b/packages/cli/src/parsers/core/flatten.test.ts @@ -0,0 +1,144 @@ +import { describe, expect, it } from "bun:test"; +import { escapeKey, flatten, unescapeKey, unflatten } from "./flatten.js"; + +describe("escapeKey", () => { + it("should escape dots in keys", () => { + expect(escapeKey("key")).toBe("key"); + expect(escapeKey("key.with.dots")).toBe("key\\.with\\.dots"); + expect(escapeKey("allow-multiple")).toBe("allow-multiple"); + }); +}); + +describe("unescapeKey", () => { + it("should unescape dots in keys", () => { + expect(unescapeKey("key")).toBe("key"); + expect(unescapeKey("key\\.with\\.dots")).toBe("key.with.dots"); + expect(unescapeKey("allow-multiple")).toBe("allow-multiple"); + }); +}); + +describe("flatten", () => { + it("should flatten a nested object with string values", () => { + const input = { + a: { + b: "c", + d: "e", + }, + f: "g", + }; + const expected = { + "a.b": "c", + "a.d": "e", + f: "g", + }; + expect(flatten(input)).toEqual(expected); + }); + + it("should flatten a nested object with mixed keys (including dots)", () => { + const input = { + chat: { + poll: { + "allow-multiple": "Allow Multiple Answers?", + "create-poll": "Poll", + "create-poll.title": "Create Poll", + "create-poll.question": "Question", + }, + }, + }; + const expected = { + "chat.poll.allow-multiple": "Allow Multiple Answers?", + "chat.poll.create-poll": "Poll", + "chat.poll.create-poll\\.title": "Create Poll", + "chat.poll.create-poll\\.question": "Question", + }; + expect(flatten(input)).toEqual(expected); + }); + + it("should handle arrays", () => { + const input = { + a: ["b", "c"], + d: [{ e: "f" }, { g: "h" }], + }; + const expected = { + "a[0]": "b", + "a[1]": "c", + "d[0].e": "f", + "d[1].g": "h", + }; + expect(flatten(input)).toEqual(expected); + }); +}); + +describe("unflatten", () => { + it("should unflatten a flattened object", () => { + const input = { + "a.b": "c", + "a.d": "e", + f: "g", + }; + const expected = { + a: { + b: "c", + d: "e", + }, + f: "g", + }; + expect(unflatten(input)).toEqual(expected); + }); + + it("should unflatten a flattened object with mixed keys (including dots)", () => { + const input = { + "chat.poll.allow-multiple": "Allow Multiple Answers?", + "chat.poll.create-poll": "Poll", + "chat.poll.create-poll\\.title": "Create Poll", + "chat.poll.create-poll\\.question": "Question", + }; + const expected = { + chat: { + poll: { + "allow-multiple": "Allow Multiple Answers?", + "create-poll": "Poll", + "create-poll.title": "Create Poll", + "create-poll.question": "Question", + }, + }, + }; + expect(unflatten(input)).toEqual(expected); + }); + + it("should handle arrays", () => { + const input = { + "a[0]": "b", + "a[1]": "c", + "d[0].e": "f", + "d[1].g": "h", + }; + const expected = { + a: ["b", "c"], + d: [{ e: "f" }, { g: "h" }], + }; + expect(unflatten(input)).toEqual(expected); + }); + + it("should round-trip correctly with mixed keys", () => { + const original = { + test: { + "*": "Allow all file types", + "image/*, .jpg, .jpeg, .png, .gif, .svg, .webp": "Images", + ".mp4, .mov, .avi, .mkv, .webm, .mpeg": "Videos", + ".mp3, .wav, .ogg, .flac, .aac, .wma, .m4a, .midi, .alac": "Audio", + ".pdf": ".pdf", + ".docx": ".docx", + ".csv": ".csv", + ".pptx": ".pptx", + ".xlsx": ".xlsx", + ".zip": ".zip", + }, + }; + + const flattened = flatten(original); + const roundTripped = unflatten(flattened); + + expect(roundTripped).toEqual(original); + }); +}); diff --git a/packages/cli/src/parsers/core/flatten.ts b/packages/cli/src/parsers/core/flatten.ts index e2787af0..2d714165 100644 --- a/packages/cli/src/parsers/core/flatten.ts +++ b/packages/cli/src/parsers/core/flatten.ts @@ -1,3 +1,21 @@ +/** + * Escapes dots in keys to prevent collisions with dots used as path separators + * @param key The key to escape + * @returns The escaped key + */ +export function escapeKey(key: string): string { + return key.replace(/\./g, "\\."); +} + +/** + * Unescapes dots in keys + * @param key The key to unescape + * @returns The unescaped key + */ +export function unescapeKey(key: string): string { + return key.replace(/\\./g, "."); +} + /** * Flattens a nested object structure into dot-notation keys * @param obj The object to flatten @@ -12,7 +30,8 @@ export function flatten( const result: Record = {}; for (const [key, value] of Object.entries(obj)) { - const newKey = prefix ? `${prefix}.${key}` : key; + const escapedKey = escapeKey(key); + const newKey = prefix ? `${prefix}.${escapedKey}` : escapedKey; if (Array.isArray(value)) { value.forEach((item, index) => { @@ -53,36 +72,93 @@ export function unflatten( ): Record { const result: Record = {}; + // First, collect all array paths and indices + const arrays: Record> = {}; + + // Process all keys to build the object structure and identify arrays for (const [key, value] of Object.entries(obj)) { - const parts = key.split(/\.|\[|\]/).filter(Boolean); + // Split by dots that are not escaped + const keyParts = key.split(/(? unescapeKey(part)); + + // Process the parts let current = result; + let path = ""; - for (let i = 0; i < parts.length - 1; i++) { - const part = parts[i]; - const nextPart = parts[i + 1]; - const isNextPartArrayIndex = /^\d+$/.test(nextPart); + for (let i = 0; i < keyParts.length; i++) { + const part = keyParts[i]; + const arrayMatch = part.match(/^(.*?)\[(\d+)\]$/); - if (!(part in current)) { - current[part] = isNextPartArrayIndex ? [] : {}; - } + if (arrayMatch) { + // We found an array notation like "items[0]" + const [_, name, indexStr] = arrayMatch; + const index = Number.parseInt(indexStr, 10); + + // Build path up to this point + const arrayPath = path ? (name ? `${path}.${name}` : path) : name; + + if (i === keyParts.length - 1) { + // This is the last part, set the value directly + if (!arrays[arrayPath]) { + arrays[arrayPath] = {}; + } + arrays[arrayPath][index] = value; + } else { + // More parts to process after this array item + if (!arrays[arrayPath]) { + arrays[arrayPath] = {}; + } + if (!arrays[arrayPath][index]) { + arrays[arrayPath][index] = {}; + } - if (Array.isArray(current[part]) && isNextPartArrayIndex) { - const index = Number.parseInt(nextPart, 10); - if (!(index in (current[part] as unknown[]))) { - (current[part] as unknown[])[index] = {}; + // Continue processing with this array item as the new current object + current = arrays[arrayPath][index] as Record; + path = ""; // Reset path for the nested object } - current = (current[part] as unknown[])[index] as Record< - string, - unknown - >; - i++; // Skip the array index part } else { - current = current[part] as Record; + // Regular property (not array) + path = path ? `${path}.${part}` : part; + + if (i === keyParts.length - 1) { + // Last part, set the value + current[part] = value; + } else { + // Not the last part, create nested object if needed + if (!(part in current)) { + current[part] = {}; + } + // Move current to the nested object + current = current[part] as Record; + } + } + } + } + + // Convert array objects to actual arrays and insert them into the result + for (const [path, indices] of Object.entries(arrays)) { + const parts = path.split("."); + let current = result; + + // Navigate to the parent object + for (let i = 0; i < parts.length - 1; i++) { + if (!(parts[i] in current)) { + current[parts[i]] = {}; } + current = current[parts[i]] as Record; } + // Convert to array const lastPart = parts[parts.length - 1]; - current[lastPart] = value; + const maxIndex = Math.max(...Object.keys(indices).map(Number)); + const array: unknown[] = new Array(maxIndex + 1); + + // Fill the array with values + for (const [indexStr, value] of Object.entries(indices)) { + array[Number(indexStr)] = value; + } + + // Set the array in the result object + current[lastPart] = array; } return result; From 21a4de06881b1d033432e2d7984c74469c31196d Mon Sep 17 00:00:00 2001 From: Pontus Abrahamsson Date: Sat, 22 Mar 2025 13:21:07 +0100 Subject: [PATCH 2/3] Handle complex keys --- examples/{test2 => complex-keys}/languine.json | 0 examples/{test2 => complex-keys}/languine.lock | 0 examples/{test2 => complex-keys}/messages/de.json | 0 examples/{test2 => complex-keys}/messages/en.json | 0 examples/next-intl/messages/de.json | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) rename examples/{test2 => complex-keys}/languine.json (100%) rename examples/{test2 => complex-keys}/languine.lock (100%) rename examples/{test2 => complex-keys}/messages/de.json (100%) rename examples/{test2 => complex-keys}/messages/en.json (100%) diff --git a/examples/test2/languine.json b/examples/complex-keys/languine.json similarity index 100% rename from examples/test2/languine.json rename to examples/complex-keys/languine.json diff --git a/examples/test2/languine.lock b/examples/complex-keys/languine.lock similarity index 100% rename from examples/test2/languine.lock rename to examples/complex-keys/languine.lock diff --git a/examples/test2/messages/de.json b/examples/complex-keys/messages/de.json similarity index 100% rename from examples/test2/messages/de.json rename to examples/complex-keys/messages/de.json diff --git a/examples/test2/messages/en.json b/examples/complex-keys/messages/en.json similarity index 100% rename from examples/test2/messages/en.json rename to examples/complex-keys/messages/en.json diff --git a/examples/next-intl/messages/de.json b/examples/next-intl/messages/de.json index 3a816f89..29b32323 100644 --- a/examples/next-intl/messages/de.json +++ b/examples/next-intl/messages/de.json @@ -13,7 +13,7 @@ "items": [ { "title": "Beste Entscheidung", - "description": "Unser Ziel war es, den Handel für KMUs zu optimieren, um ihn einfacher und schneller als je zuvor zu gestalten, und das haben wir gemeinsam erreicht.", + "description": "Unser Ziel war es, den Handel für KMU zu optimieren, um ihn einfacher und schneller als je zuvor zu gestalten, und das haben wir gemeinsam erreicht.", "author": { "name": "Hayden Bleasel", "image": "https://github.com/haydenbleasel.png" From 67821c20b9d4a2d60bd034bd9d0600f2f41ab026 Mon Sep 17 00:00:00 2001 From: Pontus Abrahamsson Date: Sat, 22 Mar 2025 13:25:45 +0100 Subject: [PATCH 3/3] Handle complex keys --- examples/next-international/locales/de.ts | 2 +- examples/next-international/locales/es.ts | 4 ++-- examples/next-international/locales/fi.ts | 2 +- examples/next-international/locales/fr.ts | 2 +- examples/next-international/locales/it.ts | 13 ++++++------- examples/next-international/locales/nl.ts | 6 +++--- examples/next-international/locales/no.ts | 2 +- examples/next-international/locales/pl.ts | 13 +++++++++---- examples/next-international/locales/pt.ts | 6 +++--- examples/next-international/locales/sv.ts | 2 +- examples/php/lang/fr/common.php | 18 ------------------ examples/php/lang/sv/common.php | 18 ++++++++++++++++++ examples/php/languine.json | 2 +- examples/react-i18next/languine.json | 3 +-- examples/react-i18next/languine.lock | 9 +++++++++ examples/react-i18next/locales/es.json | 14 ++++++++++++++ 16 files changed, 71 insertions(+), 45 deletions(-) delete mode 100644 examples/php/lang/fr/common.php create mode 100644 examples/php/lang/sv/common.php create mode 100644 examples/react-i18next/languine.lock create mode 100644 examples/react-i18next/locales/es.json diff --git a/examples/next-international/locales/de.ts b/examples/next-international/locales/de.ts index 83527745..51691cc2 100644 --- a/examples/next-international/locales/de.ts +++ b/examples/next-international/locales/de.ts @@ -4,7 +4,7 @@ export default { hero: { announcement: "Lesen Sie unseren neuesten Artikel", title: "Transformieren Sie Ihre Geschäftsabläufe noch heute", - description: "In der heutigen schnelllebigen Welt verdient Ihr Unternehmen besser als veraltete Handelssysteme. Unsere innovative Plattform optimiert die Abläufe, reduziert die Komplexität und hilft kleinen Unternehmen, in der modernen Wirtschaft erfolgreich zu sein." + description: "In der heutigen schnelllebigen Welt verdient Ihr Unternehmen mehr als veraltete Handelssysteme. Unsere innovative Plattform optimiert die Abläufe, reduziert die Komplexität und hilft kleinen Unternehmen, in der modernen Wirtschaft erfolgreich zu sein." } } } diff --git a/examples/next-international/locales/es.ts b/examples/next-international/locales/es.ts index 62ce8a51..4e4ad15d 100644 --- a/examples/next-international/locales/es.ts +++ b/examples/next-international/locales/es.ts @@ -2,8 +2,8 @@ export default { web: { home: { hero: { - announcement: "¡Hola {name}! Tienes {age} años", - title: "Transforma las Operaciones de Tu Negocio Hoy", + announcement: "Lee nuestro último artículo", + title: "Transforma las operaciones de tu negocio hoy", description: "En el mundo acelerado de hoy, tu negocio merece algo mejor que sistemas de comercio obsoletos. Nuestra plataforma innovadora optimiza las operaciones, reduce la complejidad y ayuda a las pequeñas empresas a prosperar en la economía moderna." } } diff --git a/examples/next-international/locales/fi.ts b/examples/next-international/locales/fi.ts index e9c88459..0df1ccaf 100644 --- a/examples/next-international/locales/fi.ts +++ b/examples/next-international/locales/fi.ts @@ -2,7 +2,7 @@ export default { web: { home: { hero: { - announcement: "Hei {name}! Olet {age} vuotta vanha", + announcement: "Lue viimeisin artikkelimme", title: "Muuta liiketoimintasi toimintoja tänään", description: "Nykyajan nopeassa maailmassa liiketoimintasi ansaitsee parempaa kuin vanhentuneet kaupankäyntijärjestelmät. Innovatiivinen alustamme virtaviivaistaa toimintoja, vähentää monimutkaisuutta ja auttaa pieniä yrityksiä menestymään nykyaikaisessa taloudessa." } diff --git a/examples/next-international/locales/fr.ts b/examples/next-international/locales/fr.ts index a7f92aa9..235bacb6 100644 --- a/examples/next-international/locales/fr.ts +++ b/examples/next-international/locales/fr.ts @@ -2,7 +2,7 @@ export default { web: { home: { hero: { - announcement: "Bonjour {name} ! Vous avez {age} ans", + announcement: "Lisez notre dernier article", title: "Transformez vos opérations commerciales dès aujourd'hui", description: "Dans le monde rapide d'aujourd'hui, votre entreprise mérite mieux que des systèmes de trading obsolètes. Notre plateforme innovante simplifie les opérations, réduit la complexité et aide les petites entreprises à prospérer dans l'économie moderne." } diff --git a/examples/next-international/locales/it.ts b/examples/next-international/locales/it.ts index 541bf92d..ba0746c7 100644 --- a/examples/next-international/locales/it.ts +++ b/examples/next-international/locales/it.ts @@ -2,11 +2,10 @@ export default { web: { home: { hero: { - announcement: "Ciao {name}! Hai {age} anni", - title: "Trasforma le operazioni della tua azienda oggi", - description: - "Nel mondo frenetico di oggi, la tua azienda merita di meglio rispetto ai sistemi di trading obsoleti. La nostra piattaforma innovativa semplifica le operazioni, riduce la complessità e aiuta le piccole imprese a prosperare nell'economia moderna.", - }, - }, - }, + announcement: "Leggi il nostro ultimo articolo", + title: "Trasforma le tue operazioni aziendali oggi", + description: "Nel mondo frenetico di oggi, la tua azienda merita di meglio rispetto ai sistemi di trading obsoleti. La nostra piattaforma innovativa semplifica le operazioni, riduce la complessità e aiuta le piccole imprese a prosperare nell'economia moderna." + } + } + } } as const; diff --git a/examples/next-international/locales/nl.ts b/examples/next-international/locales/nl.ts index 5765e966..7dd2edb1 100644 --- a/examples/next-international/locales/nl.ts +++ b/examples/next-international/locales/nl.ts @@ -2,9 +2,9 @@ export default { web: { home: { hero: { - announcement: "Hallo {name}! Je bent {age} jaar oud", - title: "Transformeer Vandaag Je Bedrijfsvoering", - description: "In de snelle wereld van vandaag verdient je bedrijf beter dan verouderde handelssystemen. Ons innovatieve platform stroomlijnt de operaties, vermindert complexiteit en helpt kleine bedrijven te gedijen in de moderne economie." + announcement: "Lees ons nieuwste artikel", + title: "Transformeer uw bedrijfsvoering vandaag nog", + description: "In de snelle wereld van vandaag verdient uw bedrijf beter dan verouderde handelssystemen. Ons innovatieve platform stroomlijnt de operaties, vermindert complexiteit en helpt kleine bedrijven te gedijen in de moderne economie." } } } diff --git a/examples/next-international/locales/no.ts b/examples/next-international/locales/no.ts index 0ed71d57..39789a06 100644 --- a/examples/next-international/locales/no.ts +++ b/examples/next-international/locales/no.ts @@ -2,7 +2,7 @@ export default { web: { home: { hero: { - announcement: "Hei {name}! Du er {age} år gammel", + announcement: "Les vår nyeste artikkel", title: "Transformér dine forretningsoperasjoner i dag", description: "I dagens raske verden fortjener virksomheten din bedre enn utdaterte handelssystemer. Vår innovative plattform strømlinjeformer operasjoner, reduserer kompleksitet og hjelper små bedrifter med å blomstre i den moderne økonomien." } diff --git a/examples/next-international/locales/pl.ts b/examples/next-international/locales/pl.ts index fcf196b8..b237168a 100644 --- a/examples/next-international/locales/pl.ts +++ b/examples/next-international/locales/pl.ts @@ -1,6 +1,11 @@ export default { - "web.home.hero.announcement": "Cześć {name}! Masz {age} lat", - "web.home.hero.title": "222", - "web.home.hero.description": - "W dzisiejszym szybkim świecie, Twoja firma zasługuje na więcej niż przestarzałe systemy handlowe. Nasza innowacyjna platforma usprawnia operacje, redukuje złożoność i pomaga małym firmom rozwijać się w nowoczesnej gospodarce.", + web: { + home: { + hero: { + announcement: "Przeczytaj nasz najnowszy artykuł", + title: "Przekształć swoje operacje biznesowe już dziś", + description: "W dzisiejszym szybkim świecie, Twoja firma zasługuje na więcej niż przestarzałe systemy handlowe. Nasza innowacyjna platforma upraszcza operacje, redukuje złożoność i pomaga małym firmom prosperować w nowoczesnej gospodarce." + } + } + } } as const; diff --git a/examples/next-international/locales/pt.ts b/examples/next-international/locales/pt.ts index 62b663dc..8f1d3277 100644 --- a/examples/next-international/locales/pt.ts +++ b/examples/next-international/locales/pt.ts @@ -2,9 +2,9 @@ export default { web: { home: { hero: { - announcement: "Olá {name}! Você tem {age} anos", - title: "Transforme as Operações do Seu Negócio Hoje", - description: "No mundo acelerado de hoje, seu negócio merece mais do que sistemas de comércio ultrapassados. Nossa plataforma inovadora simplifica as operações, reduz a complexidade e ajuda pequenas empresas a prosperar na economia moderna." + announcement: "Leia nosso artigo mais recente", + title: "Transforme as Operações da Sua Empresa Hoje", + description: "No mundo acelerado de hoje, sua empresa merece mais do que sistemas de comércio ultrapassados. Nossa plataforma inovadora simplifica operações, reduz a complexidade e ajuda pequenas empresas a prosperar na economia moderna." } } } diff --git a/examples/next-international/locales/sv.ts b/examples/next-international/locales/sv.ts index 81cbd95d..947a75ab 100644 --- a/examples/next-international/locales/sv.ts +++ b/examples/next-international/locales/sv.ts @@ -4,7 +4,7 @@ export default { hero: { announcement: "Läs vår senaste artikel", title: "Transformera dina affärsverksamheter idag", - description: "I dagens snabba värld förtjänar ditt företag bättre än föråldrade handelssystem. Vår innovativa plattform effektiviserar verksamheten, minskar komplexiteten och hjälper småföretag att blomstra i den moderna ekonomin." + description: "I dagens snabba värld förtjänar ditt företag bättre än föråldrade handelssystem. Vår innovativa plattform strömlinjeformar verksamheten, minskar komplexiteten och hjälper små företag att blomstra i den moderna ekonomin." } } } diff --git a/examples/php/lang/fr/common.php b/examples/php/lang/fr/common.php deleted file mode 100644 index 18168eee..00000000 --- a/examples/php/lang/fr/common.php +++ /dev/null @@ -1,18 +0,0 @@ - 'Bonjour, le monde !', - 'greeting' => 'Bonjour, :name !', - 'welcome' => 'Bienvenue dans notre application !', - 'error' => 'Une erreur s\'est produite : :message', - 'success' => 'Opération terminée avec succès.', - 'confirmation' => 'Êtes-vous sûr de vouloir :action ?', - 'instructions' => 'Veuillez suivre ces étapes pour :action :', - 'time' => [ - 'today' => 'Aujourd\'hui', - 'yesterday' => 'Hier', - 'tomorrow' => 'Demain' - ] -]; diff --git a/examples/php/lang/sv/common.php b/examples/php/lang/sv/common.php new file mode 100644 index 00000000..e0404c43 --- /dev/null +++ b/examples/php/lang/sv/common.php @@ -0,0 +1,18 @@ + 'Hej, världen!', + 'greeting' => 'Hej, :name!', + 'welcome' => 'Välkommen till vår applikation!', + 'error' => 'Ett fel inträffade: :message', + 'success' => 'Operationen slutfördes framgångsrikt.', + 'confirmation' => 'Är du säker på att du vill :action?', + 'instructions' => 'Vänligen följ dessa steg för att :action:', + 'time' => [ + 'today' => 'Idag', + 'yesterday' => 'Igår', + 'tomorrow' => 'Imorgon' + ] +]; diff --git a/examples/php/languine.json b/examples/php/languine.json index 14066d68..a346d592 100644 --- a/examples/php/languine.json +++ b/examples/php/languine.json @@ -1,7 +1,7 @@ { "locale": { "source": "en", - "targets": ["es", "fr"] + "targets": ["es", "sv"] }, "files": { "php": { diff --git a/examples/react-i18next/languine.json b/examples/react-i18next/languine.json index 8467dc3a..665373a6 100644 --- a/examples/react-i18next/languine.json +++ b/examples/react-i18next/languine.json @@ -1,12 +1,11 @@ { - "projectId": "", "locale": { "source": "en", "targets": ["es"] }, "files": { "json": { - "include": ["src/locales/[locale].json"] + "include": ["locales/[locale].json"] } } } diff --git a/examples/react-i18next/languine.lock b/examples/react-i18next/languine.lock new file mode 100644 index 00000000..565d440c --- /dev/null +++ b/examples/react-i18next/languine.lock @@ -0,0 +1,9 @@ +version: 1 +files: + locales/en.json: + welcome: 91a328d124ccee9f8e7d1f37fd1a6776 + user.greeting: 05cab6b3c28feaccc7d7b5c5158a2eda + user.profile.title: 3d8de6fcc09baf73f7175fe59f278ced + user.profile.edit: dfd87bd34b4599aaaef6d0f2c8b6e3d7 + notifications.messages: 9bedb26e51829d23bd8545dba50e361d + notifications.empty: 7d0cb81666069504046f678bb63aae39 diff --git a/examples/react-i18next/locales/es.json b/examples/react-i18next/locales/es.json new file mode 100644 index 00000000..e22feb28 --- /dev/null +++ b/examples/react-i18next/locales/es.json @@ -0,0 +1,14 @@ +{ + "welcome": "¡Bienvenido a nuestra aplicación!", + "user": { + "greeting": "¡Hola, {{name}}!", + "profile": { + "title": "Tu perfil", + "edit": "Editar perfil" + } + }, + "notifications": { + "messages": "Tienes {{count}} nuevo(s) mensaje(s).", + "empty": "No hay nuevas notificaciones." + } +}