Merge pull request #47349 from nextcloud/feat/31420/bidi-backend-support

Add bidirectional text support - Backend
pull/48311/head
Joas Schilling 2024-09-24 11:27:42 +07:00 committed by GitHub
commit 12ed773b58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 87 additions and 2 deletions

@ -16,6 +16,35 @@ $untranslatedApps = [
'testing',
];
// Next line only looks messed up, but it works. Don't touch it!
$rtlCharacters = [
'\x{061C}', // ARABIC LETTER MARK
'\x{0623}', // ARABIC LETTER ALEF WITH HAMZA ABOVE
'\x{200E}', // LEFT-TO-RIGHT MARK
'\x{200F}', // RIGHT-TO-LEFT MARK
'\x{202A}', // LEFT-TO-RIGHT EMBEDDING
'\x{202B}', // RIGHT-TO-LEFT EMBEDDING
'\x{202C}', // POP DIRECTIONAL FORMATTING
'\x{202D}', // LEFT-TO-RIGHT OVERRIDE
'\x{202E}', // RIGHT-TO-LEFT OVERRIDE
'\x{2066}', // LEFT-TO-RIGHT ISOLATE
'\x{2067}', // RIGHT-TO-LEFT ISOLATE
'\x{2068}', // FIRST STRONG ISOLATE
'\x{2069}', // POP DIRECTIONAL ISOLATE
'\x{206C}', // INHIBIT ARABIC FORM SHAPING
'\x{206D}', // ACTIVATE ARABIC FORM SHAPING
];
$rtlLanguages = [
'ar', // Arabic
'fa', // Persian
'he', // Hebrew
'ps', // Pashto,
'ug', // 'Uyghurche / Uyghur
'ur_PK', // Urdu
'uz', // Uzbek Afghan
];
$valid = 0;
$errors = [];
$apps = new \DirectoryIterator(__DIR__ . '/../apps');
@ -46,6 +75,12 @@ foreach ($directories as $dir) {
}
$content = file_get_contents($file->getPathname());
$language = pathinfo($file->getFilename(), PATHINFO_FILENAME);
if (!in_array($language, $rtlLanguages, true) && preg_match('/[' . implode('', $rtlCharacters) . ']/u', $content)) {
$errors[] = $file->getPathname() . "\n" . ' ' . 'Contains a RTL limited character in the translations.' . "\n";
}
$json = json_decode($content, true);
$translations = json_encode($json['translations']);

@ -51,7 +51,7 @@ p($theme->getTitle());
<?php emit_script_loading_tags($_); ?>
<?php print_unescaped($_['headers']); ?>
</head>
<body id="<?php p($_['bodyid']);?>" <?php foreach ($_['enabledThemes'] as $themeId) {
<body dir="<?php p($_['direction']); ?>" id="<?php p($_['bodyid']);?>" <?php foreach ($_['enabledThemes'] as $themeId) {
p("data-theme-$themeId ");
}?> data-themes=<?php p(join(',', $_['enabledThemes'])) ?>>
<?php include 'layout.noscript.warning.php'; ?>

@ -58,6 +58,19 @@ class Factory implements IFactory {
'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko'
];
/**
* Keep in sync with `build/translation-checker.php`
*/
public const RTL_LANGUAGES = [
'ar', // Arabic
'fa', // Persian
'he', // Hebrew
'ps', // Pashto,
'ug', // 'Uyghurche / Uyghur
'ur_PK', // Urdu
'uz', // Uzbek Afghan
];
private ICache $cache;
public function __construct(
@ -364,6 +377,14 @@ class Factory implements IFactory {
return in_array($lang, $languages);
}
public function getLanguageDirection(string $language): string {
if (in_array($language, self::RTL_LANGUAGES, true)) {
return 'rtl';
}
return 'ltr';
}
public function getLanguageIterator(?IUser $user = null): ILanguageIterator {
$user = $user ?? $this->userSession->getUser();
if ($user === null) {

@ -193,13 +193,15 @@ class TemplateLayout extends \OC_Template {
} else {
parent::__construct('core', 'layout.base');
}
// Send the language and the locale to our layouts
// Send the language, locale, and direction to our layouts
$lang = \OC::$server->get(IFactory::class)->findLanguage();
$locale = \OC::$server->get(IFactory::class)->findLocale($lang);
$direction = \OC::$server->getL10NFactory()->getLanguageDirection($lang);
$lang = str_replace('_', '-', $lang);
$this->assign('language', $lang);
$this->assign('locale', $locale);
$this->assign('direction', $direction);
if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
if (empty(self::$versionHash)) {

@ -103,6 +103,15 @@ interface IFactory {
*/
public function localeExists($locale);
/**
* Return the language direction
*
* @param string $language
* @return 'ltr'|'rtl'
* @since 31.0.0
*/
public function getLanguageDirection(string $language): string;
/**
* iterate through language settings (if provided) in this order:
* 1. returns the forced language or:

@ -775,4 +775,22 @@ class FactoryTest extends TestCase {
$iterator = $factory->getLanguageIterator($iUserMock);
self::assertInstanceOf(ILanguageIterator::class, $iterator);
}
public static function dataGetLanguageDirection(): array {
return [
['en', 'ltr'],
['de', 'ltr'],
['fa', 'rtl'],
['ar', 'rtl']
];
}
/**
* @dataProvider dataGetLanguageDirection
*/
public function testGetLanguageDirection(string $language, string $expectedDirection) {
$factory = $this->getFactory();
self::assertEquals($expectedDirection, $factory->getLanguageDirection($language));
}
}