*/ private array $cache = array(); /** * Returns the Stemmer for the current WordPress locale. * * Reads `get_locale()` at call time rather than caching across locale * changes (multilingual sites swap locale per request). * * @since 1.0.0 * * @return Stemmer Stemmer for the current locale, or NullStemmer if unsupported. */ public function for_current_locale(): Stemmer { $locale = function_exists( 'get_locale' ) ? get_locale() : 'en_US'; return $this->for_locale( $locale ); } /** * Returns the Stemmer for a specific locale. * * @since 1.0.0 * * @param string $locale WordPress locale string (e.g. `en_US`, `pt_BR`) * or a bare language code (`en`, `pt`). * * @return Stemmer Stemmer for the locale, or NullStemmer if unsupported. */ public function for_locale( string $locale ): Stemmer { $language = self::extract_language_code( $locale ); if ( isset( $this->cache[ $language ] ) ) { return $this->cache[ $language ]; } $stemmer = $this->build_stemmer( $language ); /** * Filters the Stemmer instance used for a given language code. * * Use this to register a custom Stemmer for a language Postlinkly * does not support out of the box. * * @since 1.0.0 * * @param Stemmer $stemmer Default Stemmer (may be NullStemmer for unsupported langs). * @param string $language Two-letter language code. */ $stemmer = apply_filters( 'postlinkly_stemmer_for_locale', $stemmer, $language ); $this->cache[ $language ] = $stemmer; return $stemmer; } /** * Returns the list of language codes with a real (non-Null) Stemmer. * * Useful for surfacing "supported languages" lists in admin UI. * * @since 1.0.0 * * @return array Sorted list of supported language codes. */ public static function supported_language_codes(): array { return array( 'ca', 'da', 'de', 'en', 'es', 'fi', 'fr', 'it', 'nl', 'no', 'pt', 'ro', 'ru', 'sv', ); } /** * Builds the Stemmer for a language code, without filtering or caching. * * @since 1.0.0 * * @param string $language Two-letter language code. * * @return Stemmer */ private function build_stemmer( string $language ): Stemmer { switch ( $language ) { case 'ca': return SnowballStemmer::catalan(); case 'da': return SnowballStemmer::danish(); case 'de': return SnowballStemmer::german(); case 'en': return SnowballStemmer::english(); case 'es': return SnowballStemmer::spanish(); case 'fi': return SnowballStemmer::finnish(); case 'fr': return SnowballStemmer::french(); case 'it': return SnowballStemmer::italian(); case 'nl': return SnowballStemmer::dutch(); case 'no': return SnowballStemmer::norwegian(); case 'pt': return SnowballStemmer::portuguese(); case 'ro': return SnowballStemmer::romanian(); case 'ru': return SnowballStemmer::russian(); case 'sv': return SnowballStemmer::swedish(); default: return new NullStemmer( $language ); } } /** * Normalises a locale string to its two-letter language code. * * `en_US` → `en`, `pt-BR` → `pt`, `en` → `en`. Returns `und` for empty input. * * @since 1.0.0 * * @param string $locale Locale string. * * @return string Normalised language code. */ private static function extract_language_code( string $locale ): string { $locale = strtolower( trim( $locale ) ); if ( '' === $locale ) { return 'und'; } // Split on either underscore or hyphen. $parts = preg_split( '/[_-]/', $locale, 2 ); if ( false === $parts || array() === $parts ) { return 'und'; } return $parts[0]; } }