= $fluency->renderLanguageLinks( id: 'my-language-links', classes: ['my-class'], divider: '|',
activeClass: 'current'); ?>
+
```
@@ -273,23 +334,23 @@ Fluency is fully documented and formatted for the API Explorer tool in the outst
All methods return [Data Transfer Objects](https://medium.com/@sjoerd_bol/how-to-use-data-transfer-objects-dtos-for-clean-php-code-3bbd47a2b3ab) that are immutable and predictable in structure and features. All values can be accessed via:
-- Object properties
-- Be converted to an array using the `toArray()` method
-- Encoded to json directly using `json_encode()`
+- Object properties `$dtoObject->property`
+- Be converted to an array using the `$dtoObject->toArray()` method
+- Encoded to JSON directly using `json_encode($dtoObject)`
+
+Data Transfer Objects also contain helper methods to access or find data within the DTO
### Translating Content
```php
$translation = $fluency->translate(
- string $sourceLanguage, // Language code used by the translation service
- string $targetLanguage, // Language code used by the translation service
- array|string $content, // String or array of strings to translate
- array|null $options, // Translation Engine specific options
- bool $caching // Override default caching behavior, false disables caching
-);
-
-// This method returns an immutable EngineTranslationData object
-//
+ sourceLanguage: 'EN', // Language code used by the translation service
+ targetLanguage: 'DE', // Language code used by the translation service
+ content: 'How do you do fellow developers?', // String or array of strings to translate
+ options: [], // Translation Engine specific options (optional)
+ caching: true // Default is true, false disables, overrides module config
+); // => EngineTranslationData
+
// $translation->toArray(); Outputs the following:
//
// array(10) {
@@ -309,18 +370,52 @@ $translation = $fluency->translate(
// 'message' => NULL,
// }
+// Get the total number of translated strings returned
count($translation); // 1
```
-### Translatable Languages
-
-This method returns all translatable languages that the current translation service recognizes. The codes for each language are used when calling `$fluency->translate()`;
+Translate multiple strings simultaneously
```php
-$translatableLanguages = $fluency->getTranslatableLanguages();
+$translation = $fluency->translate(sourceLanguage: 'EN', targetLanguage: 'DE', content: [
+ 'Must it be?',
+ 'It must be.',
+]); // => EngineTranslationData
-// This method returns an immutable EngineTranslatableLanguagesData object
+// $translation->toArray(); Outputs the following:
//
+// array(10) {
+// 'sourceLanguage' => 'EN'
+// 'targetLanguage' => 'DE'
+// 'content' => [
+// 'Must it be?',
+// 'It must be.'
+// ],
+// 'translations' => [
+// 'Muss das sein?',
+// 'Es muss sein.'
+// ],
+// 'options' => [],
+// 'fromCache' => true,
+// 'cacheKey' => '18af707954dabf4df8013f3f013bc7382a73fff6050ab0d048f7296214bfb21f',
+// 'retrievedAt' => '2023-09-17T17:16:25-07:00',
+// 'error' => NULL,
+// 'message' => NULL,
+// }
+//
+// Get the total number of translated strings returned
+count($translation); // 2
+```
+
+### Get All Translation Service Languages
+
+This method returns all languages that the current translation service recognizes. This includes languages that aren't configured in Fluency.
+
+The source/target language codes can be used when calling `$fluency->translate()`;
+
+```php
+$translatableLanguages = $fluency->getTranslatableLanguages(); // => EngineTranslatableLanguagesData
+
// $translatableLanguages->toArray(); Outputs the following:
//
//array(5) {
@@ -386,7 +481,7 @@ $translatableLanguages = $fluency->getTranslatableLanguages();
// ["message"]=>
// NULL
// }
-// // ... Removed rest of languages for brevity
+// // ... ommitted for brevity
// }
// ["fromCache"]=>
// bool(true)
@@ -405,20 +500,15 @@ $americanEnglish = $translatableLanguages->byTargetCode('EN-US'); // => EngineLa
// Get the total number of translatable languages
count($translatableLanguages); // => 31
-
```
-### Configured Languages
+### Get All Configured Languages
-Get all languages in ProcessWire that are configured in Fluency. This method packages all available
-language data from both ProcessWire and Fluency, which in certain instances may make it valuable
-to use in place of the ProcessWire `$languages` object.
+Fluency provides robust data for languages configured in Fluency. In certain instances it may be preferrable to using ProcessWire's `$language` object when modifying state is not required.
```php
-$configuredLanguages = $fluency->getConfiguredLanguages();
+$configuredLanguages = $fluency->getConfiguredLanguages(); // => AllConfiguredLanguagesData
-// Returns an immutable AllConfiguredLanguagesData instance
-//
// $translatableLanguages->toArray(); Outputs the following:
//
// array(1) {
@@ -498,33 +588,42 @@ $configuredLanguages = $fluency->getConfiguredLanguages();
// }
// }
-// Additional methods are available as well, each return their own DTO. Examples based on the return
-// value above:
+// Additional methods are available as well, each return their own DTO. Examples based on the return value shown above:
-$configuredLanguages->getDefault(); // => EngineLanguageData for English
-$configuredLanguages->getCurrent(); // => EngineLanguageData for English
-$configuredLanguages->getByProcessWireId(1028) // => EngineLanguageData for French
-$configuredLanguages->getBySourceCode('FR') // => EngineLanguageData for French
-$configuredLanguages->getByTargetCode('EN-US') // => EngineLanguageData for English
-$configuredLanguages->getBySourceName('French') // => EngineLanguageData for French
-$configuredLanguages->getByTargetname('English (American)') // => EngineLanguageData for English
+$configuredLanguages->getDefault(); // => ConfiguredLanguageData for English
+$configuredLanguages->getCurrent(); // => ConfiguredLanguageData for English
+$configuredLanguages->getByProcessWireId(1028) // => ConfiguredLanguageData for French
+$configuredLanguages->getBySourceCode('FR') // => ConfiguredLanguageData for French
+$configuredLanguages->getByTargetCode('EN-US') // => ConfiguredLanguageData for English
+$configuredLanguages->getBySourceName('French') // => ConfiguredLanguageData for French
+$configuredLanguages->getByTargetname('English (American)') // => ConfiguredLanguageData for English
-// This makes it trivial to get the short code for a ProcessWire language
-$configuredLanguages->getByProcessWireId(1028)->targetCode; // => FR
+// Since these return ConfiguredLangaugeData objects, you can access very useful information quickly.
+
+$currentLanguage = $configuredLanguages->getCurrent(); // => ConfiguredLanguageData for English
+$currentLanguage->id; // => 1017
+$currentLanguage->title; // => 'English' (Returned localized if translated in ProcessWire)
+$currentLanguage->default; // => true
+
+// All properties under engineLanguage are provided by the Translation Engine
+// The source/target language codes can be used when calling `$fluency->translate()`;
+
+$currentLanguage->engineLanguage->sourceCode; // => 'EN'
+$currentLanguage->engineLanguage->targetCode; // => 'EN-US'
+$currentLanguage->engineLanguage->sourceName; // => 'English'
+$currentLanguage->engineLanguage->targetName; // => 'English (American)'
// Get the number of ProcessWire langauges configured in Fluency
count($configuredLanguages); // => 6
```
-### API Usage
+### Translation Service API Usage
-Get API usage (must be supported by the translation engine in use):
+Note: not all translation services provide this information
```php
-$usage = $fluency->getTranslationApiUsage();
+$usage = $fluency->getTranslationApiUsage(); // => EngineApiUsageData
-// Returns an immutable EngineApiUsageData instance
-//
// $usage->toArray(); Outputs the following:
//
// array(7) {
@@ -545,15 +644,11 @@ $usage = $fluency->getTranslationApiUsage();
// }
```
-### Translation Engine
-
-Get information about the current translation engine in use
+### Translation Engine Information
```php
-$engineInfo = $fluency->getTranslationEngineInfo();
+$engineInfo = $fluency->getTranslationEngineInfo(); // => EngineInfoData
-// Returns an instance of EngineInfoData
-//
// $engineInfo->toArray() outputs the following:
//
// array(12) {
@@ -584,41 +679,29 @@ $engineInfo = $fluency->getTranslationEngineInfo();
// }
```
-### Caching
-
-Get the current number of cached translations:
+### Managing Cache
```php
+// Get the current number of cached translations:
$fluency->getCachedTranslationCount(); // Int
-```
-
-Clear the translation cache:
-```php
+// Clear the translation cache:
$fluency->clearTranslationCache(); // 0 on success
-```
-Check if the list of languages translatable by the translation engine are cached:
+// Check if the list of languages translatable by the translation engine are currently cached:
+$fluency->translatableLanguagesAreCached(); // Bool
-```php
-$fluency->translatableLanguagesAreCached(); // Boolean
-```
-
-Clear the translatable languages cache:
-
-```php
+// Clear the translatable languages cache:
$fluency->clearTranslatableLanguagesCache(); // 0 on success
```
-### Admin Fluency REST API Endpoints
+### Admin REST API Endpoints
AJAX calls can be made to endpoints in the ProcessWire admin to interact with Fluency. The current user must have the `fluency-translate` permission. Refer to `Fluency.module.php` method docblocks to review accepted methods and responses. Base admin URL will match your ProcessWire installation.
```php
-$endpoints = $fluency->getApiEndpoints();
+$endpoints = $fluency->getApiEndpoints(); // => stdClass
-// Returns a stdClass object
-//
// object(stdClass)#376 (6) {
// ["endpoints"]=>
// string(19) "/processwire/fluency/api/"
@@ -635,9 +718,23 @@ $endpoints = $fluency->getApiEndpoints();
// }
```
-## Known Limitations
+## Known Issues
+
+- The browser plugin for Grammarly may conflict with Fluency and is a known issue for many web apps. If you encounter issues, the solution is to either disable Grammarly while using Fluency in the ProcessWire admin, or log into the admin in a private browser window where Grammarly may not be running. Instructions provided by Grammarly [here](https://support.grammarly.com/hc/en-us/articles/115000091612-Turn-off-Grammarly-on-one-or-more-websites).
+
+## Contributing
+
+**Module Features**
+
+Feature suggestions are welcome. In fact, Fluency has been made better thanks to great feedback by the ProcessWire community. If you have a suggestion, [file an issue in the Fluency GitHub repository](https://github.com/SkyLundy/Fluency/issues).
+
+**Translation Engines**
+
+Fluency is modular in that it contains a framework for adding additional third party services as "Translation Engines". You can choose which Translation Engine you prefer and provide the credentials to connect via their API. As this project is open source, contributions for new third party services as Translation Engines are welcome. If you would like to request a new third party service, [file an issue in the Fluency GitHub repository](https://github.com/SkyLundy/Fluency/issues)
+
+If you'd like to develop one for yourself, feel free to fork Fluency, build a new Translation Engine, and create a pull request.
-- The browser plugin for Grammarly may conflict with Fluency and is a known issue for many web apps. If you encounter issues, the solution is to either disable Grammarly while using Fluency in the ProcessWire admin, or log into the admin in a private browser window where Grammarly may not be running. Consider disabling Grammarly for the website you are editing content on when in the admin. Instructions provided by Grammarly [here](https://support.grammarly.com/hc/en-us/articles/115000091612-Turn-off-Grammarly-on-one-or-more-websites).
+Developer documentation for integrating third party translation services as Translation Engines is located in `Fluency/app/Engines/DEVDOC.md`. _Documentation is a work in progress_. If you need help or more information, feel free to send me a PM on the [ProcessWire forum](https://processwire.com/talk/).
## Cost
diff --git a/app/FluencyMarkup.php b/app/FluencyMarkup.php
index 852bc6b..519af32 100644
--- a/app/FluencyMarkup.php
+++ b/app/FluencyMarkup.php
@@ -21,7 +21,7 @@ public static function concat(string ...$elements): string {
/**
* Render a headline tag
*/
- public static function h(int $level, string $content, string|array $classes = []): string {
+ public static function h(int $level, string $content, string|array|null $classes = []): string {
$markup =<<
%{CONTENT}
EOT;
@@ -36,7 +36,7 @@ public static function h(int $level, string $content, string|array $classes = []
/**
* Render one or more tags.
*/
- public static function p(string|array $values, string|array $classes = []): string {
+ public static function p(string|array $values, string|array|null $classes = []): string {
$markup =<<%{CONTENT}
EOT;
@@ -172,7 +172,7 @@ public static function ol(
/**
* Render one or more elements
*/
- public static function li(mixed $content, string|array $classes = []): string {
+ public static function li(mixed $content, string|array|null $classes = []): string {
$markup =<<%{CONTENT}
EOT;
@@ -190,7 +190,7 @@ public static function li(mixed $content, string|array $classes = []): string {
/**
* Render one or more elements
*/
- public static function span(mixed $content, string|array $classes = []): string {
+ public static function span(mixed $content, string|array|null $classes = []): string {
$markup =<<%{CONTENT}
EOT;
@@ -266,7 +266,6 @@ class="ft-language-select %{CLASS}"
});
}
-
return self::render($markup, [
'ID' => $id,
'OPTION_ELS' => $options,
@@ -327,6 +326,11 @@ private static function render(string $markup, ?array $variables = []): ?string
$emptyVars = array_fill_keys($templatePlaceholders[0], '');
$variables = array_merge($emptyVars, $variables);
- return strtr($markup, $variables);
+ $markup = strtr($markup, $variables);
+
+ // Remove all empty attributes
+ $markup = preg_replace('/\s([a-zA-Z\-]+)=""/', '', $markup);
+
+ return $markup;
}
}