mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 00:01:29 +00:00
Auto stash before merge of "dev" and "origin/dev"
This commit is contained in:
@@ -10,14 +10,22 @@ type LuaToken = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const frontendSourceDirectory = fileURLToPath(new URL('../', import.meta.url))
|
const frontendSourceDirectory = fileURLToPath(new URL('../', import.meta.url))
|
||||||
const englishLocaleSource = readFileSync(
|
const localeDirectory = fileURLToPath(
|
||||||
new URL('../../../sky_phone/config/locales/en.lua', import.meta.url),
|
new URL('../../../sky_phone/config/locales/', import.meta.url),
|
||||||
'utf8',
|
|
||||||
)
|
)
|
||||||
const germanLocaleSource = readFileSync(
|
const localeSources = new Map(
|
||||||
new URL('../../../sky_phone/config/locales/de.lua', import.meta.url),
|
readdirSync(localeDirectory, { withFileTypes: true })
|
||||||
'utf8',
|
.filter((entry) => entry.isFile() && entry.name.endsWith('.lua'))
|
||||||
|
.map((entry) => [
|
||||||
|
entry.name.replace(/\.lua$/, ''),
|
||||||
|
readFileSync(join(localeDirectory, entry.name), 'utf8'),
|
||||||
|
]),
|
||||||
)
|
)
|
||||||
|
const englishLocaleSource = localeSources.get('en')
|
||||||
|
const germanLocaleSource = localeSources.get('de')
|
||||||
|
if (!englishLocaleSource || !germanLocaleSource) {
|
||||||
|
throw new Error('The bundled English and German phone locales are required.')
|
||||||
|
}
|
||||||
const phoneStoreSource = readFileSync(
|
const phoneStoreSource = readFileSync(
|
||||||
new URL('./phone.ts', import.meta.url),
|
new URL('./phone.ts', import.meta.url),
|
||||||
'utf8',
|
'utf8',
|
||||||
@@ -148,6 +156,10 @@ function collectPlaceholders(value: string): string[] {
|
|||||||
return [...new Set(value.match(/\{[A-Za-z0-9_]+\}/g) ?? [])].sort()
|
return [...new Set(value.match(/\{[A-Za-z0-9_]+\}/g) ?? [])].sort()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collectNumberTokens(value: string): string[] {
|
||||||
|
return value.match(/\d+/g) ?? []
|
||||||
|
}
|
||||||
|
|
||||||
function collectDefaultLocalePaths(source: string): Set<string> {
|
function collectDefaultLocalePaths(source: string): Set<string> {
|
||||||
const ast = ts.createSourceFile(
|
const ast = ts.createSourceFile(
|
||||||
'phone.ts',
|
'phone.ts',
|
||||||
@@ -223,10 +235,26 @@ function collectFrontendFiles(directory: string): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('phone locale contract', () => {
|
describe('phone locale contract', () => {
|
||||||
const englishValues = collectLuaLocaleValues(englishLocaleSource)
|
const localeValues = new Map(
|
||||||
const germanValues = collectLuaLocaleValues(germanLocaleSource)
|
[...localeSources].map(([locale, source]) => [
|
||||||
|
locale,
|
||||||
|
collectLuaLocaleValues(source),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
const englishValues = localeValues.get('en')!
|
||||||
|
const germanValues = localeValues.get('de')!
|
||||||
|
const translatedLocaleValues = [...localeValues].filter(
|
||||||
|
([locale]) => locale !== 'en',
|
||||||
|
)
|
||||||
const englishPaths = new Set(englishValues.keys())
|
const englishPaths = new Set(englishValues.keys())
|
||||||
|
|
||||||
|
it.each([...localeSources])(
|
||||||
|
'registers the %s locale under its file name',
|
||||||
|
(locale, source) => {
|
||||||
|
expect(source.match(/^Locales\["([^"]+)"\]/)?.[1]).toBe(locale)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
it('keeps every bundled frontend fallback in en.lua', () => {
|
it('keeps every bundled frontend fallback in en.lua', () => {
|
||||||
const missing = [...collectDefaultLocalePaths(phoneStoreSource)].filter(
|
const missing = [...collectDefaultLocalePaths(phoneStoreSource)].filter(
|
||||||
(path) => !englishPaths.has(`Nui.${path}`),
|
(path) => !englishPaths.has(`Nui.${path}`),
|
||||||
@@ -271,26 +299,52 @@ describe('phone locale contract', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('keeps German structurally aligned with English', () => {
|
it.each(translatedLocaleValues)(
|
||||||
const germanPaths = new Set(germanValues.keys())
|
'keeps %s structurally aligned with English',
|
||||||
|
(_locale, values) => {
|
||||||
|
expect([...values.keys()].sort()).toEqual([...englishPaths].sort())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
expect([...germanPaths].sort()).toEqual([...englishPaths].sort())
|
it.each(translatedLocaleValues)(
|
||||||
})
|
'keeps %s interpolation placeholders aligned with English',
|
||||||
|
(_locale, values) => {
|
||||||
|
const mismatches = [...englishValues].flatMap(
|
||||||
|
([path, englishValue]) => {
|
||||||
|
const translatedValue = values.get(path)
|
||||||
|
return translatedValue !== undefined &&
|
||||||
|
JSON.stringify(collectPlaceholders(translatedValue)) !==
|
||||||
|
JSON.stringify(collectPlaceholders(englishValue))
|
||||||
|
? [
|
||||||
|
`${path}: ${collectPlaceholders(englishValue).join(', ')} != ${collectPlaceholders(translatedValue).join(', ')}`,
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
it('keeps German interpolation placeholders aligned with English', () => {
|
expect(mismatches).toEqual([])
|
||||||
const mismatches = [...englishValues].flatMap(([path, englishValue]) => {
|
},
|
||||||
const germanValue = germanValues.get(path)
|
)
|
||||||
return germanValue !== undefined &&
|
|
||||||
JSON.stringify(collectPlaceholders(germanValue)) !==
|
|
||||||
JSON.stringify(collectPlaceholders(englishValue))
|
|
||||||
? [
|
|
||||||
`${path}: ${collectPlaceholders(englishValue).join(', ')} != ${collectPlaceholders(germanValue).join(', ')}`,
|
|
||||||
]
|
|
||||||
: []
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(mismatches).toEqual([])
|
it.each(translatedLocaleValues)(
|
||||||
})
|
'keeps %s numeric source values intact',
|
||||||
|
(_locale, values) => {
|
||||||
|
const mismatches = [...englishValues].flatMap(([path, englishValue]) => {
|
||||||
|
const expected = collectNumberTokens(englishValue)
|
||||||
|
if (!expected.length) return []
|
||||||
|
|
||||||
|
const remaining = collectNumberTokens(values.get(path) ?? '')
|
||||||
|
for (const token of expected) {
|
||||||
|
const index = remaining.indexOf(token)
|
||||||
|
if (index >= 0) remaining.splice(index, 1)
|
||||||
|
else return [`${path}: missing numeric token ${token}`]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(mismatches).toEqual([])
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
it('keeps standard app names German and custom game names unchanged', () => {
|
it('keeps standard app names German and custom game names unchanged', () => {
|
||||||
const standardAppNames = {
|
const standardAppNames = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { readFileSync } from 'node:fs'
|
import { readdirSync, readFileSync } from 'node:fs'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
@@ -41,24 +41,21 @@ describe('App Store preview catalog', () => {
|
|||||||
expect(visualSignatures.size).toBe(storeAppIds.length)
|
expect(visualSignatures.size).toBe(storeAppIds.length)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('localizes every specialized preview in the fallback, English and German locales', () => {
|
it('localizes every specialized preview in the fallback and every configured locale', () => {
|
||||||
|
const localeDirectory = fileURLToPath(
|
||||||
|
new URL('../../../sky_phone/config/locales/', import.meta.url),
|
||||||
|
)
|
||||||
const localeSources = [
|
const localeSources = [
|
||||||
readFileSync(
|
readFileSync(
|
||||||
fileURLToPath(new URL('../stores/phone.ts', import.meta.url)),
|
fileURLToPath(new URL('../stores/phone.ts', import.meta.url)),
|
||||||
'utf8',
|
'utf8',
|
||||||
),
|
),
|
||||||
readFileSync(
|
...readdirSync(localeDirectory)
|
||||||
fileURLToPath(
|
.filter((fileName) => fileName.endsWith('.lua'))
|
||||||
new URL('../../../sky_phone/config/locales/en.lua', import.meta.url),
|
.sort()
|
||||||
|
.map((fileName) =>
|
||||||
|
readFileSync(`${localeDirectory}/${fileName}`, 'utf8'),
|
||||||
),
|
),
|
||||||
'utf8',
|
|
||||||
),
|
|
||||||
readFileSync(
|
|
||||||
fileURLToPath(
|
|
||||||
new URL('../../../sky_phone/config/locales/de.lua', import.meta.url),
|
|
||||||
),
|
|
||||||
'utf8',
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const appId of PREVIEWABLE_BUILTIN_APP_IDS) {
|
for (const appId of PREVIEWABLE_BUILTIN_APP_IDS) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,7 @@ client_scripts {
|
|||||||
'config/config.lua',
|
'config/config.lua',
|
||||||
'config/locales/en.lua',
|
'config/locales/en.lua',
|
||||||
'config/locales/de.lua',
|
'config/locales/de.lua',
|
||||||
|
'config/locales/es.lua',
|
||||||
'source/bridge/client/callbacks.lua',
|
'source/bridge/client/callbacks.lua',
|
||||||
'source/client/phone_configurator.lua',
|
'source/client/phone_configurator.lua',
|
||||||
'source/bridge/client/framework.lua',
|
'source/bridge/client/framework.lua',
|
||||||
@@ -77,6 +78,7 @@ server_scripts {
|
|||||||
'config/media.lua',
|
'config/media.lua',
|
||||||
'config/locales/en.lua',
|
'config/locales/en.lua',
|
||||||
'config/locales/de.lua',
|
'config/locales/de.lua',
|
||||||
|
'config/locales/es.lua',
|
||||||
'source/server/nui_build_check.lua',
|
'source/server/nui_build_check.lua',
|
||||||
'source/server/update_check.lua',
|
'source/server/update_check.lua',
|
||||||
'source/bridge/server/database.lua',
|
'source/bridge/server/database.lua',
|
||||||
|
|||||||
Reference in New Issue
Block a user