translatedLanguages(true); } /** * @param string|null $languageCode * @param bool|null $fallback * @return bool */ public function hasTranslation(string $languageCode = null, bool $fallback = null): bool { $code = $this->findTranslation($languageCode, $fallback); return null !== $code; } /** * @param string|null $languageCode * @param bool|null $fallback * @return FlexObjectInterface|PageInterface|null */ public function getTranslation(string $languageCode = null, bool $fallback = null) { if ($this->root()) { return $this; } $code = $this->findTranslation($languageCode, $fallback); if (null === $code) { $object = null; } elseif ('' === $code) { $object = $this->getLanguage() ? $this->getFlexDirectory()->getObject($this->getMasterKey(), 'storage_key') : $this; } else { $meta = $this->getMetaData(); $meta['template'] = $this->getLanguageTemplates()[$code] ?? $meta['template']; $key = $this->getStorageKey() . '|' . $meta['template'] . '.' . $code; $meta['storage_key'] = $key; $meta['lang'] = $code; $object = $this->getFlexDirectory()->loadObjects([$key => $meta])[$key] ?? null; } return $object; } /** * @param bool $includeDefault If set to true, return separate entries for '' and 'en' (default) language. * @return array */ public function getAllLanguages(bool $includeDefault = false): array { $grav = Grav::instance(); /** @var Language $language */ $language = $grav['language']; $languages = $language->getLanguages(); if (!$languages) { return []; } $translated = $this->getLanguageTemplates(); if ($includeDefault) { $languages[] = ''; } elseif (isset($translated[''])) { $default = $language->getDefault(); if (is_bool($default)) { $default = ''; } $translated[$default] = $translated['']; unset($translated['']); } $languages = array_fill_keys($languages, false); $translated = array_fill_keys(array_keys($translated), true); return array_replace($languages, $translated); } /** * Returns all translated languages. * * @param bool $includeDefault If set to true, return separate entries for '' and 'en' (default) language. * @return array */ public function getLanguages(bool $includeDefault = false): array { $languages = $this->getLanguageTemplates(); if (!$includeDefault && isset($languages[''])) { $grav = Grav::instance(); /** @var Language $language */ $language = $grav['language']; $default = $language->getDefault(); if (is_bool($default)) { $default = ''; } $languages[$default] = $languages['']; unset($languages['']); } return array_keys($languages); } /** * @return string */ public function getLanguage(): string { return $this->language() ?? ''; } /** * @param string|null $languageCode * @param bool|null $fallback * @return string|null */ public function findTranslation(string $languageCode = null, bool $fallback = null): ?string { $translated = $this->getLanguageTemplates(); // If there's no translations (including default), we have an empty folder. if (!$translated) { return ''; } // FIXME: only published is not implemented... $languages = $this->getFallbackLanguages($languageCode, $fallback); $language = null; foreach ($languages as $code) { if (isset($translated[$code])) { $language = $code; break; } } return $language; } /** * Return an array with the routes of other translated languages * * @param bool $onlyPublished only return published translations * @return array the page translated languages */ public function translatedLanguages($onlyPublished = false): array { // FIXME: only published is not implemented... $translated = $this->getLanguageTemplates(); if (!$translated) { return $translated; } $grav = Grav::instance(); /** @var Language $language */ $language = $grav['language']; $languages = $language->getLanguages(); $languages[] = ''; $translated = array_intersect_key($translated, array_flip($languages)); $list = array_fill_keys($languages, null); foreach ($translated as $languageCode => $languageFile) { $path = ($languageCode ? '/' : '') . $languageCode; $list[$languageCode] = "{$path}/{$this->getKey()}"; } return array_filter($list); } /** * Return an array listing untranslated languages available * * @param bool $includeUnpublished also list unpublished translations * @return array the page untranslated languages */ public function untranslatedLanguages($includeUnpublished = false): array { $grav = Grav::instance(); /** @var Language $language */ $language = $grav['language']; $languages = $language->getLanguages(); $translated = array_keys($this->translatedLanguages(!$includeUnpublished)); return array_values(array_diff($languages, $translated)); } /** * Get page language * * @param string|null $var * @return string|null */ public function language($var = null): ?string { return $this->loadHeaderProperty( 'lang', $var, function ($value) { $value = $value ?? $this->getMetaData()['lang'] ?? ''; return trim($value) ?: null; } ); } /** * @return array */ protected function getLanguageTemplates(): array { if (null === $this->_languages) { $template = $this->getProperty('template'); $meta = $this->getMetaData(); $translations = $meta['markdown'] ?? []; $list = []; foreach ($translations as $code => $search) { if (isset($search[$template])) { // Use main template if possible. $list[$code] = $template; } elseif (!empty($search)) { // Fall back to first matching template. $list[$code] = key($search); } } $this->_languages = $list; } return $this->_languages; } /** * @param string|null $languageCode * @param bool|null $fallback * @return array */ protected function getFallbackLanguages(string $languageCode = null, bool $fallback = null): array { $fallback = $fallback ?? true; if (!$fallback && null !== $languageCode) { return [$languageCode]; } $grav = Grav::instance(); /** @var Language $language */ $language = $grav['language']; $languageCode = $languageCode ?? ($language->getLanguage() ?: ''); if ($languageCode === '' && $fallback) { return $language->getFallbackLanguages(null, true); } return $fallback ? $language->getFallbackLanguages($languageCode, true) : [$languageCode]; } }