PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.18
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.18
1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 All 163 releases
← All changes | includes/i18n.php +124 -6 1.10.41.10.18 View file →
@@ -79,11 +79,22 @@
79 79 * @param string|null $version Optional version override.
80 80 * @param string|null $languages_path Optional languages path override.
81 81 */
82 82 public function __construct( ?string $text_domain = null, ?string $version = null, ?string $languages_path = null ) {
83 - $this->text_domain = $text_domain ?? 'woocommerce-pos';
84 - $this->version = $version ?? TRANSLATION_VERSION;
85 - $this->transient_key = 'wcpos_i18n_' . $this->text_domain;
83 + $this->text_domain = $text_domain ?? 'woocommerce-pos';
84 + $this->version = $version ?? TRANSLATION_VERSION;
85 + $this->transient_key = 'wcpos_i18n_' . $this->text_domain;
86 +
87 + if ( ! $this->maintain() ) {
88 + // A shopper's page only needs the file already on disk. Resolving the
89 + // active path, checking versions, downloading and repairing are
90 + // maintenance work (see is_maintenance_request()) — and each of them
91 + // cost a query on EVERY storefront request without an object cache.
92 + $this->languages_path = $languages_path ?? WP_LANG_DIR . '/plugins/';
93 + $this->load_existing_translation( null === $languages_path );
94 + return;
95 + }
96 +
86 97 $this->languages_path = $languages_path ?? $this->resolve_languages_path();
87 98
88 99 $this->load_translations();
89 100 }
@@ -88,8 +99,95 @@
88 99 $this->load_translations();
89 100 }
90 101
91 102 /**
103 + * Whether this request keeps the translation files fresh, or only reads them.
104 + *
105 + * Admin, POS, REST (the Store API included), cron and WP-CLI requests
106 + * maintain: they check the version markers and download. A plain storefront
107 + * request loads whatever file exists and touches nothing else.
108 + *
109 + * Detection runs on `init`, before `REST_REQUEST` is defined and before the
110 + * rewrite rules populate the POS query vars, so REST and the browser-loaded
111 + * POS routes (`/<slug>/`, `/wcpos-auth/`, `/wcpos-checkout/…`) are matched
112 + * from the request path. `wc-ajax` (add to cart, cart fragments, the classic
113 + * checkout) is shopper traffic: WooCommerce marks it DOING_AJAX, which is
114 + * why `wp_doing_ajax()` is deliberately not consulted — admin-ajax.php is
115 + * already covered by `is_admin()`.
116 + *
117 + * @return bool
118 + */
119 + public static function is_maintenance_request(): bool {
120 + // The lane classifier owns the detection (it moved there when the same
121 + // question started deciding which services a request constructs).
122 + return ! Services\Request_Lane::is_storefront();
123 + }
124 +
125 + /**
126 + * The maintenance decision, filterable so tests and hosts can force either path.
127 + *
128 + * @return bool
129 + */
130 + protected function maintain(): bool {
131 + /**
132 + * Filters whether this request maintains the translation files (version
133 + * checks and downloads) or only loads an existing one.
134 + *
135 + * @param bool $maintain Default from {@see i18n::is_maintenance_request()}.
136 + */
137 + return (bool) apply_filters( 'woocommerce_pos_i18n_maintain', self::is_maintenance_request() );
138 + }
139 +
140 + /**
141 + * Load the first existing translation file for the locale candidates, reading nothing else.
142 + *
143 + * @param bool $also_uploads Whether to also look in the uploads fallback directory
144 + * (the default when no explicit path was given).
145 + */
146 + protected function load_existing_translation( bool $also_uploads ): void {
147 + $locale = determine_locale();
148 + if ( 'en_US' === $locale || empty( $locale ) ) {
149 + return;
150 + }
151 + $primary = $this->languages_path;
152 + $fallback = null; // Resolved lazily: wp_upload_dir() is not free.
153 + foreach ( $this->get_locale_candidates( $locale ) as $candidate_locale ) {
154 + $name = $this->text_domain . '-' . $candidate_locale . '.l10n.php';
155 + $primary_file = $primary . $name;
156 + if ( $also_uploads && file_exists( $primary_file ) ) {
157 + $fallback = $fallback ?? $this->get_fallback_languages_path();
158 + $fallback_file = $fallback . $name;
159 + if ( file_exists( $fallback_file ) && 'uploads' === get_transient( $this->transient_key . '_active_path' ) ) {
160 + $this->languages_path = $fallback;
161 + if ( $this->load_translation_file( $candidate_locale, $fallback_file, false ) ) {
162 + return;
163 + }
164 + }
165 + }
166 + if ( file_exists( $primary_file ) ) {
167 + $this->languages_path = $primary;
168 + if ( $this->load_translation_file( $candidate_locale, $primary_file, false ) ) {
169 + return;
170 + }
171 + }
172 + if ( ! $also_uploads ) {
173 + continue;
174 + }
175 + if ( null === $fallback ) {
176 + $fallback = $this->get_fallback_languages_path();
177 + }
178 + if ( file_exists( $fallback . $name ) ) {
179 + // load_translation_file() derives the .mo path WordPress expects from
180 + // languages_path, so it must point at the directory the file is in.
181 + $this->languages_path = $fallback;
182 + if ( $this->load_translation_file( $candidate_locale, $fallback . $name, false ) ) {
183 + return;
184 + }
185 + }
186 + }
187 + }
188 +
189 + /**
92 190 * Load translations directly from plugin's languages folder.
93 191 * Downloads from jsDelivr if not cached or version changed.
94 192 */
95 193 protected function load_translations(): void {
@@ -222,10 +320,30 @@
222 320 * Load an existing translation file.
223 321 *
224 322 * @param string $locale Locale code for the file.
225 323 * @param string $file Path to the l10n PHP file.
324 + * @param bool $repair Whether a flat-format file may be converted and a corrupt one
325 + * deleted (maintenance requests). The storefront path passes
326 + * false and only loads a file that is already valid.
327 + *
328 + * @return bool Whether the translation file was loaded.
226 329 */
227 - protected function load_translation_file( string $locale, string $file ): void {
330 + protected function load_translation_file( string $locale, string $file, bool $repair = true ): bool {
331 + if ( ! $repair ) {
332 + // Storefront path: read-only. A file in the old flat format or one
333 + // that does not parse is simply not loaded; the next maintenance
334 + // request converts or replaces it.
335 + try {
336 + $data = include $file;
337 + } catch ( \ParseError $e ) {
338 + return false;
339 + }
340 + if ( ! \is_array( $data ) || ! isset( $data['messages'] ) ) {
341 + return false;
342 + }
343 + return load_textdomain( $this->text_domain, $this->languages_path . $this->text_domain . '-' . $locale . '.mo' );
344 + }
345 +
228 346 try {
229 347 $this->maybe_convert_file_format( $file );
230 348 } catch ( \ParseError $e ) {
231 349 // File is corrupt — delete it and clear the version transient so it re-downloads.
@@ -232,14 +350,14 @@
232 350 Logger::log( sprintf( 'i18n: Corrupt translation file deleted (%s): %s', $file, $e->getMessage() ) );
233 351 wp_delete_file( $file );
234 352 delete_transient( $this->transient_key . '_' . $locale );
235 353
236 - return;
354 + return false;
237 355 }
238 356
239 357 // Pass the .mo path — WordPress internally looks for .l10n.php first.
240 358 $mofile = $this->languages_path . $this->text_domain . '-' . $locale . '.mo';
241 - load_textdomain( $this->text_domain, $mofile );
359 + return load_textdomain( $this->text_domain, $mofile );
242 360 }
243 361
244 362 /**
245 363 * Build the transient key used for missing-locale caching.