every key in a translation array, dotted. */ function flattenKeys(array $lines, string $prefix = ''): array { $keys = []; foreach ($lines as $key => $value) { $path = $prefix === '' ? (string) $key : $prefix.'.'.$key; // Only nested arrays recurse. A list of strings — a bullet list in the // interface — is one key whose shape is the translator's business. if (is_array($value) && ! array_is_list($value)) { $keys = [...$keys, ...flattenKeys($value, $path)]; continue; } $keys[] = $path; } sort($keys); return $keys; } it('has the same keys in German and English', function () { $files = array_map( fn (string $path) => basename($path), glob(lang_path('de/*.php')) ?: [], ); expect($files)->not->toBeEmpty(); foreach ($files as $file) { $de = require lang_path('de/'.$file); $enPath = lang_path('en/'.$file); expect(file_exists($enPath))->toBeTrue("lang/en/{$file} is missing"); $en = require $enPath; $missingInEn = array_diff(flattenKeys($de), flattenKeys($en)); $missingInDe = array_diff(flattenKeys($en), flattenKeys($de)); expect($missingInEn)->toBe([], "missing in lang/en/{$file}: ".implode(', ', $missingInEn)); expect($missingInDe)->toBe([], "missing in lang/de/{$file}: ".implode(', ', $missingInDe)); } });